From 75765f656ebae28dbbc6f93d36a1e851473aeec9 Mon Sep 17 00:00:00 2001 From: Ruby Lazuli Date: Sat, 22 May 2021 08:22:42 -0500 Subject: [PATCH 001/401] Allow `--edition 2021` to be passed to rustfmt This was added to Configurations.md in #4618, but the option wasn't actually made available. This should let people who are using Rust 2021 on nightly rustc run `cargo fmt` again. --- src/bin/main.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bin/main.rs b/src/bin/main.rs index 56b07222212f..92fe8b9c5143 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -689,6 +689,7 @@ fn edition_from_edition_str(edition_str: &str) -> Result { match edition_str { "2015" => Ok(Edition::Edition2015), "2018" => Ok(Edition::Edition2018), + "2021" => Ok(Edition::Edition2021), _ => Err(format_err!("Invalid value for `--edition`")), } } From 1ca3798d2c961c43b02e312e8aa0e4bfa3f1b37e Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Sun, 9 May 2021 20:01:24 -0700 Subject: [PATCH 002/401] Improve pasta copyability of `merge_imports` deprecation message Add double quotes around `Crate` so that it can be copied directly into a `Cargo.toml` file --- src/config/config_type.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/config_type.rs b/src/config/config_type.rs index 2f567b255210..7fc4486ddcd3 100644 --- a/src/config/config_type.rs +++ b/src/config/config_type.rs @@ -409,7 +409,7 @@ macro_rules! create_config { if self.was_set().merge_imports() { eprintln!( "Warning: the `merge_imports` option is deprecated. \ - Use `imports_granularity=Crate` instead" + Use `imports_granularity=\"Crate\"` instead" ); if !self.was_set().imports_granularity() { self.imports_granularity.2 = if self.merge_imports() { From 486e774fbfbbe2be98cdd2ebdbfc3e8b92fc2a95 Mon Sep 17 00:00:00 2001 From: Michael Murphy Date: Fri, 11 Jun 2021 02:39:28 +0100 Subject: [PATCH 003/401] Adjusting help message (#4865) On stable, running with `--help|-h` shows information about `file-lines` which is a nightly-only option. This commit removes all mention of `file-lines` from the help message on stable. There is room for improvement here; perhaps a new struct called, e.g., `StableOptions` could be added to complement the existing `GetOptsOptions` struct. `StableOptions` could have a field for each field in `GetOptsOptions`, with each field's value being a `bool` that specifies whether or not the option exists on stable. Or is this adding too much complexity? --- src/bin/main.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/bin/main.rs b/src/bin/main.rs index 92fe8b9c5143..4b4aa42d9359 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -178,12 +178,15 @@ fn make_opts() -> Options { opts.optflag("v", "verbose", "Print verbose output"); opts.optflag("q", "quiet", "Print less output"); opts.optflag("V", "version", "Show version information"); - opts.optflagopt( - "h", - "help", - "Show this message or help about a specific topic: `config` or `file-lines`", - "=TOPIC", - ); + let help_topics = if is_nightly { + "`config` or `file-lines`" + } else { + "`config`" + }; + let mut help_topic_msg = "Show this message or help about a specific topic: ".to_owned(); + help_topic_msg.push_str(help_topics); + + opts.optflagopt("h", "help", &help_topic_msg, "=TOPIC"); opts } @@ -437,7 +440,7 @@ fn determine_operation(matches: &Matches) -> Result { return Ok(Operation::Help(HelpOp::None)); } else if topic == Some("config".to_owned()) { return Ok(Operation::Help(HelpOp::Config)); - } else if topic == Some("file-lines".to_owned()) { + } else if topic == Some("file-lines".to_owned()) && is_nightly() { return Ok(Operation::Help(HelpOp::FileLines)); } else { return Err(OperationError::UnknownHelpTopic(topic.unwrap())); From e634a6f9a8ad9435f2d9f6bbfb6a8eb018ee8e3f Mon Sep 17 00:00:00 2001 From: Michael Murphy Date: Wed, 16 Jun 2021 04:33:34 +0100 Subject: [PATCH 004/401] Updating outdated links (#4869) * Updating outdated links Updating the links to the docs and source code for `ast.rs`. Seems like it was moved to a new crate at some point. * Updating more outdated links This time, the links to the `fmt-rfcs` repository, which is now owned by `rust-dev-tools` (although GitHub was redirecting anyway). * Update Contributing.md Co-authored-by: Caleb Cartwright Co-authored-by: Caleb Cartwright --- Contributing.md | 4 ++-- README.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Contributing.md b/Contributing.md index 1b77dad11f0f..a108195beb9a 100644 --- a/Contributing.md +++ b/Contributing.md @@ -138,8 +138,8 @@ format. There are different nodes for every kind of item and expression in Rust. For more details see the source code in the compiler - -[ast.rs](https://dxr.mozilla.org/rust/source/src/libsyntax/ast.rs) - and/or the -[docs](https://doc.rust-lang.org/nightly/nightly-rustc/syntax/ast/index.html). +[ast.rs](https://github.com/rust-lang/rust/blob/master/compiler/rustc_ast/src/ast.rs) - and/or the +[docs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/index.html). Many nodes in the AST (but not all, annoyingly) have a `Span`. A `Span` is a range in the source code, it can easily be converted to a snippet of source diff --git a/README.md b/README.md index 500a9f9a37c8..9c7a1c4bc341 100644 --- a/README.md +++ b/README.md @@ -230,5 +230,5 @@ Apache License (Version 2.0). See [LICENSE-APACHE](LICENSE-APACHE) and [LICENSE-MIT](LICENSE-MIT) for details. [rust]: https://github.com/rust-lang/rust -[fmt rfcs]: https://github.com/rust-lang-nursery/fmt-rfcs -[style guide]: https://github.com/rust-lang-nursery/fmt-rfcs/blob/master/guide/guide.md +[fmt rfcs]: https://github.com/rust-dev-tools/fmt-rfcs +[style guide]: https://github.com/rust-dev-tools/fmt-rfcs/blob/master/guide/guide.md From b305d62e5b9e08c6f4540de0a349fbf6da3dc0e4 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Thu, 24 Jun 2021 18:22:28 -0500 Subject: [PATCH 005/401] fix: correct arm leading pipe check (#4880) In the event a pattern starts with a leading pipe the pattern span will contain, and begin with, the pipe. This updates the process to see if a match arm contains a leading pipe by leveraging this recent(ish) change to the patterns in the AST, and avoids an indexing bug that occurs when a pattern starts with a non-ascii char in the old implementation. --- src/matches.rs | 7 ++++--- .../configs/match_arm_leading_pipes/preserve.rs | 8 ++++++++ .../configs/match_arm_leading_pipes/preserve.rs | 8 ++++++++ tests/target/issue_4868.rs | 17 +++++++++++++++++ 4 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 tests/target/issue_4868.rs diff --git a/src/matches.rs b/src/matches.rs index f33fedce92da..140ec226c02e 100644 --- a/src/matches.rs +++ b/src/matches.rs @@ -19,7 +19,7 @@ use crate::source_map::SpanUtils; use crate::spanned::Spanned; use crate::utils::{ contains_skip, extra_offset, first_line_width, inner_attributes, last_line_extendable, mk_sp, - mk_sp_lo_plus_one, semicolon_for_expr, trimmed_last_line_width, unicode_str_width, + semicolon_for_expr, trimmed_last_line_width, unicode_str_width, }; /// A simple wrapper type against `ast::Arm`. Used inside `write_list()`. @@ -167,8 +167,9 @@ fn collect_beginning_verts( arms.iter() .map(|a| { context - .snippet_provider - .opt_span_before(mk_sp_lo_plus_one(a.pat.span.lo()), "|") + .snippet(a.pat.span) + .starts_with("|") + .then(|| a.pat.span().lo()) }) .collect() } diff --git a/tests/source/configs/match_arm_leading_pipes/preserve.rs b/tests/source/configs/match_arm_leading_pipes/preserve.rs index ea303e857def..5486877bde19 100644 --- a/tests/source/configs/match_arm_leading_pipes/preserve.rs +++ b/tests/source/configs/match_arm_leading_pipes/preserve.rs @@ -26,3 +26,11 @@ fn bar() { _ => {} } } + +fn f(x: NonAscii) -> bool { + match x { + // foo + | Éfgh => true, + _ => false, + } +} \ No newline at end of file diff --git a/tests/target/configs/match_arm_leading_pipes/preserve.rs b/tests/target/configs/match_arm_leading_pipes/preserve.rs index 2beb1f5d8133..4775575842ab 100644 --- a/tests/target/configs/match_arm_leading_pipes/preserve.rs +++ b/tests/target/configs/match_arm_leading_pipes/preserve.rs @@ -25,3 +25,11 @@ fn bar() { _ => {} } } + +fn f(x: NonAscii) -> bool { + match x { + // foo + | Éfgh => true, + _ => false, + } +} diff --git a/tests/target/issue_4868.rs b/tests/target/issue_4868.rs new file mode 100644 index 000000000000..763a82c3231f --- /dev/null +++ b/tests/target/issue_4868.rs @@ -0,0 +1,17 @@ +enum NonAscii { + Abcd, + Éfgh, +} + +use NonAscii::*; + +fn f(x: NonAscii) -> bool { + match x { + Éfgh => true, + _ => false, + } +} + +fn main() { + dbg!(f(Abcd)); +} From 19733f19f18e2f2a1510667c078c7fc7739b3c54 Mon Sep 17 00:00:00 2001 From: Jethro Beekman Date: Fri, 25 Jun 2021 16:17:43 +0200 Subject: [PATCH 006/401] Change line endings from CRLF to LF --- appveyor.yml | 110 +++++++++++++++++++++++++-------------------------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 7bfe696009fa..5ac99fd71f8f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,55 +1,55 @@ -# This is based on https://github.com/japaric/rust-everywhere/blob/master/appveyor.yml -# and modified (mainly removal of deployment) to suit rustfmt. - -environment: - global: - PROJECT_NAME: rustfmt - matrix: - # Stable channel - # - TARGET: i686-pc-windows-gnu - # CHANNEL: stable - # - TARGET: i686-pc-windows-msvc - # CHANNEL: stable - # - TARGET: x86_64-pc-windows-gnu - # CHANNEL: stable - # - TARGET: x86_64-pc-windows-msvc - # CHANNEL: stable - # Beta channel - # - TARGET: i686-pc-windows-gnu - # CHANNEL: beta - # - TARGET: i686-pc-windows-msvc - # CHANNEL: beta - # - TARGET: x86_64-pc-windows-gnu - # CHANNEL: beta - # - TARGET: x86_64-pc-windows-msvc - # CHANNEL: beta - # Nightly channel - - TARGET: i686-pc-windows-gnu - CHANNEL: nightly - - TARGET: i686-pc-windows-msvc - CHANNEL: nightly - - TARGET: x86_64-pc-windows-gnu - CHANNEL: nightly - - TARGET: x86_64-pc-windows-msvc - CHANNEL: nightly - -# Install Rust and Cargo -# (Based on from https://github.com/rust-lang/libc/blob/master/appveyor.yml) -install: - - appveyor-retry appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe - - if "%TARGET%" == "i686-pc-windows-gnu" set PATH=%PATH%;C:\msys64\mingw32\bin - - if "%TARGET%" == "x86_64-pc-windows-gnu" set PATH=%PATH%;C:\msys64\mingw64\bin - - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin - - rustup-init.exe --default-host %TARGET% --default-toolchain %CHANNEL% -y - - rustc -Vv - - cargo -V - -# ??? -build: false - -test_script: - - set CFG_RELEASE_CHANNEL=nightly - - set CFG_RELEASE=nightly - - cargo build --verbose - - cargo test - - cargo test -- --ignored +# This is based on https://github.com/japaric/rust-everywhere/blob/master/appveyor.yml +# and modified (mainly removal of deployment) to suit rustfmt. + +environment: + global: + PROJECT_NAME: rustfmt + matrix: + # Stable channel + # - TARGET: i686-pc-windows-gnu + # CHANNEL: stable + # - TARGET: i686-pc-windows-msvc + # CHANNEL: stable + # - TARGET: x86_64-pc-windows-gnu + # CHANNEL: stable + # - TARGET: x86_64-pc-windows-msvc + # CHANNEL: stable + # Beta channel + # - TARGET: i686-pc-windows-gnu + # CHANNEL: beta + # - TARGET: i686-pc-windows-msvc + # CHANNEL: beta + # - TARGET: x86_64-pc-windows-gnu + # CHANNEL: beta + # - TARGET: x86_64-pc-windows-msvc + # CHANNEL: beta + # Nightly channel + - TARGET: i686-pc-windows-gnu + CHANNEL: nightly + - TARGET: i686-pc-windows-msvc + CHANNEL: nightly + - TARGET: x86_64-pc-windows-gnu + CHANNEL: nightly + - TARGET: x86_64-pc-windows-msvc + CHANNEL: nightly + +# Install Rust and Cargo +# (Based on from https://github.com/rust-lang/libc/blob/master/appveyor.yml) +install: + - appveyor-retry appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe + - if "%TARGET%" == "i686-pc-windows-gnu" set PATH=%PATH%;C:\msys64\mingw32\bin + - if "%TARGET%" == "x86_64-pc-windows-gnu" set PATH=%PATH%;C:\msys64\mingw64\bin + - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin + - rustup-init.exe --default-host %TARGET% --default-toolchain %CHANNEL% -y + - rustc -Vv + - cargo -V + +# ??? +build: false + +test_script: + - set CFG_RELEASE_CHANNEL=nightly + - set CFG_RELEASE=nightly + - cargo build --verbose + - cargo test + - cargo test -- --ignored From 2cf280ed1ba84001aa4e14152ae37cea18ebcb1c Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Sun, 4 Jul 2021 22:26:08 -0500 Subject: [PATCH 007/401] docs: clarify match_arm_blocks config documentation --- Configurations.md | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/Configurations.md b/Configurations.md index 9daa70653797..d2e5613eba96 100644 --- a/Configurations.md +++ b/Configurations.md @@ -1475,7 +1475,9 @@ Copyright 2018 The Rust Project Developers.`, etc.: ## `match_arm_blocks` -Wrap the body of arms in blocks when it does not fit on the same line with the pattern of arms +Controls whether arm bodies are wrapped in cases where the first line of the body cannot fit on the same line as the `=>` operator. + +The Style Guide requires that bodies are block wrapped by default if a line break is required after the `=>`, but this option can be used to disable that behavior to prevent wrapping arm bodies in that event, so long as the body does not contain multiple statements nor line comments. - **Default value**: `true` - **Possible values**: `true`, `false` @@ -1486,10 +1488,16 @@ Wrap the body of arms in blocks when it does not fit on the same line with the p ```rust fn main() { match lorem { - true => { + ipsum => { foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo(x) } - false => println!("{}", sit), + dolor => println!("{}", sit), + sit => foo( + "foooooooooooooooooooooooo", + "baaaaaaaaaaaaaaaaaaaaaaaarr", + "baaaaaaaaaaaaaaaaaaaazzzzzzzzzzzzz", + "qqqqqqqqquuuuuuuuuuuuuuuuuuuuuuuuuuxxx", + ), } } ``` @@ -1499,9 +1507,15 @@ fn main() { ```rust fn main() { match lorem { - true => + lorem => foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo(x), - false => println!("{}", sit), + ipsum => println!("{}", sit), + sit => foo( + "foooooooooooooooooooooooo", + "baaaaaaaaaaaaaaaaaaaaaaaarr", + "baaaaaaaaaaaaaaaaaaaazzzzzzzzzzzzz", + "qqqqqqqqquuuuuuuuuuuuuuuuuuuuuuuuuuxxx", + ), } } ``` From 4c2959fb12a6bd083003ec4371126211402e265d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sun, 18 Jul 2021 10:44:39 +0200 Subject: [PATCH 008/401] fix a bunch of clippy warnings clippy::bind_instead_of_map clippy::branches_sharing_code clippy::collapsible_match clippy::inconsistent_struct_constructor clippy::int_plus_one clippy::iter_count clippy::iter_nth_zero clippy::manual_range_contains clippy::match_like_matches_macro clippy::needless::collect clippy::needless_question_mark clippy::needless_return clippy::op_ref clippy::option_as_ref_deref clippy::ptr_arg clippy::redundant_clone clippy::redundant_closure clippy::redundant_static_lifetimes clippy::search_is_some clippy::#single_char_add_str clippy::single_char_pattern clippy::single_component_path_imports clippy::single_match clippy::skip_while_next clippy::unnecessary_lazy_evaluations clippy::unnecessary_unwrap clippy::useless_conversion clippy::useless_format --- src/attr.rs | 2 +- src/cargo-fmt/main.rs | 4 ++-- src/chains.rs | 5 +---- src/closures.rs | 15 +++++-------- src/comment.rs | 21 ++++++++---------- src/config/file_lines.rs | 4 ++-- src/config/license.rs | 1 - src/emitter/diff.rs | 2 +- src/expr.rs | 30 +++++++++---------------- src/formatting/newline_style.rs | 2 +- src/imports.rs | 30 ++++++++++--------------- src/issues.rs | 14 +++--------- src/items.rs | 26 +++++++++------------- src/lib.rs | 6 +---- src/lists.rs | 11 ++++------ src/macros.rs | 39 +++++++++++++++------------------ src/missed_spans.rs | 8 +++---- src/overflow.rs | 28 ++++++++++------------- src/patterns.rs | 25 ++++++++++----------- src/rustfmt_diff.rs | 7 ++---- src/skip.rs | 6 ++--- src/source_file.rs | 2 +- src/string.rs | 4 ++-- src/syntux/parser.rs | 5 ++--- src/types.rs | 15 ++++--------- src/utils.rs | 5 ++--- src/visitor.rs | 18 +++++++-------- 27 files changed, 130 insertions(+), 205 deletions(-) diff --git a/src/attr.rs b/src/attr.rs index c5ffb074ba55..315eb10a9dbc 100644 --- a/src/attr.rs +++ b/src/attr.rs @@ -183,7 +183,7 @@ fn format_derive( } else if let SeparatorTactic::Always = context.config.trailing_comma() { // Retain the trailing comma. result.push_str(&item_str); - } else if item_str.ends_with(",") { + } else if item_str.ends_with(',') { // Remove the trailing comma. result.push_str(&item_str[..item_str.len() - 1]); } else { diff --git a/src/cargo-fmt/main.rs b/src/cargo-fmt/main.rs index 9062a2952ec1..ba693e852ffa 100644 --- a/src/cargo-fmt/main.rs +++ b/src/cargo-fmt/main.rs @@ -405,8 +405,8 @@ fn get_targets_recursive( .packages .iter() .find(|p| p.name == dependency.name && p.source.is_none()); - let manifest_path = if dependency_package.is_some() { - PathBuf::from(&dependency_package.unwrap().manifest_path) + let manifest_path = if let Some(dep_pkg) = dependency_package { + PathBuf::from(&dep_pkg.manifest_path) } else { let mut package_manifest_path = PathBuf::from(&package.manifest_path); package_manifest_path.pop(); diff --git a/src/chains.rs b/src/chains.rs index 8053f0e8fecc..614638ea2abf 100644 --- a/src/chains.rs +++ b/src/chains.rs @@ -231,10 +231,7 @@ impl ChainItem { } fn is_comment(&self) -> bool { - match self.kind { - ChainItemKind::Comment(..) => true, - _ => false, - } + matches!(self.kind, ChainItemKind::Comment(..)) } fn rewrite_method_call( diff --git a/src/closures.rs b/src/closures.rs index 3d65077ddc20..c9d46aef294a 100644 --- a/src/closures.rs +++ b/src/closures.rs @@ -336,7 +336,7 @@ pub(crate) fn rewrite_last_closure( // We force to use block for the body of the closure for certain kinds of expressions. if is_block_closure_forced(context, body) { - return rewrite_closure_with_block(body, &prefix, context, body_shape).and_then( + return rewrite_closure_with_block(body, &prefix, context, body_shape).map( |body_str| { match fn_decl.output { ast::FnRetTy::Default(..) if body_str.lines().count() <= 7 => { @@ -344,15 +344,15 @@ pub(crate) fn rewrite_last_closure( // closure. However, if the closure has a return type, then we must // keep the blocks. match rewrite_closure_expr(body, &prefix, context, shape) { - Some(ref single_line_body_str) + Some(single_line_body_str) if !single_line_body_str.contains('\n') => { - Some(single_line_body_str.clone()) + single_line_body_str } - _ => Some(body_str), + _ => body_str, } } - _ => Some(body_str), + _ => body_str, } }, ); @@ -377,10 +377,7 @@ pub(crate) fn rewrite_last_closure( pub(crate) fn args_have_many_closure(args: &[OverflowableItem<'_>]) -> bool { args.iter() .filter_map(OverflowableItem::to_expr) - .filter(|expr| match expr.kind { - ast::ExprKind::Closure(..) => true, - _ => false, - }) + .filter(|expr| matches!(expr.kind, ast::ExprKind::Closure(..))) .count() > 1 } diff --git a/src/comment.rs b/src/comment.rs index c71302fdd182..0f8118a408ec 100644 --- a/src/comment.rs +++ b/src/comment.rs @@ -67,10 +67,7 @@ impl<'a> CommentStyle<'a> { /// Returns `true` if the commenting style is for documentation. pub(crate) fn is_doc_comment(&self) -> bool { - match *self { - CommentStyle::TripleSlash | CommentStyle::Doc => true, - _ => false, - } + matches!(*self, CommentStyle::TripleSlash | CommentStyle::Doc) } pub(crate) fn opener(&self) -> &'a str { @@ -689,8 +686,8 @@ impl<'a> CommentRewrite<'a> { self.code_block_attr = None; self.item_block = None; - if line.starts_with("```") { - self.code_block_attr = Some(CodeBlockAttribute::new(&line[3..])) + if let Some(stripped) = line.strip_prefix("```") { + self.code_block_attr = Some(CodeBlockAttribute::new(stripped)) } else if self.fmt.config.wrap_comments() && ItemizedBlock::is_itemized_line(&line) { let ib = ItemizedBlock::new(&line); self.item_block = Some(ib); @@ -948,8 +945,8 @@ fn left_trim_comment_line<'a>(line: &'a str, style: &CommentStyle<'_>) -> (&'a s { (&line[4..], true) } else if let CommentStyle::Custom(opener) = *style { - if line.starts_with(opener) { - (&line[opener.len()..], true) + if let Some(ref stripped) = line.strip_prefix(opener) { + (stripped, true) } else { (&line[opener.trim_end().len()..], false) } @@ -968,8 +965,8 @@ fn left_trim_comment_line<'a>(line: &'a str, style: &CommentStyle<'_>) -> (&'a s || line.starts_with("**") { (&line[2..], line.chars().nth(1).unwrap() == ' ') - } else if line.starts_with('*') { - (&line[1..], false) + } else if let Some(stripped) = line.strip_prefix('*') { + (stripped, false) } else { (line, line.starts_with(' ')) } @@ -1682,8 +1679,8 @@ impl<'a> Iterator for CommentReducer<'a> { fn remove_comment_header(comment: &str) -> &str { if comment.starts_with("///") || comment.starts_with("//!") { &comment[3..] - } else if comment.starts_with("//") { - &comment[2..] + } else if let Some(ref stripped) = comment.strip_prefix("//") { + stripped } else if (comment.starts_with("/**") && !comment.starts_with("/**/")) || comment.starts_with("/*!") { diff --git a/src/config/file_lines.rs b/src/config/file_lines.rs index 22dd091cb510..4b799780d85d 100644 --- a/src/config/file_lines.rs +++ b/src/config/file_lines.rs @@ -305,7 +305,7 @@ impl str::FromStr for FileLines { let mut m = HashMap::new(); for js in v { let (s, r) = JsonSpan::into_tuple(js)?; - m.entry(s).or_insert_with(|| vec![]).push(r); + m.entry(s).or_insert_with(Vec::new).push(r); } Ok(FileLines::from_ranges(m)) } @@ -322,7 +322,7 @@ impl JsonSpan { fn into_tuple(self) -> Result<(FileName, Range), FileLinesError> { let (lo, hi) = self.range; let canonical = canonicalize_path_string(&self.file) - .ok_or_else(|| FileLinesError::CannotCanonicalize(self.file))?; + .ok_or(FileLinesError::CannotCanonicalize(self.file))?; Ok((canonical, Range::new(lo, hi))) } } diff --git a/src/config/license.rs b/src/config/license.rs index 121a1b1c151f..c7feb502ea91 100644 --- a/src/config/license.rs +++ b/src/config/license.rs @@ -3,7 +3,6 @@ use std::fs::File; use std::io; use std::io::Read; -use regex; use regex::Regex; #[derive(Debug)] diff --git a/src/emitter/diff.rs b/src/emitter/diff.rs index 9be4fb28f993..2fbbfedb566d 100644 --- a/src/emitter/diff.rs +++ b/src/emitter/diff.rs @@ -45,7 +45,7 @@ impl Emitter for DiffEmitter { return Ok(EmitterResult { has_diff: true }); } - return Ok(EmitterResult { has_diff }); + Ok(EmitterResult { has_diff }) } } diff --git a/src/expr.rs b/src/expr.rs index bca9f77f959e..6cfeb9977a96 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -263,15 +263,12 @@ pub(crate) fn format_expr( } fn needs_space_after_range(rhs: &ast::Expr) -> bool { - match rhs.kind { - // Don't format `.. ..` into `....`, which is invalid. - // - // This check is unnecessary for `lhs`, because a range - // starting from another range needs parentheses as `(x ..) ..` - // (`x .. ..` is a range from `x` to `..`). - ast::ExprKind::Range(None, _, _) => true, - _ => false, - } + // Don't format `.. ..` into `....`, which is invalid. + // + // This check is unnecessary for `lhs`, because a range + // starting from another range needs parentheses as `(x ..) ..` + // (`x .. ..` is a range from `x` to `..`). + matches!(rhs.kind, ast::ExprKind::Range(None, _, _)) } let default_sp_delim = |lhs: Option<&ast::Expr>, rhs: Option<&ast::Expr>| { @@ -531,7 +528,7 @@ pub(crate) fn rewrite_block_with_visitor( let inner_attrs = attrs.map(inner_attributes); let label_str = rewrite_label(label); - visitor.visit_block(block, inner_attrs.as_ref().map(|a| &**a), has_braces); + visitor.visit_block(block, inner_attrs.as_deref(), has_braces); let visitor_context = visitor.get_context(); context .skipped_range @@ -595,7 +592,7 @@ pub(crate) fn rewrite_cond( String::from("\n") + &shape.indent.block_only().to_string(context.config); control_flow .rewrite_cond(context, shape, &alt_block_sep) - .and_then(|rw| Some(rw.0)) + .map(|rw| rw.0) }), } } @@ -1157,18 +1154,11 @@ pub(crate) fn is_empty_block( } pub(crate) fn stmt_is_expr(stmt: &ast::Stmt) -> bool { - match stmt.kind { - ast::StmtKind::Expr(..) => true, - _ => false, - } + matches!(stmt.kind, ast::StmtKind::Expr(..)) } pub(crate) fn is_unsafe_block(block: &ast::Block) -> bool { - if let ast::BlockCheckMode::Unsafe(..) = block.rules { - true - } else { - false - } + matches!(block.rules, ast::BlockCheckMode::Unsafe(..)) } pub(crate) fn rewrite_literal( diff --git a/src/formatting/newline_style.rs b/src/formatting/newline_style.rs index ac6200949000..97c4fc16d6f5 100644 --- a/src/formatting/newline_style.rs +++ b/src/formatting/newline_style.rs @@ -77,7 +77,7 @@ fn convert_to_windows_newlines(formatted_text: &String) -> String { transformed } -fn convert_to_unix_newlines(formatted_text: &String) -> String { +fn convert_to_unix_newlines(formatted_text: &str) -> String { formatted_text.replace(WINDOWS_NEWLINE, UNIX_NEWLINE) } diff --git a/src/imports.rs b/src/imports.rs index 0f635fe1ccb3..64d78605f0c5 100644 --- a/src/imports.rs +++ b/src/imports.rs @@ -374,7 +374,7 @@ impl UseTree { UseTreeKind::Nested(ref list) => { // Extract comments between nested use items. // This needs to be done before sorting use items. - let items: Vec<_> = itemize_list( + let items = itemize_list( context.snippet_provider, list.iter().map(|(tree, _)| tree), "}", @@ -385,8 +385,8 @@ impl UseTree { context.snippet_provider.span_after(a.span, "{"), a.span.hi(), false, - ) - .collect(); + ); + // in case of a global path and the nested list starts at the root, // e.g., "::{foo, bar}" if a.prefix.segments.len() == 1 && leading_modsep { @@ -394,7 +394,7 @@ impl UseTree { } result.path.push(UseSegment::List( list.iter() - .zip(items.into_iter()) + .zip(items) .map(|(t, list_item)| { Self::from_ast(context, &t.0, Some(list_item), None, None, None) }) @@ -466,11 +466,8 @@ impl UseTree { // Normalise foo::self as bar -> foo as bar. if let UseSegment::Slf(_) = last { - match self.path.last() { - Some(UseSegment::Ident(_, None)) => { - aliased_self = true; - } - _ => {} + if let Some(UseSegment::Ident(_, None)) = self.path.last() { + aliased_self = true; } } @@ -572,9 +569,8 @@ impl UseTree { match self.path.clone().last().unwrap() { UseSegment::List(list) => { if list.len() == 1 && list[0].path.len() == 1 { - match list[0].path[0] { - UseSegment::Slf(..) => return vec![self], - _ => (), + if let UseSegment::Slf(..) = list[0].path[0] { + return vec![self]; }; } let prefix = &self.path[..self.path.len() - 1]; @@ -790,13 +786,9 @@ fn rewrite_nested_use_tree( } } let has_nested_list = use_tree_list.iter().any(|use_segment| { - use_segment - .path - .last() - .map_or(false, |last_segment| match last_segment { - UseSegment::List(..) => true, - _ => false, - }) + use_segment.path.last().map_or(false, |last_segment| { + matches!(last_segment, UseSegment::List(..)) + }) }); let remaining_width = if has_nested_list { diff --git a/src/issues.rs b/src/issues.rs index d369b75541ef..33fb5522aeae 100644 --- a/src/issues.rs +++ b/src/issues.rs @@ -126,11 +126,7 @@ impl BadIssueSeeker { return Seeking::Number { issue: Issue { issue_type: IssueType::Todo, - missing_number: if let ReportTactic::Unnumbered = self.report_todo { - true - } else { - false - }, + missing_number: matches!(self.report_todo, ReportTactic::Unnumbered), }, part: NumberPart::OpenParen, }; @@ -144,11 +140,7 @@ impl BadIssueSeeker { return Seeking::Number { issue: Issue { issue_type: IssueType::Fixme, - missing_number: if let ReportTactic::Unnumbered = self.report_fixme { - true - } else { - false - }, + missing_number: matches!(self.report_fixme, ReportTactic::Unnumbered), }, part: NumberPart::OpenParen, }; @@ -196,7 +188,7 @@ impl BadIssueSeeker { } } NumberPart::Number => { - if c >= '0' && c <= '9' { + if ('0'..='9').contains(&c) { part = NumberPart::CloseParen; } else { return IssueClassification::Bad(issue); diff --git a/src/items.rs b/src/items.rs index 420484c0ba11..0542358c6e7c 100644 --- a/src/items.rs +++ b/src/items.rs @@ -741,7 +741,7 @@ pub(crate) fn format_impl( // there is only one where-clause predicate // recover the suppressed comma in single line where_clause formatting if generics.where_clause.predicates.len() == 1 { - result.push_str(","); + result.push(','); } result.push_str(&format!("{}{{{}}}", sep, sep)); } else { @@ -1207,7 +1207,7 @@ impl<'a> Rewrite for TraitAliasBounds<'a> { let fits_single_line = !generic_bounds_str.contains('\n') && !where_str.contains('\n') - && generic_bounds_str.len() + where_str.len() + 1 <= shape.width; + && generic_bounds_str.len() + where_str.len() < shape.width; let space = if generic_bounds_str.is_empty() || where_str.is_empty() { Cow::from("") } else if fits_single_line { @@ -1236,8 +1236,8 @@ pub(crate) fn format_trait_alias( let lhs = format!("{}trait {} =", vis_str, generics_str); // 1 = ";" let trait_alias_bounds = TraitAliasBounds { - generics, generic_bounds, + generics, }; rewrite_assign_rhs(context, lhs, &trait_alias_bounds, shape.sub_width(1)?).map(|s| s + ";") } @@ -1993,7 +1993,7 @@ impl Rewrite for ast::Param { let num_attrs = self.attrs.len(); ( mk_sp(self.attrs[num_attrs - 1].span.hi(), self.pat.span.lo()), - param_attrs_result.contains("\n"), + param_attrs_result.contains('\n'), ) } else { (mk_sp(self.span.lo(), self.span.lo()), false) @@ -3265,22 +3265,16 @@ pub(crate) fn rewrite_extern_crate( /// Returns `true` for `mod foo;`, false for `mod foo { .. }`. pub(crate) fn is_mod_decl(item: &ast::Item) -> bool { - match item.kind { - ast::ItemKind::Mod(_, ast::ModKind::Loaded(_, ast::Inline::Yes, _)) => false, - _ => true, - } + !matches!( + item.kind, + ast::ItemKind::Mod(_, ast::ModKind::Loaded(_, ast::Inline::Yes, _)) + ) } pub(crate) fn is_use_item(item: &ast::Item) -> bool { - match item.kind { - ast::ItemKind::Use(_) => true, - _ => false, - } + matches!(item.kind, ast::ItemKind::Use(_)) } pub(crate) fn is_extern_crate(item: &ast::Item) -> bool { - match item.kind { - ast::ItemKind::ExternCrate(..) => true, - _ => false, - } + matches!(item.kind, ast::ItemKind::ExternCrate(..)) } diff --git a/src/lib.rs b/src/lib.rs index ce8a45eea653..eb314e63de36 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,7 +30,6 @@ use std::panic; use std::path::PathBuf; use std::rc::Rc; -use ignore; use rustc_ast::ast; use rustc_span::{symbol, DUMMY_SP}; use thiserror::Error; @@ -149,10 +148,7 @@ pub enum ErrorKind { impl ErrorKind { fn is_comment(&self) -> bool { - match self { - ErrorKind::LostComment => true, - _ => false, - } + matches!(self, ErrorKind::LostComment) } } diff --git a/src/lists.rs b/src/lists.rs index ccf8f784c045..73e886c55637 100644 --- a/src/lists.rs +++ b/src/lists.rs @@ -194,10 +194,7 @@ impl ListItem { // Returns `true` if the item causes something to be written. fn is_substantial(&self) -> bool { fn empty(s: &Option) -> bool { - match *s { - Some(ref s) if !s.is_empty() => false, - _ => true, - } + !matches!(*s, Some(ref s) if !s.is_empty()) } !(empty(&self.pre_comment) && empty(&self.item) && empty(&self.post_comment)) @@ -618,8 +615,8 @@ pub(crate) fn extract_post_comment( let post_snippet = post_snippet[..comment_end].trim(); let post_snippet_trimmed = if post_snippet.starts_with(|c| c == ',' || c == ':') { post_snippet[1..].trim_matches(white_space) - } else if post_snippet.starts_with(separator) { - post_snippet[separator.len()..].trim_matches(white_space) + } else if let Some(stripped) = post_snippet.strip_prefix(separator) { + stripped.trim_matches(white_space) } // not comment or over two lines else if post_snippet.ends_with(',') @@ -823,7 +820,7 @@ where pub(crate) fn total_item_width(item: &ListItem) -> usize { comment_len(item.pre_comment.as_ref().map(|x| &(*x)[..])) + comment_len(item.post_comment.as_ref().map(|x| &(*x)[..])) - + &item.item.as_ref().map_or(0, |s| unicode_str_width(&s)) + + item.item.as_ref().map_or(0, |s| unicode_str_width(&s)) } fn comment_len(comment: Option<&str>) -> usize { diff --git a/src/macros.rs b/src/macros.rs index bf4769b34aa8..6c5e32716c01 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -179,10 +179,10 @@ fn return_macro_parse_failure_fallback( .lines() .last() .map(|closing_line| { - closing_line.trim().chars().all(|ch| match ch { - '}' | ')' | ']' => true, - _ => false, - }) + closing_line + .trim() + .chars() + .all(|ch| matches!(ch, '}' | ')' | ']')) }) .unwrap_or(false); if is_like_block_indent_style { @@ -690,25 +690,22 @@ fn delim_token_to_str( impl MacroArgKind { fn starts_with_brace(&self) -> bool { - match *self { + matches!( + *self, MacroArgKind::Repeat(DelimToken::Brace, _, _, _) - | MacroArgKind::Delimited(DelimToken::Brace, _) => true, - _ => false, - } + | MacroArgKind::Delimited(DelimToken::Brace, _) + ) } fn starts_with_dollar(&self) -> bool { - match *self { - MacroArgKind::Repeat(..) | MacroArgKind::MetaVariable(..) => true, - _ => false, - } + matches!( + *self, + MacroArgKind::Repeat(..) | MacroArgKind::MetaVariable(..) + ) } fn ends_with_space(&self) -> bool { - match *self { - MacroArgKind::Separator(..) => true, - _ => false, - } + matches!(*self, MacroArgKind::Separator(..)) } fn has_meta_var(&self) -> bool { @@ -1162,10 +1159,10 @@ fn force_space_before(tok: &TokenKind) -> bool { } fn ident_like(tok: &Token) -> bool { - match tok.kind { - TokenKind::Ident(..) | TokenKind::Literal(..) | TokenKind::Lifetime(_) => true, - _ => false, - } + matches!( + tok.kind, + TokenKind::Ident(..) | TokenKind::Literal(..) | TokenKind::Lifetime(_) + ) } fn next_space(tok: &TokenKind) -> SpaceState { @@ -1399,7 +1396,7 @@ impl MacroBranch { // Undo our replacement of macro variables. // FIXME: this could be *much* more efficient. for (old, new) in &substs { - if old_body.find(new).is_some() { + if old_body.contains(new) { debug!("rewrite_macro_def: bailing matching variable: `{}`", new); return None; } diff --git a/src/missed_spans.rs b/src/missed_spans.rs index 17b11ed6cf49..263d840785a2 100644 --- a/src/missed_spans.rs +++ b/src/missed_spans.rs @@ -230,8 +230,7 @@ impl<'a> FmtVisitor<'a> { let last_char = big_snippet .chars() .rev() - .skip_while(|rev_c| [' ', '\t'].contains(rev_c)) - .next(); + .find(|rev_c| ![' ', '\t'].contains(rev_c)); let fix_indent = last_char.map_or(true, |rev_c| ['{', '\n'].contains(&rev_c)); let mut on_same_line = false; @@ -262,7 +261,7 @@ impl<'a> FmtVisitor<'a> { let comment_shape = Shape::legacy(comment_width, comment_indent); if on_same_line { - match subslice.find("\n") { + match subslice.find('\n') { None => { self.push_str(subslice); } @@ -299,8 +298,7 @@ impl<'a> FmtVisitor<'a> { match snippet[status.line_start..] .chars() // skip trailing whitespaces - .skip_while(|c| *c == ' ' || *c == '\t') - .next() + .find(|c| !(*c == ' ' || *c == '\t')) { Some('\n') | Some('\r') => { if !is_last_comment_block(subslice) { diff --git a/src/overflow.rs b/src/overflow.rs index d670b0a41e81..e32213467a51 100644 --- a/src/overflow.rs +++ b/src/overflow.rs @@ -126,21 +126,19 @@ impl<'a> OverflowableItem<'a> { OverflowableItem::MacroArg(MacroArg::Expr(expr)) => is_simple_expr(expr), OverflowableItem::NestedMetaItem(nested_meta_item) => match nested_meta_item { ast::NestedMetaItem::Literal(..) => true, - ast::NestedMetaItem::MetaItem(ref meta_item) => match meta_item.kind { - ast::MetaItemKind::Word => true, - _ => false, - }, + ast::NestedMetaItem::MetaItem(ref meta_item) => { + matches!(meta_item.kind, ast::MetaItemKind::Word) + } }, _ => false, } } pub(crate) fn is_expr(&self) -> bool { - match self { - OverflowableItem::Expr(..) => true, - OverflowableItem::MacroArg(MacroArg::Expr(..)) => true, - _ => false, - } + matches!( + self, + OverflowableItem::Expr(..) | OverflowableItem::MacroArg(MacroArg::Expr(..)) + ) } pub(crate) fn is_nested_call(&self) -> bool { @@ -154,10 +152,7 @@ impl<'a> OverflowableItem<'a> { pub(crate) fn to_expr(&self) -> Option<&'a ast::Expr> { match self { OverflowableItem::Expr(expr) => Some(expr), - OverflowableItem::MacroArg(macro_arg) => match macro_arg { - MacroArg::Expr(ref expr) => Some(expr), - _ => None, - }, + OverflowableItem::MacroArg(MacroArg::Expr(ref expr)) => Some(expr), _ => None, } } @@ -178,10 +173,9 @@ impl<'a> OverflowableItem<'a> { ast::NestedMetaItem::MetaItem(..) => true, } } - OverflowableItem::SegmentParam(seg) => match seg { - SegmentParam::Type(ty) => can_be_overflowed_type(context, ty, len), - _ => false, - }, + OverflowableItem::SegmentParam(SegmentParam::Type(ty)) => { + can_be_overflowed_type(context, ty, len) + } OverflowableItem::TuplePatField(pat) => can_be_overflowed_pat(context, pat, len), OverflowableItem::Ty(ty) => can_be_overflowed_type(context, ty, len), _ => false, diff --git a/src/patterns.rs b/src/patterns.rs index fa0ef260991d..062e9cef9bbd 100644 --- a/src/patterns.rs +++ b/src/patterns.rs @@ -238,7 +238,7 @@ impl Rewrite for Pat { if let Some(rw) = p.rewrite(context, shape) { rw } else { - format!("{}", context.snippet(p.span)) + context.snippet(p.span).to_string() } }) .collect(); @@ -310,23 +310,22 @@ fn rewrite_struct_pat( if fields_str.contains('\n') || fields_str.len() > one_line_width { // Add a missing trailing comma. if context.config.trailing_comma() == SeparatorTactic::Never { - fields_str.push_str(","); + fields_str.push(','); } - fields_str.push_str("\n"); + fields_str.push('\n'); fields_str.push_str(&nested_shape.indent.to_string(context.config)); - fields_str.push_str(".."); } else { if !fields_str.is_empty() { // there are preceding struct fields being matched on if tactic == DefinitiveListTactic::Vertical { // if the tactic is Vertical, write_list already added a trailing , - fields_str.push_str(" "); + fields_str.push(' '); } else { fields_str.push_str(", "); } } - fields_str.push_str(".."); } + fields_str.push_str(".."); } // ast::Pat doesn't have attrs so use &[] @@ -411,10 +410,7 @@ impl<'a> Spanned for TuplePatField<'a> { impl<'a> TuplePatField<'a> { fn is_dotdot(&self) -> bool { match self { - TuplePatField::Pat(pat) => match pat.kind { - ast::PatKind::Rest => true, - _ => false, - }, + TuplePatField::Pat(pat) => matches!(pat.kind, ast::PatKind::Rest), TuplePatField::Dotdot(_) => true, } } @@ -510,10 +506,11 @@ fn count_wildcard_suffix_len( ) .collect(); - for item in items.iter().rev().take_while(|i| match i.item { - Some(ref internal_string) if internal_string == "_" => true, - _ => false, - }) { + for item in items + .iter() + .rev() + .take_while(|i| matches!(i.item, Some(ref internal_string) if internal_string == "_")) + { suffix_len += 1; if item.has_comment() { diff --git a/src/rustfmt_diff.rs b/src/rustfmt_diff.rs index fc2c7d06e264..a394ce07398e 100644 --- a/src/rustfmt_diff.rs +++ b/src/rustfmt_diff.rs @@ -56,10 +56,7 @@ impl From> for ModifiedLines { let chunks = mismatches.into_iter().map(|mismatch| { let lines = mismatch.lines.iter(); let num_removed = lines - .filter(|line| match line { - DiffLine::Resulting(_) => true, - _ => false, - }) + .filter(|line| matches!(line, DiffLine::Resulting(_))) .count(); let new_lines = mismatch.lines.into_iter().filter_map(|line| match line { @@ -94,7 +91,7 @@ impl fmt::Display for ModifiedLines { "{} {} {}", chunk.line_number_orig, chunk.lines_removed, - chunk.lines.iter().count() + chunk.lines.len() )?; for line in &chunk.lines { diff --git a/src/skip.rs b/src/skip.rs index 6c500635a955..0fdc097efc23 100644 --- a/src/skip.rs +++ b/src/skip.rs @@ -32,8 +32,8 @@ impl SkipContext { } } -static RUSTFMT: &'static str = "rustfmt"; -static SKIP: &'static str = "skip"; +static RUSTFMT: &str = "rustfmt"; +static SKIP: &str = "skip"; /// Say if you're playing with `rustfmt`'s skip attribute pub(crate) fn is_skip_attr(segments: &[ast::PathSegment]) -> bool { @@ -46,7 +46,7 @@ pub(crate) fn is_skip_attr(segments: &[ast::PathSegment]) -> bool { segments[1].ident.to_string() == SKIP && ["macros", "attributes"] .iter() - .any(|&n| n == &pprust::path_segment_to_string(&segments[2])) + .any(|&n| n == pprust::path_segment_to_string(&segments[2])) } _ => false, } diff --git a/src/source_file.rs b/src/source_file.rs index 5a9a2cbd80c7..853336004d8b 100644 --- a/src/source_file.rs +++ b/src/source_file.rs @@ -18,7 +18,7 @@ use rustc_data_structures::sync::Lrc; // Append a newline to the end of each file. pub(crate) fn append_newline(s: &mut String) { - s.push_str("\n"); + s.push('\n'); } #[cfg(test)] diff --git a/src/string.rs b/src/string.rs index 080c4f177882..0cb9d817ca2d 100644 --- a/src/string.rs +++ b/src/string.rs @@ -57,7 +57,7 @@ impl<'a> StringFormat<'a> { /// This allows to fit more graphemes from the string on a line when /// SnippetState::EndWithLineFeed. fn max_width_without_indent(&self) -> Option { - Some(self.config.max_width().checked_sub(self.line_end.len())?) + self.config.max_width().checked_sub(self.line_end.len()) } } @@ -99,7 +99,7 @@ pub(crate) fn rewrite_string<'a>( if is_new_line(grapheme) { // take care of blank lines result = trim_end_but_line_feed(fmt.trim_end, result); - result.push_str("\n"); + result.push('\n'); if !is_bareline_ok && cur_start + i + 1 < graphemes.len() { result.push_str(&indent_without_newline); result.push_str(fmt.line_start); diff --git a/src/syntux/parser.rs b/src/syntux/parser.rs index 0b94749f3c6f..b5fe4335dd33 100644 --- a/src/syntux/parser.rs +++ b/src/syntux/parser.rs @@ -79,7 +79,7 @@ impl<'a> ParserBuilder<'a> { rustc_span::FileName::Custom("stdin".to_owned()), text, ) - .map_err(|db| Some(db)), + .map_err(Some), } } } @@ -196,8 +196,7 @@ impl<'a> Parser<'a> { mac: &'a ast::MacCall, ) -> Result, &'static str> { let token_stream = mac.args.inner_tokens(); - let mut parser = - rustc_parse::stream_to_parser(sess.inner(), token_stream.clone(), Some("")); + let mut parser = rustc_parse::stream_to_parser(sess.inner(), token_stream, Some("")); let mut items = vec![]; let mut process_if_cfg = true; diff --git a/src/types.rs b/src/types.rs index 974c0c5990c7..c6f89c310650 100644 --- a/src/types.rs +++ b/src/types.rs @@ -662,7 +662,7 @@ impl Rewrite for ast::Ty { let mut_str = format_mutability(mt.mutbl); let mut_len = mut_str.len(); let mut result = String::with_capacity(128); - result.push_str("&"); + result.push('&'); let ref_hi = context.snippet_provider.span_after(self.span(), "&"); let mut cmnt_lo = ref_hi; @@ -685,7 +685,7 @@ impl Rewrite for ast::Ty { } else { result.push_str(<_str); } - result.push_str(" "); + result.push(' '); cmnt_lo = lifetime.ident.span.hi(); } @@ -1048,11 +1048,7 @@ fn join_bounds_inner( true, ) .map(|v| (v, trailing_span, extendable)), - _ => Some(( - String::from(strs) + &trailing_str, - trailing_span, - extendable, - )), + _ => Some((strs + &trailing_str, trailing_span, extendable)), } }, )?; @@ -1089,10 +1085,7 @@ fn rewrite_lifetime_param( ) -> Option { let result = generic_params .iter() - .filter(|p| match p.kind { - ast::GenericParamKind::Lifetime => true, - _ => false, - }) + .filter(|p| matches!(p.kind, ast::GenericParamKind::Lifetime)) .map(|lt| lt.rewrite(context, shape)) .collect::>>()? .join(", "); diff --git a/src/utils.rs b/src/utils.rs index 614cda5f911c..06159a1b26e8 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -191,7 +191,7 @@ pub(crate) fn outer_attributes(attrs: &[ast::Attribute]) -> Vec #[inline] pub(crate) fn is_single_line(s: &str) -> bool { - s.chars().find(|&c| c == '\n').is_none() + !s.chars().any(|c| c == '\n') } #[inline] @@ -260,8 +260,7 @@ fn is_skip(meta_item: &MetaItem) -> bool { match meta_item.kind { MetaItemKind::Word => { let path_str = pprust::path_to_string(&meta_item.path); - path_str == &*skip_annotation().as_str() - || path_str == &*depr_skip_annotation().as_str() + path_str == *skip_annotation().as_str() || path_str == *depr_skip_annotation().as_str() } MetaItemKind::List(ref l) => { meta_item.has_name(sym::cfg_attr) && l.len() == 2 && is_skip_nested(&l[1]) diff --git a/src/visitor.rs b/src/visitor.rs index 079568630cf5..3f251bf7c16b 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -198,7 +198,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { let missing_span = self.next_span(hi); let snippet = self.snippet(missing_span); let len = CommentCodeSlices::new(snippet) - .nth(0) + .next() .and_then(|(kind, _, s)| { if kind == CodeCharKind::Normal { s.rfind('\n') @@ -293,7 +293,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { } let span_in_between = mk_sp(last_hi, span.lo() + BytePos::from_usize(offset)); let snippet_in_between = self.snippet(span_in_between); - let mut comment_on_same_line = !snippet_in_between.contains("\n"); + let mut comment_on_same_line = !snippet_in_between.contains('\n'); let mut comment_shape = Shape::indented(self.block_indent, config).comment(config); @@ -301,7 +301,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { self.push_str(" "); // put the first line of the comment on the same line as the // block's last line - match sub_slice.find("\n") { + match sub_slice.find('\n') { None => { self.push_str(&sub_slice); } @@ -764,7 +764,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { let hi = self.snippet_provider.span_before(search_span, ";"); let target_span = mk_sp(mac.span().lo(), hi + BytePos(1)); let rewrite = rewrite.map(|rw| { - if !rw.ends_with(";") { + if !rw.ends_with(';') { format!("{};", rw) } else { rw @@ -921,7 +921,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { !is_skip_attr(segments) } - fn walk_mod_items(&mut self, items: &Vec>) { + fn walk_mod_items(&mut self, items: &[rustc_ast::ptr::P]) { self.visit_items_with_reordering(&ptr_vec_to_ref_vec(&items)); } @@ -953,10 +953,10 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { // break the Stability Guarantee // N.B. This could be updated to utilize the version gates. let include_next_empty = if stmts.len() > 1 { - match (&stmts[0].as_ast_node().kind, &stmts[1].as_ast_node().kind) { - (ast::StmtKind::Item(_), ast::StmtKind::Empty) => true, - _ => false, - } + matches!( + (&stmts[0].as_ast_node().kind, &stmts[1].as_ast_node().kind), + (ast::StmtKind::Item(_), ast::StmtKind::Empty) + ) } else { false }; From d42be80bf772688089b90a7f2c8651adc366a3c4 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Sun, 18 Jul 2021 12:14:23 -0500 Subject: [PATCH 009/401] chore: disable clippy::matches_like_macro lint --- Cargo.lock | 24 +++++++++++++++--------- src/cargo-fmt/main.rs | 1 + src/lib.rs | 1 + 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0e12e81904c9..03bb5598007c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -73,7 +73,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "924c76597f0d9ca25d762c25a4d369d51267536465dc5064bdf0eb073ed477ea" dependencies = [ "backtrace-sys", - "cfg-if", + "cfg-if 0.1.10", "libc", "rustc-demangle", ] @@ -162,6 +162,12 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + [[package]] name = "clap" version = "2.33.0" @@ -207,7 +213,7 @@ version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "lazy_static", ] @@ -218,7 +224,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce446db02cdc3165b94ae73111e570793400d0794e46125cc4056c81cbb039f4" dependencies = [ "autocfg", - "cfg-if", + "cfg-if 0.1.10", "lazy_static", ] @@ -245,7 +251,7 @@ version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "dirs-sys", ] @@ -255,7 +261,7 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "libc", "redox_users", "winapi", @@ -401,11 +407,11 @@ checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235" [[package]] name = "log" -version = "0.4.8" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", ] [[package]] @@ -426,7 +432,7 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a85ea9fc0d4ac0deb6fe7911d38786b32fc11119afd9e9d38b84ff691ce64220" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", ] [[package]] diff --git a/src/cargo-fmt/main.rs b/src/cargo-fmt/main.rs index ba693e852ffa..90ffad927e2c 100644 --- a/src/cargo-fmt/main.rs +++ b/src/cargo-fmt/main.rs @@ -1,6 +1,7 @@ // Inspired by Paul Woolcock's cargo-fmt (https://github.com/pwoolcoc/cargo-fmt/). #![deny(warnings)] +#![allow(clippy::match_like_matches_macro)] use std::cmp::Ordering; use std::collections::{BTreeMap, BTreeSet}; diff --git a/src/lib.rs b/src/lib.rs index eb314e63de36..206d2f782909 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,7 @@ #![deny(rust_2018_idioms)] #![warn(unreachable_pub)] #![recursion_limit = "256"] +#![allow(clippy::match_like_matches_macro)] #[macro_use] extern crate derive_new; From 0832137b9e72cf639d46f42d4801db8bc6d798e0 Mon Sep 17 00:00:00 2001 From: Elliot Bobrow <77182873+ebobrow@users.noreply.github.com> Date: Mon, 19 Jul 2021 14:50:05 -0700 Subject: [PATCH 010/401] fix link in Contributing.md --- Contributing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Contributing.md b/Contributing.md index a108195beb9a..e6dc6a220376 100644 --- a/Contributing.md +++ b/Contributing.md @@ -65,7 +65,7 @@ and get a better grasp on the execution flow. ## Hack! -Here are some [good starting issues](https://github.com/rust-lang/rustfmt/issues?q=is%3Aopen+is%3Aissue+label%3Agood-first-issue). +Here are some [good starting issues](https://github.com/rust-lang/rustfmt/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22). If you've found areas which need polish and don't have issues, please submit a PR, don't feel there needs to be an issue. From 4236289b75ee55c78538c749512cdbeea5e1c332 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Sun, 25 Jul 2021 22:27:33 -0500 Subject: [PATCH 011/401] chore: bump toolchain --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index 7c9d02d933d0..b0cd4464df8e 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2021-05-13" +channel = "nightly-2021-07-23" components = ["rustc-dev"] From 778f03530f8fba92ae0d87644b8cef71d351a859 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Mon, 26 Jul 2021 21:17:15 -0500 Subject: [PATCH 012/401] ci: remove unnecessary cargo-make install on windows jobs --- .github/workflows/windows.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 08cb52eedaea..c05e8d4896ac 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -54,9 +54,6 @@ jobs: if: matrix.target == 'x86_64-pc-windows-gnu' && matrix.channel == 'nightly' shell: bash - - name: cargo-make - run: cargo install --force cargo-make - - name: build run: | rustc -Vv From e7fa07036fa3e87f536fa3bedd3daad51dfeaf16 Mon Sep 17 00:00:00 2001 From: Outvi V <19144373+outloudvi@users.noreply.github.com> Date: Thu, 22 Jul 2021 21:24:07 +0800 Subject: [PATCH 013/401] fix: make --edition 2021 visible in --help --- src/bin/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/main.rs b/src/bin/main.rs index 4b4aa42d9359..1bcc5c0dada3 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -121,7 +121,7 @@ fn make_opts() -> Options { found reverts to the input file path", "[Path for the configuration file]", ); - opts.optopt("", "edition", "Rust edition to use", "[2015|2018]"); + opts.optopt("", "edition", "Rust edition to use", "[2015|2018|2021]"); opts.optopt( "", "color", From 0b21ea2161923a87f65932b6abb5e7a94003ec3a Mon Sep 17 00:00:00 2001 From: Ellen Date: Wed, 28 Apr 2021 20:44:40 +0100 Subject: [PATCH 014/401] Unyeet const param defaults --- src/types.rs | 12 +++++++++++- tests/source/issue-4816/lib.rs | 10 ++++++++++ tests/target/issue-4816/lib.rs | 35 ++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 tests/source/issue-4816/lib.rs create mode 100644 tests/target/issue-4816/lib.rs diff --git a/src/types.rs b/src/types.rs index c6f89c310650..1d3f4669fcd6 100644 --- a/src/types.rs +++ b/src/types.rs @@ -571,13 +571,23 @@ impl Rewrite for ast::GenericParam { if let ast::GenericParamKind::Const { ref ty, kw_span: _, - default: _, + default, } = &self.kind { result.push_str("const "); result.push_str(rewrite_ident(context, self.ident)); result.push_str(": "); result.push_str(&ty.rewrite(context, shape)?); + if let Some(default) = default { + let eq_str = match context.config.type_punctuation_density() { + TypeDensity::Compressed => "=", + TypeDensity::Wide => " = ", + }; + result.push_str(eq_str); + let budget = shape.width.checked_sub(result.len())?; + let rewrite = default.rewrite(context, Shape::legacy(budget, shape.indent))?; + result.push_str(&rewrite); + } } else { result.push_str(rewrite_ident(context, self.ident)); } diff --git a/tests/source/issue-4816/lib.rs b/tests/source/issue-4816/lib.rs new file mode 100644 index 000000000000..43d540c4a5d7 --- /dev/null +++ b/tests/source/issue-4816/lib.rs @@ -0,0 +1,10 @@ +#![feature(const_generics_defaults)] +struct Foo; +struct Bar; +struct Lots; +struct NamesRHard; +struct FooBar< + const LessThan100ButClose: usize = {1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1} +>; +struct FooBarrrrrrrr; diff --git a/tests/target/issue-4816/lib.rs b/tests/target/issue-4816/lib.rs new file mode 100644 index 000000000000..246e775e1feb --- /dev/null +++ b/tests/target/issue-4816/lib.rs @@ -0,0 +1,35 @@ +#![feature(const_generics_defaults)] +struct Foo; +struct Bar; +struct Lots< + const N1BlahFooUwU: usize = { 10 + 28 + 1872 / 10 * 3 }, + const N2SecondParamOhmyyy: usize = { N1BlahFooUwU / 2 + 10 * 2 }, +>; +struct NamesRHard; +struct FooBar< + const LessThan100ButClose: usize = { + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + }, +>; +struct FooBarrrrrrrr< + const N: usize = { + 13478234326456456444323871 + + 1 + + 1 + + 1 + + 1 + + 1 + + 1 + + 1 + + 1 + + 1 + + 1 + + 1 + + 1 + + 1 + + 1 + + 1 + + 1 + + 1 + }, +>; From 8c52aae10a099abf3f481a42bef60bc5db3aba47 Mon Sep 17 00:00:00 2001 From: klensy Date: Thu, 3 Jun 2021 05:28:20 +0300 Subject: [PATCH 015/401] Bump deps * dirs-sys v0.3.4 -> v0.3.6 to drop a lot of deps regex v1.3.1 -> v1.4.3 drops thread_local 0.3.6 bytecount v0.6.0 -> v0.6.2 replaces packed_simd with packed_simd_2 ignore v0.4.11 -> v0.4.17 drop crossbeam-channel v0.4.0 * itertools 8.0 -> 9.0 bump `ignore` version in Cargo.toml * cargo_metadata 0.8 -> 0.12 * env_logger 0.6 -> 0.8 --- Cargo.lock | 380 ++++++++++++------------------------------ Cargo.toml | 8 +- src/cargo-fmt/main.rs | 2 +- 3 files changed, 110 insertions(+), 280 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 03bb5598007c..be134f3e9754 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -35,21 +35,6 @@ version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9267dff192e68f3399525901e709a48c1d3982c9c072fa32f2127a0cb0babf14" -[[package]] -name = "arrayref" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" - -[[package]] -name = "arrayvec" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" -dependencies = [ - "nodrop", -] - [[package]] name = "atty" version = "0.2.13" @@ -62,40 +47,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "0.1.7" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" - -[[package]] -name = "backtrace" -version = "0.3.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "924c76597f0d9ca25d762c25a4d369d51267536465dc5064bdf0eb073ed477ea" -dependencies = [ - "backtrace-sys", - "cfg-if 0.1.10", - "libc", - "rustc-demangle", -] - -[[package]] -name = "backtrace-sys" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d6575f128516de27e3ce99689419835fce9643a9b215a14d2b5b685be018491" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "base64" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" -dependencies = [ - "byteorder", -] +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "bitflags" @@ -103,17 +57,6 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" -[[package]] -name = "blake2b_simd" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5850aeee1552f495dd0250014cf64b82b7c8879a89d83b33bbdace2cc4f63182" -dependencies = [ - "arrayref", - "arrayvec", - "constant_time_eq", -] - [[package]] name = "bstr" version = "0.2.8" @@ -125,37 +68,35 @@ dependencies = [ [[package]] name = "bytecount" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0017894339f586ccb943b01b9555de56770c11cda818e7e3d8bd93f4ed7f46e" +checksum = "72feb31ffc86498dacdbd0fcebb56138e7177a8cc5cea4516031d15ae85a742e" dependencies = [ - "packed_simd", + "packed_simd_2", ] [[package]] -name = "byteorder" -version = "1.3.2" +name = "cargo-platform" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" +checksum = "0226944a63d1bf35a3b5f948dd7c59e263db83695c9e8bffc4037de02e30f1d7" +dependencies = [ + "serde", +] [[package]] name = "cargo_metadata" -version = "0.8.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "700b3731fd7d357223d0000f4dbf1808401b694609035c3c411fbc0cd375c426" +checksum = "7714a157da7991e23d90686b9524b9e12e0407a108647f52e9328f4b3d51ac7f" dependencies = [ + "cargo-platform", "semver", + "semver-parser", "serde", - "serde_derive", "serde_json", ] -[[package]] -name = "cc" -version = "1.0.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0213d356d3c4ea2c18c40b037c3be23cd639825c18f25ee670ac7813beeef99c" - [[package]] name = "cfg-if" version = "0.1.10" @@ -183,48 +124,14 @@ dependencies = [ "vec_map", ] -[[package]] -name = "cloudabi" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -dependencies = [ - "bitflags", -] - -[[package]] -name = "constant_time_eq" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "995a44c877f9212528ccc74b21a232f66ad69001e40ede5bcee2ac9ef2657120" - -[[package]] -name = "crossbeam-channel" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acec9a3b0b3559f15aee4f90746c4e5e293b701c0f7d3925d24e01645267b68c" -dependencies = [ - "crossbeam-utils 0.7.0", -] - [[package]] name = "crossbeam-utils" -version = "0.6.6" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" -dependencies = [ - "cfg-if 0.1.10", - "lazy_static", -] - -[[package]] -name = "crossbeam-utils" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce446db02cdc3165b94ae73111e570793400d0794e46125cc4056c81cbb039f4" +checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49" dependencies = [ "autocfg", - "cfg-if 0.1.10", + "cfg-if 1.0.0", "lazy_static", ] @@ -257,11 +164,10 @@ dependencies = [ [[package]] name = "dirs-sys" -version = "0.3.4" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b" +checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780" dependencies = [ - "cfg-if 0.1.10", "libc", "redox_users", "winapi", @@ -275,9 +181,9 @@ checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" [[package]] name = "env_logger" -version = "0.6.2" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3" +checksum = "54532e3223c5af90a6a757c90b5c5521564b07e5e7a958681bcd2afad421cdcd" dependencies = [ "atty", "humantime", @@ -286,40 +192,12 @@ dependencies = [ "termcolor", ] -[[package]] -name = "failure" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9" -dependencies = [ - "backtrace", - "failure_derive", -] - -[[package]] -name = "failure_derive" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - [[package]] name = "fnv" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" -[[package]] -name = "fuchsia-cprng" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" - [[package]] name = "getopts" version = "0.2.21" @@ -330,10 +208,21 @@ dependencies = [ ] [[package]] -name = "globset" -version = "0.4.4" +name = "getrandom" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "925aa2cac82d8834e2b2a4415b6f6879757fb5c0928fc445ae76461a12eed8f2" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi", +] + +[[package]] +name = "globset" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c152169ef1e421390738366d2f796655fec62621dabbd0fd476f905934061e4a" dependencies = [ "aho-corasick", "bstr", @@ -353,36 +242,33 @@ dependencies = [ [[package]] name = "humantime" -version = "1.3.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" -dependencies = [ - "quick-error", -] +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "ignore" -version = "0.4.11" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "522daefc3b69036f80c7d2990b28ff9e0471c683bad05ca258e0a01dd22c5a1e" +checksum = "b287fb45c60bb826a0dc68ff08742b9d88a2fea13d6e0c286b3172065aaf878c" dependencies = [ - "crossbeam-channel", + "crossbeam-utils", "globset", "lazy_static", "log", "memchr", "regex", "same-file", - "thread_local 1.0.1", + "thread_local", "walkdir", "winapi-util", ] [[package]] name = "itertools" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358" +checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" dependencies = [ "either", ] @@ -405,6 +291,12 @@ version = "0.2.77" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235" +[[package]] +name = "libm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" + [[package]] name = "log" version = "0.4.14" @@ -421,18 +313,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" [[package]] -name = "nodrop" -version = "0.1.14" +name = "packed_simd_2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" - -[[package]] -name = "packed_simd" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a85ea9fc0d4ac0deb6fe7911d38786b32fc11119afd9e9d38b84ff691ce64220" +checksum = "3278e0492f961fd4ae70909f56b2723a7e8d01a228427294e19cdfdebda89a17" dependencies = [ "cfg-if 0.1.10", + "libm", +] + +[[package]] +name = "pest" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +dependencies = [ + "ucd-trie", ] [[package]] @@ -463,19 +359,13 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.6" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27" +checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" dependencies = [ "unicode-xid", ] -[[package]] -name = "quick-error" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" - [[package]] name = "quote" version = "1.0.6" @@ -485,96 +375,42 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand_core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -dependencies = [ - "rand_core 0.4.2", -] - -[[package]] -name = "rand_core" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" - -[[package]] -name = "rand_os" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" -dependencies = [ - "cloudabi", - "fuchsia-cprng", - "libc", - "rand_core 0.4.2", - "rdrand", - "winapi", -] - -[[package]] -name = "rdrand" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -dependencies = [ - "rand_core 0.3.1", -] - [[package]] name = "redox_syscall" -version = "0.1.56" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" +checksum = "05ec8ca9416c5ea37062b502703cd7fcb207736bc294f6e0cf367ac6fc234570" +dependencies = [ + "bitflags", +] [[package]] name = "redox_users" -version = "0.3.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ecedbca3bf205f8d8f5c2b44d83cd0690e39ee84b951ed649e9f1841132b66d" +checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" dependencies = [ - "failure", - "rand_os", + "getrandom", "redox_syscall", - "rust-argon2", ] [[package]] name = "regex" -version = "1.3.1" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc220bd33bdce8f093101afe22a037b8eb0e5af33592e6a9caafff0d4cb81cbd" +checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a" dependencies = [ "aho-corasick", "memchr", "regex-syntax", - "thread_local 0.3.6", + "thread_local", ] [[package]] name = "regex-syntax" -version = "0.6.12" +version = "0.6.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716" - -[[package]] -name = "rust-argon2" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca4eaef519b494d1f2848fc602d18816fed808a981aedf4f1f00ceb7c9d32cf" -dependencies = [ - "base64", - "blake2b_simd", - "crossbeam-utils 0.6.6", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" +checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581" [[package]] name = "rustc-workspace-hack" @@ -639,9 +475,9 @@ dependencies = [ [[package]] name = "semver" -version = "0.9.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" dependencies = [ "semver-parser", "serde", @@ -649,24 +485,27 @@ dependencies = [ [[package]] name = "semver-parser" -version = "0.7.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] [[package]] name = "serde" -version = "1.0.101" +version = "1.0.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9796c9b7ba2ffe7a9ce53c2287dfc48080f4b2b362fcc245a259b3a7201119dd" +checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.101" +version = "1.0.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b133a43a1ecd55d4086bd5b4dc6c1751c68b1bfbeba7a5040442022c7e7c02e" +checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43" dependencies = [ "proc-macro2", "quote", @@ -675,9 +514,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.41" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f72eb2a68a7dc3f9a691bfda9305a1c017a6215e5a4545c258500d2099a37c2" +checksum = "dcac07dbffa1c65e7f816ab9eba78eb142c6d44410f4eeba1e26e4f5dfa56b95" dependencies = [ "itoa", "ryu", @@ -716,9 +555,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.11" +version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff0acdb207ae2fe6d5976617f887eb1e35a2ba52c13c7234c790960cdad9238" +checksum = "f3a1d708c221c5a612956ef9f75b37e454e88d1f7b899fbd3a18d4252012d663" dependencies = [ "proc-macro2", "quote", @@ -736,18 +575,6 @@ dependencies = [ "syn", ] -[[package]] -name = "synstructure" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f085a5855930c0441ca1288cf044ea4aecf4f43a91668abdb870b4ba546a203" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "unicode-xid", -] - [[package]] name = "term" version = "0.6.1" @@ -796,15 +623,6 @@ dependencies = [ "syn", ] -[[package]] -name = "thread_local" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" -dependencies = [ - "lazy_static", -] - [[package]] name = "thread_local" version = "1.0.1" @@ -823,6 +641,12 @@ dependencies = [ "serde", ] +[[package]] +name = "ucd-trie" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" + [[package]] name = "unicode-segmentation" version = "1.3.0" @@ -870,6 +694,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + [[package]] name = "winapi" version = "0.3.8" diff --git a/Cargo.toml b/Cargo.toml index 3a04fb28f7cb..e368f3eb1ec0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ rustfmt-format-diff = [] generic-simd = ["bytecount/generic-simd"] [dependencies] -itertools = "0.8" +itertools = "0.9" toml = "0.5" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" @@ -43,15 +43,15 @@ regex = "1.0" term = "0.6" diff = "0.1" log = "0.4.14" -env_logger = "0.6" +env_logger = "0.8" getopts = "0.2" derive-new = "0.5" -cargo_metadata = "0.8" +cargo_metadata = "0.12" bytecount = "0.6" unicode-width = "0.1.5" unicode_categories = "0.1.1" dirs = "2.0.1" -ignore = "0.4.11" +ignore = "0.4.17" annotate-snippets = { version = "0.8", features = ["color"] } structopt = "0.3" rustfmt-config_proc_macro = { version = "0.2", path = "config_proc_macro" } diff --git a/src/cargo-fmt/main.rs b/src/cargo-fmt/main.rs index 90ffad927e2c..f8cf698525ba 100644 --- a/src/cargo-fmt/main.rs +++ b/src/cargo-fmt/main.rs @@ -528,7 +528,7 @@ fn get_cargo_metadata( if let Some(manifest_path) = manifest_path { cmd.manifest_path(manifest_path); } - cmd.other_options(&[String::from("--offline")]); + cmd.other_options(vec![String::from("--offline")]); match cmd.exec() { Ok(metadata) => Ok(metadata), From 8cbee5604a069bf8426f0848fd4fd2c86cfff0fa Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Thu, 29 Jul 2021 21:15:56 -0500 Subject: [PATCH 016/401] ci: functionally delete appveyor --- appveyor.yml | 49 +------------------------------------------------ 1 file changed, 1 insertion(+), 48 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 5ac99fd71f8f..b3dda091e0a9 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,55 +1,8 @@ -# This is based on https://github.com/japaric/rust-everywhere/blob/master/appveyor.yml -# and modified (mainly removal of deployment) to suit rustfmt. - environment: global: PROJECT_NAME: rustfmt - matrix: - # Stable channel - # - TARGET: i686-pc-windows-gnu - # CHANNEL: stable - # - TARGET: i686-pc-windows-msvc - # CHANNEL: stable - # - TARGET: x86_64-pc-windows-gnu - # CHANNEL: stable - # - TARGET: x86_64-pc-windows-msvc - # CHANNEL: stable - # Beta channel - # - TARGET: i686-pc-windows-gnu - # CHANNEL: beta - # - TARGET: i686-pc-windows-msvc - # CHANNEL: beta - # - TARGET: x86_64-pc-windows-gnu - # CHANNEL: beta - # - TARGET: x86_64-pc-windows-msvc - # CHANNEL: beta - # Nightly channel - - TARGET: i686-pc-windows-gnu - CHANNEL: nightly - - TARGET: i686-pc-windows-msvc - CHANNEL: nightly - - TARGET: x86_64-pc-windows-gnu - CHANNEL: nightly - - TARGET: x86_64-pc-windows-msvc - CHANNEL: nightly -# Install Rust and Cargo -# (Based on from https://github.com/rust-lang/libc/blob/master/appveyor.yml) -install: - - appveyor-retry appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe - - if "%TARGET%" == "i686-pc-windows-gnu" set PATH=%PATH%;C:\msys64\mingw32\bin - - if "%TARGET%" == "x86_64-pc-windows-gnu" set PATH=%PATH%;C:\msys64\mingw64\bin - - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin - - rustup-init.exe --default-host %TARGET% --default-toolchain %CHANNEL% -y - - rustc -Vv - - cargo -V - -# ??? build: false test_script: - - set CFG_RELEASE_CHANNEL=nightly - - set CFG_RELEASE=nightly - - cargo build --verbose - - cargo test - - cargo test -- --ignored + - echo Why does no one have access to delete me? From fefb5427a2fb28459f91a776948fca0ab0f2d11c Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Fri, 6 Aug 2021 21:26:27 -0500 Subject: [PATCH 017/401] fix: handle GAT types in impls with self bounds --- src/items.rs | 82 ++++++++++++-------------------------- src/visitor.rs | 49 +++++++++-------------- tests/source/issue_4911.rs | 6 +++ tests/target/issue_4911.rs | 9 +++++ 4 files changed, 59 insertions(+), 87 deletions(-) create mode 100644 tests/source/issue_4911.rs create mode 100644 tests/target/issue_4911.rs diff --git a/src/items.rs b/src/items.rs index 0542358c6e7c..1a56acc7de1b 100644 --- a/src/items.rs +++ b/src/items.rs @@ -1497,7 +1497,7 @@ fn format_tuple_struct( Some(result) } -fn rewrite_type( +pub(crate) fn rewrite_type( context: &RewriteContext<'_>, indent: Indent, ident: symbol::Ident, @@ -1841,29 +1841,6 @@ fn rewrite_static( Some(format!("{}{};", prefix, ty_str)) } } - -pub(crate) fn rewrite_type_alias( - ident: symbol::Ident, - ty_opt: Option<&ptr::P>, - generics: &ast::Generics, - generic_bounds_opt: Option<&ast::GenericBounds>, - context: &RewriteContext<'_>, - indent: Indent, - vis: &ast::Visibility, - span: Span, -) -> Option { - rewrite_type( - context, - indent, - ident, - vis, - generics, - generic_bounds_opt, - ty_opt, - span, - ) -} - struct OpaqueType<'a> { bounds: &'a ast::GenericBounds, } @@ -1877,32 +1854,7 @@ impl<'a> Rewrite for OpaqueType<'a> { } } -pub(crate) fn rewrite_opaque_impl_type( - context: &RewriteContext<'_>, - ident: symbol::Ident, - generics: &ast::Generics, - generic_bounds: &ast::GenericBounds, - indent: Indent, -) -> Option { - let ident_str = rewrite_ident(context, ident); - // 5 = "type " - let generics_shape = Shape::indented(indent, context.config).offset_left(5)?; - let generics_str = rewrite_generics(context, ident_str, generics, generics_shape)?; - let prefix = format!("type {} =", generics_str); - let rhs = OpaqueType { - bounds: generic_bounds, - }; - - rewrite_assign_rhs( - context, - &prefix, - &rhs, - Shape::indented(indent, context.config).sub_width(1)?, - ) - .map(|s| s + ";") -} - -pub(crate) fn rewrite_associated_impl_type( +pub(crate) fn rewrite_impl_type( ident: symbol::Ident, vis: &ast::Visibility, defaultness: ast::Defaultness, @@ -1912,7 +1864,25 @@ pub(crate) fn rewrite_associated_impl_type( indent: Indent, span: Span, ) -> Option { - let result = rewrite_type_alias(ident, ty_opt, generics, None, context, indent, vis, span)?; + // Opaque type + let result = if let Some(rustc_ast::ast::Ty { + kind: ast::TyKind::ImplTrait(_, ref bounds), + .. + }) = ty_opt.map(|t| &**t) + { + rewrite_type( + context, + indent, + ident, + &DEFAULT_VISIBILITY, + generics, + None, + Some(&OpaqueType { bounds }), + span, + ) + } else { + rewrite_type(context, indent, ident, vis, generics, None, ty_opt, span) + }?; match defaultness { ast::Defaultness::Default(..) => Some(format!("default {}", result)), @@ -3164,14 +3134,14 @@ impl Rewrite for ast::ForeignItem { ast::ForeignItemKind::TyAlias(ref ty_alias_kind) => { let ast::TyAliasKind(_, ref generics, ref generic_bounds, ref type_default) = **ty_alias_kind; - rewrite_type_alias( - self.ident, - type_default.as_ref(), - generics, - Some(generic_bounds), + rewrite_type( &context, shape.indent, + self.ident, &self.vis, + generics, + Some(generic_bounds), + type_default.as_ref(), self.span, ) } diff --git a/src/visitor.rs b/src/visitor.rs index 3f251bf7c16b..12a3281eda55 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -11,9 +11,9 @@ use crate::config::Version; use crate::config::{BraceStyle, Config}; use crate::coverage::transform_missing_snippet; use crate::items::{ - format_impl, format_trait, format_trait_alias, is_mod_decl, is_use_item, - rewrite_associated_impl_type, rewrite_extern_crate, rewrite_opaque_impl_type, - rewrite_opaque_type, rewrite_type_alias, FnBraceStyle, FnSig, StaticParts, StructParts, + format_impl, format_trait, format_trait_alias, is_mod_decl, is_use_item, rewrite_extern_crate, + rewrite_impl_type, rewrite_opaque_type, rewrite_type, FnBraceStyle, FnSig, StaticParts, + StructParts, }; use crate::macros::{macro_style, rewrite_macro, rewrite_macro_def, MacroPosition}; use crate::modules::Module; @@ -579,14 +579,14 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { **alias_kind; match ty { Some(ty) => { - let rewrite = rewrite_type_alias( - item.ident, - Some(&*ty), - generics, - Some(generic_bounds), + let rewrite = rewrite_type( &self.get_context(), self.block_indent, + item.ident, &item.vis, + generics, + Some(generic_bounds), + Some(&*ty), item.span, ); self.push_rewrite(item.span, rewrite); @@ -665,14 +665,14 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { ast::AssocItemKind::TyAlias(ref ty_alias_kind) => { let ast::TyAliasKind(_, ref generics, ref generic_bounds, ref type_default) = **ty_alias_kind; - let rewrite = rewrite_type_alias( - ti.ident, - type_default.as_ref(), - generics, - Some(generic_bounds), + let rewrite = rewrite_type( &self.get_context(), self.block_indent, + ti.ident, &ti.vis, + generics, + Some(generic_bounds), + type_default.as_ref(), ti.span, ); self.push_rewrite(ti.span, rewrite); @@ -715,8 +715,9 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { ast::AssocItemKind::Const(..) => self.visit_static(&StaticParts::from_impl_item(ii)), ast::AssocItemKind::TyAlias(ref ty_alias_kind) => { let ast::TyAliasKind(defaultness, ref generics, _, ref ty) = **ty_alias_kind; - let rewrite_associated = || { - rewrite_associated_impl_type( + self.push_rewrite( + ii.span, + rewrite_impl_type( ii.ident, &ii.vis, defaultness, @@ -725,22 +726,8 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { &self.get_context(), self.block_indent, ii.span, - ) - }; - let rewrite = match ty { - None => rewrite_associated(), - Some(ty) => match ty.kind { - ast::TyKind::ImplTrait(_, ref bounds) => rewrite_opaque_impl_type( - &self.get_context(), - ii.ident, - generics, - bounds, - self.block_indent, - ), - _ => rewrite_associated(), - }, - }; - self.push_rewrite(ii.span, rewrite); + ), + ); } ast::AssocItemKind::MacCall(ref mac) => { self.visit_mac(mac, Some(ii.ident), MacroPosition::Item); diff --git a/tests/source/issue_4911.rs b/tests/source/issue_4911.rs new file mode 100644 index 000000000000..21ef6c6c491a --- /dev/null +++ b/tests/source/issue_4911.rs @@ -0,0 +1,6 @@ +#![feature(generic_associated_types)] +#![feature(min_type_alias_impl_trait)] + +impl SomeTrait for SomeType { + type SomeGAT<'a> where Self: 'a = impl SomeOtherTrait; +} \ No newline at end of file diff --git a/tests/target/issue_4911.rs b/tests/target/issue_4911.rs new file mode 100644 index 000000000000..890a62267ce6 --- /dev/null +++ b/tests/target/issue_4911.rs @@ -0,0 +1,9 @@ +#![feature(generic_associated_types)] +#![feature(min_type_alias_impl_trait)] + +impl SomeTrait for SomeType { + type SomeGAT<'a> + where + Self: 'a, + = impl SomeOtherTrait; +} From 5d8eb8d79ce01571e468df9a0dfad8b2730bdf58 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Sat, 7 Aug 2021 20:24:14 -0500 Subject: [PATCH 018/401] fix: don't drop drop generic args on assoc ty constraints --- src/types.rs | 125 ++++++++++++++++++++----------------- tests/target/issue_4943.rs | 8 +++ 2 files changed, 76 insertions(+), 57 deletions(-) create mode 100644 tests/target/issue_4943.rs diff --git a/src/types.rs b/src/types.rs index 1d3f4669fcd6..9a0d31e51dfc 100644 --- a/src/types.rs +++ b/src/types.rs @@ -174,34 +174,41 @@ impl<'a> Rewrite for SegmentParam<'a> { SegmentParam::Const(const_) => const_.rewrite(context, shape), SegmentParam::LifeTime(lt) => lt.rewrite(context, shape), SegmentParam::Type(ty) => ty.rewrite(context, shape), - SegmentParam::Binding(assoc_ty_constraint) => { - let mut result = match assoc_ty_constraint.kind { - ast::AssocTyConstraintKind::Bound { .. } => { - format!("{}: ", rewrite_ident(context, assoc_ty_constraint.ident)) - } - ast::AssocTyConstraintKind::Equality { .. } => { - match context.config.type_punctuation_density() { - TypeDensity::Wide => { - format!("{} = ", rewrite_ident(context, assoc_ty_constraint.ident)) - } - TypeDensity::Compressed => { - format!("{}=", rewrite_ident(context, assoc_ty_constraint.ident)) - } - } - } - }; - - let budget = shape.width.checked_sub(result.len())?; - let rewrite = assoc_ty_constraint - .kind - .rewrite(context, Shape::legacy(budget, shape.indent + result.len()))?; - result.push_str(&rewrite); - Some(result) - } + SegmentParam::Binding(atc) => atc.rewrite(context, shape), } } } +impl Rewrite for ast::AssocTyConstraint { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + use ast::AssocTyConstraintKind::{Bound, Equality}; + + let mut result = String::with_capacity(128); + result.push_str(rewrite_ident(context, self.ident)); + + if let Some(ref gen_args) = self.gen_args { + let budget = shape.width.checked_sub(result.len())?; + let shape = Shape::legacy(budget, shape.indent + result.len()); + let gen_str = rewrite_generic_args(gen_args, context, shape, gen_args.span())?; + result.push_str(&gen_str); + } + + let infix = match (&self.kind, context.config.type_punctuation_density()) { + (Bound { .. }, _) => ": ", + (Equality { .. }, TypeDensity::Wide) => " = ", + (Equality { .. }, TypeDensity::Compressed) => "=", + }; + result.push_str(infix); + + let budget = shape.width.checked_sub(result.len())?; + let shape = Shape::legacy(budget, shape.indent + result.len()); + let rewrite = self.kind.rewrite(context, shape)?; + result.push_str(&rewrite); + + Some(result) + } +} + impl Rewrite for ast::AssocTyConstraintKind { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { match self { @@ -240,21 +247,9 @@ fn rewrite_segment( }; if let Some(ref args) = segment.args { + let generics_str = rewrite_generic_args(args, context, shape, mk_sp(*span_lo, span_hi))?; match **args { ast::GenericArgs::AngleBracketed(ref data) if !data.args.is_empty() => { - let param_list = data - .args - .iter() - .map(|x| match x { - ast::AngleBracketedArg::Arg(generic_arg) => { - SegmentParam::from_generic_arg(generic_arg) - } - ast::AngleBracketedArg::Constraint(constraint) => { - SegmentParam::Binding(constraint) - } - }) - .collect::>(); - // HACK: squeeze out the span between the identifier and the parameters. // The hack is requried so that we don't remove the separator inside macro calls. // This does not work in the presence of comment, hoping that people are @@ -270,33 +265,14 @@ fn rewrite_segment( }; result.push_str(separator); - let generics_str = overflow::rewrite_with_angle_brackets( - context, - "", - param_list.iter(), - shape, - mk_sp(*span_lo, span_hi), - )?; - // Update position of last bracket. *span_lo = context .snippet_provider .span_after(mk_sp(*span_lo, span_hi), "<"); - - result.push_str(&generics_str) - } - ast::GenericArgs::Parenthesized(ref data) => { - result.push_str(&format_function_type( - data.inputs.iter().map(|x| &**x), - &data.output, - false, - data.span, - context, - shape, - )?); } _ => (), } + result.push_str(&generics_str) } Some(result) @@ -489,6 +465,41 @@ impl Rewrite for ast::GenericArg { } } +fn rewrite_generic_args( + gen_args: &ast::GenericArgs, + context: &RewriteContext<'_>, + shape: Shape, + span: Span, +) -> Option { + match gen_args { + ast::GenericArgs::AngleBracketed(ref data) if !data.args.is_empty() => { + let args = data + .args + .iter() + .map(|x| match x { + ast::AngleBracketedArg::Arg(generic_arg) => { + SegmentParam::from_generic_arg(generic_arg) + } + ast::AngleBracketedArg::Constraint(constraint) => { + SegmentParam::Binding(constraint) + } + }) + .collect::>(); + + overflow::rewrite_with_angle_brackets(context, "", args.iter(), shape, span) + } + ast::GenericArgs::Parenthesized(ref data) => format_function_type( + data.inputs.iter().map(|x| &**x), + &data.output, + false, + data.span, + context, + shape, + ), + _ => Some("".to_owned()), + } +} + fn rewrite_bounded_lifetime( lt: &ast::Lifetime, bounds: &[ast::GenericBound], diff --git a/tests/target/issue_4943.rs b/tests/target/issue_4943.rs new file mode 100644 index 000000000000..bc8f1a366da2 --- /dev/null +++ b/tests/target/issue_4943.rs @@ -0,0 +1,8 @@ +impl SomeStruct { + fn process(v: T) -> ::R + where + Self: GAT = T>, + { + SomeStruct::do_something(v) + } +} From 1d6002ae3887536578e87b17935a83b94953a8d8 Mon Sep 17 00:00:00 2001 From: Nipunn Koorapati Date: Sat, 7 Aug 2021 18:08:38 -0700 Subject: [PATCH 019/401] Enable triagebot assignment in rustfmt --- triagebot.toml | 1 + 1 file changed, 1 insertion(+) create mode 100644 triagebot.toml diff --git a/triagebot.toml b/triagebot.toml new file mode 100644 index 000000000000..fa0824ac53c0 --- /dev/null +++ b/triagebot.toml @@ -0,0 +1 @@ +[assign] From 667a2da7af0ed32361f1ea195f7a42911a54f1a8 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Sun, 31 May 2020 09:23:39 -0700 Subject: [PATCH 020/401] Fix stable toggle on gh pages configuration site A demonstration of the fix is included in the PR associated with this commit. --- docs/index.html | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/index.html b/docs/index.html index 56d1917e2b61..1af435103985 100644 --- a/docs/index.html +++ b/docs/index.html @@ -155,7 +155,9 @@ head: val[0].text, value: val, stable: val.some((elem) => { - return !!elem.text && elem.text.includes("**Stable**: Yes") + return elem.type === "list" && + !!elem.raw && + elem.raw.includes("**Stable**: Yes"); }), text: val.reduce((result, next) => { return next.text != null @@ -188,4 +190,4 @@ } - \ No newline at end of file + From 6959d03a3aab11515acb5ca2f0e3e5940b451dee Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Sun, 31 May 2020 17:12:09 -0700 Subject: [PATCH 021/401] Show configs from different versions on github pages See https://gushiermainecoon.htmlpasta.com/ for a demo of this change. Part of #4178 --- docs/index.html | 102 ++++++++++++++++++++++++++++++------------------ 1 file changed, 65 insertions(+), 37 deletions(-) diff --git a/docs/index.html b/docs/index.html index 1af435103985..49435e6ab368 100644 --- a/docs/index.html +++ b/docs/index.html @@ -5,6 +5,7 @@ + @@ -137,12 +146,27 @@ }, []); ast.links = {}; + queryParams.set('version', this.version); + queryParams.set('search', this.searchCondition); + const curUrl = window.location.pathname + + '?' + queryParams.toString() + window.location.hash; + history.pushState(null, '', curUrl); + + const renderer = new marked.Renderer(); + renderer.heading = function(text, level) { + const id = htmlToId(text); + return ` + ${text} + `; + }; + return marked.parser(ast, { highlight(code, lang) { return hljs.highlight(lang ? lang : 'rust', code).value; }, headerIds: true, - headerPrefix: '' + headerPrefix: '', + renderer, }); } }, @@ -156,13 +180,10 @@ }, mounted() { if (UrlHash === '') return; - const interval = setInterval(() => { - const target = document.querySelector(`#${UrlHash}`); - if (target != null) { - target.scrollIntoView(true); - clearInterval(interval); - } - }, 100); + const target = document.querySelector(`#${UrlHash}`); + if (target != null) { + target.scrollIntoView(true); + } } }); const extractDepthOnes = (ast) => { @@ -228,6 +249,11 @@ configurationDescriptions }; } + function htmlToId(text) { + const tmpl = document.createElement('template'); + tmpl.innerHTML = text.trim(); + return encodeURIComponent(CSS.escape(tmpl.content.textContent)); + } From bf47fc17a3bbcf93f665ec2b19fbe794ead555af Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Wed, 15 Jul 2020 17:50:43 -0700 Subject: [PATCH 027/401] Ensure that doc url hash IDs are scrolled to on page load I broke this a few weeks ago so I'll fix it Demo: https://5f0fa445faca4aff5f580029--naughty-borg-09b903.netlify.app/?version=master&search=#brace_style --- docs/index.html | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/docs/index.html b/docs/index.html index 40fafd44428b..1f299b918205 100644 --- a/docs/index.html +++ b/docs/index.html @@ -108,7 +108,8 @@ shouldStable: false, version: versionNumber, oldVersion: undefined, - versionOptions: ['master'] + versionOptions: ['master'], + scrolledOnce: false, }, asyncComputed: { async outputHtml() { @@ -156,11 +157,11 @@ renderer.heading = function(text, level) { const id = htmlToId(text); return ` - ${text} + ${text} `; }; - return marked.parser(ast, { + const html = marked.parser(ast, { highlight(code, lang) { return hljs.highlight(lang ? lang : 'rust', code).value; }, @@ -168,6 +169,8 @@ headerPrefix: '', renderer, }); + document.dispatchEvent(new Event('htmlbuilt')); + return html; } }, created: async function() { @@ -178,12 +181,15 @@ .filter(tag => tag.startsWith('v')); this.versionOptions = this.versionOptions.concat(tagOptions); }, - mounted() { + updated: function() { if (UrlHash === '') return; - const target = document.querySelector(`#${UrlHash}`); - if (target != null) { - target.scrollIntoView(true); - } + this.$nextTick(() => { + const target = document.querySelector(`#${UrlHash}`); + if (target != null && !this.scrolledOnce) { + target.scrollIntoView(true); + this.scrolledOnce = true; + } + }); } }); const extractDepthOnes = (ast) => { From f5c782f321572e7eece7d42c51a049c1ae825b5a Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Wed, 15 Jul 2020 17:52:52 -0700 Subject: [PATCH 028/401] fixup! Ensure that doc url hash IDs are scrolled to on page load --- docs/index.html | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/index.html b/docs/index.html index 1f299b918205..3cc0c45a7591 100644 --- a/docs/index.html +++ b/docs/index.html @@ -161,7 +161,7 @@ `; }; - const html = marked.parser(ast, { + return marked.parser(ast, { highlight(code, lang) { return hljs.highlight(lang ? lang : 'rust', code).value; }, @@ -169,8 +169,6 @@ headerPrefix: '', renderer, }); - document.dispatchEvent(new Event('htmlbuilt')); - return html; } }, created: async function() { @@ -181,7 +179,7 @@ .filter(tag => tag.startsWith('v')); this.versionOptions = this.versionOptions.concat(tagOptions); }, - updated: function() { + updated() { if (UrlHash === '') return; this.$nextTick(() => { const target = document.querySelector(`#${UrlHash}`); From 5c7ac69393a20e18bcc07f0d69383bc6254d1b40 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Wed, 15 Jul 2020 18:27:19 -0700 Subject: [PATCH 029/401] Warn when rate limit is on docs page Demo: https://5f0fad2f06c62143ac519413--festive-golick-afb5e0.netlify.app --- docs/index.html | 58 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 45 insertions(+), 13 deletions(-) diff --git a/docs/index.html b/docs/index.html index 3cc0c45a7591..eb5ded4ac30e 100644 --- a/docs/index.html +++ b/docs/index.html @@ -116,20 +116,22 @@ if (this.version !== this.oldVersion) { const ConfigurationMdUrl = `https://raw.githubusercontent.com/rust-lang/rustfmt/${this.version}/Configurations.md`; + let res; try { - const res = await axios.get(ConfigurationMdUrl); - const { - about, - configurationAbout, - configurationDescriptions - } = parseMarkdownAst(res.data); - this.aboutHtml = marked.parser(about); - this.configurationAboutHtml = marked.parser(configurationAbout); - this.configurationDescriptions = configurationDescriptions; - this.oldVersion = this.version; - } catch(error) { - this.aboutHtml = "

Failed to get configuration options for this version, please select the version from the dropdown above.

"; + res = await axios.get(ConfigurationMdUrl).catch(e => { throw e }); + } catch(e) { + this.handleReqFailure(e); + return; } + const { + about, + configurationAbout, + configurationDescriptions + } = parseMarkdownAst(res.data); + this.aboutHtml = marked.parser(about); + this.configurationAboutHtml = marked.parser(configurationAbout); + this.configurationDescriptions = configurationDescriptions; + this.oldVersion = this.version; } const ast = this.configurationDescriptions @@ -172,7 +174,13 @@ } }, created: async function() { - const {data: tags} = await axios.get(RusfmtTagsUrl); + let tags; + try { + tags = (await axios.get(RusfmtTagsUrl)).data; + } catch(e) { + this.handleReqFailure(e); + return; + } const reMajorVersion = /v(\d+)/; const tagOptions = tags .map(tag => tag.name) @@ -188,6 +196,30 @@ this.scrolledOnce = true; } }); + }, + methods: { + handleReqFailure(e) { + if (e.response.status === 404) { + this.aboutHtml = + "

Failed to get configuration options for this version, please select the version from the dropdown above.

"; + } else if ( + e.response.status === 403 && + e.response.headers["X-RateLimit-Remaining"] === 0 + ) { + const resetDate = new Date( + e.response.headers['X-RateLimit-Reset'] * 1000 + ).toLocaleString(); + this.aboutHtml = + `

You have hit the GitHub API rate limit; documentation cannot be updated.` + + `

The rate limit will be reset at ${resetDate}.

`; + } else { + this.aboutHtml = + `

Ecountered an error when fetching documentation data:

` + + `
${e.response.data}
` + + `

We would appreciate a bug report.` + + `

Try refreshing the page.

`; + } + } } }); const extractDepthOnes = (ast) => { From 3c36a0c0b74c2e5cb4c27a53e596b5729a5b3c65 Mon Sep 17 00:00:00 2001 From: jdollar Date: Sat, 3 Oct 2020 19:05:31 -0400 Subject: [PATCH 030/401] Exluding v0.8.1 and v0.7 from the config dropdown Older tags of the repo don't have the configuration.md file that the docs/index.html file uses to display configuration options. Removing them from the list since they don't apply to the use case of the documentation page. --- docs/index.html | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/index.html b/docs/index.html index eb5ded4ac30e..bab4d2ec63bb 100644 --- a/docs/index.html +++ b/docs/index.html @@ -181,10 +181,12 @@ this.handleReqFailure(e); return; } - const reMajorVersion = /v(\d+)/; + + const excludedTagVersions = new Set(['v0.7', 'v0.8.1']); + const tagOptions = tags .map(tag => tag.name) - .filter(tag => tag.startsWith('v')); + .filter(tag => tag.startsWith('v') && !excludedTagVersions.has(tag)); this.versionOptions = this.versionOptions.concat(tagOptions); }, updated() { From 042c2ec369a61d52cef935cbb67ae9b81da03d0f Mon Sep 17 00:00:00 2001 From: gunadhya <6939749+gunadhya@users.noreply.github.com> Date: Sat, 16 Jan 2021 01:34:11 +0530 Subject: [PATCH 031/401] Added Updated default version to Rustfmt docs config --- docs/index.html | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/index.html b/docs/index.html index bab4d2ec63bb..f31c695f7ca0 100644 --- a/docs/index.html +++ b/docs/index.html @@ -87,6 +87,7 @@ From d19f69cd7106d8e84d4e92dad4d94249128a9d35 Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 30 Aug 2021 20:59:45 +1200 Subject: [PATCH 039/401] test: add missing source for #4943 --- tests/source/issue_4943.rs | 9 +++++++++ tests/target/issue_4943.rs | 2 ++ 2 files changed, 11 insertions(+) create mode 100644 tests/source/issue_4943.rs diff --git a/tests/source/issue_4943.rs b/tests/source/issue_4943.rs new file mode 100644 index 000000000000..0793b7b4fe1c --- /dev/null +++ b/tests/source/issue_4943.rs @@ -0,0 +1,9 @@ +#![feature(generic_associated_types)] + +impl SomeStruct { + fn process(v: T) -> ::R + where Self: GAT = T> + { + SomeStruct::do_something(v) + } +} diff --git a/tests/target/issue_4943.rs b/tests/target/issue_4943.rs index bc8f1a366da2..318f7ebed6e0 100644 --- a/tests/target/issue_4943.rs +++ b/tests/target/issue_4943.rs @@ -1,3 +1,5 @@ +#![feature(generic_associated_types)] + impl SomeStruct { fn process(v: T) -> ::R where From 33d1368674be78b5f058e1fb83d08e0d381ed2d6 Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 30 Aug 2021 21:09:21 +1200 Subject: [PATCH 040/401] test: add test for #4257 --- tests/source/issue_4257.rs | 13 +++++++++++++ tests/target/issue_4257.rs | 18 ++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 tests/source/issue_4257.rs create mode 100644 tests/target/issue_4257.rs diff --git a/tests/source/issue_4257.rs b/tests/source/issue_4257.rs new file mode 100644 index 000000000000..2b887fadb621 --- /dev/null +++ b/tests/source/issue_4257.rs @@ -0,0 +1,13 @@ +#![feature(generic_associated_types)] +#![allow(incomplete_features)] + +trait Trait { + type Type<'a> where T: 'a; + fn foo(x: &T) -> Self::Type<'_>; +} +impl Trait for () { + type Type<'a> where T: 'a = &'a T; + fn foo(x: &T) -> Self::Type<'_> { + x + } +} diff --git a/tests/target/issue_4257.rs b/tests/target/issue_4257.rs new file mode 100644 index 000000000000..1ebaaf2b6001 --- /dev/null +++ b/tests/target/issue_4257.rs @@ -0,0 +1,18 @@ +#![feature(generic_associated_types)] +#![allow(incomplete_features)] + +trait Trait { + type Type<'a> + where + T: 'a; + fn foo(x: &T) -> Self::Type<'_>; +} +impl Trait for () { + type Type<'a> + where + T: 'a, + = &'a T; + fn foo(x: &T) -> Self::Type<'_> { + x + } +} From bfc60466bdbab90d578c6912083fe2e238e29166 Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 30 Aug 2021 21:13:42 +1200 Subject: [PATCH 041/401] test: add test for #4954 --- tests/source/issue_4954.rs | 5 +++++ tests/target/issue_4954.rs | 7 +++++++ 2 files changed, 12 insertions(+) create mode 100644 tests/source/issue_4954.rs create mode 100644 tests/target/issue_4954.rs diff --git a/tests/source/issue_4954.rs b/tests/source/issue_4954.rs new file mode 100644 index 000000000000..8011c601b654 --- /dev/null +++ b/tests/source/issue_4954.rs @@ -0,0 +1,5 @@ +trait Foo { + type Arg<'a>; +} + +struct Bar(T) where for<'a> T: Foo = ()>; diff --git a/tests/target/issue_4954.rs b/tests/target/issue_4954.rs new file mode 100644 index 000000000000..aa5e79befe9c --- /dev/null +++ b/tests/target/issue_4954.rs @@ -0,0 +1,7 @@ +trait Foo { + type Arg<'a>; +} + +struct Bar(T) +where + for<'a> T: Foo = ()>; From ae5696a7c4c2edf642c37f06c526eb6bdf78c23b Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 30 Aug 2021 21:17:43 +1200 Subject: [PATCH 042/401] test: add test for #4322 --- tests/source/issue_4322.rs | 3 +++ tests/target/issue_4322.rs | 5 +++++ 2 files changed, 8 insertions(+) create mode 100644 tests/source/issue_4322.rs create mode 100644 tests/target/issue_4322.rs diff --git a/tests/source/issue_4322.rs b/tests/source/issue_4322.rs new file mode 100644 index 000000000000..b28cc7cdd12f --- /dev/null +++ b/tests/source/issue_4322.rs @@ -0,0 +1,3 @@ +trait Bar { + type X<'a> where Self: 'a; +} diff --git a/tests/target/issue_4322.rs b/tests/target/issue_4322.rs new file mode 100644 index 000000000000..0ec0547119f4 --- /dev/null +++ b/tests/target/issue_4322.rs @@ -0,0 +1,5 @@ +trait Bar { + type X<'a> + where + Self: 'a; +} From 59063e8b407acf2d187ba12616f9f36d041eff8c Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Thu, 16 Apr 2020 07:52:47 -0500 Subject: [PATCH 043/401] fix: newline width calc in combine w/ comments (#4123) --- src/comment.rs | 2 +- tests/target/issue_4031.rs | 21 +++++++++++++++ tests/target/issue_4110.rs | 55 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 tests/target/issue_4031.rs create mode 100644 tests/target/issue_4110.rs diff --git a/src/comment.rs b/src/comment.rs index 0f8118a408ec..62b624acd495 100644 --- a/src/comment.rs +++ b/src/comment.rs @@ -184,7 +184,7 @@ pub(crate) fn combine_strs_with_missing_comments( let missing_comment = rewrite_missing_comment(span, shape, context)?; if missing_comment.is_empty() { - if allow_extend && prev_str.len() + first_sep.len() + next_str.len() <= shape.width { + if allow_extend && one_line_width <= shape.width { result.push_str(first_sep); } else if !prev_str.is_empty() { result.push_str(&indent.to_string_with_newline(config)) diff --git a/tests/target/issue_4031.rs b/tests/target/issue_4031.rs new file mode 100644 index 000000000000..065d5395c7e7 --- /dev/null +++ b/tests/target/issue_4031.rs @@ -0,0 +1,21 @@ +fn foo() { + with_woff2_glyf_table("tests/fonts/woff2/SFNT-TTF-Composite.woff2", |glyf| { + let actual = glyf + .records + .iter() + .map(|glyph| match glyph { + GlyfRecord::Parsed( + found @ Glyph { + data: GlyphData::Composite { .. }, + .. + }, + ) => Some(found), + _ => None, + }) + .find(|candidate| candidate.is_some()) + .unwrap() + .unwrap(); + + assert_eq!(*actual, expected) + }); +} diff --git a/tests/target/issue_4110.rs b/tests/target/issue_4110.rs new file mode 100644 index 000000000000..4a58c3946e12 --- /dev/null +++ b/tests/target/issue_4110.rs @@ -0,0 +1,55 @@ +fn bindings() { + let err = match (place_desc, explanation) { + ( + Some(ref name), + BorrowExplanation::MustBeValidFor { + category: + category @ (ConstraintCategory::Return + | ConstraintCategory::CallArgument + | ConstraintCategory::OpaqueType), + from_closure: false, + ref region_name, + span, + .. + }, + ) if borrow_spans.for_generator() | borrow_spans.for_closure() => self + .report_escaping_closure_capture( + borrow_spans, + borrow_span, + region_name, + category, + span, + &format!("`{}`", name), + ), + ( + ref name, + BorrowExplanation::MustBeValidFor { + category: ConstraintCategory::Assignment, + from_closure: false, + region_name: + RegionName { + source: RegionNameSource::AnonRegionFromUpvar(upvar_span, ref upvar_name), + .. + }, + span, + .. + }, + ) => self.report_escaping_data(borrow_span, name, upvar_span, upvar_name, span), + (Some(name), explanation) => self.report_local_value_does_not_live_long_enough( + location, + &name, + &borrow, + drop_span, + borrow_spans, + explanation, + ), + (None, explanation) => self.report_temporary_value_does_not_live_long_enough( + location, + &borrow, + drop_span, + borrow_spans, + proper_span, + explanation, + ), + }; +} From a59cac29f4dcfd6e9aba03db07b33767809fcea0 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Mon, 6 Sep 2021 15:35:25 -0500 Subject: [PATCH 044/401] fix: use correct spans for params with attrs --- src/spanned.rs | 2 +- tests/source/issue_4032.rs | 4 ++++ tests/target/issue_4032.rs | 18 ++++++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 tests/source/issue_4032.rs create mode 100644 tests/target/issue_4032.rs diff --git a/src/spanned.rs b/src/spanned.rs index 7e3786b7cd94..8e6c75a3744a 100644 --- a/src/spanned.rs +++ b/src/spanned.rs @@ -104,7 +104,7 @@ impl Spanned for ast::Arm { impl Spanned for ast::Param { fn span(&self) -> Span { if crate::items::is_named_param(self) { - mk_sp(self.pat.span.lo(), self.ty.span.hi()) + mk_sp(crate::items::span_lo_for_param(self), self.ty.span.hi()) } else { self.ty.span } diff --git a/tests/source/issue_4032.rs b/tests/source/issue_4032.rs new file mode 100644 index 000000000000..11ded074c345 --- /dev/null +++ b/tests/source/issue_4032.rs @@ -0,0 +1,4 @@ +fn a1(#[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa] a: u8) {} +fn b1(#[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa] bb: u8) {} +fn a2(#[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa] a: u8) {} +fn b2(#[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa] bb: u8) {} diff --git a/tests/target/issue_4032.rs b/tests/target/issue_4032.rs new file mode 100644 index 000000000000..2e7e624ca6e6 --- /dev/null +++ b/tests/target/issue_4032.rs @@ -0,0 +1,18 @@ +fn a1( + #[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa] + a: u8, +) { +} +fn b1( + #[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa] + bb: u8, +) { +} +fn a2( + #[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa] a: u8, +) { +} +fn b2( + #[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa] bb: u8, +) { +} From c2f0e99d854d399880d05f546953659c2d170d54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Campinas?= Date: Tue, 6 Oct 2020 03:44:48 +0200 Subject: [PATCH 045/401] try to write the parameter on a new line in case the attribute/parameter together are over max_width --- src/comment.rs | 14 ++++++++------ src/items.rs | 33 +++++++++++++++++++++++++++------ 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/src/comment.rs b/src/comment.rs index 62b624acd495..42449d1060f7 100644 --- a/src/comment.rs +++ b/src/comment.rs @@ -10,7 +10,8 @@ use crate::rewrite::RewriteContext; use crate::shape::{Indent, Shape}; use crate::string::{rewrite_string, StringFormat}; use crate::utils::{ - count_newlines, first_line_width, last_line_width, trim_left_preserve_layout, unicode_str_width, + count_newlines, first_line_width, last_line_width, trim_left_preserve_layout, + trimmed_last_line_width, unicode_str_width, }; use crate::{ErrorKind, FormattingError}; @@ -171,11 +172,12 @@ pub(crate) fn combine_strs_with_missing_comments( String::with_capacity(prev_str.len() + next_str.len() + shape.indent.width() + 128); result.push_str(prev_str); let mut allow_one_line = !prev_str.contains('\n') && !next_str.contains('\n'); - let first_sep = if prev_str.is_empty() || next_str.is_empty() { - "" - } else { - " " - }; + let first_sep = + if prev_str.is_empty() || next_str.is_empty() || trimmed_last_line_width(prev_str) == 0 { + "" + } else { + " " + }; let mut one_line_width = last_line_width(prev_str) + first_line_width(next_str) + first_sep.len(); diff --git a/src/items.rs b/src/items.rs index 4fa7190c138d..6ba83b577ae2 100644 --- a/src/items.rs +++ b/src/items.rs @@ -1980,12 +1980,13 @@ impl Rewrite for ast::Param { has_multiple_attr_lines, ) } else if is_named_param(self) { + let param_name = &self + .pat + .rewrite(context, Shape::legacy(shape.width, shape.indent))?; let mut result = combine_strs_with_missing_comments( context, ¶m_attrs_result, - &self - .pat - .rewrite(context, Shape::legacy(shape.width, shape.indent))?, + param_name, span, shape, !has_multiple_attr_lines, @@ -1999,10 +2000,30 @@ impl Rewrite for ast::Param { result.push_str(&after_comment); let overhead = last_line_width(&result); let max_width = shape.width.checked_sub(overhead)?; - let ty_str = self + if let Some(ty_str) = self .ty - .rewrite(context, Shape::legacy(max_width, shape.indent))?; - result.push_str(&ty_str); + .rewrite(context, Shape::legacy(max_width, shape.indent)) + { + result.push_str(&ty_str); + } else { + result = combine_strs_with_missing_comments( + context, + &(param_attrs_result + &shape.to_string_with_newline(context.config)), + param_name, + span, + shape, + !has_multiple_attr_lines, + )?; + result.push_str(&before_comment); + result.push_str(colon_spaces(context.config)); + result.push_str(&after_comment); + let overhead = last_line_width(&result); + let max_width = shape.width.checked_sub(overhead)?; + let ty_str = self + .ty + .rewrite(context, Shape::legacy(max_width, shape.indent))?; + result.push_str(&ty_str); + } } Some(result) From 3d8cd57c2f166cb92204e95a5f4c73ab20fed4f2 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Mon, 6 Sep 2021 17:30:16 -0500 Subject: [PATCH 046/401] tests: add files for issue 4579 --- tests/source/issue_4579.rs | 15 +++++++++++++++ tests/target/issue_4579.rs | 16 ++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 tests/source/issue_4579.rs create mode 100644 tests/target/issue_4579.rs diff --git a/tests/source/issue_4579.rs b/tests/source/issue_4579.rs new file mode 100644 index 000000000000..73f345233ba7 --- /dev/null +++ b/tests/source/issue_4579.rs @@ -0,0 +1,15 @@ +// rustfmt-hard_tabs: true + +#[macro_export] +macro_rules! main { + () => { + #[spirv(fragment)] + pub fn main_fs( + mut out_color: ::spirv_std::storage_class::Output, + #[spirv(descriptor_set = 1)]iChannelResolution: ::spirv_std::storage_class::UniformConstant< + [::spirv_std::glam::Vec3A; 4], + >, + ) { + } + }; +} diff --git a/tests/target/issue_4579.rs b/tests/target/issue_4579.rs new file mode 100644 index 000000000000..7b0a5f3a62e4 --- /dev/null +++ b/tests/target/issue_4579.rs @@ -0,0 +1,16 @@ +// rustfmt-hard_tabs: true + +#[macro_export] +macro_rules! main { + () => { + #[spirv(fragment)] + pub fn main_fs( + mut out_color: ::spirv_std::storage_class::Output, + #[spirv(descriptor_set = 1)] + iChannelResolution: ::spirv_std::storage_class::UniformConstant< + [::spirv_std::glam::Vec3A; 4], + >, + ) { + } + }; +} From 57548aa096f31d90b9606f882f5815a93b2570b3 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Tue, 7 Sep 2021 19:49:56 -0500 Subject: [PATCH 047/401] fix: resolve idempotency issue in extern body elements --- src/missed_spans.rs | 20 ++++++++++++-------- tests/source/issue_4963.rs | 5 +++++ tests/target/issue_4963.rs | 9 +++++++++ 3 files changed, 26 insertions(+), 8 deletions(-) create mode 100644 tests/source/issue_4963.rs create mode 100644 tests/target/issue_4963.rs diff --git a/src/missed_spans.rs b/src/missed_spans.rs index 263d840785a2..28edcb784b40 100644 --- a/src/missed_spans.rs +++ b/src/missed_spans.rs @@ -51,6 +51,14 @@ impl<'a> FmtVisitor<'a> { } pub(crate) fn format_missing_with_indent(&mut self, end: BytePos) { + self.format_missing_indent(end, true) + } + + pub(crate) fn format_missing_no_indent(&mut self, end: BytePos) { + self.format_missing_indent(end, false) + } + + fn format_missing_indent(&mut self, end: BytePos, should_indent: bool) { let config = self.config; self.format_missing_inner(end, |this, last_snippet, snippet| { this.push_str(last_snippet.trim_end()); @@ -58,14 +66,10 @@ impl<'a> FmtVisitor<'a> { // No new lines in the snippet. this.push_str("\n"); } - let indent = this.block_indent.to_string(config); - this.push_str(&indent); - }) - } - - pub(crate) fn format_missing_no_indent(&mut self, end: BytePos) { - self.format_missing_inner(end, |this, last_snippet, _| { - this.push_str(last_snippet.trim_end()); + if should_indent { + let indent = this.block_indent.to_string(config); + this.push_str(&indent); + } }) } diff --git a/tests/source/issue_4963.rs b/tests/source/issue_4963.rs new file mode 100644 index 000000000000..32e1f6cd41bf --- /dev/null +++ b/tests/source/issue_4963.rs @@ -0,0 +1,5 @@ +mod test { + extern "C" {fn test();} +} + +extern "C" {fn test();} \ No newline at end of file diff --git a/tests/target/issue_4963.rs b/tests/target/issue_4963.rs new file mode 100644 index 000000000000..0c3c13579c13 --- /dev/null +++ b/tests/target/issue_4963.rs @@ -0,0 +1,9 @@ +mod test { + extern "C" { + fn test(); + } +} + +extern "C" { + fn test(); +} From a80688329c7bf696245b5ed6f3eeda6a80ab3721 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Wed, 8 Sep 2021 18:59:49 -0500 Subject: [PATCH 048/401] fix: handle param doc comments for macro scenarios --- src/items.rs | 9 ++++++--- tests/target/issue_4936.rs | 10 ++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 tests/target/issue_4936.rs diff --git a/src/items.rs b/src/items.rs index 6ba83b577ae2..e8eb1c5dfbb5 100644 --- a/src/items.rs +++ b/src/items.rs @@ -1960,14 +1960,17 @@ impl Rewrite for ast::Param { let param_attrs_result = self .attrs .rewrite(context, Shape::legacy(shape.width, shape.indent))?; - let (span, has_multiple_attr_lines) = if !self.attrs.is_empty() { + // N.B. Doc comments aren't typically valid syntax, but could appear + // in the presence of certain macros - https://github.com/rust-lang/rustfmt/issues/4936 + let (span, has_multiple_attr_lines, has_doc_comments) = if !self.attrs.is_empty() { let num_attrs = self.attrs.len(); ( mk_sp(self.attrs[num_attrs - 1].span.hi(), self.pat.span.lo()), param_attrs_result.contains('\n'), + self.attrs.iter().any(|a| a.is_doc_comment()), ) } else { - (mk_sp(self.span.lo(), self.span.lo()), false) + (mk_sp(self.span.lo(), self.span.lo()), false, false) }; if let Some(ref explicit_self) = self.to_self() { @@ -1989,7 +1992,7 @@ impl Rewrite for ast::Param { param_name, span, shape, - !has_multiple_attr_lines, + !has_multiple_attr_lines && !has_doc_comments, )?; if !is_empty_infer(&*self.ty, self.pat.span) { diff --git a/tests/target/issue_4936.rs b/tests/target/issue_4936.rs new file mode 100644 index 000000000000..c19e505fd03a --- /dev/null +++ b/tests/target/issue_4936.rs @@ -0,0 +1,10 @@ +#[discard_params_doc] +trait Trait { + fn foo( + &self, + /// some docs + bar: String, + /// another docs + baz: i32, + ); +} From b10ab51fed62f54010a786e3f7d018700464b786 Mon Sep 17 00:00:00 2001 From: Nipunn Koorapati Date: Sat, 7 Aug 2021 18:00:51 -0700 Subject: [PATCH 049/401] rustfmt doc code blocks with multiple comma-separated attributes Added test covering this. Chose to treat the code block as rust if and only if all of the comma-separated attributes are rust-valid. Chose to allow/preserve whitespace around commas Fixes #3158 --- src/comment.rs | 52 ++++++++++++--------------- tests/source/issue-3158.rs | 74 ++++++++++++++++++++++++++++++++++++++ tests/target/issue-3158.rs | 74 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 171 insertions(+), 29 deletions(-) create mode 100644 tests/source/issue-3158.rs create mode 100644 tests/target/issue-3158.rs diff --git a/src/comment.rs b/src/comment.rs index 42449d1060f7..608254248585 100644 --- a/src/comment.rs +++ b/src/comment.rs @@ -394,28 +394,26 @@ fn identify_comment( } } -/// Attributes for code blocks in rustdoc. -/// See https://doc.rust-lang.org/rustdoc/print.html#attributes +/// Enum indicating if the code block contains rust based on attributes enum CodeBlockAttribute { Rust, - Ignore, - Text, - ShouldPanic, - NoRun, - CompileFail, + NotRust, } impl CodeBlockAttribute { - fn new(attribute: &str) -> CodeBlockAttribute { - match attribute { - "rust" | "" => CodeBlockAttribute::Rust, - "ignore" => CodeBlockAttribute::Ignore, - "text" => CodeBlockAttribute::Text, - "should_panic" => CodeBlockAttribute::ShouldPanic, - "no_run" => CodeBlockAttribute::NoRun, - "compile_fail" => CodeBlockAttribute::CompileFail, - _ => CodeBlockAttribute::Text, + /// Parse comma separated attributes list. Return rust only if all + /// attributes are valid rust attributes + /// See https://doc.rust-lang.org/rustdoc/print.html#attributes + fn new(attributes: &str) -> CodeBlockAttribute { + for attribute in attributes.split(",") { + match attribute.trim() { + "" | "rust" | "should_panic" | "no_run" | "edition2015" | "edition2018" + | "edition2021" => (), + "ignore" | "compile_fail" | "text" => return CodeBlockAttribute::NotRust, + _ => return CodeBlockAttribute::NotRust, + } } + CodeBlockAttribute::Rust } } @@ -649,25 +647,21 @@ impl<'a> CommentRewrite<'a> { } else if self.code_block_attr.is_some() { if line.starts_with("```") { let code_block = match self.code_block_attr.as_ref().unwrap() { - CodeBlockAttribute::Ignore | CodeBlockAttribute::Text => { - trim_custom_comment_prefix(&self.code_block_buffer) - } - _ if self.code_block_buffer.is_empty() => String::new(), - _ => { + CodeBlockAttribute::Rust + if self.fmt.config.format_code_in_doc_comments() + && !self.code_block_buffer.is_empty() => + { let mut config = self.fmt.config.clone(); config.set().wrap_comments(false); - if config.format_code_in_doc_comments() { - if let Some(s) = - crate::format_code_block(&self.code_block_buffer, &config, false) - { - trim_custom_comment_prefix(&s.snippet) - } else { - trim_custom_comment_prefix(&self.code_block_buffer) - } + if let Some(s) = + crate::format_code_block(&self.code_block_buffer, &config, false) + { + trim_custom_comment_prefix(&s.snippet) } else { trim_custom_comment_prefix(&self.code_block_buffer) } } + _ => trim_custom_comment_prefix(&self.code_block_buffer), }; if !code_block.is_empty() { self.result.push_str(&self.comment_line_separator); diff --git a/tests/source/issue-3158.rs b/tests/source/issue-3158.rs new file mode 100644 index 000000000000..315073db6af5 --- /dev/null +++ b/tests/source/issue-3158.rs @@ -0,0 +1,74 @@ +// rustfmt-format_code_in_doc_comments: true + +/// Should format +/// ```rust +/// assert!( false ); +/// ``` +/// +/// Should format +/// ```rust,should_panic +/// assert!( false ); +/// ``` +/// +/// Should format +/// ```rust,should_panic,edition2018 +/// assert!( false ); +/// ``` +/// +/// Should format +/// ```rust , should_panic , edition2018 +/// assert!( false ); +/// ``` +/// +/// Should not format +/// ```ignore +/// assert!( false ); +/// ``` +/// +/// Should not format (not all are rust) +/// ```rust,ignore +/// assert!( false ); +/// ``` +/// +/// Should not format (rust compile_fail) +/// ```compile_fail +/// assert!( false ); +/// ``` +/// +/// Should not format (rust compile_fail) +/// ```rust,compile_fail +/// assert!( false ); +/// ``` +/// +/// Various unspecified ones that should format +/// ``` +/// assert!( false ); +/// ``` +/// +/// ```, +/// assert!( false ); +/// ``` +/// +/// ```,,,,, +/// assert!( false ); +/// ``` +/// +/// ```,,, rust ,, +/// assert!( false ); +/// ``` +/// +/// Should not format +/// ```,,, rust , ignore, +/// assert!( false ); +/// ``` +/// +/// Few empty ones +/// ``` +/// ``` +/// +/// ```rust +/// ``` +/// +/// ```ignore +/// ``` +fn foo() {} diff --git a/tests/target/issue-3158.rs b/tests/target/issue-3158.rs new file mode 100644 index 000000000000..4bbbdc1d0392 --- /dev/null +++ b/tests/target/issue-3158.rs @@ -0,0 +1,74 @@ +// rustfmt-format_code_in_doc_comments: true + +/// Should format +/// ```rust +/// assert!(false); +/// ``` +/// +/// Should format +/// ```rust,should_panic +/// assert!(false); +/// ``` +/// +/// Should format +/// ```rust,should_panic,edition2018 +/// assert!(false); +/// ``` +/// +/// Should format +/// ```rust , should_panic , edition2018 +/// assert!(false); +/// ``` +/// +/// Should not format +/// ```ignore +/// assert!( false ); +/// ``` +/// +/// Should not format (not all are rust) +/// ```rust,ignore +/// assert!( false ); +/// ``` +/// +/// Should not format (rust compile_fail) +/// ```compile_fail +/// assert!( false ); +/// ``` +/// +/// Should not format (rust compile_fail) +/// ```rust,compile_fail +/// assert!( false ); +/// ``` +/// +/// Various unspecified ones that should format +/// ``` +/// assert!(false); +/// ``` +/// +/// ```, +/// assert!(false); +/// ``` +/// +/// ```,,,,, +/// assert!(false); +/// ``` +/// +/// ```,,, rust ,, +/// assert!(false); +/// ``` +/// +/// Should not format +/// ```,,, rust , ignore, +/// assert!( false ); +/// ``` +/// +/// Few empty ones +/// ``` +/// ``` +/// +/// ```rust +/// ``` +/// +/// ```ignore +/// ``` +fn foo() {} From 67a59f6ee35c3cfd75e2aab5a8475608e167840b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Fri, 8 Nov 2019 23:50:18 +0200 Subject: [PATCH 050/401] opts: rephrase wording for --all and -p --- src/cargo-fmt/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cargo-fmt/main.rs b/src/cargo-fmt/main.rs index f8cf698525ba..f1125fa0bda1 100644 --- a/src/cargo-fmt/main.rs +++ b/src/cargo-fmt/main.rs @@ -36,7 +36,7 @@ pub struct Opts { #[structopt(long = "version")] version: bool, - /// Specify package to format (only usable in workspaces) + /// Specify package to format #[structopt(short = "p", long = "package", value_name = "package")] packages: Vec, @@ -53,7 +53,7 @@ pub struct Opts { #[structopt(name = "rustfmt_options", raw(true))] rustfmt_options: Vec, - /// Format all packages (only usable in workspaces) + /// Format all packages, and also their local path-based dependencies #[structopt(long = "all")] format_all: bool, } From d4ffd1efa4c83084f05f6e83585ae0a70450bcfd Mon Sep 17 00:00:00 2001 From: Seiichi Uchida Date: Fri, 10 Jul 2020 11:23:12 +0900 Subject: [PATCH 051/401] Support @generated marker to skip code formatting This is a copy of #4296 with these changes: * file is not reopened again to find if the file is generated * first five lines are scanned for `@generated` marker instead of one * no attempt is made to only search for marker in comments `@generated` marker is used by certain tools to understand that the file is generated, so it should be treated differently than a file written by a human: * linters should not be invoked on these files, * diffs in these files are less important, * and these files should not be reformatted. This PR proposes builtin support for `@generated` marker. I have not found a standard for a generated file marker, but: * Facebook [uses `@generated` marker](https://tinyurl.com/fb-generated) * Phabricator tool which was spawned from Facebook internal tool [also understands `@generated` marker](https://git.io/JnVHa) * Cargo inserts `@generated` marker into [generated Cargo.lock files](https://git.io/JnVHP) My personal story is that rust-protobuf project which I maintain was broken twice because of incompatibilities/bugs in rustfmt marker handling: [one](https://github.com/stepancheg/rust-protobuf/issues/493), [two](https://github.com/stepancheg/rust-protobuf/issues/551). (Also, rust-protobuf started generating `@generated` marker [6 years ago](https://git.io/JnV5h)). While rustfmt AST markers are useful to apply to a certain AST elements, disable whole-file-at-once all-tools-at-once text level marker might be easier to use and more reliable for generated code. --- Configurations.md | 9 +++++++++ src/config/mod.rs | 2 ++ src/formatting.rs | 9 ++++++++- src/formatting/generated.rs | 7 +++++++ src/syntux/session.rs | 6 ++++++ src/test/mod.rs | 2 +- tests/source/configs/format_generated_files/false.rs | 8 ++++++++ tests/source/configs/format_generated_files/true.rs | 8 ++++++++ tests/target/configs/format_generated_files/false.rs | 8 ++++++++ tests/target/configs/format_generated_files/true.rs | 6 ++++++ 10 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 src/formatting/generated.rs create mode 100644 tests/source/configs/format_generated_files/false.rs create mode 100644 tests/source/configs/format_generated_files/true.rs create mode 100644 tests/target/configs/format_generated_files/false.rs create mode 100644 tests/target/configs/format_generated_files/true.rs diff --git a/Configurations.md b/Configurations.md index 469deccc56ef..84e8c3f7db61 100644 --- a/Configurations.md +++ b/Configurations.md @@ -924,6 +924,15 @@ fn add_one(x: i32) -> i32 { } ``` +## `format_generated_files` + +Format generated files. A file is considered generated +if any of the first five lines contains `@generated` marker. + +- **Default value**: `false` +- **Possible values**: `true`, `false` +- **Stable**: No + ## `format_macro_matchers` Format the metavariable matching patterns in macros. diff --git a/src/config/mod.rs b/src/config/mod.rs index 8c04363b1fd4..3d6e32fdb60f 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -136,6 +136,7 @@ create_config! { inline_attribute_width: usize, 0, false, "Write an item and its attribute on the same line \ if their combined width is below a threshold"; + format_generated_files: bool, false, false, "Format generated files"; // Options that can change the source code beyond whitespace/blocks (somewhat linty things) merge_derives: bool, true, true, "Merge multiple `#[derive(...)]` into a single one"; @@ -604,6 +605,7 @@ blank_lines_lower_bound = 0 edition = "2015" version = "One" inline_attribute_width = 0 +format_generated_files = false merge_derives = true use_try_shorthand = false use_field_init_shorthand = false diff --git a/src/formatting.rs b/src/formatting.rs index e0403574eebc..9ef47b887cad 100644 --- a/src/formatting.rs +++ b/src/formatting.rs @@ -10,6 +10,7 @@ use rustc_span::Span; use self::newline_style::apply_newline_style; use crate::comment::{CharClasses, FullCodeCharKind}; use crate::config::{Config, FileName, Verbosity}; +use crate::formatting::generated::is_generated_file; use crate::issues::BadIssueSeeker; use crate::modules::Module; use crate::syntux::parser::{DirectoryOwnership, Parser, ParserError}; @@ -18,6 +19,7 @@ use crate::utils::count_newlines; use crate::visitor::FmtVisitor; use crate::{modules, source_file, ErrorKind, FormatReport, Input, Session}; +mod generated; mod newline_style; // A map of the files of a crate, with their new content @@ -103,7 +105,12 @@ fn format_project( context.parse_session.set_silent_emitter(); for (path, module) in files { - let should_ignore = !input_is_stdin && context.ignore_file(&path); + let source_file = context.parse_session.span_to_file_contents(module.span); + let src = source_file.src.as_ref().expect("SourceFile without src"); + + let should_ignore = (!input_is_stdin && context.ignore_file(&path)) + || (!config.format_generated_files() && is_generated_file(src)); + if (config.skip_children() && path != main_file) || should_ignore { continue; } diff --git a/src/formatting/generated.rs b/src/formatting/generated.rs new file mode 100644 index 000000000000..58f43f17ee15 --- /dev/null +++ b/src/formatting/generated.rs @@ -0,0 +1,7 @@ +/// Returns `true` if the given span is a part of generated files. +pub(super) fn is_generated_file(original_snippet: &str) -> bool { + original_snippet + .lines() + .take(5) // looking for marker only in the beginning of the file + .any(|line| line.contains("@generated")) +} diff --git a/src/syntux/session.rs b/src/syntux/session.rs index 870f0acfe395..94257e1ce7f1 100644 --- a/src/syntux/session.rs +++ b/src/syntux/session.rs @@ -175,6 +175,12 @@ impl ParseSess { self.parse_sess.source_map().span_to_filename(span).into() } + pub(crate) fn span_to_file_contents(&self, span: Span) -> Lrc { + self.parse_sess + .source_map() + .lookup_source_file(span.data().lo) + } + pub(crate) fn span_to_first_line_string(&self, span: Span) -> String { let file_lines = self.parse_sess.source_map().span_to_lines(span).ok(); diff --git a/src/test/mod.rs b/src/test/mod.rs index cb52346a13a4..ece1b91bfd7c 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -694,7 +694,7 @@ fn read_significant_comments(file_name: &Path) -> HashMap { reader .lines() .map(|line| line.expect("failed getting line")) - .take_while(|line| line_regex.is_match(line)) + .filter(|line| line_regex.is_match(line)) .filter_map(|line| { regex.captures_iter(&line).next().map(|capture| { ( diff --git a/tests/source/configs/format_generated_files/false.rs b/tests/source/configs/format_generated_files/false.rs new file mode 100644 index 000000000000..dec1e00d117b --- /dev/null +++ b/tests/source/configs/format_generated_files/false.rs @@ -0,0 +1,8 @@ +// @generated +// rustfmt-format_generated_files: false + +fn main() +{ + println!("hello, world") + ; +} diff --git a/tests/source/configs/format_generated_files/true.rs b/tests/source/configs/format_generated_files/true.rs new file mode 100644 index 000000000000..a25ddc25a6a4 --- /dev/null +++ b/tests/source/configs/format_generated_files/true.rs @@ -0,0 +1,8 @@ +// @generated +// rustfmt-format_generated_files: true + +fn main() +{ + println!("hello, world") + ; +} diff --git a/tests/target/configs/format_generated_files/false.rs b/tests/target/configs/format_generated_files/false.rs new file mode 100644 index 000000000000..dec1e00d117b --- /dev/null +++ b/tests/target/configs/format_generated_files/false.rs @@ -0,0 +1,8 @@ +// @generated +// rustfmt-format_generated_files: false + +fn main() +{ + println!("hello, world") + ; +} diff --git a/tests/target/configs/format_generated_files/true.rs b/tests/target/configs/format_generated_files/true.rs new file mode 100644 index 000000000000..5fea7e8b3413 --- /dev/null +++ b/tests/target/configs/format_generated_files/true.rs @@ -0,0 +1,6 @@ +// @generated +// rustfmt-format_generated_files: true + +fn main() { + println!("hello, world"); +} From 9d65b7dcd19557bca9aa4b175efa14a35db6d713 Mon Sep 17 00:00:00 2001 From: Arjen Laarhoven Date: Thu, 15 Jul 2021 19:55:52 +0200 Subject: [PATCH 052/401] feat: upper- or lowercase hexadecimal literals --- Configurations.md | 7 ++++++ src/config/mod.rs | 3 +++ src/config/options.rs | 11 ++++++++++ src/expr.rs | 33 +++++++++++++++++++++++++++- tests/source/hex_literal_lower.rs | 5 +++++ tests/source/hex_literal_upper.rs | 5 +++++ tests/target/hex_literal_lower.rs | 5 +++++ tests/target/hex_literal_preserve.rs | 5 +++++ tests/target/hex_literal_upper.rs | 5 +++++ 9 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 tests/source/hex_literal_lower.rs create mode 100644 tests/source/hex_literal_upper.rs create mode 100644 tests/target/hex_literal_lower.rs create mode 100644 tests/target/hex_literal_preserve.rs create mode 100644 tests/target/hex_literal_upper.rs diff --git a/Configurations.md b/Configurations.md index 84e8c3f7db61..b8f8f3053968 100644 --- a/Configurations.md +++ b/Configurations.md @@ -1056,6 +1056,13 @@ fn lorem() -> usize { See also: [`tab_spaces`](#tab_spaces). +## `hex_literal_case` + +Control the case of the letters in hexadecimal literal values + +- **Default value**: `Preserve` +- **Possible values**: `Upper`, `Lower` +- **Stable**: No ## `hide_parse_errors` diff --git a/src/config/mod.rs b/src/config/mod.rs index 3d6e32fdb60f..c6cee8ed2273 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -69,6 +69,8 @@ create_config! { format_macro_matchers: bool, false, false, "Format the metavariable matching patterns in macros"; format_macro_bodies: bool, true, false, "Format the bodies of macros"; + hex_literal_case: HexLiteralCase, HexLiteralCase::Preserve, false, + "Format hexadecimal integer literals"; // Single line expressions and items empty_item_single_line: bool, true, false, @@ -570,6 +572,7 @@ license_template_path = "" format_strings = false format_macro_matchers = false format_macro_bodies = true +hex_literal_case = "Preserve" empty_item_single_line = true struct_lit_single_line = true fn_single_line = false diff --git a/src/config/options.rs b/src/config/options.rs index db15ee97a40a..e92f8e8a5315 100644 --- a/src/config/options.rs +++ b/src/config/options.rs @@ -129,6 +129,17 @@ pub enum ImportGranularity { One, } +/// Controls how rustfmt should handle case in hexadecimal literals. +#[config_type] +pub enum HexLiteralCase { + /// Leave the literal as-is + Preserve, + /// Ensure all literals use uppercase lettering + Upper, + /// Ensure all literals use lowercase lettering + Lower, +} + #[config_type] pub enum ReportTactic { Always, diff --git a/src/expr.rs b/src/expr.rs index 6cfeb9977a96..01cc388c186e 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -13,7 +13,7 @@ use crate::comment::{ rewrite_missing_comment, CharClasses, FindUncommented, }; use crate::config::lists::*; -use crate::config::{Config, ControlBraceStyle, IndentStyle, Version}; +use crate::config::{Config, ControlBraceStyle, HexLiteralCase, IndentStyle, Version}; use crate::lists::{ definitive_tactic, itemize_list, shape_for_tactic, struct_lit_formatting, struct_lit_shape, struct_lit_tactic, write_list, ListFormatting, Separator, @@ -1168,6 +1168,7 @@ pub(crate) fn rewrite_literal( ) -> Option { match l.kind { ast::LitKind::Str(_, ast::StrStyle::Cooked) => rewrite_string_lit(context, l.span, shape), + ast::LitKind::Int(..) => rewrite_int_lit(context, l, shape), _ => wrap_str( context.snippet(l.span).to_owned(), context.config.max_width(), @@ -1202,6 +1203,36 @@ fn rewrite_string_lit(context: &RewriteContext<'_>, span: Span, shape: Shape) -> ) } +fn rewrite_int_lit(context: &RewriteContext<'_>, lit: &ast::Lit, shape: Shape) -> Option { + let span = lit.span; + let symbol = lit.token.symbol.as_str(); + + if symbol.starts_with("0x") { + let hex_lit = match context.config.hex_literal_case() { + HexLiteralCase::Preserve => None, + HexLiteralCase::Upper => Some(symbol[2..].to_ascii_uppercase()), + HexLiteralCase::Lower => Some(symbol[2..].to_ascii_lowercase()), + }; + if let Some(hex_lit) = hex_lit { + return wrap_str( + format!( + "0x{}{}", + hex_lit, + lit.token.suffix.map_or(String::new(), |s| s.to_string()) + ), + context.config.max_width(), + shape, + ); + } + } + + wrap_str( + context.snippet(span).to_owned(), + context.config.max_width(), + shape, + ) +} + fn choose_separator_tactic(context: &RewriteContext<'_>, span: Span) -> Option { if context.inside_macro() { if span_ends_with_comma(context, span) { diff --git a/tests/source/hex_literal_lower.rs b/tests/source/hex_literal_lower.rs new file mode 100644 index 000000000000..ce307b3aa521 --- /dev/null +++ b/tests/source/hex_literal_lower.rs @@ -0,0 +1,5 @@ +// rustfmt-hex_literal_case: Lower +fn main() { + let h1 = 0xCAFE_5EA7; + let h2 = 0xCAFE_F00Du32; +} diff --git a/tests/source/hex_literal_upper.rs b/tests/source/hex_literal_upper.rs new file mode 100644 index 000000000000..b1092ad71ba1 --- /dev/null +++ b/tests/source/hex_literal_upper.rs @@ -0,0 +1,5 @@ +// rustfmt-hex_literal_case: Upper +fn main() { + let h1 = 0xCaFE_5ea7; + let h2 = 0xCAFE_F00Du32; +} diff --git a/tests/target/hex_literal_lower.rs b/tests/target/hex_literal_lower.rs new file mode 100644 index 000000000000..5c27fded1674 --- /dev/null +++ b/tests/target/hex_literal_lower.rs @@ -0,0 +1,5 @@ +// rustfmt-hex_literal_case: Lower +fn main() { + let h1 = 0xcafe_5ea7; + let h2 = 0xcafe_f00du32; +} diff --git a/tests/target/hex_literal_preserve.rs b/tests/target/hex_literal_preserve.rs new file mode 100644 index 000000000000..e8774d0bb24e --- /dev/null +++ b/tests/target/hex_literal_preserve.rs @@ -0,0 +1,5 @@ +// rustfmt-hex_literal_case: Preserve +fn main() { + let h1 = 0xcAfE_5Ea7; + let h2 = 0xCaFe_F00du32; +} diff --git a/tests/target/hex_literal_upper.rs b/tests/target/hex_literal_upper.rs new file mode 100644 index 000000000000..48bb93d2c1c0 --- /dev/null +++ b/tests/target/hex_literal_upper.rs @@ -0,0 +1,5 @@ +// rustfmt-hex_literal_case: Upper +fn main() { + let h1 = 0xCAFE_5EA7; + let h2 = 0xCAFE_F00Du32; +} From 17cb2b147e11c9ad274b316026b1001fbd7007d4 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Sat, 26 Oct 2019 21:07:04 -0500 Subject: [PATCH 053/401] feat: add --check flag to cargo fmt (#3890) --- src/cargo-fmt/main.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/cargo-fmt/main.rs b/src/cargo-fmt/main.rs index f1125fa0bda1..89ce454ac4d0 100644 --- a/src/cargo-fmt/main.rs +++ b/src/cargo-fmt/main.rs @@ -56,6 +56,10 @@ pub struct Opts { /// Format all packages, and also their local path-based dependencies #[structopt(long = "all")] format_all: bool, + + /// Run rustfmt in check mode + #[structopt(long = "check")] + check: bool, } fn main() { @@ -104,6 +108,12 @@ fn execute() -> i32 { let strategy = CargoFmtStrategy::from_opts(&opts); let mut rustfmt_args = opts.rustfmt_options; + if opts.check { + let check_flag = String::from("--check"); + if !rustfmt_args.contains(&check_flag) { + rustfmt_args.push(check_flag); + } + } if let Some(message_format) = opts.message_format { if let Err(msg) = convert_message_format_to_rustfmt_args(&message_format, &mut rustfmt_args) { @@ -553,6 +563,7 @@ mod cargo_fmt_tests { assert_eq!(false, o.quiet); assert_eq!(false, o.verbose); assert_eq!(false, o.version); + assert_eq!(false, o.check); assert_eq!(empty, o.packages); assert_eq!(empty, o.rustfmt_options); assert_eq!(false, o.format_all); @@ -571,6 +582,7 @@ mod cargo_fmt_tests { "p2", "--message-format", "short", + "--check", "--", "--edition", "2018", @@ -578,6 +590,7 @@ mod cargo_fmt_tests { assert_eq!(true, o.quiet); assert_eq!(false, o.verbose); assert_eq!(false, o.version); + assert_eq!(true, o.check); assert_eq!(vec!["p1", "p2"], o.packages); assert_eq!(vec!["--edition", "2018"], o.rustfmt_options); assert_eq!(false, o.format_all); @@ -606,12 +619,12 @@ mod cargo_fmt_tests { fn mandatory_separator() { assert!( Opts::clap() - .get_matches_from_safe(&["test", "--check"]) + .get_matches_from_safe(&["test", "--emit"]) .is_err() ); assert!( !Opts::clap() - .get_matches_from_safe(&["test", "--", "--check"]) + .get_matches_from_safe(&["test", "--", "--emit"]) .is_err() ); } From 7aa69e5bc87f32fdcbf037abce2d575335480b9a Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Thu, 16 Sep 2021 21:25:09 -0500 Subject: [PATCH 054/401] refactor: use iter workaround for contains() gap --- src/cargo-fmt/main.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cargo-fmt/main.rs b/src/cargo-fmt/main.rs index 89ce454ac4d0..147b19d7a48c 100644 --- a/src/cargo-fmt/main.rs +++ b/src/cargo-fmt/main.rs @@ -109,9 +109,9 @@ fn execute() -> i32 { let strategy = CargoFmtStrategy::from_opts(&opts); let mut rustfmt_args = opts.rustfmt_options; if opts.check { - let check_flag = String::from("--check"); - if !rustfmt_args.contains(&check_flag) { - rustfmt_args.push(check_flag); + let check_flag = "--check"; + if !rustfmt_args.iter().any(|o| o == check_flag) { + rustfmt_args.push(check_flag.to_owned()); } } if let Some(message_format) = opts.message_format { From dd445aba080cd337300644c77137fedcad482623 Mon Sep 17 00:00:00 2001 From: Ulyssa Date: Wed, 15 Sep 2021 08:58:23 -0700 Subject: [PATCH 055/401] Trailing comma on match block goes missing when guard is on its own line --- src/matches.rs | 1 + tests/source/match-block-trailing-comma.rs | 8 ++++++++ tests/target/match-block-trailing-comma.rs | 10 ++++++++++ 3 files changed, 19 insertions(+) diff --git a/src/matches.rs b/src/matches.rs index 140ec226c02e..5a6ed0ec06e5 100644 --- a/src/matches.rs +++ b/src/matches.rs @@ -409,6 +409,7 @@ fn rewrite_match_body( } result.push_str(&nested_indent_str); result.push_str(&body_str); + result.push_str(&comma); return Some(result); } diff --git a/tests/source/match-block-trailing-comma.rs b/tests/source/match-block-trailing-comma.rs index e9daac13bf96..baa05b79c161 100644 --- a/tests/source/match-block-trailing-comma.rs +++ b/tests/source/match-block-trailing-comma.rs @@ -8,6 +8,14 @@ fn foo() { "line1"; "line2" } + ThisIsA::Guard if true => { + "line1"; + "line2" + } + ThisIsA::ReallyLongPattern(ThatWillForce::TheGuard, ToWrapOnto::TheFollowingLine) if true => { + "line1"; + "line2" + } b => (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb), } diff --git a/tests/target/match-block-trailing-comma.rs b/tests/target/match-block-trailing-comma.rs index 44d1f289f8e0..5ab433a2e6cf 100644 --- a/tests/target/match-block-trailing-comma.rs +++ b/tests/target/match-block-trailing-comma.rs @@ -8,6 +8,16 @@ fn foo() { "line1"; "line2" }, + ThisIsA::Guard if true => { + "line1"; + "line2" + }, + ThisIsA::ReallyLongPattern(ThatWillForce::TheGuard, ToWrapOnto::TheFollowingLine) + if true => + { + "line1"; + "line2" + }, b => ( aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, From 74df7b3265702949105161b36aee8b0975907210 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Sun, 19 Sep 2021 22:15:50 -0500 Subject: [PATCH 056/401] deps: update cargo_metadata to include 'path' --- Cargo.lock | 44 ++++++++++++++------------------------------ Cargo.toml | 2 +- 2 files changed, 15 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index be134f3e9754..7263f0474770 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -75,6 +75,15 @@ dependencies = [ "packed_simd_2", ] +[[package]] +name = "camino" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52d74260d9bf6944e2208aa46841b4b8f0d7ffc0849a06837b2f510337f86b2b" +dependencies = [ + "serde", +] + [[package]] name = "cargo-platform" version = "0.1.1" @@ -86,13 +95,13 @@ dependencies = [ [[package]] name = "cargo_metadata" -version = "0.12.3" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714a157da7991e23d90686b9524b9e12e0407a108647f52e9328f4b3d51ac7f" +checksum = "c297bd3135f558552f99a0daa180876984ea2c4ffa7470314540dff8c654109a" dependencies = [ + "camino", "cargo-platform", "semver", - "semver-parser", "serde", "serde_json", ] @@ -322,15 +331,6 @@ dependencies = [ "libm", ] -[[package]] -name = "pest" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" -dependencies = [ - "ucd-trie", -] - [[package]] name = "proc-macro-error" version = "0.4.11" @@ -475,23 +475,13 @@ dependencies = [ [[package]] name = "semver" -version = "0.11.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012" dependencies = [ - "semver-parser", "serde", ] -[[package]] -name = "semver-parser" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" -dependencies = [ - "pest", -] - [[package]] name = "serde" version = "1.0.126" @@ -641,12 +631,6 @@ dependencies = [ "serde", ] -[[package]] -name = "ucd-trie" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" - [[package]] name = "unicode-segmentation" version = "1.3.0" diff --git a/Cargo.toml b/Cargo.toml index e368f3eb1ec0..970b13eab353 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,7 +46,7 @@ log = "0.4.14" env_logger = "0.8" getopts = "0.2" derive-new = "0.5" -cargo_metadata = "0.12" +cargo_metadata = "0.14" bytecount = "0.6" unicode-width = "0.1.5" unicode_categories = "0.1.1" From 7f6229b9aada69389be4ead0ecbec8aed5892a24 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Mon, 20 Sep 2021 18:58:38 -0500 Subject: [PATCH 057/401] tests: restructure and extend cargo-fmt tests --- .gitignore | 1 + src/cargo-fmt/test/message_format.rs | 80 ++++++++++ src/cargo-fmt/test/mod.rs | 137 ++++++++++++++++++ src/cargo-fmt/test/targets.rs | 134 +++++++++++++++++ .../divergent-crate-dir-names/Cargo.toml | 13 ++ .../dependency-dir-name/Cargo.toml | 10 ++ .../dependency-dir-name/src/lib.rs | 7 + .../subdep-dir-name/Cargo.toml | 7 + .../subdep-dir-name/src/lib.rs | 7 + .../divergent-crate-dir-names/src/main.rs | 3 + .../workspaces/path-dep-above/e/Cargo.toml | 6 + .../workspaces/path-dep-above/e/src/main.rs | 1 + .../workspaces/path-dep-above/ws/Cargo.toml | 5 + .../workspaces/path-dep-above/ws/a/Cargo.toml | 6 + .../path-dep-above/ws/a/d/Cargo.toml | 7 + .../path-dep-above/ws/a/d/f/Cargo.toml | 4 + .../path-dep-above/ws/a/d/f/src/lib.rs | 1 + .../path-dep-above/ws/a/d/src/lib.rs | 1 + .../path-dep-above/ws/a/src/main.rs | 1 + .../workspaces/path-dep-above/ws/b/Cargo.toml | 6 + .../path-dep-above/ws/b/src/main.rs | 1 + .../workspaces/path-dep-above/ws/c/Cargo.toml | 4 + .../workspaces/path-dep-above/ws/c/src/lib.rs | 1 + 23 files changed, 443 insertions(+) create mode 100644 src/cargo-fmt/test/message_format.rs create mode 100644 src/cargo-fmt/test/mod.rs create mode 100644 src/cargo-fmt/test/targets.rs create mode 100644 tests/cargo-fmt/source/divergent-crate-dir-names/Cargo.toml create mode 100644 tests/cargo-fmt/source/divergent-crate-dir-names/dependency-dir-name/Cargo.toml create mode 100644 tests/cargo-fmt/source/divergent-crate-dir-names/dependency-dir-name/src/lib.rs create mode 100644 tests/cargo-fmt/source/divergent-crate-dir-names/dependency-dir-name/subdep-dir-name/Cargo.toml create mode 100644 tests/cargo-fmt/source/divergent-crate-dir-names/dependency-dir-name/subdep-dir-name/src/lib.rs create mode 100644 tests/cargo-fmt/source/divergent-crate-dir-names/src/main.rs create mode 100644 tests/cargo-fmt/source/workspaces/path-dep-above/e/Cargo.toml create mode 100644 tests/cargo-fmt/source/workspaces/path-dep-above/e/src/main.rs create mode 100644 tests/cargo-fmt/source/workspaces/path-dep-above/ws/Cargo.toml create mode 100644 tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/Cargo.toml create mode 100644 tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/d/Cargo.toml create mode 100644 tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/d/f/Cargo.toml create mode 100644 tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/d/f/src/lib.rs create mode 100644 tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/d/src/lib.rs create mode 100644 tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/src/main.rs create mode 100644 tests/cargo-fmt/source/workspaces/path-dep-above/ws/b/Cargo.toml create mode 100644 tests/cargo-fmt/source/workspaces/path-dep-above/ws/b/src/main.rs create mode 100644 tests/cargo-fmt/source/workspaces/path-dep-above/ws/c/Cargo.toml create mode 100644 tests/cargo-fmt/source/workspaces/path-dep-above/ws/c/src/lib.rs diff --git a/.gitignore b/.gitignore index 37adf8751ca8..71cf88f79e67 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ # Generated by Cargo # will have compiled files and executables /target +tests/cargo-fmt/**/target # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock diff --git a/src/cargo-fmt/test/message_format.rs b/src/cargo-fmt/test/message_format.rs new file mode 100644 index 000000000000..bf44924f13c3 --- /dev/null +++ b/src/cargo-fmt/test/message_format.rs @@ -0,0 +1,80 @@ +use super::*; + +#[test] +fn invalid_message_format() { + assert_eq!( + convert_message_format_to_rustfmt_args("awesome", &mut vec![]), + Err(String::from( + "invalid --message-format value: awesome. Allowed values are: short|json|human" + )), + ); +} + +#[test] +fn json_message_format_and_check_arg() { + let mut args = vec![String::from("--check")]; + assert_eq!( + convert_message_format_to_rustfmt_args("json", &mut args), + Err(String::from( + "cannot include --check arg when --message-format is set to json" + )), + ); +} + +#[test] +fn json_message_format_and_emit_arg() { + let mut args = vec![String::from("--emit"), String::from("checkstyle")]; + assert_eq!( + convert_message_format_to_rustfmt_args("json", &mut args), + Err(String::from( + "cannot include --emit arg when --message-format is set to json" + )), + ); +} + +#[test] +fn json_message_format() { + let mut args = vec![String::from("--edition"), String::from("2018")]; + assert!(convert_message_format_to_rustfmt_args("json", &mut args).is_ok()); + assert_eq!( + args, + vec![ + String::from("--edition"), + String::from("2018"), + String::from("--emit"), + String::from("json") + ] + ); +} + +#[test] +fn human_message_format() { + let exp_args = vec![String::from("--emit"), String::from("json")]; + let mut act_args = exp_args.clone(); + assert!(convert_message_format_to_rustfmt_args("human", &mut act_args).is_ok()); + assert_eq!(act_args, exp_args); +} + +#[test] +fn short_message_format() { + let mut args = vec![String::from("--check")]; + assert!(convert_message_format_to_rustfmt_args("short", &mut args).is_ok()); + assert_eq!(args, vec![String::from("--check"), String::from("-l")]); +} + +#[test] +fn short_message_format_included_short_list_files_flag() { + let mut args = vec![String::from("--check"), String::from("-l")]; + assert!(convert_message_format_to_rustfmt_args("short", &mut args).is_ok()); + assert_eq!(args, vec![String::from("--check"), String::from("-l")]); +} + +#[test] +fn short_message_format_included_long_list_files_flag() { + let mut args = vec![String::from("--check"), String::from("--files-with-diff")]; + assert!(convert_message_format_to_rustfmt_args("short", &mut args).is_ok()); + assert_eq!( + args, + vec![String::from("--check"), String::from("--files-with-diff")] + ); +} diff --git a/src/cargo-fmt/test/mod.rs b/src/cargo-fmt/test/mod.rs new file mode 100644 index 000000000000..360503632c7e --- /dev/null +++ b/src/cargo-fmt/test/mod.rs @@ -0,0 +1,137 @@ +use super::*; + +mod message_format; +mod targets; + +#[test] +fn default_options() { + let empty: Vec = vec![]; + let o = Opts::from_iter(&empty); + assert_eq!(false, o.quiet); + assert_eq!(false, o.verbose); + assert_eq!(false, o.version); + assert_eq!(false, o.check); + assert_eq!(empty, o.packages); + assert_eq!(empty, o.rustfmt_options); + assert_eq!(false, o.format_all); + assert_eq!(None, o.manifest_path); + assert_eq!(None, o.message_format); +} + +#[test] +fn good_options() { + let o = Opts::from_iter(&[ + "test", + "-q", + "-p", + "p1", + "-p", + "p2", + "--message-format", + "short", + "--check", + "--", + "--edition", + "2018", + ]); + assert_eq!(true, o.quiet); + assert_eq!(false, o.verbose); + assert_eq!(false, o.version); + assert_eq!(true, o.check); + assert_eq!(vec!["p1", "p2"], o.packages); + assert_eq!(vec!["--edition", "2018"], o.rustfmt_options); + assert_eq!(false, o.format_all); + assert_eq!(Some(String::from("short")), o.message_format); +} + +#[test] +fn unexpected_option() { + assert!( + Opts::clap() + .get_matches_from_safe(&["test", "unexpected"]) + .is_err() + ); +} + +#[test] +fn unexpected_flag() { + assert!( + Opts::clap() + .get_matches_from_safe(&["test", "--flag"]) + .is_err() + ); +} + +#[test] +fn mandatory_separator() { + assert!( + Opts::clap() + .get_matches_from_safe(&["test", "--emit"]) + .is_err() + ); + assert!( + !Opts::clap() + .get_matches_from_safe(&["test", "--", "--emit"]) + .is_err() + ); +} + +#[test] +fn multiple_packages_one_by_one() { + let o = Opts::from_iter(&[ + "test", + "-p", + "package1", + "--package", + "package2", + "-p", + "package3", + ]); + assert_eq!(3, o.packages.len()); +} + +#[test] +fn multiple_packages_grouped() { + let o = Opts::from_iter(&[ + "test", + "--package", + "package1", + "package2", + "-p", + "package3", + "package4", + ]); + assert_eq!(4, o.packages.len()); +} + +#[test] +fn empty_packages_1() { + assert!(Opts::clap().get_matches_from_safe(&["test", "-p"]).is_err()); +} + +#[test] +fn empty_packages_2() { + assert!( + Opts::clap() + .get_matches_from_safe(&["test", "-p", "--", "--check"]) + .is_err() + ); +} + +#[test] +fn empty_packages_3() { + assert!( + Opts::clap() + .get_matches_from_safe(&["test", "-p", "--verbose"]) + .is_err() + ); +} + +#[test] +fn empty_packages_4() { + assert!( + Opts::clap() + .get_matches_from_safe(&["test", "-p", "--check"]) + .is_err() + ); +} diff --git a/src/cargo-fmt/test/targets.rs b/src/cargo-fmt/test/targets.rs new file mode 100644 index 000000000000..b7e7fabdf715 --- /dev/null +++ b/src/cargo-fmt/test/targets.rs @@ -0,0 +1,134 @@ +use super::*; + +struct ExpTarget { + path: &'static str, + edition: &'static str, + kind: &'static str, +} + +mod all_targets { + use super::*; + + fn assert_correct_targets_loaded( + manifest_suffix: &str, + source_root: &str, + exp_targets: &[ExpTarget], + exp_num_targets: usize, + ) { + let root_path = Path::new("tests/cargo-fmt/source").join(source_root); + let get_path = |exp: &str| PathBuf::from(&root_path).join(exp).canonicalize().unwrap(); + let manifest_path = Path::new(&root_path).join(manifest_suffix); + let targets = get_targets(&CargoFmtStrategy::All, Some(manifest_path.as_path())) + .expect("Targets should have been loaded"); + + assert_eq!(targets.len(), exp_num_targets); + + for target in exp_targets { + assert!(targets.contains(&Target { + path: get_path(target.path), + edition: target.edition.to_owned(), + kind: target.kind.to_owned(), + })); + } + } + + mod different_crate_and_dir_names { + use super::*; + + fn assert_correct_targets_loaded(manifest_suffix: &str) { + let exp_targets = vec![ + ExpTarget { + path: "dependency-dir-name/subdep-dir-name/src/lib.rs", + edition: "2018", + kind: "lib", + }, + ExpTarget { + path: "dependency-dir-name/src/lib.rs", + edition: "2018", + kind: "lib", + }, + ExpTarget { + path: "src/main.rs", + edition: "2018", + kind: "main", + }, + ]; + super::assert_correct_targets_loaded( + manifest_suffix, + "divergent-crate-dir-names", + &exp_targets, + 3, + ); + } + + #[test] + fn correct_targets_from_root() { + assert_correct_targets_loaded("Cargo.toml"); + } + + #[test] + fn correct_targets_from_sub_local_dep() { + assert_correct_targets_loaded("dependency-dir-name/Cargo.toml"); + } + } + + mod workspaces { + use super::*; + + fn assert_correct_targets_loaded(manifest_suffix: &str) { + let exp_targets = vec![ + ExpTarget { + path: "ws/a/src/main.rs", + edition: "2018", + kind: "bin", + }, + ExpTarget { + path: "ws/b/src/main.rs", + edition: "2018", + kind: "bin", + }, + ExpTarget { + path: "ws/c/src/lib.rs", + edition: "2018", + kind: "lib", + }, + ExpTarget { + path: "ws/a/d/src/lib.rs", + edition: "2018", + kind: "lib", + }, + ExpTarget { + path: "e/src/main.rs", + edition: "2018", + kind: "main", + }, + ExpTarget { + path: "ws/a/d/f/src/lib.rs", + edition: "2018", + kind: "lib", + }, + ]; + super::assert_correct_targets_loaded( + manifest_suffix, + "workspaces/path-dep-above", + &exp_targets, + 6, + ); + } + + #[test] + fn includes_outside_workspace_deps() { + assert_correct_targets_loaded("ws/Cargo.toml"); + } + + #[test] + fn includes_workspace_from_dep_above() { + assert_correct_targets_loaded("e/Cargo.toml"); + } + + #[test] + fn includes_all_packages_from_workspace_subdir() { + assert_correct_targets_loaded("ws/a/d/f/Cargo.toml"); + } + } +} diff --git a/tests/cargo-fmt/source/divergent-crate-dir-names/Cargo.toml b/tests/cargo-fmt/source/divergent-crate-dir-names/Cargo.toml new file mode 100644 index 000000000000..315364a64573 --- /dev/null +++ b/tests/cargo-fmt/source/divergent-crate-dir-names/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "cargo-fmt-test" +version = "0.1.0" +authors = ["calebcartwright"] +edition = "2018" + +[dependencies] +indexmap = "1.0.2" + +[workspace] +members = [ + "dependency-dir-name", +] \ No newline at end of file diff --git a/tests/cargo-fmt/source/divergent-crate-dir-names/dependency-dir-name/Cargo.toml b/tests/cargo-fmt/source/divergent-crate-dir-names/dependency-dir-name/Cargo.toml new file mode 100644 index 000000000000..4493882bf40a --- /dev/null +++ b/tests/cargo-fmt/source/divergent-crate-dir-names/dependency-dir-name/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "dependency-crate-name" +version = "0.1.0" +authors = ["calebcartwright"] +edition = "2018" + +[dependencies] +subdep-crate-name = { path = "subdep-dir-name" } +indexmap = "1.0.2" +rusty-hook = "0.8.4" \ No newline at end of file diff --git a/tests/cargo-fmt/source/divergent-crate-dir-names/dependency-dir-name/src/lib.rs b/tests/cargo-fmt/source/divergent-crate-dir-names/dependency-dir-name/src/lib.rs new file mode 100644 index 000000000000..e93b18d725b9 --- /dev/null +++ b/tests/cargo-fmt/source/divergent-crate-dir-names/dependency-dir-name/src/lib.rs @@ -0,0 +1,7 @@ +#[cfg(test)] +mod tests { +#[test] +fn it_works() { + assert_eq!(2 + 2, 4); +} +} diff --git a/tests/cargo-fmt/source/divergent-crate-dir-names/dependency-dir-name/subdep-dir-name/Cargo.toml b/tests/cargo-fmt/source/divergent-crate-dir-names/dependency-dir-name/subdep-dir-name/Cargo.toml new file mode 100644 index 000000000000..7dad09f4077b --- /dev/null +++ b/tests/cargo-fmt/source/divergent-crate-dir-names/dependency-dir-name/subdep-dir-name/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "subdep-crate-name" +version = "0.1.0" +authors = ["calebcartwright"] +edition = "2018" + +[dependencies] diff --git a/tests/cargo-fmt/source/divergent-crate-dir-names/dependency-dir-name/subdep-dir-name/src/lib.rs b/tests/cargo-fmt/source/divergent-crate-dir-names/dependency-dir-name/subdep-dir-name/src/lib.rs new file mode 100644 index 000000000000..1c08c1c4fd38 --- /dev/null +++ b/tests/cargo-fmt/source/divergent-crate-dir-names/dependency-dir-name/subdep-dir-name/src/lib.rs @@ -0,0 +1,7 @@ +#[cfg(test)] +mod tests { +#[test] +fn sub_test_that_works() { + assert_eq!(3 + 3, 6); +} + } diff --git a/tests/cargo-fmt/source/divergent-crate-dir-names/src/main.rs b/tests/cargo-fmt/source/divergent-crate-dir-names/src/main.rs new file mode 100644 index 000000000000..f5c339a8dd14 --- /dev/null +++ b/tests/cargo-fmt/source/divergent-crate-dir-names/src/main.rs @@ -0,0 +1,3 @@ +fn main() { +println!("Hello, world!"); +} diff --git a/tests/cargo-fmt/source/workspaces/path-dep-above/e/Cargo.toml b/tests/cargo-fmt/source/workspaces/path-dep-above/e/Cargo.toml new file mode 100644 index 000000000000..df1886c82be3 --- /dev/null +++ b/tests/cargo-fmt/source/workspaces/path-dep-above/e/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "e" +version = "0.1.0" +edition = "2018" +[dependencies] +c = { path = "../ws/c" } diff --git a/tests/cargo-fmt/source/workspaces/path-dep-above/e/src/main.rs b/tests/cargo-fmt/source/workspaces/path-dep-above/e/src/main.rs new file mode 100644 index 000000000000..1c26a3895f37 --- /dev/null +++ b/tests/cargo-fmt/source/workspaces/path-dep-above/e/src/main.rs @@ -0,0 +1 @@ +struct E{ } diff --git a/tests/cargo-fmt/source/workspaces/path-dep-above/ws/Cargo.toml b/tests/cargo-fmt/source/workspaces/path-dep-above/ws/Cargo.toml new file mode 100644 index 000000000000..202739b613b8 --- /dev/null +++ b/tests/cargo-fmt/source/workspaces/path-dep-above/ws/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = [ + "a", + "b" +] \ No newline at end of file diff --git a/tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/Cargo.toml b/tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/Cargo.toml new file mode 100644 index 000000000000..712a113448fb --- /dev/null +++ b/tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "a" +version = "0.1.0" +edition = "2018" +[dependencies] +d = { path = "./d" } diff --git a/tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/d/Cargo.toml b/tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/d/Cargo.toml new file mode 100644 index 000000000000..fb0f06fe5fce --- /dev/null +++ b/tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/d/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "d" +version = "0.1.0" +edition = "2018" +[dependencies] +e = { path = "../../../e" } +f = { path = "f" } diff --git a/tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/d/f/Cargo.toml b/tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/d/f/Cargo.toml new file mode 100644 index 000000000000..5c4fa5617886 --- /dev/null +++ b/tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/d/f/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "f" +version = "0.1.0" +edition = "2018" diff --git a/tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/d/f/src/lib.rs b/tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/d/f/src/lib.rs new file mode 100644 index 000000000000..c655c4d5e1a8 --- /dev/null +++ b/tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/d/f/src/lib.rs @@ -0,0 +1 @@ +struct F{ } \ No newline at end of file diff --git a/tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/d/src/lib.rs b/tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/d/src/lib.rs new file mode 100644 index 000000000000..04e6e4cb9402 --- /dev/null +++ b/tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/d/src/lib.rs @@ -0,0 +1 @@ +struct D{ } \ No newline at end of file diff --git a/tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/src/main.rs b/tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/src/main.rs new file mode 100644 index 000000000000..04e6e4cb9402 --- /dev/null +++ b/tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/src/main.rs @@ -0,0 +1 @@ +struct D{ } \ No newline at end of file diff --git a/tests/cargo-fmt/source/workspaces/path-dep-above/ws/b/Cargo.toml b/tests/cargo-fmt/source/workspaces/path-dep-above/ws/b/Cargo.toml new file mode 100644 index 000000000000..47a24ff4f275 --- /dev/null +++ b/tests/cargo-fmt/source/workspaces/path-dep-above/ws/b/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "b" +version = "0.1.0" +edition = "2018" +[dependencies] +c = { path = "../c" } diff --git a/tests/cargo-fmt/source/workspaces/path-dep-above/ws/b/src/main.rs b/tests/cargo-fmt/source/workspaces/path-dep-above/ws/b/src/main.rs new file mode 100644 index 000000000000..4833bbc69b48 --- /dev/null +++ b/tests/cargo-fmt/source/workspaces/path-dep-above/ws/b/src/main.rs @@ -0,0 +1 @@ +struct B{ } \ No newline at end of file diff --git a/tests/cargo-fmt/source/workspaces/path-dep-above/ws/c/Cargo.toml b/tests/cargo-fmt/source/workspaces/path-dep-above/ws/c/Cargo.toml new file mode 100644 index 000000000000..49fa6c395eb6 --- /dev/null +++ b/tests/cargo-fmt/source/workspaces/path-dep-above/ws/c/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "c" +version = "0.1.0" +edition = "2018" diff --git a/tests/cargo-fmt/source/workspaces/path-dep-above/ws/c/src/lib.rs b/tests/cargo-fmt/source/workspaces/path-dep-above/ws/c/src/lib.rs new file mode 100644 index 000000000000..1245ac91d60a --- /dev/null +++ b/tests/cargo-fmt/source/workspaces/path-dep-above/ws/c/src/lib.rs @@ -0,0 +1 @@ +struct C{ } \ No newline at end of file From 4b9d637f5846b92f2c17b98985c47dea795064d2 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Mon, 20 Sep 2021 19:09:16 -0500 Subject: [PATCH 058/401] refactor: simplify local dep lookups --- src/cargo-fmt/main.rs | 280 ++++-------------------------------------- 1 file changed, 26 insertions(+), 254 deletions(-) diff --git a/src/cargo-fmt/main.rs b/src/cargo-fmt/main.rs index 147b19d7a48c..1d423ac34919 100644 --- a/src/cargo-fmt/main.rs +++ b/src/cargo-fmt/main.rs @@ -17,6 +17,10 @@ use std::str; use structopt::StructOpt; +#[path = "test/mod.rs"] +#[cfg(test)] +mod cargo_fmt_tests; + #[derive(StructOpt, Debug)] #[structopt( bin_name = "cargo fmt", @@ -356,7 +360,7 @@ fn get_targets_root_only( manifest_path: Option<&Path>, targets: &mut BTreeSet, ) -> Result<(), io::Error> { - let metadata = get_cargo_metadata(manifest_path, false)?; + let metadata = get_cargo_metadata(manifest_path)?; let workspace_root_path = PathBuf::from(&metadata.workspace_root).canonicalize()?; let (in_workspace_root, current_dir_manifest) = if let Some(target_manifest) = manifest_path { ( @@ -400,34 +404,29 @@ fn get_targets_recursive( mut targets: &mut BTreeSet, visited: &mut BTreeSet, ) -> Result<(), io::Error> { - let metadata = get_cargo_metadata(manifest_path, false)?; - let metadata_with_deps = get_cargo_metadata(manifest_path, true)?; - - for package in metadata.packages { + let metadata = get_cargo_metadata(manifest_path)?; + for package in &metadata.packages { add_targets(&package.targets, &mut targets); - // Look for local dependencies. - for dependency in package.dependencies { - if dependency.source.is_some() || visited.contains(&dependency.name) { + // Look for local dependencies using information available since cargo v1.51 + // It's theoretically possible someone could use a newer version of rustfmt with + // a much older version of `cargo`, but we don't try to explicitly support that scenario. + // If someone reports an issue with path-based deps not being formatted, be sure to + // confirm their version of `cargo` (not `cargo-fmt`) is >= v1.51 + // https://github.com/rust-lang/cargo/pull/8994 + for dependency in &package.dependencies { + if dependency.path.is_none() || visited.contains(&dependency.name) { continue; } - let dependency_package = metadata_with_deps - .packages - .iter() - .find(|p| p.name == dependency.name && p.source.is_none()); - let manifest_path = if let Some(dep_pkg) = dependency_package { - PathBuf::from(&dep_pkg.manifest_path) - } else { - let mut package_manifest_path = PathBuf::from(&package.manifest_path); - package_manifest_path.pop(); - package_manifest_path.push(&dependency.name); - package_manifest_path.push("Cargo.toml"); - package_manifest_path - }; - - if manifest_path.exists() { - visited.insert(dependency.name); + let manifest_path = PathBuf::from(dependency.path.as_ref().unwrap()).join("Cargo.toml"); + if manifest_path.exists() + && !metadata + .packages + .iter() + .any(|p| p.manifest_path.eq(&manifest_path)) + { + visited.insert(dependency.name.to_owned()); get_targets_recursive(Some(&manifest_path), &mut targets, visited)?; } } @@ -441,8 +440,7 @@ fn get_targets_with_hitlist( hitlist: &[String], targets: &mut BTreeSet, ) -> Result<(), io::Error> { - let metadata = get_cargo_metadata(manifest_path, false)?; - + let metadata = get_cargo_metadata(manifest_path)?; let mut workspace_hitlist: BTreeSet<&String> = BTreeSet::from_iter(hitlist); for package in metadata.packages { @@ -527,14 +525,9 @@ fn run_rustfmt( .unwrap_or(SUCCESS)) } -fn get_cargo_metadata( - manifest_path: Option<&Path>, - include_deps: bool, -) -> Result { +fn get_cargo_metadata(manifest_path: Option<&Path>) -> Result { let mut cmd = cargo_metadata::MetadataCommand::new(); - if !include_deps { - cmd.no_deps(); - } + cmd.no_deps(); if let Some(manifest_path) = manifest_path { cmd.manifest_path(manifest_path); } @@ -551,224 +544,3 @@ fn get_cargo_metadata( } } } - -#[cfg(test)] -mod cargo_fmt_tests { - use super::*; - - #[test] - fn default_options() { - let empty: Vec = vec![]; - let o = Opts::from_iter(&empty); - assert_eq!(false, o.quiet); - assert_eq!(false, o.verbose); - assert_eq!(false, o.version); - assert_eq!(false, o.check); - assert_eq!(empty, o.packages); - assert_eq!(empty, o.rustfmt_options); - assert_eq!(false, o.format_all); - assert_eq!(None, o.manifest_path); - assert_eq!(None, o.message_format); - } - - #[test] - fn good_options() { - let o = Opts::from_iter(&[ - "test", - "-q", - "-p", - "p1", - "-p", - "p2", - "--message-format", - "short", - "--check", - "--", - "--edition", - "2018", - ]); - assert_eq!(true, o.quiet); - assert_eq!(false, o.verbose); - assert_eq!(false, o.version); - assert_eq!(true, o.check); - assert_eq!(vec!["p1", "p2"], o.packages); - assert_eq!(vec!["--edition", "2018"], o.rustfmt_options); - assert_eq!(false, o.format_all); - assert_eq!(Some(String::from("short")), o.message_format); - } - - #[test] - fn unexpected_option() { - assert!( - Opts::clap() - .get_matches_from_safe(&["test", "unexpected"]) - .is_err() - ); - } - - #[test] - fn unexpected_flag() { - assert!( - Opts::clap() - .get_matches_from_safe(&["test", "--flag"]) - .is_err() - ); - } - - #[test] - fn mandatory_separator() { - assert!( - Opts::clap() - .get_matches_from_safe(&["test", "--emit"]) - .is_err() - ); - assert!( - !Opts::clap() - .get_matches_from_safe(&["test", "--", "--emit"]) - .is_err() - ); - } - - #[test] - fn multiple_packages_one_by_one() { - let o = Opts::from_iter(&[ - "test", - "-p", - "package1", - "--package", - "package2", - "-p", - "package3", - ]); - assert_eq!(3, o.packages.len()); - } - - #[test] - fn multiple_packages_grouped() { - let o = Opts::from_iter(&[ - "test", - "--package", - "package1", - "package2", - "-p", - "package3", - "package4", - ]); - assert_eq!(4, o.packages.len()); - } - - #[test] - fn empty_packages_1() { - assert!(Opts::clap().get_matches_from_safe(&["test", "-p"]).is_err()); - } - - #[test] - fn empty_packages_2() { - assert!( - Opts::clap() - .get_matches_from_safe(&["test", "-p", "--", "--check"]) - .is_err() - ); - } - - #[test] - fn empty_packages_3() { - assert!( - Opts::clap() - .get_matches_from_safe(&["test", "-p", "--verbose"]) - .is_err() - ); - } - - #[test] - fn empty_packages_4() { - assert!( - Opts::clap() - .get_matches_from_safe(&["test", "-p", "--check"]) - .is_err() - ); - } - - mod convert_message_format_to_rustfmt_args_tests { - use super::*; - - #[test] - fn invalid_message_format() { - assert_eq!( - convert_message_format_to_rustfmt_args("awesome", &mut vec![]), - Err(String::from( - "invalid --message-format value: awesome. Allowed values are: short|json|human" - )), - ); - } - - #[test] - fn json_message_format_and_check_arg() { - let mut args = vec![String::from("--check")]; - assert_eq!( - convert_message_format_to_rustfmt_args("json", &mut args), - Err(String::from( - "cannot include --check arg when --message-format is set to json" - )), - ); - } - - #[test] - fn json_message_format_and_emit_arg() { - let mut args = vec![String::from("--emit"), String::from("checkstyle")]; - assert_eq!( - convert_message_format_to_rustfmt_args("json", &mut args), - Err(String::from( - "cannot include --emit arg when --message-format is set to json" - )), - ); - } - - #[test] - fn json_message_format() { - let mut args = vec![String::from("--edition"), String::from("2018")]; - assert!(convert_message_format_to_rustfmt_args("json", &mut args).is_ok()); - assert_eq!( - args, - vec![ - String::from("--edition"), - String::from("2018"), - String::from("--emit"), - String::from("json") - ] - ); - } - - #[test] - fn human_message_format() { - let exp_args = vec![String::from("--emit"), String::from("json")]; - let mut act_args = exp_args.clone(); - assert!(convert_message_format_to_rustfmt_args("human", &mut act_args).is_ok()); - assert_eq!(act_args, exp_args); - } - - #[test] - fn short_message_format() { - let mut args = vec![String::from("--check")]; - assert!(convert_message_format_to_rustfmt_args("short", &mut args).is_ok()); - assert_eq!(args, vec![String::from("--check"), String::from("-l")]); - } - - #[test] - fn short_message_format_included_short_list_files_flag() { - let mut args = vec![String::from("--check"), String::from("-l")]; - assert!(convert_message_format_to_rustfmt_args("short", &mut args).is_ok()); - assert_eq!(args, vec![String::from("--check"), String::from("-l")]); - } - - #[test] - fn short_message_format_included_long_list_files_flag() { - let mut args = vec![String::from("--check"), String::from("--files-with-diff")]; - assert!(convert_message_format_to_rustfmt_args("short", &mut args).is_ok()); - assert_eq!( - args, - vec![String::from("--check"), String::from("--files-with-diff")] - ); - } - } -} From cb144c35e7d7151bb4b6ccd9b30a4141959166d1 Mon Sep 17 00:00:00 2001 From: Lucas Kent Date: Fri, 10 Sep 2021 17:50:40 +1000 Subject: [PATCH 059/401] In Configurations.md demonstrate both cases for noop selections --- Configurations.md | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/Configurations.md b/Configurations.md index b8f8f3053968..ff83f02f87ba 100644 --- a/Configurations.md +++ b/Configurations.md @@ -1684,6 +1684,9 @@ pub enum Foo {} #### `false`: ```rust +#[derive(Eq, PartialEq, Debug, Copy, Clone)] +pub enum Bar {} + #[derive(Eq, PartialEq)] #[derive(Debug)] #[derive(Copy, Clone)] @@ -1857,6 +1860,9 @@ Convert `#![doc]` and `#[doc]` attributes to `//!` and `///` doc comments. #![doc = "Example documentation"] #[doc = "Example item documentation"] +pub enum Bar {} + +/// Example item documentation pub enum Foo {} ``` @@ -1971,6 +1977,8 @@ fn main() { #### `false`: ```rust fn main() { + (foo()); + ((((foo())))); } ``` @@ -1996,6 +2004,14 @@ impl Iterator for Dummy { type Item = i32; } + +impl Iterator for Dummy { + type Item = i32; + + fn next(&mut self) -> Option { + None + } +} ``` #### `true` @@ -2552,7 +2568,8 @@ fn main() { let x = 1; let y = 2; let z = 3; - let a = Foo { x: x, y: y, z: z }; + let a = Foo { x, y, z }; + let b = Foo { x: x, y: y, z: z }; } ``` @@ -2721,6 +2738,8 @@ Replace uses of the try! macro by the ? shorthand ```rust fn main() { + let lorem = ipsum.map(|dolor| dolor.sit())?; + let lorem = try!(ipsum.map(|dolor| dolor.sit())); } ``` @@ -2792,6 +2811,12 @@ Break comments to fit on the line #### `false` (default): ```rust +// 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. + // 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. ``` From a5138b34d5f2644d916e906a3d460856a1b1a42a Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Mon, 27 Sep 2021 16:55:23 -0400 Subject: [PATCH 060/401] Prevent removal of qualified path for tuple struct inside macro fixes 5005 This was very similar to 4964 and the fix was to extract and pass along the qself of the ``PatKind::TupleStruct`` --- src/patterns.rs | 5 +++-- tests/target/issue-5005/minimum_example.rs | 9 +++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 tests/target/issue-5005/minimum_example.rs diff --git a/src/patterns.rs b/src/patterns.rs index 0c6a6f3e8143..4c6a2d5d75b9 100644 --- a/src/patterns.rs +++ b/src/patterns.rs @@ -226,8 +226,9 @@ impl Rewrite for Pat { PatKind::Path(ref q_self, ref path) => { rewrite_path(context, PathContext::Expr, q_self.as_ref(), path, shape) } - PatKind::TupleStruct(_, ref path, ref pat_vec) => { - let path_str = rewrite_path(context, PathContext::Expr, None, path, shape)?; + PatKind::TupleStruct(ref q_self, ref path, ref pat_vec) => { + let path_str = + rewrite_path(context, PathContext::Expr, q_self.as_ref(), path, shape)?; rewrite_tuple_pat(pat_vec, Some(path_str), self.span, context, shape) } PatKind::Lit(ref expr) => expr.rewrite(context, shape), diff --git a/tests/target/issue-5005/minimum_example.rs b/tests/target/issue-5005/minimum_example.rs new file mode 100644 index 000000000000..11cc645fa535 --- /dev/null +++ b/tests/target/issue-5005/minimum_example.rs @@ -0,0 +1,9 @@ +#![feature(more_qualified_paths)] +macro_rules! show { + ($ty:ty, $ex:expr) => { + match $ex { + <$ty>::A(_val) => println!("got a"), // formatting should not remove <$ty>:: + <$ty>::B => println!("got b"), + } + }; +} From e3203ef5e6902e0bbb7b62ebd34b3d53cb5d28b8 Mon Sep 17 00:00:00 2001 From: Ulyssa Date: Mon, 20 Sep 2021 16:29:28 -0700 Subject: [PATCH 061/401] Add tests for binop_separator = Back --- tests/source/binop-separator-back/bitwise.rs | 14 ++++++++ tests/source/binop-separator-back/comp.rs | 23 +++++++++++++ tests/source/binop-separator-back/logic.rs | 7 ++++ tests/source/binop-separator-back/math.rs | 7 ++++ tests/source/binop-separator-back/patterns.rs | 9 +++++ tests/source/binop-separator-back/range.rs | 7 ++++ tests/target/binop-separator-back/bitwise.rs | 18 ++++++++++ tests/target/binop-separator-back/comp.rs | 33 +++++++++++++++++++ tests/target/binop-separator-back/logic.rs | 10 ++++++ tests/target/binop-separator-back/math.rs | 23 +++++++++++++ tests/target/binop-separator-back/patterns.rs | 11 +++++++ tests/target/binop-separator-back/range.rs | 9 +++++ 12 files changed, 171 insertions(+) create mode 100644 tests/source/binop-separator-back/bitwise.rs create mode 100644 tests/source/binop-separator-back/comp.rs create mode 100644 tests/source/binop-separator-back/logic.rs create mode 100644 tests/source/binop-separator-back/math.rs create mode 100644 tests/source/binop-separator-back/patterns.rs create mode 100644 tests/source/binop-separator-back/range.rs create mode 100644 tests/target/binop-separator-back/bitwise.rs create mode 100644 tests/target/binop-separator-back/comp.rs create mode 100644 tests/target/binop-separator-back/logic.rs create mode 100644 tests/target/binop-separator-back/math.rs create mode 100644 tests/target/binop-separator-back/patterns.rs create mode 100644 tests/target/binop-separator-back/range.rs diff --git a/tests/source/binop-separator-back/bitwise.rs b/tests/source/binop-separator-back/bitwise.rs new file mode 100644 index 000000000000..3804bf3215b1 --- /dev/null +++ b/tests/source/binop-separator-back/bitwise.rs @@ -0,0 +1,14 @@ +// rustfmt-binop_separator: Back + +fn main() { + let value = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ^ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ; + + let value = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ & abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ; + + let value = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ | abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ; + + let value = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ << abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ; + + let value = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ >> abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ; + +} diff --git a/tests/source/binop-separator-back/comp.rs b/tests/source/binop-separator-back/comp.rs new file mode 100644 index 000000000000..50a27127445d --- /dev/null +++ b/tests/source/binop-separator-back/comp.rs @@ -0,0 +1,23 @@ +// rustfmt-binop_separator: Back + +fn main() { + if abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ < abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ { + // + } + + if abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ <= abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ { + // + } + + if abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ > abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ { + // + } + + if abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ >= abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ { + // + } + + if abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ == abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ { + // + } +} diff --git a/tests/source/binop-separator-back/logic.rs b/tests/source/binop-separator-back/logic.rs new file mode 100644 index 000000000000..8c297e5a6750 --- /dev/null +++ b/tests/source/binop-separator-back/logic.rs @@ -0,0 +1,7 @@ +// rustfmt-binop_separator: Back + +fn main() { + if abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ && abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ || abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ { + // + } +} diff --git a/tests/source/binop-separator-back/math.rs b/tests/source/binop-separator-back/math.rs new file mode 100644 index 000000000000..3af4aad16051 --- /dev/null +++ b/tests/source/binop-separator-back/math.rs @@ -0,0 +1,7 @@ +// rustfmt-binop_separator: Back + +fn main() { + let value = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ; + + let value = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ * abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ - abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ / abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ * abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ * abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ / abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ / abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ; +} diff --git a/tests/source/binop-separator-back/patterns.rs b/tests/source/binop-separator-back/patterns.rs new file mode 100644 index 000000000000..a8c3b5cdd9b1 --- /dev/null +++ b/tests/source/binop-separator-back/patterns.rs @@ -0,0 +1,9 @@ +// rustfmt-binop_separator: Back + +fn main() { + match val { + ThisIsA::ReallyLongPatternNameToHelpOverflowTheNextValueOntoTheNextLine | ThisIsA::SecondValueSeparatedByAPipe | ThisIsA::ThirdValueSeparatedByAPipe => { + // + } + } +} diff --git a/tests/source/binop-separator-back/range.rs b/tests/source/binop-separator-back/range.rs new file mode 100644 index 000000000000..bdd3de9922b0 --- /dev/null +++ b/tests/source/binop-separator-back/range.rs @@ -0,0 +1,7 @@ +// rustfmt-binop_separator: Back + +fn main() { + let value = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ..abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ; + + let value = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ..=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ; +} diff --git a/tests/target/binop-separator-back/bitwise.rs b/tests/target/binop-separator-back/bitwise.rs new file mode 100644 index 000000000000..ce32c05ef703 --- /dev/null +++ b/tests/target/binop-separator-back/bitwise.rs @@ -0,0 +1,18 @@ +// rustfmt-binop_separator: Back + +fn main() { + let value = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ^ + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ; + + let value = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ & + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ; + + let value = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ | + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ; + + let value = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ << + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ; + + let value = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ >> + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ; +} diff --git a/tests/target/binop-separator-back/comp.rs b/tests/target/binop-separator-back/comp.rs new file mode 100644 index 000000000000..efd837bcfe38 --- /dev/null +++ b/tests/target/binop-separator-back/comp.rs @@ -0,0 +1,33 @@ +// rustfmt-binop_separator: Back + +fn main() { + if abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ < + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + { + // + } + + if abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ <= + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + { + // + } + + if abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ > + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + { + // + } + + if abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ >= + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + { + // + } + + if abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ == + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + { + // + } +} diff --git a/tests/target/binop-separator-back/logic.rs b/tests/target/binop-separator-back/logic.rs new file mode 100644 index 000000000000..5f69fd5f55e4 --- /dev/null +++ b/tests/target/binop-separator-back/logic.rs @@ -0,0 +1,10 @@ +// rustfmt-binop_separator: Back + +fn main() { + if abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ && + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ || + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + { + // + } +} diff --git a/tests/target/binop-separator-back/math.rs b/tests/target/binop-separator-back/math.rs new file mode 100644 index 000000000000..7a3f27e733b2 --- /dev/null +++ b/tests/target/binop-separator-back/math.rs @@ -0,0 +1,23 @@ +// rustfmt-binop_separator: Back + +fn main() { + let value = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ; + + let value = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ * + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ - + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ / + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ * + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ * + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ / + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ / + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ; +} diff --git a/tests/target/binop-separator-back/patterns.rs b/tests/target/binop-separator-back/patterns.rs new file mode 100644 index 000000000000..2e59713526a0 --- /dev/null +++ b/tests/target/binop-separator-back/patterns.rs @@ -0,0 +1,11 @@ +// rustfmt-binop_separator: Back + +fn main() { + match val { + ThisIsA::ReallyLongPatternNameToHelpOverflowTheNextValueOntoTheNextLine | + ThisIsA::SecondValueSeparatedByAPipe | + ThisIsA::ThirdValueSeparatedByAPipe => { + // + } + } +} diff --git a/tests/target/binop-separator-back/range.rs b/tests/target/binop-separator-back/range.rs new file mode 100644 index 000000000000..19e5a81cd9cd --- /dev/null +++ b/tests/target/binop-separator-back/range.rs @@ -0,0 +1,9 @@ +// rustfmt-binop_separator: Back + +fn main() { + let value = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.. + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ; + + let value = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ..= + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ; +} From f0f449d6edda5a40057fc82ea02cc9abeae4d012 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Fri, 17 Sep 2021 18:56:30 -0700 Subject: [PATCH 062/401] Wrap long array and slice patterns. Closes #4530. --- src/overflow.rs | 4 +++- src/patterns.rs | 12 +++++++++++- tests/source/issue-4530.rs | 4 ++++ tests/target/issue-4530.rs | 9 +++++++++ 4 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 tests/source/issue-4530.rs create mode 100644 tests/target/issue-4530.rs diff --git a/src/overflow.rs b/src/overflow.rs index e32213467a51..ac24181c7805 100644 --- a/src/overflow.rs +++ b/src/overflow.rs @@ -77,6 +77,7 @@ pub(crate) enum OverflowableItem<'a> { FieldDef(&'a ast::FieldDef), TuplePatField(&'a TuplePatField<'a>), Ty(&'a ast::Ty), + Pat(&'a ast::Pat), } impl<'a> Rewrite for OverflowableItem<'a> { @@ -116,6 +117,7 @@ impl<'a> OverflowableItem<'a> { OverflowableItem::FieldDef(sf) => f(*sf), OverflowableItem::TuplePatField(pat) => f(*pat), OverflowableItem::Ty(ty) => f(*ty), + OverflowableItem::Pat(pat) => f(*pat), } } @@ -232,7 +234,7 @@ macro_rules! impl_into_overflowable_item_for_rustfmt_types { } } -impl_into_overflowable_item_for_ast_node!(Expr, GenericParam, NestedMetaItem, FieldDef, Ty); +impl_into_overflowable_item_for_ast_node!(Expr, GenericParam, NestedMetaItem, FieldDef, Ty, Pat); impl_into_overflowable_item_for_rustfmt_types!([MacroArg], [SegmentParam, TuplePatField]); pub(crate) fn into_overflowable_list<'a, T>( diff --git a/src/patterns.rs b/src/patterns.rs index 4c6a2d5d75b9..34987b1d59e4 100644 --- a/src/patterns.rs +++ b/src/patterns.rs @@ -4,6 +4,7 @@ use rustc_span::{BytePos, Span}; use crate::comment::{combine_strs_with_missing_comments, FindUncommented}; use crate::config::lists::*; +use crate::config::Version; use crate::expr::{can_be_overflowed_expr, rewrite_unary_prefix, wrap_struct_field}; use crate::lists::{ definitive_tactic, itemize_list, shape_for_tactic, struct_lit_formatting, struct_lit_shape, @@ -232,7 +233,7 @@ impl Rewrite for Pat { rewrite_tuple_pat(pat_vec, Some(path_str), self.span, context, shape) } PatKind::Lit(ref expr) => expr.rewrite(context, shape), - PatKind::Slice(ref slice_pat) => { + PatKind::Slice(ref slice_pat) if context.config.version() == Version::One => { let rw: Vec = slice_pat .iter() .map(|p| { @@ -245,6 +246,15 @@ impl Rewrite for Pat { .collect(); Some(format!("[{}]", rw.join(", "))) } + PatKind::Slice(ref slice_pat) => overflow::rewrite_with_square_brackets( + context, + "", + slice_pat.iter(), + shape, + self.span, + None, + None, + ), PatKind::Struct(ref qself, ref path, ref fields, ellipsis) => { rewrite_struct_pat(qself, path, fields, ellipsis, self.span, context, shape) } diff --git a/tests/source/issue-4530.rs b/tests/source/issue-4530.rs new file mode 100644 index 000000000000..9d2882abb3c1 --- /dev/null +++ b/tests/source/issue-4530.rs @@ -0,0 +1,4 @@ +// rustfmt-version: Two +fn main() { + let [aaaaaaaaaaaaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, cccccccccccccccccccccccccc, ddddddddddddddddddddddddd] = panic!(); +} diff --git a/tests/target/issue-4530.rs b/tests/target/issue-4530.rs new file mode 100644 index 000000000000..296dc559a934 --- /dev/null +++ b/tests/target/issue-4530.rs @@ -0,0 +1,9 @@ +// rustfmt-version: Two +fn main() { + let [ + aaaaaaaaaaaaaaaaaaaaaaaaaa, + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, + cccccccccccccccccccccccccc, + ddddddddddddddddddddddddd, + ] = panic!(); +} From 40f4993c67ff54e413da17496769407ab85f3924 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Thu, 23 Sep 2021 22:43:03 -0400 Subject: [PATCH 063/401] Update derive attibute span to start after opening '(' Fixes 4984 When parsing derive attributes we're only concerned about the traits and comments listed between the opening and closing parentheses. Derive attribute spans currently start at the '#'. Span starts here | v #[derive(...)] After this update the derive spans start after the opening '('. Span starts here | V #[derive(...)] --- src/attr.rs | 5 +++- tests/source/issue-4984/minimum_example.rs | 2 ++ tests/source/issue-4984/multi_line_derive.rs | 20 +++++++++++++++ tests/target/issue-4984/minimum_example.rs | 2 ++ tests/target/issue-4984/multi_line_derive.rs | 26 ++++++++++++++++++++ 5 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 tests/source/issue-4984/minimum_example.rs create mode 100644 tests/source/issue-4984/multi_line_derive.rs create mode 100644 tests/target/issue-4984/minimum_example.rs create mode 100644 tests/target/issue-4984/multi_line_derive.rs diff --git a/src/attr.rs b/src/attr.rs index 315eb10a9dbc..a5982820e3de 100644 --- a/src/attr.rs +++ b/src/attr.rs @@ -13,6 +13,7 @@ use crate::lists::{definitive_tactic, itemize_list, write_list, ListFormatting, use crate::overflow; use crate::rewrite::{Rewrite, RewriteContext}; use crate::shape::Shape; +use crate::source_map::SpanUtils; use crate::types::{rewrite_path, PathContext}; use crate::utils::{count_newlines, mk_sp}; @@ -116,7 +117,9 @@ fn format_derive( |span| span.lo(), |span| span.hi(), |span| Some(context.snippet(*span).to_owned()), - attr.span.lo(), + // We update derive attribute spans to start after the opening '(' + // This helps us focus parsing to just what's inside #[derive(...)] + context.snippet_provider.span_after(attr.span, "("), attr.span.hi(), false, ); diff --git a/tests/source/issue-4984/minimum_example.rs b/tests/source/issue-4984/minimum_example.rs new file mode 100644 index 000000000000..677f87377169 --- /dev/null +++ b/tests/source/issue-4984/minimum_example.rs @@ -0,0 +1,2 @@ +#[derive(/*Debug, */Clone)] +struct Foo; diff --git a/tests/source/issue-4984/multi_line_derive.rs b/tests/source/issue-4984/multi_line_derive.rs new file mode 100644 index 000000000000..73921dd17354 --- /dev/null +++ b/tests/source/issue-4984/multi_line_derive.rs @@ -0,0 +1,20 @@ +#[derive( +/* ---------- Some really important comment that just had to go inside the derive --------- */ +Debug, Clone, Eq, PartialEq, +)] +struct Foo { + a: i32, + b: T, +} + +#[derive( +/* + Some really important comment that just had to go inside the derive. + Also had to be put over multiple lines +*/ +Debug, Clone, Eq, PartialEq, +)] +struct Bar { + a: i32, + b: T, +} diff --git a/tests/target/issue-4984/minimum_example.rs b/tests/target/issue-4984/minimum_example.rs new file mode 100644 index 000000000000..f0599c5d694b --- /dev/null +++ b/tests/target/issue-4984/minimum_example.rs @@ -0,0 +1,2 @@ +#[derive(/*Debug, */ Clone)] +struct Foo; diff --git a/tests/target/issue-4984/multi_line_derive.rs b/tests/target/issue-4984/multi_line_derive.rs new file mode 100644 index 000000000000..5fbd9784adc9 --- /dev/null +++ b/tests/target/issue-4984/multi_line_derive.rs @@ -0,0 +1,26 @@ +#[derive( + /* ---------- Some really important comment that just had to go inside the derive --------- */ + Debug, + Clone, + Eq, + PartialEq, +)] +struct Foo { + a: i32, + b: T, +} + +#[derive( + /* + Some really important comment that just had to go inside the derive. + Also had to be put over multiple lines + */ + Debug, + Clone, + Eq, + PartialEq, +)] +struct Bar { + a: i32, + b: T, +} From 365a2f8f6e710f67d6485fe9004bb3f64da977bc Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Wed, 29 Sep 2021 14:18:54 -0400 Subject: [PATCH 064/401] Add additional test cases for issue 4984 --- tests/source/issue-4984/multiple_comments_within.rs | 8 ++++++++ tests/target/issue-4984/multiple_comments_within.rs | 11 +++++++++++ tests/target/issue-4984/should_not_change.rs | 5 +++++ 3 files changed, 24 insertions(+) create mode 100644 tests/source/issue-4984/multiple_comments_within.rs create mode 100644 tests/target/issue-4984/multiple_comments_within.rs create mode 100644 tests/target/issue-4984/should_not_change.rs diff --git a/tests/source/issue-4984/multiple_comments_within.rs b/tests/source/issue-4984/multiple_comments_within.rs new file mode 100644 index 000000000000..eb474a723d01 --- /dev/null +++ b/tests/source/issue-4984/multiple_comments_within.rs @@ -0,0 +1,8 @@ +#[derive( +/* ---------- Some really important comment that just had to go inside the derive --------- */ +Debug, Clone,/* Another comment */Eq, PartialEq, +)] +struct Foo { + a: i32, + b: T, +} diff --git a/tests/target/issue-4984/multiple_comments_within.rs b/tests/target/issue-4984/multiple_comments_within.rs new file mode 100644 index 000000000000..d2924f0d0f2e --- /dev/null +++ b/tests/target/issue-4984/multiple_comments_within.rs @@ -0,0 +1,11 @@ +#[derive( + /* ---------- Some really important comment that just had to go inside the derive --------- */ + Debug, + Clone, + /* Another comment */ Eq, + PartialEq, +)] +struct Foo { + a: i32, + b: T, +} diff --git a/tests/target/issue-4984/should_not_change.rs b/tests/target/issue-4984/should_not_change.rs new file mode 100644 index 000000000000..e46ee511084f --- /dev/null +++ b/tests/target/issue-4984/should_not_change.rs @@ -0,0 +1,5 @@ +#[derive(Clone, Debug, Eq, PartialEq)] +struct Foo; + +#[derive(Clone)] +struct Bar; From 8b58cce673d6ae4fbc1f3a8d28859bc100a72ed8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Sat, 16 May 2020 22:20:51 +0200 Subject: [PATCH 065/401] Stabilize match_block_trailing_comma. (#4145) Servo has used this since forever, and it'd be useful to be able to use rustfmt stable there so that we can use the same rustfmt version in both Firefox and Servo. Feel free to close this if there's any reason it shouldn't be done. --- Configurations.md | 2 +- src/config/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Configurations.md b/Configurations.md index ff83f02f87ba..06db897a42e7 100644 --- a/Configurations.md +++ b/Configurations.md @@ -1626,7 +1626,7 @@ Put a trailing comma after a block based match arm (non-block arms are not affec - **Default value**: `false` - **Possible values**: `true`, `false` -- **Stable**: No (tracking issue: #3380) +- **Stable**: Yes #### `false` (default): diff --git a/src/config/mod.rs b/src/config/mod.rs index c6cee8ed2273..398a1814d8d1 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -127,7 +127,7 @@ create_config! { "Add trailing semicolon after break, continue and return"; trailing_comma: SeparatorTactic, SeparatorTactic::Vertical, false, "How to handle trailing commas for lists"; - match_block_trailing_comma: bool, false, false, + match_block_trailing_comma: bool, false, true, "Put a trailing comma after a block based match arm (non-block arms are not affected)"; blank_lines_upper_bound: usize, 1, false, "Maximum number of blank lines which can be put between items"; From d41805704d4a07ac77e0bc59ea5c91cdb69dea26 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Mon, 11 Oct 2021 18:30:35 -0400 Subject: [PATCH 066/401] Prevent structs with ".." from being rewritten with alignment rustfmt should only support rewriting a struct in an expression position with alignment (non-default behavior) when there is no rest (with or without a base) and all of the fields are non-shorthand. --- src/expr.rs | 11 ++-- .../source/issue-4926/deeply_nested_struct.rs | 35 ++++++++++++ ...ply_nested_struct_with_long_field_names.rs | 43 +++++++++++++++ .../deeply_nested_struct_with_many_fields.rs | 44 +++++++++++++++ tests/source/issue-4926/enum_struct_field.rs | 35 ++++++++++++ tests/source/issue-4926/minimum_example.rs | 10 ++++ .../struct_with_long_field_names.rs | 21 ++++++++ .../issue-4926/struct_with_many_fields.rs | 21 ++++++++ .../target/issue-4926/deeply_nested_struct.rs | 38 +++++++++++++ ...ply_nested_struct_with_long_field_names.rs | 44 +++++++++++++++ .../deeply_nested_struct_with_many_fields.rs | 54 +++++++++++++++++++ tests/target/issue-4926/enum_struct_field.rs | 41 ++++++++++++++ tests/target/issue-4926/minimum_example.rs | 10 ++++ .../struct_with_long_field_names.rs | 24 +++++++++ .../issue-4926/struct_with_many_fields.rs | 34 ++++++++++++ 15 files changed, 458 insertions(+), 7 deletions(-) create mode 100644 tests/source/issue-4926/deeply_nested_struct.rs create mode 100644 tests/source/issue-4926/deeply_nested_struct_with_long_field_names.rs create mode 100644 tests/source/issue-4926/deeply_nested_struct_with_many_fields.rs create mode 100644 tests/source/issue-4926/enum_struct_field.rs create mode 100644 tests/source/issue-4926/minimum_example.rs create mode 100644 tests/source/issue-4926/struct_with_long_field_names.rs create mode 100644 tests/source/issue-4926/struct_with_many_fields.rs create mode 100644 tests/target/issue-4926/deeply_nested_struct.rs create mode 100644 tests/target/issue-4926/deeply_nested_struct_with_long_field_names.rs create mode 100644 tests/target/issue-4926/deeply_nested_struct_with_many_fields.rs create mode 100644 tests/target/issue-4926/enum_struct_field.rs create mode 100644 tests/target/issue-4926/minimum_example.rs create mode 100644 tests/target/issue-4926/struct_with_long_field_names.rs create mode 100644 tests/target/issue-4926/struct_with_many_fields.rs diff --git a/src/expr.rs b/src/expr.rs index 01cc388c186e..3a54426b0ddb 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -1528,12 +1528,12 @@ fn rewrite_struct_lit<'a>( let path_shape = shape.sub_width(2)?; let path_str = rewrite_path(context, PathContext::Expr, None, path, path_shape)?; - let has_base = match struct_rest { + let has_base_or_rest = match struct_rest { ast::StructRest::None if fields.is_empty() => return Some(format!("{} {{}}", path_str)), ast::StructRest::Rest(_) if fields.is_empty() => { return Some(format!("{} {{ .. }}", path_str)); } - ast::StructRest::Base(_) => true, + ast::StructRest::Rest(_) | ast::StructRest::Base(_) => true, _ => false, }; @@ -1542,7 +1542,7 @@ fn rewrite_struct_lit<'a>( let one_line_width = h_shape.map_or(0, |shape| shape.width); let body_lo = context.snippet_provider.span_after(span, "{"); - let fields_str = if struct_lit_can_be_aligned(fields, has_base) + let fields_str = if struct_lit_can_be_aligned(fields, has_base_or_rest) && context.config.struct_field_align_threshold() > 0 { rewrite_with_alignment( @@ -1614,10 +1614,7 @@ fn rewrite_struct_lit<'a>( nested_shape, tactic, context, - force_no_trailing_comma - || has_base - || !context.use_block_indent() - || matches!(struct_rest, ast::StructRest::Rest(_)), + force_no_trailing_comma || has_base_or_rest || !context.use_block_indent(), ); write_list(&item_vec, &fmt)? diff --git a/tests/source/issue-4926/deeply_nested_struct.rs b/tests/source/issue-4926/deeply_nested_struct.rs new file mode 100644 index 000000000000..e55e41bd1a58 --- /dev/null +++ b/tests/source/issue-4926/deeply_nested_struct.rs @@ -0,0 +1,35 @@ + +// rustfmt-struct_field_align_threshold: 30 + +struct X { + a: i32, + b: i32, + c: i32, +} + +fn test(x: X) { + let d = { + let e = { + let f = { + let g = { + let h = { + let i = { + let j = { + matches!( + x, + X { a: 1_000, b: 1_000, .. } + ) + }; + j + }; + i + }; + h + }; + g + }; + f + }; + e + }; +} \ No newline at end of file diff --git a/tests/source/issue-4926/deeply_nested_struct_with_long_field_names.rs b/tests/source/issue-4926/deeply_nested_struct_with_long_field_names.rs new file mode 100644 index 000000000000..516699fa2b8b --- /dev/null +++ b/tests/source/issue-4926/deeply_nested_struct_with_long_field_names.rs @@ -0,0 +1,43 @@ +// rustfmt-struct_field_align_threshold: 30 + +struct X { + really_really_long_field_a: i32, + really_really_really_long_field_b: i32, + really_really_really_really_long_field_c: i32, + really_really_really_really_really_long_field_d: i32, + really_really_really_really_really_really_long_field_e: i32, + f: i32, +} + +fn test(x: X) { + let d = { + let e = { + let f = { + let g = { + let h = { + let i = { + let j = { + matches!( + x, + X { + really_really_long_field_a: 10, + really_really_really_long_field_b: 10, + really_really_really_really_long_field_c: 10, + really_really_really_really_really_long_field_d: 10, + really_really_really_really_really_really_long_field_e: 10, .. + } + ) + }; + j + }; + i + }; + h + }; + g + }; + f + }; + e + }; +} diff --git a/tests/source/issue-4926/deeply_nested_struct_with_many_fields.rs b/tests/source/issue-4926/deeply_nested_struct_with_many_fields.rs new file mode 100644 index 000000000000..38fd6f02cf06 --- /dev/null +++ b/tests/source/issue-4926/deeply_nested_struct_with_many_fields.rs @@ -0,0 +1,44 @@ +// rustfmt-struct_field_align_threshold: 30 + +struct X { + a: i32, + b: i32, + c: i32, + d: i32, + e: i32, + f: i32, + g: i32, + h: i32, + i: i32, + j: i32, + k: i32, +} + +fn test(x: X) { + let d = { + let e = { + let f = { + let g = { + let h = { + let i = { + let j = { + matches!( + x, + X { + a: 1_000, b: 1_000, c: 1_000, d: 1_000, e: 1_000, f: 1_000, g: 1_000, h: 1_000, i: 1_000, j: 1_000, .. + } + ) + }; + j + }; + i + }; + h + }; + g + }; + f + }; + e + }; +} diff --git a/tests/source/issue-4926/enum_struct_field.rs b/tests/source/issue-4926/enum_struct_field.rs new file mode 100644 index 000000000000..336378537df3 --- /dev/null +++ b/tests/source/issue-4926/enum_struct_field.rs @@ -0,0 +1,35 @@ +// rustfmt-struct_field_align_threshold: 30 +// rustfmt-enum_discrim_align_threshold: 30 +// rustfmt-imports_layout: HorizontalVertical + +#[derive(Default)] +struct InnerStructA { bbbbbbbbb: i32, cccccccc: i32 } + +enum SomeEnumNamedD { + E(InnerStructA), + F { + ggggggggggggggggggggggggg: bool, + h: bool, + } +} + +impl SomeEnumNamedD { + fn f_variant() -> Self { + Self::F { ggggggggggggggggggggggggg: true, h: true } + } +} + +fn main() { + let kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk = SomeEnumNamedD::f_variant(); + let something_we_care_about = matches!( + kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk, + SomeEnumNamedD::F { + ggggggggggggggggggggggggg: true, + .. + } + ); + + if something_we_care_about { + println!("Yup it happened"); + } +} diff --git a/tests/source/issue-4926/minimum_example.rs b/tests/source/issue-4926/minimum_example.rs new file mode 100644 index 000000000000..2c3045dea489 --- /dev/null +++ b/tests/source/issue-4926/minimum_example.rs @@ -0,0 +1,10 @@ +// rustfmt-struct_field_align_threshold: 30 + +struct X { a: i32, b: i32 } + +fn test(x: X) { + let y = matches!(x, X { + a: 1, + .. + }); +} diff --git a/tests/source/issue-4926/struct_with_long_field_names.rs b/tests/source/issue-4926/struct_with_long_field_names.rs new file mode 100644 index 000000000000..b8a37f0714ee --- /dev/null +++ b/tests/source/issue-4926/struct_with_long_field_names.rs @@ -0,0 +1,21 @@ +// rustfmt-struct_field_align_threshold: 30 + +struct X { + really_really_long_field_a: i32, + really_really_really_long_field_b: i32, + really_really_really_really_long_field_c: i32, + really_really_really_really_really_long_field_d: i32, + really_really_really_really_really_really_long_field_e: i32, + f: i32, +} + +fn test(x: X) { + let y = matches!(x, X { + really_really_long_field_a: 10, + really_really_really_long_field_b: 10, + really_really_really_really_long_field_c: 10, + really_really_really_really_really_long_field_d: 10, + really_really_really_really_really_really_long_field_e: 10, + .. + }); +} diff --git a/tests/source/issue-4926/struct_with_many_fields.rs b/tests/source/issue-4926/struct_with_many_fields.rs new file mode 100644 index 000000000000..4adfd3b30629 --- /dev/null +++ b/tests/source/issue-4926/struct_with_many_fields.rs @@ -0,0 +1,21 @@ +// rustfmt-struct_field_align_threshold: 30 + +struct X { + a: i32, + b: i32, + c: i32, + d: i32, + e: i32, + f: i32, + g: i32, + h: i32, + i: i32, + j: i32, + k: i32, +} + +fn test(x: X) { + let y = matches!(x, X { + a: 1_000, b: 1_000, c: 1_000, d: 1_000, e: 1_000, f: 1_000, g: 1_000, h: 1_000, i: 1_000, j: 1_000, .. + }); +} \ No newline at end of file diff --git a/tests/target/issue-4926/deeply_nested_struct.rs b/tests/target/issue-4926/deeply_nested_struct.rs new file mode 100644 index 000000000000..072cf2f6674a --- /dev/null +++ b/tests/target/issue-4926/deeply_nested_struct.rs @@ -0,0 +1,38 @@ +// rustfmt-struct_field_align_threshold: 30 + +struct X { + a: i32, + b: i32, + c: i32, +} + +fn test(x: X) { + let d = { + let e = { + let f = { + let g = { + let h = { + let i = { + let j = { + matches!( + x, + X { + a: 1_000, + b: 1_000, + .. + } + ) + }; + j + }; + i + }; + h + }; + g + }; + f + }; + e + }; +} diff --git a/tests/target/issue-4926/deeply_nested_struct_with_long_field_names.rs b/tests/target/issue-4926/deeply_nested_struct_with_long_field_names.rs new file mode 100644 index 000000000000..c7bc7f7296d6 --- /dev/null +++ b/tests/target/issue-4926/deeply_nested_struct_with_long_field_names.rs @@ -0,0 +1,44 @@ +// rustfmt-struct_field_align_threshold: 30 + +struct X { + really_really_long_field_a: i32, + really_really_really_long_field_b: i32, + really_really_really_really_long_field_c: i32, + really_really_really_really_really_long_field_d: i32, + really_really_really_really_really_really_long_field_e: i32, + f: i32, +} + +fn test(x: X) { + let d = { + let e = { + let f = { + let g = { + let h = { + let i = { + let j = { + matches!( + x, + X { + really_really_long_field_a: 10, + really_really_really_long_field_b: 10, + really_really_really_really_long_field_c: 10, + really_really_really_really_really_long_field_d: 10, + really_really_really_really_really_really_long_field_e: 10, + .. + } + ) + }; + j + }; + i + }; + h + }; + g + }; + f + }; + e + }; +} diff --git a/tests/target/issue-4926/deeply_nested_struct_with_many_fields.rs b/tests/target/issue-4926/deeply_nested_struct_with_many_fields.rs new file mode 100644 index 000000000000..69793162519a --- /dev/null +++ b/tests/target/issue-4926/deeply_nested_struct_with_many_fields.rs @@ -0,0 +1,54 @@ +// rustfmt-struct_field_align_threshold: 30 + +struct X { + a: i32, + b: i32, + c: i32, + d: i32, + e: i32, + f: i32, + g: i32, + h: i32, + i: i32, + j: i32, + k: i32, +} + +fn test(x: X) { + let d = { + let e = { + let f = { + let g = { + let h = { + let i = { + let j = { + matches!( + x, + X { + a: 1_000, + b: 1_000, + c: 1_000, + d: 1_000, + e: 1_000, + f: 1_000, + g: 1_000, + h: 1_000, + i: 1_000, + j: 1_000, + .. + } + ) + }; + j + }; + i + }; + h + }; + g + }; + f + }; + e + }; +} diff --git a/tests/target/issue-4926/enum_struct_field.rs b/tests/target/issue-4926/enum_struct_field.rs new file mode 100644 index 000000000000..2471df84653c --- /dev/null +++ b/tests/target/issue-4926/enum_struct_field.rs @@ -0,0 +1,41 @@ +// rustfmt-struct_field_align_threshold: 30 +// rustfmt-enum_discrim_align_threshold: 30 +// rustfmt-imports_layout: HorizontalVertical + +#[derive(Default)] +struct InnerStructA { + bbbbbbbbb: i32, + cccccccc: i32, +} + +enum SomeEnumNamedD { + E(InnerStructA), + F { + ggggggggggggggggggggggggg: bool, + h: bool, + }, +} + +impl SomeEnumNamedD { + fn f_variant() -> Self { + Self::F { + ggggggggggggggggggggggggg: true, + h: true, + } + } +} + +fn main() { + let kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk = SomeEnumNamedD::f_variant(); + let something_we_care_about = matches!( + kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk, + SomeEnumNamedD::F { + ggggggggggggggggggggggggg: true, + .. + } + ); + + if something_we_care_about { + println!("Yup it happened"); + } +} diff --git a/tests/target/issue-4926/minimum_example.rs b/tests/target/issue-4926/minimum_example.rs new file mode 100644 index 000000000000..06e18427465c --- /dev/null +++ b/tests/target/issue-4926/minimum_example.rs @@ -0,0 +1,10 @@ +// rustfmt-struct_field_align_threshold: 30 + +struct X { + a: i32, + b: i32, +} + +fn test(x: X) { + let y = matches!(x, X { a: 1, .. }); +} diff --git a/tests/target/issue-4926/struct_with_long_field_names.rs b/tests/target/issue-4926/struct_with_long_field_names.rs new file mode 100644 index 000000000000..ac4674ab5d52 --- /dev/null +++ b/tests/target/issue-4926/struct_with_long_field_names.rs @@ -0,0 +1,24 @@ +// rustfmt-struct_field_align_threshold: 30 + +struct X { + really_really_long_field_a: i32, + really_really_really_long_field_b: i32, + really_really_really_really_long_field_c: i32, + really_really_really_really_really_long_field_d: i32, + really_really_really_really_really_really_long_field_e: i32, + f: i32, +} + +fn test(x: X) { + let y = matches!( + x, + X { + really_really_long_field_a: 10, + really_really_really_long_field_b: 10, + really_really_really_really_long_field_c: 10, + really_really_really_really_really_long_field_d: 10, + really_really_really_really_really_really_long_field_e: 10, + .. + } + ); +} diff --git a/tests/target/issue-4926/struct_with_many_fields.rs b/tests/target/issue-4926/struct_with_many_fields.rs new file mode 100644 index 000000000000..96dfe14bf7dd --- /dev/null +++ b/tests/target/issue-4926/struct_with_many_fields.rs @@ -0,0 +1,34 @@ +// rustfmt-struct_field_align_threshold: 30 + +struct X { + a: i32, + b: i32, + c: i32, + d: i32, + e: i32, + f: i32, + g: i32, + h: i32, + i: i32, + j: i32, + k: i32, +} + +fn test(x: X) { + let y = matches!( + x, + X { + a: 1_000, + b: 1_000, + c: 1_000, + d: 1_000, + e: 1_000, + f: 1_000, + g: 1_000, + h: 1_000, + i: 1_000, + j: 1_000, + .. + } + ); +} From f7c4a44149b4e3a683f5506929050f2b50117328 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Tue, 5 Oct 2021 01:24:10 -0400 Subject: [PATCH 067/401] Adjust non-empty tuple struct span to start before fields Resolves 5011 Tuple structs with visibility modifiers and comments before the first field were incorrectly formatted. Comments would duplicate part of the visibility modifier and struct name. When trying to parse the tuple fields the ``items::Context`` searches for the opening '(', but because the visibility modifier introduces another '(' -- for example ``pub(crate)`` -- the parsing gets messed up. Now the span is adjusted to start after the struct identifier, or after any generics. Adjusting the span in this way ensures that the ``items::Contex`` will correctly find the tuple fields. --- src/items.rs | 7 ++++++- tests/source/issue-5011.rs | 12 ++++++++++++ tests/target/issue-5011.rs | 8 ++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 tests/source/issue-5011.rs create mode 100644 tests/target/issue-5011.rs diff --git a/src/items.rs b/src/items.rs index e8eb1c5dfbb5..471a365e4706 100644 --- a/src/items.rs +++ b/src/items.rs @@ -1469,12 +1469,17 @@ fn format_tuple_struct( format_empty_struct_or_tuple(context, inner_span, offset, &mut result, "(", ")"); } else { let shape = Shape::indented(offset, context.config).sub_width(1)?; + let lo = if let Some(generics) = struct_parts.generics { + generics.span.hi() + } else { + struct_parts.ident.span.hi() + }; result = overflow::rewrite_with_parens( context, &result, fields.iter(), shape, - span, + mk_sp(lo, span.hi()), context.config.fn_call_width(), None, )?; diff --git a/tests/source/issue-5011.rs b/tests/source/issue-5011.rs new file mode 100644 index 000000000000..b48292164e43 --- /dev/null +++ b/tests/source/issue-5011.rs @@ -0,0 +1,12 @@ +pub(crate) struct ASlash( + // hello + i32 +); + +pub(crate) struct AStar( + /* hello */ + i32 +); + +pub(crate) struct BStar(/* hello */ i32); + diff --git a/tests/target/issue-5011.rs b/tests/target/issue-5011.rs new file mode 100644 index 000000000000..9ad4a1929bdb --- /dev/null +++ b/tests/target/issue-5011.rs @@ -0,0 +1,8 @@ +pub(crate) struct ASlash( + // hello + i32, +); + +pub(crate) struct AStar(/* hello */ i32); + +pub(crate) struct BStar(/* hello */ i32); From f2fb3c9659e072d7df6315906f1adbd06c3bc9e3 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Wed, 6 Oct 2021 13:09:24 -0400 Subject: [PATCH 068/401] Update connector search in ControlFlow::rewrite_pat_expr for for loops Resolves 5009 For loops represented by a ControlFlow object use " in" as their connector. rustfmt searches for the first uncommented occurrence of the word "in" within the current span and adjusts it's starting point to look for comments right after that. visually this looks like this: rustfmt starts looking for comments here | V for x in /* ... */ 0..1 {} This works well in most cases, however when the pattern also contains the word "in", this leads to issues. rustfmt starts looking for comments here | V for in_here in /* ... */ 0..1 {} ------- pattern In order to correctly identify the connector, the new approach first updates the span to start after the pattern and then searches for the first uncommented occurrence of "in". --- src/expr.rs | 2 +- tests/target/issue-5009/1_minimum_example.rs | 4 +++ .../2_many_in_connectors_in_pattern.rs | 3 ++ ...sted_for_loop_with_connector_in_pattern.rs | 5 +++ .../4_nested_for_loop_with_if_elseif_else.rs | 13 ++++++++ ...r_loop_with_connector_in_if_elseif_else.rs | 15 +++++++++ ...sted_for_loop_with_connector_in_pattern.rs | 32 +++++++++++++++++++ 7 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 tests/target/issue-5009/1_minimum_example.rs create mode 100644 tests/target/issue-5009/2_many_in_connectors_in_pattern.rs create mode 100644 tests/target/issue-5009/3_nested_for_loop_with_connector_in_pattern.rs create mode 100644 tests/target/issue-5009/4_nested_for_loop_with_if_elseif_else.rs create mode 100644 tests/target/issue-5009/5_nested_for_loop_with_connector_in_if_elseif_else.rs create mode 100644 tests/target/issue-5009/6_deeply_nested_for_loop_with_connector_in_pattern.rs diff --git a/src/expr.rs b/src/expr.rs index 3a54426b0ddb..f40f80e42532 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -822,7 +822,7 @@ impl<'a> ControlFlow<'a> { let pat_string = pat.rewrite(context, pat_shape)?; let comments_lo = context .snippet_provider - .span_after(self.span, self.connector.trim()); + .span_after(self.span.with_lo(pat.span.hi()), self.connector.trim()); let comments_span = mk_sp(comments_lo, expr.span.lo()); return rewrite_assign_rhs_with_comments( context, diff --git a/tests/target/issue-5009/1_minimum_example.rs b/tests/target/issue-5009/1_minimum_example.rs new file mode 100644 index 000000000000..55836f4bf52c --- /dev/null +++ b/tests/target/issue-5009/1_minimum_example.rs @@ -0,0 +1,4 @@ +fn main() { + // the "in" inside the pattern produced invalid syntax + for variable_in_here /* ... */ in 0..1 {} +} diff --git a/tests/target/issue-5009/2_many_in_connectors_in_pattern.rs b/tests/target/issue-5009/2_many_in_connectors_in_pattern.rs new file mode 100644 index 000000000000..d83590c6852f --- /dev/null +++ b/tests/target/issue-5009/2_many_in_connectors_in_pattern.rs @@ -0,0 +1,3 @@ +fn main() { + for in_in_in_in_in_in_in_in /* ... */ in 0..1 {} +} diff --git a/tests/target/issue-5009/3_nested_for_loop_with_connector_in_pattern.rs b/tests/target/issue-5009/3_nested_for_loop_with_connector_in_pattern.rs new file mode 100644 index 000000000000..9c800723939b --- /dev/null +++ b/tests/target/issue-5009/3_nested_for_loop_with_connector_in_pattern.rs @@ -0,0 +1,5 @@ +fn main() { + for variable_in_x /* ... */ in 0..1 { + for variable_in_y /* ... */ in 0..1 {} + } +} diff --git a/tests/target/issue-5009/4_nested_for_loop_with_if_elseif_else.rs b/tests/target/issue-5009/4_nested_for_loop_with_if_elseif_else.rs new file mode 100644 index 000000000000..a716d0d3082a --- /dev/null +++ b/tests/target/issue-5009/4_nested_for_loop_with_if_elseif_else.rs @@ -0,0 +1,13 @@ +fn main() { + for variable_in_x /* ... */ in 0..1 { + for variable_in_y /* ... */ in 0..1 { + if false { + + } else if false { + + } else { + + } + } + } +} diff --git a/tests/target/issue-5009/5_nested_for_loop_with_connector_in_if_elseif_else.rs b/tests/target/issue-5009/5_nested_for_loop_with_connector_in_if_elseif_else.rs new file mode 100644 index 000000000000..41ea46d4cb9b --- /dev/null +++ b/tests/target/issue-5009/5_nested_for_loop_with_connector_in_if_elseif_else.rs @@ -0,0 +1,15 @@ +fn main() { + let in_ = false; + + for variable_in_x /* ... */ in 0..1 { + for variable_in_y /* ... */ in 0..1 { + if in_ { + + } else if in_ { + + } else { + + } + } + } +} diff --git a/tests/target/issue-5009/6_deeply_nested_for_loop_with_connector_in_pattern.rs b/tests/target/issue-5009/6_deeply_nested_for_loop_with_connector_in_pattern.rs new file mode 100644 index 000000000000..789e54f7e5fb --- /dev/null +++ b/tests/target/issue-5009/6_deeply_nested_for_loop_with_connector_in_pattern.rs @@ -0,0 +1,32 @@ +fn main() { + for variable_in_a /* ... */ in 0..1 { + for variable_in_b /* ... */ in 0..1 { + for variable_in_c /* ... */ in 0..1 { + for variable_in_d /* ... */ in 0..1 { + for variable_in_e /* ... */ in 0..1 { + for variable_in_f /* ... */ in 0..1 { + for variable_in_g /* ... */ in 0..1 { + for variable_in_h /* ... */ in 0..1 { + for variable_in_i /* ... */ in 0..1 { + for variable_in_j /* ... */ in 0..1 { + for variable_in_k /* ... */ in 0..1 { + for variable_in_l /* ... */ in 0..1 { + for variable_in_m /* ... */ in 0..1 { + for variable_in_n /* ... */ in 0..1 { + for variable_in_o /* ... */ in 0..1 { + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } +} From c9c1932be3299b40eab63106deeb5d5ad2283b61 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Wed, 13 Oct 2021 21:22:34 -0500 Subject: [PATCH 069/401] feat: stabilize disable_all_formatting --- Configurations.md | 6 ++++-- src/config/mod.rs | 2 +- src/test/mod.rs | 5 ----- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/Configurations.md b/Configurations.md index 06db897a42e7..7a77dbe154b6 100644 --- a/Configurations.md +++ b/Configurations.md @@ -521,11 +521,13 @@ fn main() { ## `disable_all_formatting` -Don't reformat anything +Don't reformat anything. + +Note that this option may be soft-deprecated in the future once the [ignore](#ignore) option is stabilized. Nightly toolchain users are encouraged to use [ignore](#ignore) instead when possible. - **Default value**: `false` - **Possible values**: `true`, `false` -- **Stable**: No (tracking issue: #3388) +- **Stable**: Yes ## `edition` diff --git a/src/config/mod.rs b/src/config/mod.rs index 398a1814d8d1..c5419d860c94 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -155,7 +155,7 @@ create_config! { "Require a specific version of rustfmt"; unstable_features: bool, false, false, "Enables unstable features. Only available on nightly channel"; - disable_all_formatting: bool, false, false, "Don't reformat anything"; + disable_all_formatting: bool, false, true, "Don't reformat anything"; skip_children: bool, false, false, "Don't reformat out of line modules"; hide_parse_errors: bool, false, false, "Hide errors from the parser"; error_on_line_overflow: bool, false, false, "Error if unable to get all lines within max_width"; diff --git a/src/test/mod.rs b/src/test/mod.rs index ece1b91bfd7c..48d61289a9b8 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -469,11 +469,6 @@ fn stdin_works_with_modified_lines() { #[test] fn stdin_disable_all_formatting_test() { init_log(); - match option_env!("CFG_RELEASE_CHANNEL") { - None | Some("nightly") => {} - // These tests require nightly. - _ => return, - } let input = String::from("fn main() { println!(\"This should not be formatted.\"); }"); let mut child = Command::new(rustfmt().to_str().unwrap()) .stdin(Stdio::piped()) From 0cff306b61922ed5f460a4f6cbd4134498a3c42f Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Wed, 13 Oct 2021 21:41:50 -0500 Subject: [PATCH 070/401] ci: fix release asset upload job --- .github/workflows/upload-assets.yml | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/.github/workflows/upload-assets.yml b/.github/workflows/upload-assets.yml index 9a5fd0dd1d31..f4dd39444530 100644 --- a/.github/workflows/upload-assets.yml +++ b/.github/workflows/upload-assets.yml @@ -1,8 +1,10 @@ name: upload on: + push: release: types: [created] + workflow_dispatch: jobs: build-release: @@ -14,42 +16,40 @@ jobs: - build: linux-x86_64 os: ubuntu-latest rust: nightly + target: x86_64-unknown-linux-gnu - build: macos-x86_64 os: macos-latest rust: nightly + target: x86_64-apple-darwin - build: windows-x86_64-gnu os: windows-latest rust: nightly-x86_64-gnu + target: x86_64-pc-windows-gnu - build: windows-x86_64-msvc os: windows-latest rust: nightly-x86_64-msvc + target: x86_64-pc-windows-msvc runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: ${{ matrix.rust }} - override: true + # Run build + - name: install rustup + run: | + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > rustup-init.sh + sh rustup-init.sh -y --default-toolchain none + rustup target add ${{ matrix.target }} - name: Add mingw64 to path for x86_64-gnu run: echo "C:\msys64\mingw64\bin" >> $GITHUB_PATH if: matrix.rust == 'nightly-x86_64-gnu' shell: bash - - name: Install cargo-make - uses: actions-rs/cargo@v1 - with: - command: install - args: --force cargo-make - - name: Build release binaries uses: actions-rs/cargo@v1 with: - command: make - args: release + command: build + args: --release - name: Build archive shell: bash @@ -70,6 +70,7 @@ jobs: fi - name: Upload Release Asset + if: github.event_name == 'release' uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 5f4811ed7bc600e0cbe40c962e8933adb9baaddf Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Thu, 14 Oct 2021 17:16:28 -0400 Subject: [PATCH 071/401] Handle DefinitiveListTactic::SpecialMacro when writing pre-comments Resolves 4615 Previously only Vertical and Mixed enum variants of DefinitiveListTactic were considered when rewriting pre-comments for inner items in lists::write_list. Because we failed to considering the SpecialMacro variant we ended up in a scenario where a ListItem with a pre_comment and a pre_comment_style of ListItemCommentStyle::DifferentLine was written on the same line as the list item itself. Now we apply the same pre-comment formatting to SpecialMacro, Vertical, and Mixed variants of DefinitiveListTactic. --- src/lists.rs | 46 +++++++++++----------- tests/source/issue-4615/minimum_example.rs | 4 ++ tests/target/issue-4615/minimum_example.rs | 5 +++ 3 files changed, 33 insertions(+), 22 deletions(-) create mode 100644 tests/source/issue-4615/minimum_example.rs create mode 100644 tests/target/issue-4615/minimum_example.rs diff --git a/src/lists.rs b/src/lists.rs index 73e886c55637..f0c2ae1499f0 100644 --- a/src/lists.rs +++ b/src/lists.rs @@ -367,29 +367,31 @@ where result.push_str(&comment); if !inner_item.is_empty() { - if tactic == DefinitiveListTactic::Vertical || tactic == DefinitiveListTactic::Mixed - { - // We cannot keep pre-comments on the same line if the comment if normalized. - let keep_comment = if formatting.config.normalize_comments() - || item.pre_comment_style == ListItemCommentStyle::DifferentLine - { - false - } else { - // We will try to keep the comment on the same line with the item here. - // 1 = ` ` - let total_width = total_item_width(item) + item_sep_len + 1; - total_width <= formatting.shape.width - }; - if keep_comment { - result.push(' '); - } else { - result.push('\n'); - result.push_str(indent_str); - // This is the width of the item (without comments). - line_len = item.item.as_ref().map_or(0, |s| unicode_str_width(&s)); + match tactic { + DefinitiveListTactic::SpecialMacro(_) + | DefinitiveListTactic::Vertical + | DefinitiveListTactic::Mixed => { + // We cannot keep pre-comments on the same line if the comment is normalized + let keep_comment = if formatting.config.normalize_comments() + || item.pre_comment_style == ListItemCommentStyle::DifferentLine + { + false + } else { + // We will try to keep the comment on the same line with the item here. + // 1 = ` ` + let total_width = total_item_width(item) + item_sep_len + 1; + total_width <= formatting.shape.width + }; + if keep_comment { + result.push(' '); + } else { + result.push('\n'); + result.push_str(indent_str); + // This is the width of the item (without comments). + line_len = item.item.as_ref().map_or(0, |s| unicode_str_width(&s)); + } } - } else { - result.push(' '); + _ => result.push(' '), } } item_max_width = None; diff --git a/tests/source/issue-4615/minimum_example.rs b/tests/source/issue-4615/minimum_example.rs new file mode 100644 index 000000000000..89af5d1239dd --- /dev/null +++ b/tests/source/issue-4615/minimum_example.rs @@ -0,0 +1,4 @@ +info!(//debug + "{}: sending function_code={:04x} data={:04x} crc=0x{:04X} data={:02X?}", + self.name, function_code, data, crc, output_cmd +); diff --git a/tests/target/issue-4615/minimum_example.rs b/tests/target/issue-4615/minimum_example.rs new file mode 100644 index 000000000000..223b89b812d3 --- /dev/null +++ b/tests/target/issue-4615/minimum_example.rs @@ -0,0 +1,5 @@ +info!( + //debug + "{}: sending function_code={:04x} data={:04x} crc=0x{:04X} data={:02X?}", + self.name, function_code, data, crc, output_cmd +); From 1ae5c35f8d2a0d2ce5d914a479839dc0f3eb70f9 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Mon, 18 Oct 2021 20:48:58 -0400 Subject: [PATCH 072/401] Replace match expression with match! macro This is a follow up to 5f4811ed7bc600e0cbe40c962e8933adb9baaddf The matches! macro expresses the condition more succinctly and avoids the extra level of indentation introduced with the match arm body. --- src/lists.rs | 46 ++++++++++++++++++++++------------------------ 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/src/lists.rs b/src/lists.rs index f0c2ae1499f0..c04b47876169 100644 --- a/src/lists.rs +++ b/src/lists.rs @@ -367,31 +367,29 @@ where result.push_str(&comment); if !inner_item.is_empty() { - match tactic { - DefinitiveListTactic::SpecialMacro(_) - | DefinitiveListTactic::Vertical - | DefinitiveListTactic::Mixed => { - // We cannot keep pre-comments on the same line if the comment is normalized - let keep_comment = if formatting.config.normalize_comments() - || item.pre_comment_style == ListItemCommentStyle::DifferentLine - { - false - } else { - // We will try to keep the comment on the same line with the item here. - // 1 = ` ` - let total_width = total_item_width(item) + item_sep_len + 1; - total_width <= formatting.shape.width - }; - if keep_comment { - result.push(' '); - } else { - result.push('\n'); - result.push_str(indent_str); - // This is the width of the item (without comments). - line_len = item.item.as_ref().map_or(0, |s| unicode_str_width(&s)); - } + use DefinitiveListTactic::*; + if matches!(tactic, Vertical | Mixed | SpecialMacro(_)) { + // We cannot keep pre-comments on the same line if the comment is normalized. + let keep_comment = if formatting.config.normalize_comments() + || item.pre_comment_style == ListItemCommentStyle::DifferentLine + { + false + } else { + // We will try to keep the comment on the same line with the item here. + // 1 = ` ` + let total_width = total_item_width(item) + item_sep_len + 1; + total_width <= formatting.shape.width + }; + if keep_comment { + result.push(' '); + } else { + result.push('\n'); + result.push_str(indent_str); + // This is the width of the item (without comments). + line_len = item.item.as_ref().map_or(0, |s| unicode_str_width(&s)); } - _ => result.push(' '), + } else { + result.push(' ') } } item_max_width = None; From 5f79583c3c83aa3b8a832e4effacb9141de4c2aa Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Tue, 19 Oct 2021 23:13:06 -0500 Subject: [PATCH 073/401] chore: bump toolchain, fix conflict --- Cargo.lock | 6 ------ rust-toolchain | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8e4f90989652..7263f0474770 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -118,12 +118,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - [[package]] name = "clap" version = "2.33.0" diff --git a/rust-toolchain b/rust-toolchain index b0cd4464df8e..b19ecbdb07c4 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2021-07-23" +channel = "nightly-2021-10-20" components = ["rustc-dev"] From efa8f5521d3813cc897ba29ea0ef98c7aef66bb6 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Wed, 20 Oct 2021 00:00:30 -0500 Subject: [PATCH 074/401] chore: bump version and changelog --- CHANGELOG.md | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 69 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68354b6ceaf2..b59438dc4fe7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,73 @@ ## [Unreleased] +## [1.4.38] 2021-10-20 + +### Changed + +- Switched from `rustc-ap-*` crates to `rustc_private` for consumption model of rustc internals +- `annotate-snippets` updated to v0.8 [PR #4762](https://github.com/rust-lang/rustfmt/pull/4762) +- Greatly improved the performance of `cargo fmt` in large workspaces utilizing the `--all` flag by updating to a newer version of `cargo_metadata` that leverages updated `cargo` output from v1.51+ [PR #4997](https://github.com/rust-lang/rustfmt/pull/4997) +- Improved formatting of long slice patterns [#4530](https://github.com/rust-lang/rustfmt/issues/4530) + - **Note you must have `version = Two` in your configuration to take advantage of the new formatting** +- Stabilized `match_block_trailing_comma` configuration option [#3380](https://github.com/rust-lang/rustfmt/issues/3380) - [https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#match_block_trailing_comma](https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#match_block_trailing_comma) +- Stabilized `disable_all_formatting` configuration option [#5026](https://github.com/rust-lang/rustfmt/pull/5026) - [https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#disable_all_formatting](https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#disable_all_formatting) +- Various improvements to the configuration documentation website [https://rust-lang.github.io/rustfmt/?version=v1.4.38]([https://rust-lang.github.io/rustfmt/?version=v1.4.38]) +- Addressed various clippy and rustc warnings + + +### Fixed + +- Resolved issue where specious whitespace would be inserted when a block style comment was terminated within string literal processing [#4312](https://github.com/rust-lang/rustfmt/issues/4312) +- Nested out-of-line mods are again parsed and formatted [#4874](https://github.com/rust-lang/rustfmt/issues/4874) +- Accepts `2021` for edition value from rustfmt command line [PR #4847](https://github.com/rust-lang/rustfmt/pull/4847) +- Unstable command line options are no longer displayed in `--help` text on stable [PR #4798](https://github.com/rust-lang/rustfmt/issues/4798) +- Stopped panicking on patterns in match arms which start with non-ascii characters [#4868](https://github.com/rust-lang/rustfmt/issues/4868) +- Stopped stripping defaults on const params [#4816](https://github.com/rust-lang/rustfmt/issues/4816) +- Fixed issue with dropped content with GAT aliases with self bounds in impls [#4911](https://github.com/rust-lang/rustfmt/issues/4911) +- Stopped removing generic args on associated type constraints [#4943](https://github.com/rust-lang/rustfmt/issues/4943) +- Stopped dropping visibility on certain trait and impl items [#4960](https://github.com/rust-lang/rustfmt/issues/4960) +- Fixed dropping of qualified paths in struct patterns [#4908](https://github.com/rust-lang/rustfmt/issues/4908) and [#5005](https://github.com/rust-lang/rustfmt/issues/5005) +- Fixed bug in line width calculation that was causing specious formatting of certain patterns [#4031](https://github.com/rust-lang/rustfmt/issues/4031) + - **Note that this bug fix may cause observable formatting changes in cases where code had been formatted with prior versions of rustfmt that contained the bug** +- Fixed bug where rustfmt would drop parameter attributes if they were too long in certain cases [#4579](https://github.com/rust-lang/rustfmt/issues/4579) +- Resolved idempotency issue with extern body elements [#4963](https://github.com/rust-lang/rustfmt/issues/4963) +- rustfmt will now handle doc-style comments on function parameters, since they could appear with certain macro usage patterns even though it's generally invalid syntax [#4936](https://github.com/rust-lang/rustfmt/issues/4936) +- Fixed bug in `match_block_trailing_comma` where commas were not added to the blocks of bodies whose arm had a guard that did not fit on the same line as the pattern [#4998](https://github.com/rust-lang/rustfmt/pull/4998) +- Fixed bug in cases where derive attributes started with a block style comment [#4984](https://github.com/rust-lang/rustfmt/issues/4984) +- Fixed issue where the struct rest could be lost when `struct_field_align_threshold` was enabled [#4926](https://github.com/rust-lang/rustfmt/issues/4926) +- Handles cases where certain control flow type expressions have comments between patterns/keywords and the pattern ident contains the keyword [#5009](https://github.com/rust-lang/rustfmt/issues/5009) +- Handles tuple structs that have explicit visibilities and start with a block style comment [#5011](https://github.com/rust-lang/rustfmt/issues/5011) +- Handles leading line-style comments in certain types of macro calls [#4615](https://github.com/rust-lang/rustfmt/issues/4615) + + +### Added +- Granular width heuristic options made available for user control [PR #4782](https://github.com/rust-lang/rustfmt/pull/4782). This includes the following: + - [`array_width`](https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#array_width) + - [`attr_fn_like_width`](https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#attr_fn_like_width) + - [`chain_width`](https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#chain_width) + - [`fn_call_width`](https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#fn_call_width) + - [`single_line_if_else_max_width`](https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#single_line_if_else_max_width) + - [`struct_lit_width`](https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#struct_lit_width) + - [`struct_variant_width`](https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#struct_variant_width) + +Note this hit the rustup distributions prior to the v1.4.38 release as part of an out-of-cycle updates, but is listed in this version because the feature was not in the other v1.4.37 releases. See also the `use_small_heuristics` section on the configuration site for more information +[https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#use_small_heuristics](https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#use_small_heuristics) + +- 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) +- 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 +https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#hex_literal_case + +- `cargo fmt` now directly supports the `--check` flag, which means it's now possible to run `cargo fmt --check` instead of the more verbose `cargo fmt -- --check` [#3888](https://github.com/rust-lang/rustfmt/issues/3888) + +### Install/Download Options +- **rustup (nightly)** - *pending* +- **GitHub Release Binaries** - [Release v1.4.38](https://github.com/rust-lang/rustfmt/releases/tag/v1.4.38) +- **Build from source** - [Tag v1.4.38](https://github.com/rust-lang/rustfmt/tree/v1.4.38), see instructions for how to [install rustfmt from source][install-from-source] + ## [1.4.37] 2021-04-03 ### Changed diff --git a/Cargo.lock b/Cargo.lock index 7263f0474770..2ef83ddd1ae6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -429,7 +429,7 @@ dependencies = [ [[package]] name = "rustfmt-nightly" -version = "1.4.37" +version = "1.4.38" dependencies = [ "annotate-snippets", "anyhow", diff --git a/Cargo.toml b/Cargo.toml index d282766e00b4..8d9c4a7fb20c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rustfmt-nightly" -version = "1.4.37" +version = "1.4.38" description = "Tool to find and fix Rust formatting issues" repository = "https://github.com/rust-lang/rustfmt" readme = "README.md" From 599b2fd9c47f913c6c3540b4805528d8bacd3d7e Mon Sep 17 00:00:00 2001 From: Martinez Date: Sat, 23 Oct 2021 19:01:48 +0300 Subject: [PATCH 075/401] Add One option to group_imports (#4966) * Add Together option to group_imports * Rename option to One * Rename files from Together to One --- Configurations.md | 19 ++++++++++++++++++- src/config/options.rs | 2 ++ src/reorder.rs | 4 +++- .../group_imports/One-merge_imports.rs | 17 +++++++++++++++++ .../configs/group_imports/One-nested.rs | 7 +++++++ .../configs/group_imports/One-no_reorder.rs | 16 ++++++++++++++++ tests/source/configs/group_imports/One.rs | 15 +++++++++++++++ .../group_imports/One-merge_imports.rs | 14 ++++++++++++++ .../configs/group_imports/One-nested.rs | 6 ++++++ .../configs/group_imports/One-no_reorder.rs | 12 ++++++++++++ tests/target/configs/group_imports/One.rs | 11 +++++++++++ 11 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 tests/source/configs/group_imports/One-merge_imports.rs create mode 100644 tests/source/configs/group_imports/One-nested.rs create mode 100644 tests/source/configs/group_imports/One-no_reorder.rs create mode 100644 tests/source/configs/group_imports/One.rs create mode 100644 tests/target/configs/group_imports/One-merge_imports.rs create mode 100644 tests/target/configs/group_imports/One-nested.rs create mode 100644 tests/target/configs/group_imports/One-no_reorder.rs create mode 100644 tests/target/configs/group_imports/One.rs diff --git a/Configurations.md b/Configurations.md index 7a77dbe154b6..13826883d2f4 100644 --- a/Configurations.md +++ b/Configurations.md @@ -2062,7 +2062,7 @@ use sit; Controls the strategy for how imports are grouped together. - **Default value**: `Preserve` -- **Possible values**: `Preserve`, `StdExternalCrate` +- **Possible values**: `Preserve`, `StdExternalCrate`, `One` - **Stable**: No #### `Preserve` (default): @@ -2108,6 +2108,23 @@ use super::update::convert_publish_payload; use crate::models::Event; ``` +#### `One`: + +Discard existing import groups, and create a single group for everything + +```rust +use super::schema::{Context, Payload}; +use super::update::convert_publish_payload; +use crate::models::Event; +use alloc::alloc::Layout; +use broker::database::PooledConnection; +use chrono::Utc; +use core::f32; +use juniper::{FieldError, FieldResult}; +use std::sync::Arc; +use uuid::Uuid; +``` + ## `reorder_modules` Reorder `mod` declarations alphabetically in group. diff --git a/src/config/options.rs b/src/config/options.rs index e92f8e8a5315..a17d48349c05 100644 --- a/src/config/options.rs +++ b/src/config/options.rs @@ -112,6 +112,8 @@ pub enum GroupImportsTactic { /// 2. other imports /// 3. `self` / `crate` / `super` imports StdExternalCrate, + /// Discard existing groups, and create a single group for everything + One, } #[config_type] diff --git a/src/reorder.rs b/src/reorder.rs index 2c58350d4feb..0732c8ee7005 100644 --- a/src/reorder.rs +++ b/src/reorder.rs @@ -118,7 +118,9 @@ fn rewrite_reorderable_or_regroupable_items( }; let mut regrouped_items = match context.config.group_imports() { - GroupImportsTactic::Preserve => vec![normalized_items], + GroupImportsTactic::Preserve | GroupImportsTactic::One => { + vec![normalized_items] + } GroupImportsTactic::StdExternalCrate => group_imports(normalized_items), }; diff --git a/tests/source/configs/group_imports/One-merge_imports.rs b/tests/source/configs/group_imports/One-merge_imports.rs new file mode 100644 index 000000000000..157d3857908a --- /dev/null +++ b/tests/source/configs/group_imports/One-merge_imports.rs @@ -0,0 +1,17 @@ +// rustfmt-group_imports: One +// rustfmt-imports_granularity: Crate +use chrono::Utc; +use super::update::convert_publish_payload; + +use juniper::{FieldError, FieldResult}; +use uuid::Uuid; +use alloc::alloc::Layout; + +use std::sync::Arc; +use alloc::vec::Vec; + +use broker::database::PooledConnection; + +use super::schema::{Context, Payload}; +use core::f32; +use crate::models::Event; diff --git a/tests/source/configs/group_imports/One-nested.rs b/tests/source/configs/group_imports/One-nested.rs new file mode 100644 index 000000000000..109bd07e1ee2 --- /dev/null +++ b/tests/source/configs/group_imports/One-nested.rs @@ -0,0 +1,7 @@ +// rustfmt-group_imports: One +mod test { + use crate::foo::bar; + + use std::path; + use crate::foo::bar2; +} diff --git a/tests/source/configs/group_imports/One-no_reorder.rs b/tests/source/configs/group_imports/One-no_reorder.rs new file mode 100644 index 000000000000..f82f62c7f5b2 --- /dev/null +++ b/tests/source/configs/group_imports/One-no_reorder.rs @@ -0,0 +1,16 @@ +// rustfmt-group_imports: One +// rustfmt-reorder_imports: false +use chrono::Utc; +use super::update::convert_publish_payload; + +use juniper::{FieldError, FieldResult}; +use uuid::Uuid; +use alloc::alloc::Layout; + +use std::sync::Arc; + +use broker::database::PooledConnection; + +use super::schema::{Context, Payload}; +use core::f32; +use crate::models::Event; diff --git a/tests/source/configs/group_imports/One.rs b/tests/source/configs/group_imports/One.rs new file mode 100644 index 000000000000..5ab7a950805b --- /dev/null +++ b/tests/source/configs/group_imports/One.rs @@ -0,0 +1,15 @@ +// rustfmt-group_imports: One +use chrono::Utc; +use super::update::convert_publish_payload; + +use juniper::{FieldError, FieldResult}; +use uuid::Uuid; +use alloc::alloc::Layout; + +use std::sync::Arc; + +use broker::database::PooledConnection; + +use super::schema::{Context, Payload}; +use core::f32; +use crate::models::Event; diff --git a/tests/target/configs/group_imports/One-merge_imports.rs b/tests/target/configs/group_imports/One-merge_imports.rs new file mode 100644 index 000000000000..52e0e1c5ac21 --- /dev/null +++ b/tests/target/configs/group_imports/One-merge_imports.rs @@ -0,0 +1,14 @@ +// rustfmt-group_imports: One +// rustfmt-imports_granularity: Crate +use super::{ + schema::{Context, Payload}, + update::convert_publish_payload, +}; +use crate::models::Event; +use alloc::{alloc::Layout, vec::Vec}; +use broker::database::PooledConnection; +use chrono::Utc; +use core::f32; +use juniper::{FieldError, FieldResult}; +use std::sync::Arc; +use uuid::Uuid; diff --git a/tests/target/configs/group_imports/One-nested.rs b/tests/target/configs/group_imports/One-nested.rs new file mode 100644 index 000000000000..5b648548260f --- /dev/null +++ b/tests/target/configs/group_imports/One-nested.rs @@ -0,0 +1,6 @@ +// rustfmt-group_imports: One +mod test { + use crate::foo::bar; + use crate::foo::bar2; + use std::path; +} diff --git a/tests/target/configs/group_imports/One-no_reorder.rs b/tests/target/configs/group_imports/One-no_reorder.rs new file mode 100644 index 000000000000..015e841d0148 --- /dev/null +++ b/tests/target/configs/group_imports/One-no_reorder.rs @@ -0,0 +1,12 @@ +// rustfmt-group_imports: One +// rustfmt-reorder_imports: false +use chrono::Utc; +use super::update::convert_publish_payload; +use juniper::{FieldError, FieldResult}; +use uuid::Uuid; +use alloc::alloc::Layout; +use std::sync::Arc; +use broker::database::PooledConnection; +use super::schema::{Context, Payload}; +use core::f32; +use crate::models::Event; diff --git a/tests/target/configs/group_imports/One.rs b/tests/target/configs/group_imports/One.rs new file mode 100644 index 000000000000..3094c7ae1157 --- /dev/null +++ b/tests/target/configs/group_imports/One.rs @@ -0,0 +1,11 @@ +// rustfmt-group_imports: One +use super::schema::{Context, Payload}; +use super::update::convert_publish_payload; +use crate::models::Event; +use alloc::alloc::Layout; +use broker::database::PooledConnection; +use chrono::Utc; +use core::f32; +use juniper::{FieldError, FieldResult}; +use std::sync::Arc; +use uuid::Uuid; From d454e8106024fcb517d8fc20d4bdd2c54229695a Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Sat, 23 Oct 2021 17:18:47 -0400 Subject: [PATCH 076/401] Add rustdoc CI check Resolves 5038 rust-lang/rust builds now document rustfmt (rust-lang/rust#87119). The build process is updated with doc checks to ensure that rustfmt doesn't introduce warnings. Warnings are treated as hard errors in rust-lang/rust and won't show until bors tests the changes (refs rust-lang/rust#90087). --- .github/workflows/rustdoc_check.yml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 .github/workflows/rustdoc_check.yml diff --git a/.github/workflows/rustdoc_check.yml b/.github/workflows/rustdoc_check.yml new file mode 100644 index 000000000000..ca96d30f5863 --- /dev/null +++ b/.github/workflows/rustdoc_check.yml @@ -0,0 +1,25 @@ +name: rustdoc check +on: + push: + branches: + - master + pull_request: + +jobs: + rustdoc_check: + runs-on: ubuntu-latest + name: rustdoc check + steps: + - name: checkout + uses: actions/checkout@v2 + + - name: install rustup + run: | + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > rustup-init.sh + sh rustup-init.sh -y --default-toolchain none + rustup target add x86_64-unknown-linux-gnu + + - name: document rustfmt + env: + RUSTDOCFLAGS: --document-private-items --enable-index-page --show-type-layout --generate-link-to-definition -Zunstable-options -Dwarnings + run: cargo doc -Zskip-rustdoc-fingerprint --no-deps -p rustfmt-nightly -p rustfmt-config_proc_macro From c493ee4828ab8e94ed175ec781afe7351bf89784 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sat, 23 Oct 2021 23:07:57 +0200 Subject: [PATCH 077/401] fix clippy::needless_borrow --- src/attr.rs | 4 ++-- src/attr/doc_comment.rs | 2 +- src/cargo-fmt/main.rs | 6 +++--- src/chains.rs | 4 ++-- src/comment.rs | 14 +++++++------- src/emitter/checkstyle.rs | 4 ++-- src/emitter/diff.rs | 2 +- src/expr.rs | 12 ++++++------ src/formatting.rs | 4 ++-- src/imports.rs | 4 ++-- src/items.rs | 30 +++++++++++++++--------------- src/lib.rs | 4 ++-- src/lists.rs | 4 ++-- src/macros.rs | 16 ++++++++-------- src/matches.rs | 10 +++++----- src/modules.rs | 4 ++-- src/overflow.rs | 2 +- src/pairs.rs | 16 ++++++++-------- src/patterns.rs | 2 +- src/syntux/parser.rs | 2 +- src/syntux/session.rs | 2 +- src/test/mod.rs | 4 ++-- src/types.rs | 4 ++-- src/utils.rs | 6 +++--- src/visitor.rs | 20 ++++++++++---------- 25 files changed, 91 insertions(+), 91 deletions(-) diff --git a/src/attr.rs b/src/attr.rs index a5982820e3de..76b66e9da809 100644 --- a/src/attr.rs +++ b/src/attr.rs @@ -451,7 +451,7 @@ impl Rewrite for [ast::Attribute] { if next.is_doc_comment() { let snippet = context.snippet(missing_span); let (_, mlb) = has_newlines_before_after_comment(snippet); - result.push_str(&mlb); + result.push_str(mlb); } } result.push('\n'); @@ -484,7 +484,7 @@ impl Rewrite for [ast::Attribute] { if next.is_doc_comment() { let snippet = context.snippet(missing_span); let (_, mlb) = has_newlines_before_after_comment(snippet); - result.push_str(&mlb); + result.push_str(mlb); } } result.push('\n'); diff --git a/src/attr/doc_comment.rs b/src/attr/doc_comment.rs index c3dcb84c9488..f653a12a8afe 100644 --- a/src/attr/doc_comment.rs +++ b/src/attr/doc_comment.rs @@ -77,7 +77,7 @@ mod tests { ) { assert_eq!( expected_comment, - format!("{}", DocCommentFormatter::new(&literal, style)) + format!("{}", DocCommentFormatter::new(literal, style)) ); } } diff --git a/src/cargo-fmt/main.rs b/src/cargo-fmt/main.rs index 1d423ac34919..759b21218c35 100644 --- a/src/cargo-fmt/main.rs +++ b/src/cargo-fmt/main.rs @@ -401,12 +401,12 @@ fn get_targets_root_only( fn get_targets_recursive( manifest_path: Option<&Path>, - mut targets: &mut BTreeSet, + targets: &mut BTreeSet, visited: &mut BTreeSet, ) -> Result<(), io::Error> { let metadata = get_cargo_metadata(manifest_path)?; for package in &metadata.packages { - add_targets(&package.targets, &mut targets); + add_targets(&package.targets, targets); // Look for local dependencies using information available since cargo v1.51 // It's theoretically possible someone could use a newer version of rustfmt with @@ -427,7 +427,7 @@ fn get_targets_recursive( .any(|p| p.manifest_path.eq(&manifest_path)) { visited.insert(dependency.name.to_owned()); - get_targets_recursive(Some(&manifest_path), &mut targets, visited)?; + get_targets_recursive(Some(&manifest_path), targets, visited)?; } } } diff --git a/src/chains.rs b/src/chains.rs index 614638ea2abf..e26e24ec55ad 100644 --- a/src/chains.rs +++ b/src/chains.rs @@ -568,7 +568,7 @@ impl<'a> ChainFormatterShared<'a> { } else { self.rewrites .iter() - .map(|rw| utils::unicode_str_width(&rw)) + .map(|rw| utils::unicode_str_width(rw)) .sum() } + last.tries; let one_line_budget = if self.child_count == 1 { @@ -673,7 +673,7 @@ impl<'a> ChainFormatterShared<'a> { ChainItemKind::Comment(_, CommentPosition::Top) => result.push_str(&connector), _ => result.push_str(&connector), } - result.push_str(&rewrite); + result.push_str(rewrite); } Some(result) diff --git a/src/comment.rs b/src/comment.rs index dc4f4516252b..a3cd0359e6ba 100644 --- a/src/comment.rs +++ b/src/comment.rs @@ -563,7 +563,7 @@ impl<'a> CommentRewrite<'a> { result.push_str(line); result.push_str(match iter.peek() { Some(next_line) if next_line.is_empty() => sep.trim_end(), - Some(..) => &sep, + Some(..) => sep, None => "", }); } @@ -622,7 +622,7 @@ impl<'a> CommentRewrite<'a> { let is_last = i == count_newlines(orig); if let Some(ref mut ib) = self.item_block { - if ib.add_line(&line) { + if ib.add_line(line) { return false; } self.is_prev_line_multi_line = false; @@ -684,8 +684,8 @@ impl<'a> CommentRewrite<'a> { self.item_block = None; if let Some(stripped) = line.strip_prefix("```") { self.code_block_attr = Some(CodeBlockAttribute::new(stripped)) - } else if self.fmt.config.wrap_comments() && ItemizedBlock::is_itemized_line(&line) { - let ib = ItemizedBlock::new(&line); + } else if self.fmt.config.wrap_comments() && ItemizedBlock::is_itemized_line(line) { + let ib = ItemizedBlock::new(line); self.item_block = Some(ib); return false; } @@ -941,7 +941,7 @@ fn left_trim_comment_line<'a>(line: &'a str, style: &CommentStyle<'_>) -> (&'a s { (&line[4..], true) } else if let CommentStyle::Custom(opener) = *style { - if let Some(ref stripped) = line.strip_prefix(opener) { + if let Some(stripped) = line.strip_prefix(opener) { (stripped, true) } else { (&line[opener.trim_end().len()..], false) @@ -1570,7 +1570,7 @@ pub(crate) fn recover_comment_removed( context.parse_sess.span_to_filename(span), vec![FormattingError::from_span( span, - &context.parse_sess, + context.parse_sess, ErrorKind::LostComment, )], ); @@ -1675,7 +1675,7 @@ impl<'a> Iterator for CommentReducer<'a> { fn remove_comment_header(comment: &str) -> &str { if comment.starts_with("///") || comment.starts_with("//!") { &comment[3..] - } else if let Some(ref stripped) = comment.strip_prefix("//") { + } else if let Some(stripped) = comment.strip_prefix("//") { stripped } else if (comment.starts_with("/**") && !comment.starts_with("/**/")) || comment.starts_with("/*!") diff --git a/src/emitter/checkstyle.rs b/src/emitter/checkstyle.rs index 4448214f3ff2..76f2527db3da 100644 --- a/src/emitter/checkstyle.rs +++ b/src/emitter/checkstyle.rs @@ -121,7 +121,7 @@ mod tests { format!(r#""#, bin_file), format!( r#""#, - XmlEscaped(&r#" println!("Hello, world!");"#), + XmlEscaped(r#" println!("Hello, world!");"#), ), String::from(""), ]; @@ -129,7 +129,7 @@ mod tests { format!(r#""#, lib_file), format!( r#""#, - XmlEscaped(&r#" println!("Greetings!");"#), + XmlEscaped(r#" println!("Greetings!");"#), ), String::from(""), ]; diff --git a/src/emitter/diff.rs b/src/emitter/diff.rs index 2fbbfedb566d..7264ad8bbf36 100644 --- a/src/emitter/diff.rs +++ b/src/emitter/diff.rs @@ -23,7 +23,7 @@ impl Emitter for DiffEmitter { }: FormattedFile<'_>, ) -> Result { const CONTEXT_SIZE: usize = 3; - let mismatch = make_diff(&original_text, formatted_text, CONTEXT_SIZE); + let mismatch = make_diff(original_text, formatted_text, CONTEXT_SIZE); let has_diff = !mismatch.is_empty(); if has_diff { diff --git a/src/expr.rs b/src/expr.rs index 7f1dd363f937..c67c14b19852 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -257,7 +257,7 @@ pub(crate) fn format_expr( } _ => false, }, - ast::ExprKind::Unary(_, ref expr) => needs_space_before_range(context, &expr), + ast::ExprKind::Unary(_, ref expr) => needs_space_before_range(context, expr), _ => false, } } @@ -423,7 +423,7 @@ fn rewrite_empty_block( prefix: &str, shape: Shape, ) -> Option { - if block_has_statements(&block) { + if block_has_statements(block) { return None; } @@ -1148,7 +1148,7 @@ pub(crate) fn is_empty_block( block: &ast::Block, attrs: Option<&[ast::Attribute]>, ) -> bool { - !block_has_statements(&block) + !block_has_statements(block) && !block_contains_comment(context, block) && attrs.map_or(true, |a| inner_attributes(a).is_empty()) } @@ -1621,7 +1621,7 @@ fn rewrite_struct_lit<'a>( }; let fields_str = - wrap_struct_field(context, &attrs, &fields_str, shape, v_shape, one_line_width)?; + wrap_struct_field(context, attrs, &fields_str, shape, v_shape, one_line_width)?; Some(format!("{} {{{}}}", path_str, fields_str)) // FIXME if context.config.indent_style() == Visual, but we run out @@ -1888,7 +1888,7 @@ pub(crate) fn rewrite_assign_rhs_expr( shape: Shape, rhs_tactics: RhsTactics, ) -> Option { - let last_line_width = last_line_width(&lhs).saturating_sub(if lhs.contains('\n') { + let last_line_width = last_line_width(lhs).saturating_sub(if lhs.contains('\n') { shape.indent.width() } else { 0 @@ -1947,7 +1947,7 @@ pub(crate) fn rewrite_assign_rhs_with_comments, R: Rewrite>( if contains_comment { let rhs = rhs.trim_start(); - combine_strs_with_missing_comments(context, &lhs, &rhs, between_span, shape, allow_extend) + combine_strs_with_missing_comments(context, &lhs, rhs, between_span, shape, allow_extend) } else { Some(lhs + &rhs) } diff --git a/src/formatting.rs b/src/formatting.rs index 9ef47b887cad..7d0facb8f12c 100644 --- a/src/formatting.rs +++ b/src/formatting.rs @@ -155,7 +155,7 @@ impl<'a, T: FormatHandler + 'a> FormatContext<'a, T> { let snippet_provider = self.parse_session.snippet_provider(module.span); let mut visitor = FmtVisitor::from_parse_sess( &self.parse_session, - &self.config, + self.config, &snippet_provider, self.report.clone(), ); @@ -180,7 +180,7 @@ impl<'a, T: FormatHandler + 'a> FormatContext<'a, T> { &mut visitor.buffer, &path, &visitor.skipped_range.borrow(), - &self.config, + self.config, &self.report, ); diff --git a/src/imports.rs b/src/imports.rs index 5ac799366894..40e0d06f99df 100644 --- a/src/imports.rs +++ b/src/imports.rs @@ -275,7 +275,7 @@ impl UseTree { shape: Shape, ) -> Option { let vis = self.visibility.as_ref().map_or(Cow::from(""), |vis| { - crate::utils::format_visibility(context, &vis) + crate::utils::format_visibility(context, vis) }); let use_str = self .rewrite(context, shape.offset_left(vis.len())?) @@ -929,7 +929,7 @@ impl Rewrite for UseTree { fn rewrite(&self, context: &RewriteContext<'_>, mut shape: Shape) -> Option { let mut result = String::with_capacity(256); let mut iter = self.path.iter().peekable(); - while let Some(ref segment) = iter.next() { + while let Some(segment) = iter.next() { let segment_str = segment.rewrite(context, shape)?; result.push_str(&segment_str); if iter.peek().is_some() { diff --git a/src/items.rs b/src/items.rs index 1cb1a2701c36..1c7899b3ac3b 100644 --- a/src/items.rs +++ b/src/items.rs @@ -226,7 +226,7 @@ impl<'a> FnSig<'a> { fn to_str(&self, context: &RewriteContext<'_>) -> String { let mut result = String::with_capacity(128); // Vis defaultness constness unsafety abi. - result.push_str(&*format_visibility(context, &self.visibility)); + result.push_str(&*format_visibility(context, self.visibility)); result.push_str(format_defaultness(self.defaultness)); result.push_str(format_constness(self.constness)); result.push_str(format_async(&self.is_async)); @@ -1220,7 +1220,7 @@ impl<'a> Rewrite for TraitAliasBounds<'a> { } else if fits_single_line { Cow::from(" ") } else { - shape.indent.to_string_with_newline(&context.config) + shape.indent.to_string_with_newline(context.config) }; Some(format!("{}{}{}", generic_bounds_str, space, where_str)) @@ -1238,7 +1238,7 @@ pub(crate) fn format_trait_alias( let alias = rewrite_ident(context, ident); // 6 = "trait ", 2 = " =" let g_shape = shape.offset_left(6)?.sub_width(2)?; - let generics_str = rewrite_generics(context, &alias, generics, g_shape)?; + let generics_str = rewrite_generics(context, alias, generics, g_shape)?; let vis_str = format_visibility(context, vis); let lhs = format!("{}trait {} =", vis_str, generics_str); // 1 = ";" @@ -1386,7 +1386,7 @@ fn format_empty_struct_or_tuple( closer: &str, ) { // 3 = " {}" or "();" - let used_width = last_line_used_width(&result, offset.width()) + 3; + let used_width = last_line_used_width(result, offset.width()) + 3; if used_width > context.config.max_width() { result.push_str(&offset.to_string_with_newline(context.config)) } @@ -2066,7 +2066,7 @@ fn rewrite_explicit_self( )?; Some(combine_strs_with_missing_comments( context, - ¶m_attrs, + param_attrs, &format!("&{} {}self", lifetime_str, mut_str), span, shape, @@ -2075,7 +2075,7 @@ fn rewrite_explicit_self( } None => Some(combine_strs_with_missing_comments( context, - ¶m_attrs, + param_attrs, &format!("&{}self", mut_str), span, shape, @@ -2091,7 +2091,7 @@ fn rewrite_explicit_self( Some(combine_strs_with_missing_comments( context, - ¶m_attrs, + param_attrs, &format!("{}self: {}", format_mutability(mutability), type_str), span, shape, @@ -2100,7 +2100,7 @@ fn rewrite_explicit_self( } ast::SelfKind::Value(mutability) => Some(combine_strs_with_missing_comments( context, - ¶m_attrs, + param_attrs, &format!("{}self", format_mutability(mutability)), span, shape, @@ -2226,7 +2226,7 @@ fn rewrite_fn_base( } // Skip `pub(crate)`. - let lo_after_visibility = get_bytepos_after_visibility(&fn_sig.visibility, span); + let lo_after_visibility = get_bytepos_after_visibility(fn_sig.visibility, span); // A conservative estimation, the goal is to be over all parens in generics let params_start = fn_sig .generics @@ -2984,7 +2984,7 @@ fn format_header( let mut result = String::with_capacity(128); let shape = Shape::indented(offset, context.config); - result.push_str(&format_visibility(context, vis).trim()); + result.push_str(format_visibility(context, vis).trim()); // Check for a missing comment between the visibility and the item name. let after_vis = vis.span.hi(); @@ -3005,7 +3005,7 @@ fn format_header( } } - result.push_str(&rewrite_ident(context, ident)); + result.push_str(rewrite_ident(context, ident)); result } @@ -3133,7 +3133,7 @@ impl Rewrite for ast::ForeignItem { let inner_attrs = inner_attributes(&self.attrs); let fn_ctxt = visit::FnCtxt::Foreign; visitor.visit_fn( - visit::FnKind::Fn(fn_ctxt, self.ident, &fn_sig, &self.vis, Some(body)), + visit::FnKind::Fn(fn_ctxt, self.ident, fn_sig, &self.vis, Some(body)), generics, &fn_sig.decl, self.span, @@ -3146,7 +3146,7 @@ impl Rewrite for ast::ForeignItem { context, shape.indent, self.ident, - &FnSig::from_method_sig(&fn_sig, generics, &self.vis), + &FnSig::from_method_sig(fn_sig, generics, &self.vis), span, FnBraceStyle::None, ) @@ -3171,7 +3171,7 @@ impl Rewrite for ast::ForeignItem { let ast::TyAliasKind(_, ref generics, ref generic_bounds, ref type_default) = **ty_alias_kind; rewrite_type( - &context, + context, shape.indent, self.ident, &self.vis, @@ -3229,7 +3229,7 @@ fn rewrite_attrs( combine_strs_with_missing_comments( context, &attrs_str, - &item_str, + item_str, missed_span, shape, allow_extend, diff --git a/src/lib.rs b/src/lib.rs index 47a7b9d4dbe3..792a1080f0e9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -283,7 +283,7 @@ impl FormatReport { writeln!( t, "{}", - FormatReportFormatterBuilder::new(&self) + FormatReportFormatterBuilder::new(self) .enable_colors(true) .build() )?; @@ -297,7 +297,7 @@ impl FormatReport { impl fmt::Display for FormatReport { // Prints all the formatting errors. fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - write!(fmt, "{}", FormatReportFormatterBuilder::new(&self).build())?; + write!(fmt, "{}", FormatReportFormatterBuilder::new(self).build())?; Ok(()) } } diff --git a/src/lists.rs b/src/lists.rs index c04b47876169..d341ec8e6b0e 100644 --- a/src/lists.rs +++ b/src/lists.rs @@ -386,7 +386,7 @@ where result.push('\n'); result.push_str(indent_str); // This is the width of the item (without comments). - line_len = item.item.as_ref().map_or(0, |s| unicode_str_width(&s)); + line_len = item.item.as_ref().map_or(0, |s| unicode_str_width(s)); } } else { result.push(' ') @@ -820,7 +820,7 @@ where pub(crate) fn total_item_width(item: &ListItem) -> usize { comment_len(item.pre_comment.as_ref().map(|x| &(*x)[..])) + comment_len(item.post_comment.as_ref().map(|x| &(*x)[..])) - + item.item.as_ref().map_or(0, |s| unicode_str_width(&s)) + + item.item.as_ref().map_or(0, |s| unicode_str_width(s)) } fn comment_len(comment: Option<&str>) -> usize { diff --git a/src/macros.rs b/src/macros.rs index 927187dfd8a2..ef747638e33e 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -186,7 +186,7 @@ fn return_macro_parse_failure_fallback( }) .unwrap_or(false); if is_like_block_indent_style { - return trim_left_preserve_layout(context.snippet(span), indent, &context.config); + return trim_left_preserve_layout(context.snippet(span), indent, context.config); } context.skipped_range.borrow_mut().push(( @@ -437,7 +437,7 @@ fn rewrite_macro_inner( // the `macro_name!` and `{ /* macro_body */ }` but skip modifying // anything in between the braces (for now). let snippet = context.snippet(mac.span()).trim_start_matches(|c| c != '{'); - match trim_left_preserve_layout(snippet, shape.indent, &context.config) { + match trim_left_preserve_layout(snippet, shape.indent, context.config) { Some(macro_body) => Some(format!("{} {}", macro_name, macro_body)), None => Some(format!("{} {}", macro_name, snippet)), } @@ -901,7 +901,7 @@ impl MacroArgParser { break; } TokenTree::Token(ref t) => { - buffer.push_str(&pprust::token_to_string(&t)); + buffer.push_str(&pprust::token_to_string(t)); } _ => return None, } @@ -1045,7 +1045,7 @@ fn wrap_macro_args_inner( let mut iter = args.iter().peekable(); let indent_str = shape.indent.to_string_with_newline(context.config); - while let Some(ref arg) = iter.next() { + while let Some(arg) = iter.next() { result.push_str(&arg.rewrite(context, shape, use_multiple_lines)?); if use_multiple_lines @@ -1055,7 +1055,7 @@ fn wrap_macro_args_inner( result.pop(); } result.push_str(&indent_str); - } else if let Some(ref next_arg) = iter.peek() { + } else if let Some(next_arg) = iter.peek() { let space_before_dollar = !arg.kind.ends_with_space() && next_arg.kind.starts_with_dollar(); let space_before_brace = next_arg.kind.starts_with_brace(); @@ -1370,7 +1370,7 @@ impl MacroBranch { { s += &indent_str; } - (s + l + "\n", indent_next_line(kind, &l, &config)) + (s + l + "\n", indent_next_line(kind, l, &config)) }, ) .0; @@ -1514,11 +1514,11 @@ fn rewrite_macro_with_items( MacroArg::Item(item) => item, _ => return None, }; - visitor.visit_item(&item); + visitor.visit_item(item); } let mut result = String::with_capacity(256); - result.push_str(¯o_name); + result.push_str(macro_name); result.push_str(opener); result.push_str(&visitor.block_indent.to_string_with_newline(context.config)); result.push_str(visitor.buffer.trim()); diff --git a/src/matches.rs b/src/matches.rs index 5a6ed0ec06e5..25b953ef4257 100644 --- a/src/matches.rs +++ b/src/matches.rs @@ -319,7 +319,7 @@ fn flatten_arm_body<'a>( let can_extend = |expr| !context.config.force_multiline_blocks() && can_flatten_block_around_this(expr); - if let Some(ref block) = block_can_be_flattened(context, body) { + if let Some(block) = block_can_be_flattened(context, body) { if let ast::StmtKind::Expr(ref expr) = block.stmts[0].kind { if let ast::ExprKind::Block(..) = expr.kind { flatten_arm_body(context, expr, None) @@ -393,7 +393,7 @@ fn rewrite_match_body( if comment_str.is_empty() { String::new() } else { - rewrite_comment(comment_str, false, shape, &context.config)? + rewrite_comment(comment_str, false, shape, context.config)? } }; @@ -408,8 +408,8 @@ fn rewrite_match_body( result.push_str(&arrow_comment); } result.push_str(&nested_indent_str); - result.push_str(&body_str); - result.push_str(&comma); + result.push_str(body_str); + result.push_str(comma); return Some(result); } @@ -451,7 +451,7 @@ fn rewrite_match_body( result.push_str(&arrow_comment); } result.push_str(&block_sep); - result.push_str(&body_str); + result.push_str(body_str); result.push_str(&body_suffix); Some(result) }; diff --git a/src/modules.rs b/src/modules.rs index ded34d9032f9..b0c1604a6027 100644 --- a/src/modules.rs +++ b/src/modules.rs @@ -290,7 +290,7 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> { }; self.visit_sub_mod_after_directory_update(sub_mod, Some(directory)) } - SubModKind::Internal(ref item) => { + SubModKind::Internal(item) => { self.push_inline_mod_directory(item.ident, &item.attrs); self.visit_sub_mod_after_directory_update(sub_mod, None) } @@ -317,7 +317,7 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> { } match (sub_mod.ast_mod_kind, sub_mod.items) { (Some(Cow::Borrowed(ast::ModKind::Loaded(items, _, _))), _) => { - self.visit_mod_from_ast(&items) + self.visit_mod_from_ast(items) } (Some(Cow::Owned(..)), Cow::Owned(items)) => self.visit_mod_outside_ast(items), (_, _) => Ok(()), diff --git a/src/overflow.rs b/src/overflow.rs index ac24181c7805..3475f5c378cd 100644 --- a/src/overflow.rs +++ b/src/overflow.rs @@ -394,7 +394,7 @@ impl<'a> Context<'a> { ) -> Option { let last_item = self.last_item()?; let rewrite = match last_item { - OverflowableItem::Expr(ref expr) => { + OverflowableItem::Expr(expr) => { match expr.kind { // When overflowing the closure which consists of a single control flow // expression, force to use block if its condition uses multi line. diff --git a/src/pairs.rs b/src/pairs.rs index 0f3d5e8f878b..d1c75126ea4a 100644 --- a/src/pairs.rs +++ b/src/pairs.rs @@ -55,11 +55,11 @@ fn rewrite_pairs_one_line( for ((_, rewrite), s) in list.list.iter().zip(list.separators.iter()) { if let Some(rewrite) = rewrite { - if !is_single_line(&rewrite) || result.len() > shape.width { + if !is_single_line(rewrite) || result.len() > shape.width { return None; } - result.push_str(&rewrite); + result.push_str(rewrite); result.push(' '); result.push_str(s); result.push(' '); @@ -94,18 +94,18 @@ fn rewrite_pairs_multiline( shape: Shape, context: &RewriteContext<'_>, ) -> Option { - let rhs_offset = shape.rhs_overhead(&context.config); + let rhs_offset = shape.rhs_overhead(context.config); let nested_shape = (match context.config.indent_style() { IndentStyle::Visual => shape.visual_indent(0), IndentStyle::Block => shape.block_indent(context.config.tab_spaces()), }) - .with_max_width(&context.config) + .with_max_width(context.config) .sub_width(rhs_offset)?; let indent_str = nested_shape.indent.to_string_with_newline(context.config); let mut result = String::new(); - result.push_str(&list.list[0].1.as_ref()?); + result.push_str(list.list[0].1.as_ref()?); for ((e, default_rw), s) in list.list[1..].iter().zip(list.separators.iter()) { // The following test checks if we should keep two subexprs on the same @@ -144,7 +144,7 @@ fn rewrite_pairs_multiline( } } - result.push_str(&default_rw.as_ref()?); + result.push_str(default_rw.as_ref()?); } Some(result) } @@ -264,12 +264,12 @@ impl FlattenPair for ast::Expr { return node.rewrite(context, shape); } let nested_overhead = sep + 1; - let rhs_offset = shape.rhs_overhead(&context.config); + let rhs_offset = shape.rhs_overhead(context.config); let nested_shape = (match context.config.indent_style() { IndentStyle::Visual => shape.visual_indent(0), IndentStyle::Block => shape.block_indent(context.config.tab_spaces()), }) - .with_max_width(&context.config) + .with_max_width(context.config) .sub_width(rhs_offset)?; let default_shape = match context.config.binop_separator() { SeparatorPlace::Back => nested_shape.sub_width(nested_overhead)?, diff --git a/src/patterns.rs b/src/patterns.rs index ba8d8024a970..2676c6473556 100644 --- a/src/patterns.rs +++ b/src/patterns.rs @@ -482,7 +482,7 @@ fn rewrite_tuple_pat( let path_str = path_str.unwrap_or_default(); overflow::rewrite_with_parens( - &context, + context, &path_str, pat_vec.iter(), shape, diff --git a/src/syntux/parser.rs b/src/syntux/parser.rs index b5fe4335dd33..14b92238cfa6 100644 --- a/src/syntux/parser.rs +++ b/src/syntux/parser.rs @@ -112,7 +112,7 @@ impl<'a> Parser<'a> { span: Span, ) -> Result<(Vec, Vec>, Span), ParserError> { let result = catch_unwind(AssertUnwindSafe(|| { - let mut parser = new_parser_from_file(sess.inner(), &path, Some(span)); + let mut parser = new_parser_from_file(sess.inner(), path, Some(span)); match parser.parse_mod(&TokenKind::Eof) { Ok(result) => Some(result), Err(mut e) => { diff --git a/src/syntux/session.rs b/src/syntux/session.rs index 946c076d9f2d..cdb4893d443b 100644 --- a/src/syntux/session.rs +++ b/src/syntux/session.rs @@ -164,7 +164,7 @@ impl ParseSess { } pub(crate) fn ignore_file(&self, path: &FileName) -> bool { - self.ignore_path_set.as_ref().is_match(&path) + self.ignore_path_set.as_ref().is_match(path) } pub(crate) fn set_silent_emitter(&mut self) { diff --git a/src/test/mod.rs b/src/test/mod.rs index 48d61289a9b8..e2620508c340 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -535,9 +535,9 @@ fn check_files(files: Vec, opt_config: &Option) -> (Vec { - print!("{}", FormatReportFormatterBuilder::new(&report).build()); + print!("{}", FormatReportFormatterBuilder::new(report).build()); fails += 1; } Ok(report) => reports.push(report), diff --git a/src/types.rs b/src/types.rs index 62c05ba078c5..9ea90c5e46dd 100644 --- a/src/types.rs +++ b/src/types.rs @@ -728,7 +728,7 @@ impl Rewrite for ast::Ty { result = combine_strs_with_missing_comments( context, result.trim_end(), - &mt.ty.rewrite(&context, shape)?, + &mt.ty.rewrite(context, shape)?, before_ty_span, shape, true, @@ -738,7 +738,7 @@ impl Rewrite for ast::Ty { let budget = shape.width.checked_sub(used_width)?; let ty_str = mt .ty - .rewrite(&context, Shape::legacy(budget, shape.indent + used_width))?; + .rewrite(context, Shape::legacy(budget, shape.indent + used_width))?; result.push_str(&ty_str); } diff --git a/src/utils.rs b/src/utils.rs index 29e1e070d411..3a8713c5bdb0 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -42,7 +42,7 @@ pub(crate) fn is_same_visibility(a: &Visibility, b: &Visibility) -> bool { ( VisibilityKind::Restricted { path: p, .. }, VisibilityKind::Restricted { path: q, .. }, - ) => pprust::path_to_string(&p) == pprust::path_to_string(&q), + ) => pprust::path_to_string(p) == pprust::path_to_string(q), (VisibilityKind::Public, VisibilityKind::Public) | (VisibilityKind::Inherited, VisibilityKind::Inherited) | ( @@ -689,7 +689,7 @@ mod test { #[test] fn test_remove_trailing_white_spaces() { let s = " r#\"\n test\n \"#"; - assert_eq!(remove_trailing_white_spaces(&s), s); + assert_eq!(remove_trailing_white_spaces(s), s); } #[test] @@ -698,7 +698,7 @@ mod test { let config = Config::default(); let indent = Indent::new(4, 0); assert_eq!( - trim_left_preserve_layout(&s, indent, &config), + trim_left_preserve_layout(s, indent, &config), Some("aaa\n bbb\n ccc".to_string()) ); } diff --git a/src/visitor.rs b/src/visitor.rs index d854d90b40b6..c37e1cb10117 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -164,7 +164,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { ); } else { let shape = self.shape(); - let rewrite = self.with_context(|ctx| stmt.rewrite(&ctx, shape)); + let rewrite = self.with_context(|ctx| stmt.rewrite(ctx, shape)); self.push_rewrite(stmt.span(), rewrite) } } @@ -273,9 +273,9 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { let comment_snippet = self.snippet(span); - let align_to_right = if unindent_comment && contains_comment(&comment_snippet) { + let align_to_right = if unindent_comment && contains_comment(comment_snippet) { let first_lines = comment_snippet.splitn(2, '/').next().unwrap_or(""); - last_line_width(first_lines) > last_line_width(&comment_snippet) + last_line_width(first_lines) > last_line_width(comment_snippet) } else { false }; @@ -439,7 +439,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { let filtered_attrs; let mut attrs = &item.attrs; let skip_context_saved = self.skip_context.clone(); - self.skip_context.update_with_attrs(&attrs); + self.skip_context.update_with_attrs(attrs); let should_visit_node_again = match item.kind { // For use/extern crate items, skip rewriting attributes but check for a skip attribute. @@ -488,12 +488,12 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { ast::ItemKind::Use(ref tree) => self.format_import(item, tree), ast::ItemKind::Impl { .. } => { let block_indent = self.block_indent; - let rw = self.with_context(|ctx| format_impl(&ctx, item, block_indent)); + let rw = self.with_context(|ctx| format_impl(ctx, item, block_indent)); self.push_rewrite(item.span, rw); } ast::ItemKind::Trait(..) => { let block_indent = self.block_indent; - let rw = self.with_context(|ctx| format_trait(&ctx, item, block_indent)); + let rw = self.with_context(|ctx| format_trait(ctx, item, block_indent)); self.push_rewrite(item.span, rw); } ast::ItemKind::TraitAlias(ref generics, ref generic_bounds) => { @@ -552,7 +552,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { visit::FnKind::Fn( fn_ctxt, item.ident, - &fn_signature, + fn_signature, &item.vis, Some(body), ), @@ -567,7 +567,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { let rewrite = self.rewrite_required_fn( indent, item.ident, - &fn_signature, + fn_signature, &item.vis, generics, item.span, @@ -718,7 +718,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { &ii.vis, defaultness, ty.as_ref(), - &generics, + generics, &self.get_context(), self.block_indent, ii.span, @@ -905,7 +905,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { } fn walk_mod_items(&mut self, items: &[rustc_ast::ptr::P]) { - self.visit_items_with_reordering(&ptr_vec_to_ref_vec(&items)); + self.visit_items_with_reordering(&ptr_vec_to_ref_vec(items)); } fn walk_stmts(&mut self, stmts: &[Stmt<'_>], include_current_empty_semi: bool) { From 0b8ffac0297a46d7d2d2f8328cbc49de7e7144f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sat, 23 Oct 2021 23:21:52 +0200 Subject: [PATCH 078/401] fix a bunch of the other clippy warnings that look interesting --- src/comment.rs | 4 ++-- src/expr.rs | 6 +++--- src/matches.rs | 2 +- src/modules.rs | 2 +- src/patterns.rs | 6 +++--- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/comment.rs b/src/comment.rs index a3cd0359e6ba..7b76c232937d 100644 --- a/src/comment.rs +++ b/src/comment.rs @@ -405,7 +405,7 @@ impl CodeBlockAttribute { /// attributes are valid rust attributes /// See fn new(attributes: &str) -> CodeBlockAttribute { - for attribute in attributes.split(",") { + for attribute in attributes.split(',') { match attribute.trim() { "" | "rust" | "should_panic" | "no_run" | "edition2015" | "edition2018" | "edition2021" => (), @@ -1384,7 +1384,7 @@ impl<'a> Iterator for LineClasses<'a> { None => unreachable!(), }; - while let Some((kind, c)) = self.base.next() { + for (kind, c) in self.base.by_ref() { // needed to set the kind of the ending character on the last line self.kind = kind; if c == '\n' { diff --git a/src/expr.rs b/src/expr.rs index c67c14b19852..1ca01f9db9a4 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -1207,11 +1207,11 @@ fn rewrite_int_lit(context: &RewriteContext<'_>, lit: &ast::Lit, shape: Shape) - let span = lit.span; let symbol = lit.token.symbol.as_str(); - if symbol.starts_with("0x") { + if let Some(symbol_stripped) = symbol.strip_prefix("0x") { let hex_lit = match context.config.hex_literal_case() { HexLiteralCase::Preserve => None, - HexLiteralCase::Upper => Some(symbol[2..].to_ascii_uppercase()), - HexLiteralCase::Lower => Some(symbol[2..].to_ascii_lowercase()), + HexLiteralCase::Upper => Some(symbol_stripped.to_ascii_uppercase()), + HexLiteralCase::Lower => Some(symbol_stripped.to_ascii_lowercase()), }; if let Some(hex_lit) = hex_lit { return wrap_str( diff --git a/src/matches.rs b/src/matches.rs index 25b953ef4257..22d23fc1cdba 100644 --- a/src/matches.rs +++ b/src/matches.rs @@ -168,7 +168,7 @@ fn collect_beginning_verts( .map(|a| { context .snippet(a.pat.span) - .starts_with("|") + .starts_with('|') .then(|| a.pat.span().lo()) }) .collect() diff --git a/src/modules.rs b/src/modules.rs index b0c1604a6027..88d434d759db 100644 --- a/src/modules.rs +++ b/src/modules.rs @@ -197,7 +197,7 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> { /// Visit modules from AST. fn visit_mod_from_ast( &mut self, - items: &'ast Vec>, + items: &'ast [rustc_ast::ptr::P], ) -> Result<(), ModuleResolutionError> { for item in items { if is_cfg_if(item) { diff --git a/src/patterns.rs b/src/patterns.rs index 2676c6473556..a80d63201f98 100644 --- a/src/patterns.rs +++ b/src/patterns.rs @@ -456,11 +456,11 @@ fn rewrite_tuple_pat( context: &RewriteContext<'_>, shape: Shape, ) -> Option { - let mut pat_vec: Vec<_> = pats.iter().map(|x| TuplePatField::Pat(x)).collect(); - - if pat_vec.is_empty() { + if pats.is_empty() { return Some(format!("{}()", path_str.unwrap_or_default())); } + let mut pat_vec: Vec<_> = pats.iter().map(TuplePatField::Pat).collect(); + let wildcard_suffix_len = count_wildcard_suffix_len(context, &pat_vec, span, shape); let (pat_vec, span) = if context.config.condense_wildcard_suffixes() && wildcard_suffix_len >= 2 { From 8b766f35bcf0b2c2dcd82bd4e4ec82af49c16367 Mon Sep 17 00:00:00 2001 From: Seiichi Uchida Date: Mon, 11 May 2020 09:50:41 +0900 Subject: [PATCH 079/401] Remove legacy-rustfmt.toml (#4169) --- legacy-rustfmt.toml | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 legacy-rustfmt.toml diff --git a/legacy-rustfmt.toml b/legacy-rustfmt.toml deleted file mode 100644 index f976fa68e4c7..000000000000 --- a/legacy-rustfmt.toml +++ /dev/null @@ -1,2 +0,0 @@ -indent_style = "Visual" -combine_control_expr = false From bc46af97423cef7484a806e8282c545d7607889e Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Tue, 19 Oct 2021 01:14:51 -0400 Subject: [PATCH 080/401] Retain trailing comments in module when using rustfmt::skip attribute Resolves 5033 Trailing comments at the end of the root Module were removed because the module span did not extend until the end of the file. The root Module's span now encompasses the entire file, which ensures that no comments are lost when using ``#![rustfmt::skip]`` --- src/modules.rs | 6 ++++-- tests/target/issue-5033/minimum_example.rs | 8 ++++++++ tests/target/issue-5033/nested_modules.rs | 11 +++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 tests/target/issue-5033/minimum_example.rs create mode 100644 tests/target/issue-5033/nested_modules.rs diff --git a/src/modules.rs b/src/modules.rs index 88d434d759db..9e75f41ae36e 100644 --- a/src/modules.rs +++ b/src/modules.rs @@ -16,7 +16,7 @@ use crate::syntux::parser::{ Directory, DirectoryOwnership, ModError, ModulePathSuccess, Parser, ParserError, }; use crate::syntux::session::ParseSess; -use crate::utils::contains_skip; +use crate::utils::{contains_skip, mk_sp}; mod visitor; @@ -135,10 +135,12 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> { self.visit_mod_from_ast(&krate.items)?; } + let snippet_provider = self.parse_sess.snippet_provider(krate.span); + self.file_map.insert( root_filename, Module::new( - krate.span, + mk_sp(snippet_provider.start_pos(), snippet_provider.end_pos()), None, Cow::Borrowed(&krate.items), Cow::Borrowed(&krate.attrs), diff --git a/tests/target/issue-5033/minimum_example.rs b/tests/target/issue-5033/minimum_example.rs new file mode 100644 index 000000000000..0e7df41deb2d --- /dev/null +++ b/tests/target/issue-5033/minimum_example.rs @@ -0,0 +1,8 @@ +// leading comment + +#![rustfmt::skip] +fn main() { + println!("main"); // commented +} + +// post comment diff --git a/tests/target/issue-5033/nested_modules.rs b/tests/target/issue-5033/nested_modules.rs new file mode 100644 index 000000000000..7a11133b60bb --- /dev/null +++ b/tests/target/issue-5033/nested_modules.rs @@ -0,0 +1,11 @@ +#![rustfmt::skip] + +mod a { + mod b { + + } + + // trailing comment b +} + +// trailing comment a From ed5a0250d3f9401046253ebaf25105f2be7749f2 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Sat, 30 Oct 2021 10:10:08 -0500 Subject: [PATCH 081/401] refactor: minor parser cleanup --- src/syntux/parser.rs | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/syntux/parser.rs b/src/syntux/parser.rs index 14b92238cfa6..d1bb2f80004a 100644 --- a/src/syntux/parser.rs +++ b/src/syntux/parser.rs @@ -125,18 +125,12 @@ impl<'a> Parser<'a> { } })); match result { - Ok(Some(m)) => { - if !sess.has_errors() { - return Ok(m); - } - - if sess.can_reset_errors() { - sess.reset_errors(); - return Ok(m); - } - Err(ParserError::ParseError) + Ok(Some(m)) if !sess.has_errors() => Ok(m), + Ok(Some(m)) if sess.can_reset_errors() => { + sess.reset_errors(); + Ok(m) } - Ok(None) => Err(ParserError::ParseError), + Ok(_) => Err(ParserError::ParseError), Err(..) if path.exists() => Err(ParserError::ParseError), Err(_) => Err(ParserError::ParsePanicError), } From 54b1e0bb15934bedad7c3df4626b291bfe742522 Mon Sep 17 00:00:00 2001 From: enterprisey Date: Thu, 14 Nov 2019 09:44:59 -0500 Subject: [PATCH 082/401] README grammar fix (#3926) Remove comma splice --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9c7a1c4bc341..62e8ea608913 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ cargo +nightly fmt ## Limitations -Rustfmt tries to work on as much Rust code as possible, sometimes, the code +Rustfmt tries to work on as much Rust code as possible. Sometimes, the code doesn't even need to compile! As we approach a 1.0 release we are also looking to limit areas of instability; in particular, post-1.0, the formatting of most code should not change as Rustfmt improves. However, there are some things that From a24ed3c322b9b9a6b7dc9f3f18e4142d3adabf46 Mon Sep 17 00:00:00 2001 From: chansuke Date: Wed, 25 Nov 2020 19:31:02 +0900 Subject: [PATCH 083/401] Fix MSRV --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 62e8ea608913..eb39468d4d14 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,7 @@ rustfmt to exit with an error code if the input is not formatted correctly. It will also print any found differences. (Older versions of Rustfmt don't support `--check`, use `--write-mode diff`). -A minimal Travis setup could look like this (requires Rust 1.24.0 or greater): +A minimal Travis setup could look like this (requires Rust 1.31.0 or greater): ```yaml language: rust From a4d7011c18eeae7fbb11286822b9519dc9a9325f Mon Sep 17 00:00:00 2001 From: Jonathan <30177086+MonliH@users.noreply.github.com> Date: Sun, 11 Oct 2020 16:28:45 +0000 Subject: [PATCH 084/401] Document RUSTFMT environment variable (#4464) * Document RUSTFMT env var * Move documentation up * Apply suggestions from code review Co-authored-by: Caleb Cartwright * Fix accedental removal Co-authored-by: Caleb Cartwright # Conflicts: # README.md --- README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/README.md b/README.md index eb39468d4d14..b3d21e6fb87c 100644 --- a/README.md +++ b/README.md @@ -102,6 +102,25 @@ read data from stdin. Alternatively, you can use `cargo fmt` to format all binary and library targets of your crate. You can run `rustfmt --help` for information about available arguments. +The easiest way to run rustfmt against a project is with `cargo fmt`. `cargo fmt` works on both +single-crate projects and [cargo workspaces](https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html). +Please see `cargo fmt --help` for usage information. + +You can specify the path to your own `rustfmt` binary for cargo to use by setting the`RUSTFMT` +environment variable. This was added in v1.4.22, so you must have this version or newer to leverage this feature (`cargo fmt --version`) + +### Running `rustfmt` directly + +To format individual files or arbitrary codes from stdin, the `rustfmt` binary should be used. Some +examples follow: + +- `rustfmt lib.rs main.rs` will format "lib.rs" and "main.rs" in place +- `rustfmt` will read a code from stdin and write formatting to stdout + - `echo "fn main() {}" | rustfmt` would emit "fn main() {}". + +For more information, including arguments and emit options, see `rustfmt --help`. + +### Verifying code is formatted When running with `--check`, Rustfmt will exit with `0` if Rustfmt would not make any formatting changes to the input, and `1` if Rustfmt would make changes. From 5ce82e15133501cdfa96d58370e9b4c3873546b1 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Fri, 8 Oct 2021 19:22:12 -0400 Subject: [PATCH 085/401] Prevent trailing whitespace in where clause bound predicate resolves 5012 resolves 4850 This behavior was noticed when using the ``trailing_comma = "Never"`` configuration option (5012). This behavior was also noticed when using default configurations (4850). rustfmt would add a trailing space to where clause bounds that had an empty right hand side. Now no trailing space is added to the end of these where clause bounds. --- src/expr.rs | 3 +++ tests/target/issue-5012/trailing_comma_always.rs | 8 ++++++++ tests/target/issue-5012/trailing_comma_never.rs | 8 ++++++++ tests/target/issue_4850.rs | 4 ++++ 4 files changed, 23 insertions(+) create mode 100644 tests/target/issue-5012/trailing_comma_always.rs create mode 100644 tests/target/issue-5012/trailing_comma_never.rs create mode 100644 tests/target/issue_4850.rs diff --git a/src/expr.rs b/src/expr.rs index 1ca01f9db9a4..58942e442de0 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -1962,6 +1962,9 @@ fn choose_rhs( has_rhs_comment: bool, ) -> Option { match orig_rhs { + Some(ref new_str) if new_str.is_empty() => { + return Some(String::new()); + } Some(ref new_str) if !new_str.contains('\n') && unicode_str_width(new_str) <= shape.width => { diff --git a/tests/target/issue-5012/trailing_comma_always.rs b/tests/target/issue-5012/trailing_comma_always.rs new file mode 100644 index 000000000000..ff9c40fbbd8d --- /dev/null +++ b/tests/target/issue-5012/trailing_comma_always.rs @@ -0,0 +1,8 @@ +// rustfmt-trailing_comma: Always + +pub struct Matrix +where + [T; R * C]:, +{ + contents: [T; R * C], +} diff --git a/tests/target/issue-5012/trailing_comma_never.rs b/tests/target/issue-5012/trailing_comma_never.rs new file mode 100644 index 000000000000..2fac8eae52b8 --- /dev/null +++ b/tests/target/issue-5012/trailing_comma_never.rs @@ -0,0 +1,8 @@ +// rustfmt-trailing_comma: Never + +pub struct Matrix +where + [T; R * C]: +{ + contents: [T; R * C] +} diff --git a/tests/target/issue_4850.rs b/tests/target/issue_4850.rs new file mode 100644 index 000000000000..7d4da9022fe4 --- /dev/null +++ b/tests/target/issue_4850.rs @@ -0,0 +1,4 @@ +impl ThisIsALongStructNameToPushTheWhereToWrapLolololol where + [(); this_is_a_long_const_function_name()]: +{ +} From c1eab154c9139a281d17c5ae70151bac4fcbf60f Mon Sep 17 00:00:00 2001 From: Peter Hall Date: Wed, 7 Oct 2020 12:55:01 +0100 Subject: [PATCH 086/401] Use a custom env var for log settings intead of default RUST_LOG # Conflicts: # src/rustfmt/main.rs --- Contributing.md | 2 +- src/bin/main.rs | 2 +- src/format-diff/main.rs | 2 +- src/git-rustfmt/main.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Contributing.md b/Contributing.md index e6dc6a220376..3073996019ee 100644 --- a/Contributing.md +++ b/Contributing.md @@ -59,7 +59,7 @@ example, the `issue-1111.rs` test file is configured by the file ## Debugging Some `rewrite_*` methods use the `debug!` macro for printing useful information. -These messages can be printed by using the environment variable `RUST_LOG=rustfmt=DEBUG`. +These messages can be printed by using the environment variable `RUSTFMT_LOG=rustfmt=DEBUG`. These traces can be helpful in understanding which part of the code was used and get a better grasp on the execution flow. diff --git a/src/bin/main.rs b/src/bin/main.rs index 1bcc5c0dada3..9d2e97c9479f 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -20,7 +20,7 @@ use crate::rustfmt::{ }; fn main() { - env_logger::init(); + env_logger::Builder::from_env("RUSTFMT_LOG").init(); let opts = make_opts(); let exit_code = match execute(&opts) { diff --git a/src/format-diff/main.rs b/src/format-diff/main.rs index c751932273b0..655aeda42bf2 100644 --- a/src/format-diff/main.rs +++ b/src/format-diff/main.rs @@ -64,7 +64,7 @@ pub struct Opts { } fn main() { - env_logger::init(); + env_logger::Builder::from_env("RUSTFMT_LOG").init(); let opts = Opts::from_args(); if let Err(e) = run(opts) { println!("{}", e); diff --git a/src/git-rustfmt/main.rs b/src/git-rustfmt/main.rs index 16f5d1dd4f23..579778edbe74 100644 --- a/src/git-rustfmt/main.rs +++ b/src/git-rustfmt/main.rs @@ -170,7 +170,7 @@ impl Config { } fn main() { - env_logger::init(); + env_logger::Builder::from_env("RUSTFMT_LOG").init(); let opts = make_opts(); let matches = opts From bd86077c58b0460a17ae071edbca9e6bba0aa6b1 Mon Sep 17 00:00:00 2001 From: r00ster Date: Mon, 1 Nov 2021 19:53:35 +0100 Subject: [PATCH 087/401] Remove grave accent that shouldn't be there --- src/config/options.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/options.rs b/src/config/options.rs index a17d48349c05..bce9e5d07f26 100644 --- a/src/config/options.rs +++ b/src/config/options.rs @@ -20,7 +20,7 @@ pub enum NewlineStyle { Windows, /// Force CR (`\n). Unix, - /// `\r\n` in Windows, `\n`` on other platforms. + /// `\r\n` in Windows, `\n` on other platforms. Native, } From e75c4d7ee9a48b8599a3a61d3b79ccbe0a18d77e Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Wed, 3 Nov 2021 21:10:01 -0500 Subject: [PATCH 088/401] ci: drop appveyor --- appveyor.yml | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 appveyor.yml diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index b3dda091e0a9..000000000000 --- a/appveyor.yml +++ /dev/null @@ -1,8 +0,0 @@ -environment: - global: - PROJECT_NAME: rustfmt - -build: false - -test_script: - - echo Why does no one have access to delete me? From a5f85058ac2e3f330bd48dd8de26bf429fc28c30 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Thu, 4 Nov 2021 18:00:22 -0500 Subject: [PATCH 089/401] fix: handle external mods imported via external->inline load hierarchy --- src/modules.rs | 4 +- src/test/mod_resolver.rs | 42 ++++++++++++++------ tests/mod-resolver/issue-5063/foo.rs | 2 + tests/mod-resolver/issue-5063/foo/bar/baz.rs | 1 + tests/mod-resolver/issue-5063/main.rs | 5 +++ 5 files changed, 41 insertions(+), 13 deletions(-) create mode 100644 tests/mod-resolver/issue-5063/foo.rs create mode 100644 tests/mod-resolver/issue-5063/foo/bar/baz.rs create mode 100644 tests/mod-resolver/issue-5063/main.rs diff --git a/src/modules.rs b/src/modules.rs index 9e75f41ae36e..b1f229d9daaf 100644 --- a/src/modules.rs +++ b/src/modules.rs @@ -321,7 +321,9 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> { (Some(Cow::Borrowed(ast::ModKind::Loaded(items, _, _))), _) => { self.visit_mod_from_ast(items) } - (Some(Cow::Owned(..)), Cow::Owned(items)) => self.visit_mod_outside_ast(items), + (Some(Cow::Owned(ast::ModKind::Loaded(items, _, _))), _) | (_, Cow::Owned(items)) => { + self.visit_mod_outside_ast(items) + } (_, _) => Ok(()), } } diff --git a/src/test/mod_resolver.rs b/src/test/mod_resolver.rs index e0b55e3efb2c..ae4a0d0fccb1 100644 --- a/src/test/mod_resolver.rs +++ b/src/test/mod_resolver.rs @@ -5,21 +5,39 @@ use super::read_config; use crate::{FileName, Input, Session}; -#[test] -fn nested_out_of_line_mods_loaded() { - // See also https://github.com/rust-lang/rustfmt/issues/4874 - let filename = "tests/mod-resolver/issue-4874/main.rs"; - let input_file = PathBuf::from(filename); +fn verify_mod_resolution(input_file_name: &str, exp_misformatted_files: &[&str]) { + let input_file = PathBuf::from(input_file_name); let config = read_config(&input_file); let mut session = Session::::new(config, None); let report = session - .format(Input::File(filename.into())) + .format(Input::File(input_file_name.into())) .expect("Should not have had any execution errors"); let errors_by_file = &report.internal.borrow().0; - assert!(errors_by_file.contains_key(&FileName::Real(PathBuf::from( - "tests/mod-resolver/issue-4874/bar/baz.rs", - )))); - assert!(errors_by_file.contains_key(&FileName::Real(PathBuf::from( - "tests/mod-resolver/issue-4874/foo/qux.rs", - )))); + for exp_file in exp_misformatted_files { + assert!(errors_by_file.contains_key(&FileName::Real(PathBuf::from(exp_file)))); + } +} + +#[test] +fn nested_out_of_line_mods_loaded() { + // See also https://github.com/rust-lang/rustfmt/issues/4874 + verify_mod_resolution( + "tests/mod-resolver/issue-4874/main.rs", + &[ + "tests/mod-resolver/issue-4874/bar/baz.rs", + "tests/mod-resolver/issue-4874/foo/qux.rs", + ], + ); +} + +#[test] +fn out_of_line_nested_inline_within_out_of_line() { + // See also https://github.com/rust-lang/rustfmt/issues/5063 + verify_mod_resolution( + "tests/mod-resolver/issue-5063/main.rs", + &[ + "tests/mod-resolver/issue-5063/foo/bar/baz.rs", + "tests/mod-resolver/issue-5063/foo.rs", + ], + ); } diff --git a/tests/mod-resolver/issue-5063/foo.rs b/tests/mod-resolver/issue-5063/foo.rs new file mode 100644 index 000000000000..d56974773fb9 --- /dev/null +++ b/tests/mod-resolver/issue-5063/foo.rs @@ -0,0 +1,2 @@ +mod bar { + mod baz;} \ No newline at end of file diff --git a/tests/mod-resolver/issue-5063/foo/bar/baz.rs b/tests/mod-resolver/issue-5063/foo/bar/baz.rs new file mode 100644 index 000000000000..3519b0ee59c8 --- /dev/null +++ b/tests/mod-resolver/issue-5063/foo/bar/baz.rs @@ -0,0 +1 @@ +fn baz() { } \ No newline at end of file diff --git a/tests/mod-resolver/issue-5063/main.rs b/tests/mod-resolver/issue-5063/main.rs new file mode 100644 index 000000000000..41c81c7bb433 --- /dev/null +++ b/tests/mod-resolver/issue-5063/main.rs @@ -0,0 +1,5 @@ +fn main() { + println!("Hello, world!"); +} + +mod foo; \ No newline at end of file From 9027db984be06dc99fd88264e9dbe84541547f08 Mon Sep 17 00:00:00 2001 From: Dmitry Murzin Date: Sat, 6 Jun 2020 17:19:50 +0300 Subject: [PATCH 090/401] Update IntelliJ Integration (#4238) --- intellij.md | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/intellij.md b/intellij.md index 7aea6222b8bc..6a711c0171ae 100644 --- a/intellij.md +++ b/intellij.md @@ -3,19 +3,28 @@ ## Installation - Install [CLion](https://www.jetbrains.com/clion/), [IntelliJ Ultimate or CE](https://www.jetbrains.com/idea/) through the direct download link or using the [JetBrains Toolbox](https://www.jetbrains.com/toolbox/). - CLion provides a built-in debugger interface but its not free like IntelliJ CE - which does not provide the debugger interface. (IntelliJ seems to lack the toolchain for that, see this discussion [intellij-rust/issues/535](https://github.com/intellij-rust/intellij-rust/issues/535)) - -- Install the [Rust Plugin](https://intellij-rust.github.io/) by navigating to File -> Settings -> Plugins and press "Install JetBrains Plugin" - ![plugins](https://user-images.githubusercontent.com/1133787/47240861-f40af680-d3e9-11e8-9b82-cdd5c8d5f5b8.png) + CLion and IntelliJ Ultimate [provide a built-in debugger interface](https://github.com/intellij-rust/intellij-rust#compatible-ides) but they are not free like IntelliJ CE. -- Press "Install" on the rust plugin - ![install rust](https://user-images.githubusercontent.com/1133787/47240803-c0c86780-d3e9-11e8-9265-22f735e4d7ed.png) +- Install the [Rust Plugin](https://intellij-rust.github.io/) by navigating to File → Settings → Plugins and searching the plugin in the Marketplace + ![plugins](https://user-images.githubusercontent.com/6505554/83944518-6f1e5c00-a81d-11ea-9c35-e16948811ba8.png) + +- Press "Install" on the Rust plugin + ![install rust](https://user-images.githubusercontent.com/6505554/83944533-82c9c280-a81d-11ea-86b3-ee2e31bc7d12.png) - Restart CLion/IntelliJ ## Configuration -- Open the settings window (File -> Settings) and search for "reformat" +### Run Rustfmt on save + +- Open Rustfmt settings (File → Settings → Languages & Frameworks → Rust → Rustfmt) and enable "Run rustfmt on Save" + ![run_rustfmt_on_save](https://user-images.githubusercontent.com/6505554/83944610-3468f380-a81e-11ea-9c34-0cbd18dd4969.png) + +- IntellJ uses autosave, so now your files will always be formatted according to rustfmt. Alternatively you can use Ctrl+S to reformat file manually + +### Bind shortcut to "Reformat File with Rustfmt" action + +- Open the settings window (File → Settings) and search for "reformat" ![keymap](https://user-images.githubusercontent.com/1133787/47240922-2ae10c80-d3ea-11e8-9d8f-c798d9749240.png) - Right-click on "Reformat File with Rustfmt" and assign a keyboard shortcut From 4d50e7c7606efca32bd937a4c60772a616fdbc33 Mon Sep 17 00:00:00 2001 From: mujpao Date: Mon, 1 Nov 2021 23:13:30 -0700 Subject: [PATCH 091/401] Put empty trait braces on same line if possible --- src/items.rs | 15 ++++-- tests/source/empty-item-single-line-false.rs | 46 +++++++++++++++++++ .../item-brace-style-always-next-line.rs | 35 ++++++++++++++ tests/target/empty-item-single-line-false.rs | 41 +++++++++++++++++ .../item-brace-style-always-next-line.rs | 29 ++++++++++++ 5 files changed, 163 insertions(+), 3 deletions(-) create mode 100644 tests/source/empty-item-single-line-false.rs create mode 100644 tests/target/empty-item-single-line-false.rs diff --git a/src/items.rs b/src/items.rs index 1c7899b3ac3b..e37a1b696584 100644 --- a/src/items.rs +++ b/src/items.rs @@ -1122,12 +1122,24 @@ pub(crate) fn format_trait( } } + let block_span = mk_sp(generics.where_clause.span.hi(), item.span.hi()); + let snippet = context.snippet(block_span); + let open_pos = snippet.find_uncommented("{")? + 1; + match context.config.brace_style() { _ if last_line_contains_single_line_comment(&result) || last_line_width(&result) + 2 > context.budget(offset.width()) => { result.push_str(&offset.to_string_with_newline(context.config)); } + _ if context.config.empty_item_single_line() + && trait_items.is_empty() + && !result.contains('\n') + && !contains_comment(&snippet[open_pos..]) => + { + result.push_str(" {}"); + return Some(result); + } BraceStyle::AlwaysNextLine => { result.push_str(&offset.to_string_with_newline(context.config)); } @@ -1144,9 +1156,6 @@ pub(crate) fn format_trait( } result.push('{'); - let block_span = mk_sp(generics.where_clause.span.hi(), item.span.hi()); - let snippet = context.snippet(block_span); - let open_pos = snippet.find_uncommented("{")? + 1; let outer_indent_str = offset.block_only().to_string_with_newline(context.config); if !trait_items.is_empty() || contains_comment(&snippet[open_pos..]) { diff --git a/tests/source/empty-item-single-line-false.rs b/tests/source/empty-item-single-line-false.rs new file mode 100644 index 000000000000..20c5bc83b466 --- /dev/null +++ b/tests/source/empty-item-single-line-false.rs @@ -0,0 +1,46 @@ +// rustfmt-brace_style: AlwaysNextLine +// rustfmt-empty_item_single_line: false + +fn function() +{ + +} + +struct Struct +{ + +} + +enum Enum +{ + +} + +trait Trait +{ + +} + +impl Trait for T +{ + +} + +trait Trait2 +where + T: Copy + Display + Write + Read + FromStr, {} + +trait Trait3 +where + T: Something + + SomethingElse + + Sync + + Send + + Display + + Debug + + Copy + + Hash + + Debug + + Display + + Write + + Read, {} diff --git a/tests/source/item-brace-style-always-next-line.rs b/tests/source/item-brace-style-always-next-line.rs index 38094d67a773..0fb6405120aa 100644 --- a/tests/source/item-brace-style-always-next-line.rs +++ b/tests/source/item-brace-style-always-next-line.rs @@ -27,3 +27,38 @@ mod M { struct D where T: Copy {} } + + +fn function() +{ + +} + +trait Trait +{ + +} + +impl Trait for T +{ + +} + +trait Trait2 +where + T: Copy + Display + Write + Read + FromStr, {} + +trait Trait3 +where + T: Something + + SomethingElse + + Sync + + Send + + Display + + Debug + + Copy + + Hash + + Debug + + Display + + Write + + Read, {} diff --git a/tests/target/empty-item-single-line-false.rs b/tests/target/empty-item-single-line-false.rs new file mode 100644 index 000000000000..bf7f70e7c432 --- /dev/null +++ b/tests/target/empty-item-single-line-false.rs @@ -0,0 +1,41 @@ +// rustfmt-brace_style: AlwaysNextLine +// rustfmt-empty_item_single_line: false + +fn function() +{ +} + +struct Struct {} + +enum Enum {} + +trait Trait +{ +} + +impl Trait for T +{ +} + +trait Trait2 +where + T: Copy + Display + Write + Read + FromStr, +{ +} + +trait Trait3 +where + T: Something + + SomethingElse + + Sync + + Send + + Display + + Debug + + Copy + + Hash + + Debug + + Display + + Write + + Read, +{ +} diff --git a/tests/target/item-brace-style-always-next-line.rs b/tests/target/item-brace-style-always-next-line.rs index 531ac5986831..4935fac04f11 100644 --- a/tests/target/item-brace-style-always-next-line.rs +++ b/tests/target/item-brace-style-always-next-line.rs @@ -40,3 +40,32 @@ mod M where T: Copy, {} } + +fn function() {} + +trait Trait {} + +impl Trait for T {} + +trait Trait2 +where + T: Copy + Display + Write + Read + FromStr, +{ +} + +trait Trait3 +where + T: Something + + SomethingElse + + Sync + + Send + + Display + + Debug + + Copy + + Hash + + Debug + + Display + + Write + + Read, +{ +} From 19c5c74951db8673aa3e3edba8393ce90346f96f Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Sat, 6 Nov 2021 12:59:06 -0500 Subject: [PATCH 092/401] refactor: dedupe & simplify ty alias formatting --- src/items.rs | 166 ++++++++++++++++++++++--------------------------- src/visitor.rs | 81 +++++++----------------- 2 files changed, 97 insertions(+), 150 deletions(-) diff --git a/src/items.rs b/src/items.rs index e37a1b696584..13d8a416a1ef 100644 --- a/src/items.rs +++ b/src/items.rs @@ -1185,18 +1185,6 @@ pub(crate) fn format_trait( } } -struct OpaqueTypeBounds<'a> { - generic_bounds: &'a ast::GenericBounds, -} - -impl<'a> Rewrite for OpaqueTypeBounds<'a> { - fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { - self.generic_bounds - .rewrite(context, shape) - .map(|s| format!("impl {}", s)) - } -} - pub(crate) struct TraitAliasBounds<'a> { generic_bounds: &'a ast::GenericBounds, generics: &'a ast::Generics, @@ -1518,17 +1506,79 @@ fn format_tuple_struct( Some(result) } -pub(crate) fn rewrite_type( - context: &RewriteContext<'_>, +pub(crate) enum ItemVisitorKind<'a> { + Item(&'a ast::Item), + AssocTraitItem(&'a ast::AssocItem), + AssocImplItem(&'a ast::AssocItem), + ForeignItem(&'a ast::ForeignItem), +} + +struct TyAliasRewriteInfo<'c, 'g>( + &'c RewriteContext<'c>, + Indent, + &'g ast::Generics, + symbol::Ident, + Span, +); + +pub(crate) fn rewrite_type_alias<'a, 'b>( + ty_alias_kind: &ast::TyAliasKind, + context: &RewriteContext<'a>, indent: Indent, - ident: symbol::Ident, - vis: &ast::Visibility, - generics: &ast::Generics, - generic_bounds_opt: Option<&ast::GenericBounds>, - rhs: Option<&R>, + visitor_kind: &ItemVisitorKind<'b>, span: Span, +) -> Option { + use ItemVisitorKind::*; + + let ast::TyAliasKind(defaultness, ref generics, ref generic_bounds, ref ty) = *ty_alias_kind; + let ty_opt = ty.as_ref().map(|t| &**t); + let (ident, vis) = match visitor_kind { + Item(i) => (i.ident, &i.vis), + AssocTraitItem(i) | AssocImplItem(i) => (i.ident, &i.vis), + ForeignItem(i) => (i.ident, &i.vis), + }; + let rw_info = &TyAliasRewriteInfo(context, indent, generics, ident, span); + + // Type Aliases are formatted slightly differently depending on the context + // in which they appear, whether they are opaque, and whether they are associated. + // https://rustc-dev-guide.rust-lang.org/opaque-types-type-alias-impl-trait.html + // https://github.com/rust-dev-tools/fmt-rfcs/blob/master/guide/items.md#type-aliases + match (visitor_kind, ty_opt) { + (Item(_), None) => { + let op_ty = OpaqueType { generic_bounds }; + rewrite_ty(rw_info, Some(generic_bounds), Some(&op_ty), vis) + } + (Item(_), Some(ty)) => rewrite_ty(rw_info, Some(generic_bounds), Some(&*ty), vis), + (AssocImplItem(_), _) => { + let result = if let Some(ast::Ty { + kind: ast::TyKind::ImplTrait(_, ref generic_bounds), + .. + }) = ty_opt + { + let op_ty = OpaqueType { generic_bounds }; + rewrite_ty(rw_info, None, Some(&op_ty), &DEFAULT_VISIBILITY) + } else { + rewrite_ty(rw_info, None, ty.as_ref(), vis) + }?; + match defaultness { + ast::Defaultness::Default(..) => Some(format!("default {}", result)), + _ => Some(result), + } + } + (AssocTraitItem(_), _) | (ForeignItem(_), _) => { + rewrite_ty(rw_info, Some(generic_bounds), ty.as_ref(), vis) + } + } +} + +fn rewrite_ty( + rw_info: &TyAliasRewriteInfo<'_, '_>, + generic_bounds_opt: Option<&ast::GenericBounds>, + rhs: Option<&R>, + vis: &ast::Visibility, ) -> Option { let mut result = String::with_capacity(128); + let TyAliasRewriteInfo(context, indent, generics, ident, span) = *rw_info; result.push_str(&format!("{}type ", format_visibility(context, vis))); let ident_str = rewrite_ident(context, ident); @@ -1616,28 +1666,6 @@ pub(crate) fn rewrite_type( } } -pub(crate) fn rewrite_opaque_type( - context: &RewriteContext<'_>, - indent: Indent, - ident: symbol::Ident, - generic_bounds: &ast::GenericBounds, - generics: &ast::Generics, - vis: &ast::Visibility, - span: Span, -) -> Option { - let opaque_type_bounds = OpaqueTypeBounds { generic_bounds }; - rewrite_type( - context, - indent, - ident, - vis, - generics, - Some(generic_bounds), - Some(&opaque_type_bounds), - span, - ) -} - fn type_annotation_spacing(config: &Config) -> (&str, &str) { ( if config.space_before_colon() { " " } else { "" }, @@ -1863,54 +1891,18 @@ fn rewrite_static( } } struct OpaqueType<'a> { - bounds: &'a ast::GenericBounds, + generic_bounds: &'a ast::GenericBounds, } impl<'a> Rewrite for OpaqueType<'a> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { let shape = shape.offset_left(5)?; // `impl ` - self.bounds + self.generic_bounds .rewrite(context, shape) .map(|s| format!("impl {}", s)) } } -pub(crate) fn rewrite_impl_type( - ident: symbol::Ident, - vis: &ast::Visibility, - defaultness: ast::Defaultness, - ty_opt: Option<&ptr::P>, - generics: &ast::Generics, - context: &RewriteContext<'_>, - indent: Indent, - span: Span, -) -> Option { - // Opaque type - let result = if let Some(rustc_ast::ast::Ty { - kind: ast::TyKind::ImplTrait(_, ref bounds), - .. - }) = ty_opt.map(|t| &**t) - { - rewrite_type( - context, - indent, - ident, - &DEFAULT_VISIBILITY, - generics, - None, - Some(&OpaqueType { bounds }), - span, - ) - } else { - rewrite_type(context, indent, ident, vis, generics, None, ty_opt, span) - }?; - - match defaultness { - ast::Defaultness::Default(..) => Some(format!("default {}", result)), - _ => Some(result), - } -} - impl Rewrite for ast::FnRetTy { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { match *self { @@ -3176,19 +3168,9 @@ impl Rewrite for ast::ForeignItem { // 1 = ; rewrite_assign_rhs(context, prefix, &**ty, shape.sub_width(1)?).map(|s| s + ";") } - ast::ForeignItemKind::TyAlias(ref ty_alias_kind) => { - let ast::TyAliasKind(_, ref generics, ref generic_bounds, ref type_default) = - **ty_alias_kind; - rewrite_type( - context, - shape.indent, - self.ident, - &self.vis, - generics, - Some(generic_bounds), - type_default.as_ref(), - self.span, - ) + ast::ForeignItemKind::TyAlias(ref ty_alias) => { + let (kind, span) = (&ItemVisitorKind::ForeignItem(&self), self.span); + rewrite_type_alias(ty_alias, context, shape.indent, kind, span) } ast::ForeignItemKind::MacCall(ref mac) => { rewrite_macro(mac, None, context, shape, MacroPosition::Item) diff --git a/src/visitor.rs b/src/visitor.rs index c37e1cb10117..f385245248da 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -12,8 +12,7 @@ use crate::config::{BraceStyle, Config}; use crate::coverage::transform_missing_snippet; use crate::items::{ format_impl, format_trait, format_trait_alias, is_mod_decl, is_use_item, rewrite_extern_crate, - rewrite_impl_type, rewrite_opaque_type, rewrite_type, FnBraceStyle, FnSig, StaticParts, - StructParts, + rewrite_type_alias, FnBraceStyle, FnSig, ItemVisitorKind, StaticParts, StructParts, }; use crate::macros::{macro_style, rewrite_macro, rewrite_macro_def, MacroPosition}; use crate::modules::Module; @@ -576,35 +575,8 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { } } ast::ItemKind::TyAlias(ref alias_kind) => { - let ast::TyAliasKind(_, ref generics, ref generic_bounds, ref ty) = - **alias_kind; - match ty { - Some(ty) => { - let rewrite = rewrite_type( - &self.get_context(), - self.block_indent, - item.ident, - &item.vis, - generics, - Some(generic_bounds), - Some(&*ty), - item.span, - ); - self.push_rewrite(item.span, rewrite); - } - None => { - let rewrite = rewrite_opaque_type( - &self.get_context(), - self.block_indent, - item.ident, - generic_bounds, - generics, - &item.vis, - item.span, - ); - self.push_rewrite(item.span, rewrite); - } - } + use ItemVisitorKind::Item; + self.visit_ty_alias_kind(alias_kind, &Item(&item), item.span); } ast::ItemKind::GlobalAsm(..) => { let snippet = Some(self.snippet(item.span).to_owned()); @@ -627,6 +599,22 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { self.skip_context = skip_context_saved; } + fn visit_ty_alias_kind( + &mut self, + ty_kind: &ast::TyAliasKind, + visitor_kind: &ItemVisitorKind<'_>, + span: Span, + ) { + let rewrite = rewrite_type_alias( + ty_kind, + &self.get_context(), + self.block_indent, + visitor_kind, + span, + ); + self.push_rewrite(span, rewrite); + } + pub(crate) fn visit_trait_item(&mut self, ti: &ast::AssocItem) { skip_out_of_file_lines_range_visitor!(self, ti.span); @@ -659,19 +647,8 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { } } ast::AssocItemKind::TyAlias(ref ty_alias_kind) => { - let ast::TyAliasKind(_, ref generics, ref generic_bounds, ref type_default) = - **ty_alias_kind; - let rewrite = rewrite_type( - &self.get_context(), - self.block_indent, - ti.ident, - &ti.vis, - generics, - Some(generic_bounds), - type_default.as_ref(), - ti.span, - ); - self.push_rewrite(ti.span, rewrite); + use ItemVisitorKind::AssocTraitItem; + self.visit_ty_alias_kind(ty_alias_kind, &AssocTraitItem(&ti), ti.span); } ast::AssocItemKind::MacCall(ref mac) => { self.visit_mac(mac, Some(ti.ident), MacroPosition::Item); @@ -710,20 +687,8 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { } ast::AssocItemKind::Const(..) => self.visit_static(&StaticParts::from_impl_item(ii)), ast::AssocItemKind::TyAlias(ref ty_alias_kind) => { - let ast::TyAliasKind(defaultness, ref generics, _, ref ty) = **ty_alias_kind; - self.push_rewrite( - ii.span, - rewrite_impl_type( - ii.ident, - &ii.vis, - defaultness, - ty.as_ref(), - generics, - &self.get_context(), - self.block_indent, - ii.span, - ), - ); + use ItemVisitorKind::AssocImplItem; + self.visit_ty_alias_kind(ty_alias_kind, &AssocImplItem(&ii), ii.span); } ast::AssocItemKind::MacCall(ref mac) => { self.visit_mac(mac, Some(ii.ident), MacroPosition::Item); From e4472d3b07b2c34b27688303745d4cc0e74da9a5 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Sat, 6 Nov 2021 17:12:25 -0500 Subject: [PATCH 093/401] refactor: dedupe associated item visitation --- src/visitor.rs | 90 ++++++++++++++++++++------------------------------ 1 file changed, 35 insertions(+), 55 deletions(-) diff --git a/src/visitor.rs b/src/visitor.rs index f385245248da..25b0085b3eff 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -615,85 +615,65 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { self.push_rewrite(span, rewrite); } - pub(crate) fn visit_trait_item(&mut self, ti: &ast::AssocItem) { - skip_out_of_file_lines_range_visitor!(self, ti.span); + fn visit_assoc_item(&mut self, visitor_kind: &ItemVisitorKind<'_>) { + use ItemVisitorKind::*; + // TODO(calebcartwright): Not sure the skip spans are correct + let (ai, skip_span, assoc_ctxt) = match visitor_kind { + AssocTraitItem(ai) => (*ai, ai.span(), visit::AssocCtxt::Trait), + AssocImplItem(ai) => (*ai, ai.span, visit::AssocCtxt::Impl), + _ => unreachable!(), + }; + skip_out_of_file_lines_range_visitor!(self, ai.span); - if self.visit_attrs(&ti.attrs, ast::AttrStyle::Outer) { - self.push_skipped_with_span(ti.attrs.as_slice(), ti.span(), ti.span()); + if self.visit_attrs(&ai.attrs, ast::AttrStyle::Outer) { + self.push_skipped_with_span(&ai.attrs.as_slice(), skip_span, skip_span); return; } // TODO(calebcartwright): consider enabling box_patterns feature gate - match ti.kind { - ast::AssocItemKind::Const(..) => self.visit_static(&StaticParts::from_trait_item(ti)), - ast::AssocItemKind::Fn(ref fn_kind) => { + match (&ai.kind, visitor_kind) { + (ast::AssocItemKind::Const(..), AssocTraitItem(_)) => { + self.visit_static(&StaticParts::from_trait_item(&ai)) + } + (ast::AssocItemKind::Const(..), AssocImplItem(_)) => { + self.visit_static(&StaticParts::from_impl_item(&ai)) + } + (ast::AssocItemKind::Fn(ref fn_kind), _) => { let ast::FnKind(defaultness, ref sig, ref generics, ref block) = **fn_kind; if let Some(ref body) = block { - let inner_attrs = inner_attributes(&ti.attrs); - let fn_ctxt = visit::FnCtxt::Assoc(visit::AssocCtxt::Trait); + let inner_attrs = inner_attributes(&ai.attrs); + let fn_ctxt = visit::FnCtxt::Assoc(assoc_ctxt); self.visit_fn( - visit::FnKind::Fn(fn_ctxt, ti.ident, sig, &ti.vis, Some(body)), + visit::FnKind::Fn(fn_ctxt, ai.ident, sig, &ai.vis, Some(body)), generics, &sig.decl, - ti.span, + ai.span, defaultness, Some(&inner_attrs), ); } else { let indent = self.block_indent; let rewrite = - self.rewrite_required_fn(indent, ti.ident, sig, &ti.vis, generics, ti.span); - self.push_rewrite(ti.span, rewrite); + self.rewrite_required_fn(indent, ai.ident, sig, &ai.vis, generics, ai.span); + self.push_rewrite(ai.span, rewrite); } } - ast::AssocItemKind::TyAlias(ref ty_alias_kind) => { - use ItemVisitorKind::AssocTraitItem; - self.visit_ty_alias_kind(ty_alias_kind, &AssocTraitItem(&ti), ti.span); + (ast::AssocItemKind::TyAlias(ref ty_alias_kind), _) => { + self.visit_ty_alias_kind(ty_alias_kind, visitor_kind, ai.span); } - ast::AssocItemKind::MacCall(ref mac) => { - self.visit_mac(mac, Some(ti.ident), MacroPosition::Item); + (ast::AssocItemKind::MacCall(ref mac), _) => { + self.visit_mac(mac, Some(ai.ident), MacroPosition::Item); } + _ => unreachable!(), } } + pub(crate) fn visit_trait_item(&mut self, ti: &ast::AssocItem) { + self.visit_assoc_item(&ItemVisitorKind::AssocTraitItem(ti)); + } + pub(crate) fn visit_impl_item(&mut self, ii: &ast::AssocItem) { - skip_out_of_file_lines_range_visitor!(self, ii.span); - - if self.visit_attrs(&ii.attrs, ast::AttrStyle::Outer) { - self.push_skipped_with_span(ii.attrs.as_slice(), ii.span, ii.span); - return; - } - - match ii.kind { - ast::AssocItemKind::Fn(ref fn_kind) => { - let ast::FnKind(defaultness, ref sig, ref generics, ref block) = **fn_kind; - if let Some(ref body) = block { - let inner_attrs = inner_attributes(&ii.attrs); - let fn_ctxt = visit::FnCtxt::Assoc(visit::AssocCtxt::Impl); - self.visit_fn( - visit::FnKind::Fn(fn_ctxt, ii.ident, sig, &ii.vis, Some(body)), - generics, - &sig.decl, - ii.span, - defaultness, - Some(&inner_attrs), - ); - } else { - let indent = self.block_indent; - let rewrite = - self.rewrite_required_fn(indent, ii.ident, sig, &ii.vis, generics, ii.span); - self.push_rewrite(ii.span, rewrite); - } - } - ast::AssocItemKind::Const(..) => self.visit_static(&StaticParts::from_impl_item(ii)), - ast::AssocItemKind::TyAlias(ref ty_alias_kind) => { - use ItemVisitorKind::AssocImplItem; - self.visit_ty_alias_kind(ty_alias_kind, &AssocImplItem(&ii), ii.span); - } - ast::AssocItemKind::MacCall(ref mac) => { - self.visit_mac(mac, Some(ii.ident), MacroPosition::Item); - } - } + self.visit_assoc_item(&ItemVisitorKind::AssocImplItem(ii)); } fn visit_mac(&mut self, mac: &ast::MacCall, ident: Option, pos: MacroPosition) { From 31bc54a9a8d556ddb124dc177ea82569250651d1 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Sun, 7 Nov 2021 19:06:24 -0600 Subject: [PATCH 094/401] chore: bump toolchain --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index b19ecbdb07c4..1d2cad667511 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2021-10-20" +channel = "nightly-2021-11-08" components = ["rustc-dev"] From e6d1bf5acba29a8944f3bd537bcefb5e5972e5f1 Mon Sep 17 00:00:00 2001 From: Wu Yu Wei Date: Tue, 31 Mar 2020 14:36:12 +0800 Subject: [PATCH 095/401] Link tracking issues in Configurations.md (#4096) # Conflicts: # Configurations.md --- Configurations.md | 94 +++++++++++++++++++++++------------------------ 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/Configurations.md b/Configurations.md index 13826883d2f4..4a281251f227 100644 --- a/Configurations.md +++ b/Configurations.md @@ -47,7 +47,7 @@ Where to put a binary operator when a binary expression goes multiline. - **Default value**: `"Front"` - **Possible values**: `"Front"`, `"Back"` -- **Stable**: No (tracking issue: #3368) +- **Stable**: No (tracking issue: [#3368](https://github.com/rust-lang/rustfmt/issues/3368)) #### `"Front"` (default): @@ -88,7 +88,7 @@ them, additional blank lines are inserted. - **Default value**: `0` - **Possible values**: *unsigned integer* -- **Stable**: No (tracking issue: #3382) +- **Stable**: No (tracking issue: [#3382](https://github.com/rust-lang/rustfmt/issues/3382)) ### Example Original Code (rustfmt will not change it with the default value of `0`): @@ -128,7 +128,7 @@ lines are found, they are trimmed down to match this integer. - **Default value**: `1` - **Possible values**: any non-negative integer -- **Stable**: No (tracking issue: #3381) +- **Stable**: No (tracking issue: [#3381](https://github.com/rust-lang/rustfmt/issues/3381)) ### Example Original Code: @@ -186,7 +186,7 @@ Brace style for items - **Default value**: `"SameLineWhere"` - **Possible values**: `"AlwaysNextLine"`, `"PreferSameLine"`, `"SameLineWhere"` -- **Stable**: No (tracking issue: #3376) +- **Stable**: No (tracking issue: [#3376](https://github.com/rust-lang/rustfmt/issues/3376)) ### Functions @@ -313,7 +313,7 @@ Whether to use colored output or not. - **Default value**: `"Auto"` - **Possible values**: "Auto", "Always", "Never" -- **Stable**: No (tracking issue: #3385) +- **Stable**: No (tracking issue: [#3385](https://github.com/rust-lang/rustfmt/issues/3385)) ## `combine_control_expr` @@ -321,7 +321,7 @@ Combine control expressions with function calls. - **Default value**: `true` - **Possible values**: `true`, `false` -- **Stable**: No (tracking issue: #3369) +- **Stable**: No (tracking issue: [#3369](https://github.com/rust-lang/rustfmt/issues/3369)) #### `true` (default): @@ -429,7 +429,7 @@ Maximum length of comments. No effect unless`wrap_comments = true`. - **Default value**: `80` - **Possible values**: any positive integer -- **Stable**: No (tracking issue: #3349) +- **Stable**: No (tracking issue: [#3349](https://github.com/rust-lang/rustfmt/issues/3349)) **Note:** A value of `0` results in [`wrap_comments`](#wrap_comments) being applied regardless of a line's width. @@ -452,7 +452,7 @@ Replace strings of _ wildcards by a single .. in tuple patterns - **Default value**: `false` - **Possible values**: `true`, `false` -- **Stable**: No (tracking issue: #3384) +- **Stable**: No (tracking issue: [#3384](https://github.com/rust-lang/rustfmt/issues/3384)) #### `false` (default): @@ -477,7 +477,7 @@ Brace style for control flow constructs - **Default value**: `"AlwaysSameLine"` - **Possible values**: `"AlwaysNextLine"`, `"AlwaysSameLine"`, `"ClosingNextLine"` -- **Stable**: No (tracking issue: #3377) +- **Stable**: No (tracking issue: [#3377](https://github.com/rust-lang/rustfmt/issues/3377)) #### `"AlwaysSameLine"` (default): @@ -551,7 +551,7 @@ Put empty-body functions and impls on a single line - **Default value**: `true` - **Possible values**: `true`, `false` -- **Stable**: No (tracking issue: #3356) +- **Stable**: No (tracking issue: [#3356](https://github.com/rust-lang/rustfmt/issues/3356)) #### `true` (default): @@ -584,7 +584,7 @@ doesn't get ignored when aligning. - **Default value** : 0 - **Possible values**: any positive integer -- **Stable**: No (tracking issue: #3372) +- **Stable**: No (tracking issue: [#3372](https://github.com/rust-lang/rustfmt/issues/3372)) #### `0` (default): @@ -630,7 +630,7 @@ using a shorter name. - **Default value**: `false` - **Possible values**: `true`, `false` -- **Stable**: No (tracking issue: #3391) +- **Stable**: No (tracking issue: [#3391](https://github.com/rust-lang/rustfmt/issues/3391)) See also [`max_width`](#max_width). @@ -641,7 +641,7 @@ trailing whitespaces. - **Default value**: `false` - **Possible values**: `true`, `false` -- **Stable**: No (tracking issue: #3392) +- **Stable**: No (tracking issue: [#3392](https://github.com/rust-lang/rustfmt/issues/3392)) ## `fn_args_layout` @@ -771,7 +771,7 @@ Put single-expression functions on a single line - **Default value**: `false` - **Possible values**: `true`, `false` -- **Stable**: No (tracking issue: #3358) +- **Stable**: No (tracking issue: [#3358](https://github.com/rust-lang/rustfmt/issues/3358)) #### `false` (default): @@ -832,7 +832,7 @@ Force multiline closure and match arm bodies to be wrapped in a block - **Default value**: `false` - **Possible values**: `false`, `true` -- **Stable**: No (tracking issue: #3374) +- **Stable**: No (tracking issue: [#3374](https://github.com/rust-lang/rustfmt/issues/3374)) #### `false` (default): @@ -881,7 +881,7 @@ Format code snippet included in doc comments. - **Default value**: `false` - **Possible values**: `true`, `false` -- **Stable**: No (tracking issue: #3348) +- **Stable**: No (tracking issue: [#3348](https://github.com/rust-lang/rustfmt/issues/3348)) #### `false` (default): @@ -941,7 +941,7 @@ Format the metavariable matching patterns in macros. - **Default value**: `false` - **Possible values**: `true`, `false` -- **Stable**: No (tracking issue: #3354) +- **Stable**: No (tracking issue: [#3354](https://github.com/rust-lang/rustfmt/issues/3354)) #### `false` (default): @@ -978,7 +978,7 @@ Format the bodies of macros. - **Default value**: `true` - **Possible values**: `true`, `false` -- **Stable**: No (tracking issue: #3355) +- **Stable**: No (tracking issue: [#3355](https://github.com/rust-lang/rustfmt/issues/3355)) #### `true` (default): @@ -1011,7 +1011,7 @@ Format string literals where necessary - **Default value**: `false` - **Possible values**: `true`, `false` -- **Stable**: No (tracking issue: #3353) +- **Stable**: No (tracking issue: [#3353](https://github.com/rust-lang/rustfmt/issues/3353)) #### `false` (default): @@ -1072,7 +1072,7 @@ Do not show parse errors if the parser failed to parse files. - **Default value**: `false` - **Possible values**: `true`, `false` -- **Stable**: No (tracking issue: #3390) +- **Stable**: No (tracking issue: [#3390](https://github.com/rust-lang/rustfmt/issues/3390)) ## `ignore` @@ -1081,7 +1081,7 @@ The pattern format is the same as [.gitignore](https://git-scm.com/docs/gitignor - **Default value**: format every file - **Possible values**: See an example below -- **Stable**: No (tracking issue: #3395) +- **Stable**: No (tracking issue: [#3395](https://github.com/rust-lang/rustfmt/issues/3395)) ### Example @@ -1114,7 +1114,7 @@ Indent style of imports - **Default Value**: `"Block"` - **Possible values**: `"Block"`, `"Visual"` -- **Stable**: No (tracking issue: #3360) +- **Stable**: No (tracking issue: [#3360](https://github.com/rust-lang/rustfmt/issues/3360)) #### `"Block"` (default): @@ -1140,7 +1140,7 @@ Item layout inside a imports block - **Default value**: "Mixed" - **Possible values**: "Horizontal", "HorizontalVertical", "Mixed", "Vertical" -- **Stable**: No (tracking issue: #3361) +- **Stable**: No (tracking issue: [#3361](https://github.com/rust-lang/rustfmt/issues/3361)) #### `"Mixed"` (default): @@ -1203,7 +1203,7 @@ Indent on expressions or items. - **Default value**: `"Block"` - **Possible values**: `"Block"`, `"Visual"` -- **Stable**: No (tracking issue: #3346) +- **Stable**: No (tracking issue: [#3346](https://github.com/rust-lang/rustfmt/issues/3346)) ### Array @@ -1456,7 +1456,7 @@ Write an item and its attribute on the same line if their combined width is belo - **Default value**: 0 - **Possible values**: any positive integer -- **Stable**: No (tracking issue: #3343) +- **Stable**: No (tracking issue: [#3343](https://github.com/rust-lang/rustfmt/issues/3343)) ### Example @@ -1477,7 +1477,7 @@ Check whether beginnings of files match a license template. - **Default value**: `""` - **Possible values**: path to a license template file -- **Stable**: No (tracking issue: #3352) +- **Stable**: No (tracking issue: [#3352](https://github.com/rust-lang/rustfmt/issues/3352)) A license template is a plain text file which is matched literally against the beginning of each source file, except for `{}`-delimited blocks, which are @@ -1499,7 +1499,7 @@ The Style Guide requires that bodies are block wrapped by default if a line brea - **Default value**: `true` - **Possible values**: `true`, `false` -- **Stable**: No (tracking issue: #3373) +- **Stable**: No (tracking issue: [#3373](https://github.com/rust-lang/rustfmt/issues/3373)) #### `true` (default): @@ -1826,7 +1826,7 @@ Convert /* */ comments to // comments where possible - **Default value**: `false` - **Possible values**: `true`, `false` -- **Stable**: No (tracking issue: #3350) +- **Stable**: No (tracking issue: [#3350](https://github.com/rust-lang/rustfmt/issues/3350)) #### `false` (default): @@ -1854,7 +1854,7 @@ Convert `#![doc]` and `#[doc]` attributes to `//!` and `///` doc comments. - **Default value**: `false` - **Possible values**: `true`, `false` -- **Stable**: No (tracking issue: #3351) +- **Stable**: No (tracking issue: [#3351](https://github.com/rust-lang/rustfmt/issues/3351)) #### `false` (default): @@ -1885,7 +1885,7 @@ instead of being indented on a new line. - **Default value**: `false` - **Possible values**: `true`, `false` -- **Stable**: No (tracking issue: #3370) +- **Stable**: No (tracking issue: [#3370](https://github.com/rust-lang/rustfmt/issues/3370)) #### `false` (default): @@ -1992,7 +1992,7 @@ Reorder impl items. `type` and `const` are put first, then macros and methods. - **Default value**: `false` - **Possible values**: `true`, `false` -- **Stable**: No (tracking issue: #3363) +- **Stable**: No (tracking issue: [#3363](https://github.com/rust-lang/rustfmt/issues/3363)) #### `false` (default) @@ -2166,7 +2166,7 @@ Report `FIXME` items in comments. - **Default value**: `"Never"` - **Possible values**: `"Always"`, `"Unnumbered"`, `"Never"` -- **Stable**: No (tracking issue: #3394) +- **Stable**: No (tracking issue: [#3394](https://github.com/rust-lang/rustfmt/issues/3394)) Warns about any comments containing `FIXME` in them when set to `"Always"`. If it contains a `#X` (with `X` being a number) in parentheses following the @@ -2181,7 +2181,7 @@ Report `TODO` items in comments. - **Default value**: `"Never"` - **Possible values**: `"Always"`, `"Unnumbered"`, `"Never"` -- **Stable**: No (tracking issue: #3393) +- **Stable**: No (tracking issue: [#3393](https://github.com/rust-lang/rustfmt/issues/3393)) Warns about any comments containing `TODO` in them when set to `"Always"`. If it contains a `#X` (with `X` being a number) in parentheses following the @@ -2196,7 +2196,7 @@ specific version of rustfmt is used in your CI, use this option. - **Default value**: `CARGO_PKG_VERSION` - **Possible values**: any published version (e.g. `"0.3.8"`) -- **Stable**: No (tracking issue: #3386) +- **Stable**: No (tracking issue: [#3386](https://github.com/rust-lang/rustfmt/issues/3386)) ## `skip_children` @@ -2204,7 +2204,7 @@ Don't reformat out of line modules - **Default value**: `false` - **Possible values**: `true`, `false` -- **Stable**: No (tracking issue: #3389) +- **Stable**: No (tracking issue: [#3389](https://github.com/rust-lang/rustfmt/issues/3386)) ## `single_line_if_else_max_width` @@ -2224,7 +2224,7 @@ Leave a space after the colon. - **Default value**: `true` - **Possible values**: `true`, `false` -- **Stable**: No (tracking issue: #3366) +- **Stable**: No (tracking issue: [#3366](https://github.com/rust-lang/rustfmt/issues/3366)) #### `true` (default): @@ -2256,7 +2256,7 @@ Leave a space before the colon. - **Default value**: `false` - **Possible values**: `true`, `false` -- **Stable**: No (tracking issue: #3365) +- **Stable**: No (tracking issue: [#3365](https://github.com/rust-lang/rustfmt/issues/3365)) #### `false` (default): @@ -2288,7 +2288,7 @@ Put spaces around the .., ..=, and ... range operators - **Default value**: `false` - **Possible values**: `true`, `false` -- **Stable**: No (tracking issue: #3367) +- **Stable**: No (tracking issue: [#3367](https://github.com/rust-lang/rustfmt/issues/3367)) #### `false` (default): @@ -2344,7 +2344,7 @@ The maximum diff of width between struct fields to be aligned with each other. - **Default value** : 0 - **Possible values**: any non-negative integer -- **Stable**: No (tracking issue: #3371) +- **Stable**: No (tracking issue: [#3371](https://github.com/rust-lang/rustfmt/issues/3371)) #### `0` (default): @@ -2372,7 +2372,7 @@ Put small struct literals on a single line - **Default value**: `true` - **Possible values**: `true`, `false` -- **Stable**: No (tracking issue: #3357) +- **Stable**: No (tracking issue: [#3357](https://github.com/rust-lang/rustfmt/issues/3357)) #### `true` (default): @@ -2460,7 +2460,7 @@ How to handle trailing commas for lists - **Default value**: `"Vertical"` - **Possible values**: `"Always"`, `"Never"`, `"Vertical"` -- **Stable**: No (tracking issue: #3379) +- **Stable**: No (tracking issue: [#3379](https://github.com/rust-lang/rustfmt/issues/3379)) #### `"Vertical"` (default): @@ -2518,7 +2518,7 @@ Add trailing semicolon after break, continue and return - **Default value**: `true` - **Possible values**: `true`, `false` -- **Stable**: No (tracking issue: #3378) +- **Stable**: No (tracking issue: [#3378](https://github.com/rust-lang/rustfmt/issues/3378)) #### `true` (default): ```rust @@ -2540,7 +2540,7 @@ Determines if `+` or `=` are wrapped in spaces in the punctuation of types - **Default value**: `"Wide"` - **Possible values**: `"Compressed"`, `"Wide"` -- **Stable**: No (tracking issue: #3364) +- **Stable**: No (tracking issue: [#3364](https://github.com/rust-lang/rustfmt/issues/3364)) #### `"Wide"` (default): @@ -2564,7 +2564,7 @@ Enable unstable features on the unstable channel. - **Default value**: `false` - **Possible values**: `true`, `false` -- **Stable**: No (tracking issue: #3387) +- **Stable**: No (tracking issue: [#3387](https://github.com/rust-lang/rustfmt/issues/3387)) ## `use_field_init_shorthand` @@ -2779,7 +2779,7 @@ version number. - **Default value**: `One` - **Possible values**: `One`, `Two` -- **Stable**: No (tracking issue: #3383) +- **Stable**: No (tracking issue: [#3383](https://github.com/rust-lang/rustfmt/issues/3383)) ### Example @@ -2793,7 +2793,7 @@ Forces the `where` clause to be laid out on a single line. - **Default value**: `false` - **Possible values**: `true`, `false` -- **Stable**: No (tracking issue: #3359) +- **Stable**: No (tracking issue: [#3359](https://github.com/rust-lang/rustfmt/issues/3359)) #### `false` (default): @@ -2825,7 +2825,7 @@ Break comments to fit on the line - **Default value**: `false` - **Possible values**: `true`, `false` -- **Stable**: No (tracking issue: #3347) +- **Stable**: No (tracking issue: [#3347](https://github.com/rust-lang/rustfmt/issues/3347)) #### `false` (default): From eee8f0419dca910187350ac38fe9694b8b7919b9 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Wed, 17 Nov 2021 12:51:37 -0600 Subject: [PATCH 096/401] refactor: cleanup duplicative Impl handling code --- src/items.rs | 503 +++++++++++++++++++++++-------------------------- src/visitor.rs | 4 +- 2 files changed, 237 insertions(+), 270 deletions(-) diff --git a/src/items.rs b/src/items.rs index 50121a8b6b50..849e6c06218f 100644 --- a/src/items.rs +++ b/src/items.rs @@ -579,6 +579,25 @@ impl<'a> FmtVisitor<'a> { fn visit_impl_items(&mut self, items: &[ptr::P]) { if self.get_context().config.reorder_impl_items() { + type TyOpt = Option>; + use crate::ast::AssocItemKind::*; + let is_type = |ty: &TyOpt| { + ty.as_ref() + .map_or(true, |t| !matches!(t.kind, ast::TyKind::ImplTrait(..))) + }; + let is_opaque = |ty: &TyOpt| !is_type(ty); + let both_type = |left: &TyOpt, right: &TyOpt| is_type(left) && is_type(right); + let both_opaque = |left: &TyOpt, right: &TyOpt| is_opaque(left) && is_opaque(right); + let need_empty_line = |a: &ast::AssocItemKind, b: &ast::AssocItemKind| match (a, b) { + (TyAlias(lty), TyAlias(rty)) + if both_type(<y.ty, &rty.ty) || both_opaque(<y.ty, &rty.ty) => + { + false + } + (Const(..), Const(..)) => false, + _ => true, + }; + // Create visitor for each items, then reorder them. let mut buffer = vec![]; for item in items { @@ -587,50 +606,6 @@ impl<'a> FmtVisitor<'a> { self.buffer.clear(); } - fn is_type(ty: &Option>) -> bool { - if let Some(lty) = ty { - if let ast::TyKind::ImplTrait(..) = lty.kind { - return false; - } - } - true - } - - fn is_opaque(ty: &Option>) -> bool { - !is_type(ty) - } - - fn both_type( - a: &Option>, - b: &Option>, - ) -> bool { - is_type(a) && is_type(b) - } - - fn both_opaque( - a: &Option>, - b: &Option>, - ) -> bool { - is_opaque(a) && is_opaque(b) - } - - // In rustc-ap-v638 the `OpaqueTy` AssocItemKind variant was removed but - // we still need to differentiate to maintain sorting order. - - // type -> opaque -> const -> macro -> method - use crate::ast::AssocItemKind::*; - fn need_empty_line(a: &ast::AssocItemKind, b: &ast::AssocItemKind) -> bool { - match (a, b) { - (TyAlias(lty), TyAlias(rty)) - if both_type(<y.ty, &rty.ty) || both_opaque(<y.ty, &rty.ty) => - { - false - } - (Const(..), Const(..)) => false, - _ => true, - } - } - buffer.sort_by(|(_, a), (_, b)| match (&a.kind, &b.kind) { (TyAlias(lty), TyAlias(rty)) if both_type(<y.ty, &rty.ty) || both_opaque(<y.ty, &rty.ty) => @@ -676,136 +651,133 @@ impl<'a> FmtVisitor<'a> { pub(crate) fn format_impl( context: &RewriteContext<'_>, item: &ast::Item, + iimpl: &ast::Impl, offset: Indent, ) -> Option { - if let ast::ItemKind::Impl(impl_kind) = &item.kind { - let ast::Impl { - ref generics, - ref self_ty, - ref items, - .. - } = **impl_kind; - let mut result = String::with_capacity(128); - let ref_and_type = format_impl_ref_and_type(context, item, offset)?; - let sep = offset.to_string_with_newline(context.config); - result.push_str(&ref_and_type); + let ast::Impl { + generics, + self_ty, + items, + .. + } = iimpl; + let mut result = String::with_capacity(128); + let ref_and_type = format_impl_ref_and_type(context, item, iimpl, offset)?; + let sep = offset.to_string_with_newline(context.config); + result.push_str(&ref_and_type); - let where_budget = if result.contains('\n') { - context.config.max_width() - } else { - context.budget(last_line_width(&result)) - }; - - let mut option = WhereClauseOption::snuggled(&ref_and_type); - let snippet = context.snippet(item.span); - let open_pos = snippet.find_uncommented("{")? + 1; - if !contains_comment(&snippet[open_pos..]) - && items.is_empty() - && generics.where_clause.predicates.len() == 1 - && !result.contains('\n') - { - option.suppress_comma(); - option.snuggle(); - option.allow_single_line(); - } - - let missing_span = mk_sp(self_ty.span.hi(), item.span.hi()); - let where_span_end = context.snippet_provider.opt_span_before(missing_span, "{"); - let where_clause_str = rewrite_where_clause( - context, - &generics.where_clause, - context.config.brace_style(), - Shape::legacy(where_budget, offset.block_only()), - false, - "{", - where_span_end, - self_ty.span.hi(), - option, - )?; - - // If there is no where-clause, we may have missing comments between the trait name and - // the opening brace. - if generics.where_clause.predicates.is_empty() { - if let Some(hi) = where_span_end { - match recover_missing_comment_in_span( - mk_sp(self_ty.span.hi(), hi), - Shape::indented(offset, context.config), - context, - last_line_width(&result), - ) { - Some(ref missing_comment) if !missing_comment.is_empty() => { - result.push_str(missing_comment); - } - _ => (), - } - } - } - - if is_impl_single_line(context, items.as_slice(), &result, &where_clause_str, item)? { - result.push_str(&where_clause_str); - if where_clause_str.contains('\n') || last_line_contains_single_line_comment(&result) { - // if the where_clause contains extra comments AND - // there is only one where-clause predicate - // recover the suppressed comma in single line where_clause formatting - if generics.where_clause.predicates.len() == 1 { - result.push(','); - } - result.push_str(&format!("{}{{{}}}", sep, sep)); - } else { - result.push_str(" {}"); - } - return Some(result); - } - - result.push_str(&where_clause_str); - - let need_newline = last_line_contains_single_line_comment(&result) || result.contains('\n'); - match context.config.brace_style() { - _ if need_newline => result.push_str(&sep), - BraceStyle::AlwaysNextLine => result.push_str(&sep), - BraceStyle::PreferSameLine => result.push(' '), - BraceStyle::SameLineWhere => { - if !where_clause_str.is_empty() { - result.push_str(&sep); - } else { - result.push(' '); - } - } - } - - result.push('{'); - // this is an impl body snippet(impl SampleImpl { /* here */ }) - let lo = max(self_ty.span.hi(), generics.where_clause.span.hi()); - let snippet = context.snippet(mk_sp(lo, item.span.hi())); - let open_pos = snippet.find_uncommented("{")? + 1; - - if !items.is_empty() || contains_comment(&snippet[open_pos..]) { - let mut visitor = FmtVisitor::from_context(context); - let item_indent = offset.block_only().block_indent(context.config); - visitor.block_indent = item_indent; - visitor.last_pos = lo + BytePos(open_pos as u32); - - visitor.visit_attrs(&item.attrs, ast::AttrStyle::Inner); - visitor.visit_impl_items(items); - - visitor.format_missing(item.span.hi() - BytePos(1)); - - let inner_indent_str = visitor.block_indent.to_string_with_newline(context.config); - let outer_indent_str = offset.block_only().to_string_with_newline(context.config); - - result.push_str(&inner_indent_str); - result.push_str(visitor.buffer.trim()); - result.push_str(&outer_indent_str); - } else if need_newline || !context.config.empty_item_single_line() { - result.push_str(&sep); - } - - result.push('}'); - - Some(result) + let where_budget = if result.contains('\n') { + context.config.max_width() } else { - unreachable!(); + context.budget(last_line_width(&result)) + }; + + let mut option = WhereClauseOption::snuggled(&ref_and_type); + let snippet = context.snippet(item.span); + let open_pos = snippet.find_uncommented("{")? + 1; + if !contains_comment(&snippet[open_pos..]) + && items.is_empty() + && generics.where_clause.predicates.len() == 1 + && !result.contains('\n') + { + option.suppress_comma(); + option.snuggle(); + option.allow_single_line(); } + + let missing_span = mk_sp(self_ty.span.hi(), item.span.hi()); + let where_span_end = context.snippet_provider.opt_span_before(missing_span, "{"); + let where_clause_str = rewrite_where_clause( + context, + &generics.where_clause, + context.config.brace_style(), + Shape::legacy(where_budget, offset.block_only()), + false, + "{", + where_span_end, + self_ty.span.hi(), + option, + )?; + + // If there is no where-clause, we may have missing comments between the trait name and + // the opening brace. + if generics.where_clause.predicates.is_empty() { + if let Some(hi) = where_span_end { + match recover_missing_comment_in_span( + mk_sp(self_ty.span.hi(), hi), + Shape::indented(offset, context.config), + context, + last_line_width(&result), + ) { + Some(ref missing_comment) if !missing_comment.is_empty() => { + result.push_str(missing_comment); + } + _ => (), + } + } + } + + if is_impl_single_line(context, items.as_slice(), &result, &where_clause_str, item)? { + result.push_str(&where_clause_str); + if where_clause_str.contains('\n') || last_line_contains_single_line_comment(&result) { + // if the where_clause contains extra comments AND + // there is only one where-clause predicate + // recover the suppressed comma in single line where_clause formatting + if generics.where_clause.predicates.len() == 1 { + result.push(','); + } + result.push_str(&format!("{}{{{}}}", sep, sep)); + } else { + result.push_str(" {}"); + } + return Some(result); + } + + result.push_str(&where_clause_str); + + let need_newline = last_line_contains_single_line_comment(&result) || result.contains('\n'); + match context.config.brace_style() { + _ if need_newline => result.push_str(&sep), + BraceStyle::AlwaysNextLine => result.push_str(&sep), + BraceStyle::PreferSameLine => result.push(' '), + BraceStyle::SameLineWhere => { + if !where_clause_str.is_empty() { + result.push_str(&sep); + } else { + result.push(' '); + } + } + } + + result.push('{'); + // this is an impl body snippet(impl SampleImpl { /* here */ }) + let lo = max(self_ty.span.hi(), generics.where_clause.span.hi()); + let snippet = context.snippet(mk_sp(lo, item.span.hi())); + let open_pos = snippet.find_uncommented("{")? + 1; + + if !items.is_empty() || contains_comment(&snippet[open_pos..]) { + let mut visitor = FmtVisitor::from_context(context); + let item_indent = offset.block_only().block_indent(context.config); + visitor.block_indent = item_indent; + visitor.last_pos = lo + BytePos(open_pos as u32); + + visitor.visit_attrs(&item.attrs, ast::AttrStyle::Inner); + visitor.visit_impl_items(items); + + visitor.format_missing(item.span.hi() - BytePos(1)); + + let inner_indent_str = visitor.block_indent.to_string_with_newline(context.config); + let outer_indent_str = offset.block_only().to_string_with_newline(context.config); + + result.push_str(&inner_indent_str); + result.push_str(visitor.buffer.trim()); + result.push_str(&outer_indent_str); + } else if need_newline || !context.config.empty_item_single_line() { + result.push_str(&sep); + } + + result.push('}'); + + Some(result) } fn is_impl_single_line( @@ -830,111 +802,106 @@ fn is_impl_single_line( fn format_impl_ref_and_type( context: &RewriteContext<'_>, item: &ast::Item, + iimpl: &ast::Impl, offset: Indent, ) -> Option { - if let ast::ItemKind::Impl(impl_kind) = &item.kind { - let ast::Impl { - unsafety, - polarity, - defaultness, - constness, - ref generics, - of_trait: ref trait_ref, - ref self_ty, - .. - } = **impl_kind; - let mut result = String::with_capacity(128); + let ast::Impl { + unsafety, + polarity, + defaultness, + constness, + ref generics, + of_trait: ref trait_ref, + ref self_ty, + .. + } = *iimpl; + let mut result = String::with_capacity(128); - result.push_str(&format_visibility(context, &item.vis)); - result.push_str(format_defaultness(defaultness)); - result.push_str(format_unsafety(unsafety)); + result.push_str(&format_visibility(context, &item.vis)); + result.push_str(format_defaultness(defaultness)); + result.push_str(format_unsafety(unsafety)); - let shape = if context.config.version() == Version::Two { - Shape::indented(offset + last_line_width(&result), context.config) - } else { - generics_shape_from_config( - context.config, - Shape::indented(offset + last_line_width(&result), context.config), - 0, - )? - }; - let generics_str = rewrite_generics(context, "impl", generics, shape)?; - result.push_str(&generics_str); - result.push_str(format_constness_right(constness)); - - let polarity_str = match polarity { - ast::ImplPolarity::Negative(_) => "!", - ast::ImplPolarity::Positive => "", - }; - - let polarity_overhead; - let trait_ref_overhead; - if let Some(ref trait_ref) = *trait_ref { - let result_len = last_line_width(&result); - result.push_str(&rewrite_trait_ref( - context, - trait_ref, - offset, - polarity_str, - result_len, - )?); - polarity_overhead = 0; // already written - trait_ref_overhead = " for".len(); - } else { - polarity_overhead = polarity_str.len(); - trait_ref_overhead = 0; - } - - // Try to put the self type in a single line. - let curly_brace_overhead = if generics.where_clause.predicates.is_empty() { - // If there is no where-clause adapt budget for type formatting to take space and curly - // brace into account. - match context.config.brace_style() { - BraceStyle::AlwaysNextLine => 0, - _ => 2, - } - } else { - 0 - }; - let used_space = last_line_width(&result) - + polarity_overhead - + trait_ref_overhead - + curly_brace_overhead; - // 1 = space before the type. - let budget = context.budget(used_space + 1); - if let Some(self_ty_str) = self_ty.rewrite(context, Shape::legacy(budget, offset)) { - if !self_ty_str.contains('\n') { - if trait_ref.is_some() { - result.push_str(" for "); - } else { - result.push(' '); - result.push_str(polarity_str); - } - result.push_str(&self_ty_str); - return Some(result); - } - } - - // Couldn't fit the self type on a single line, put it on a new line. - result.push('\n'); - // Add indentation of one additional tab. - let new_line_offset = offset.block_indent(context.config); - result.push_str(&new_line_offset.to_string(context.config)); - if trait_ref.is_some() { - result.push_str("for "); - } else { - result.push_str(polarity_str); - } - let budget = context.budget(last_line_width(&result) + polarity_overhead); - let type_offset = match context.config.indent_style() { - IndentStyle::Visual => new_line_offset + trait_ref_overhead, - IndentStyle::Block => new_line_offset, - }; - result.push_str(&*self_ty.rewrite(context, Shape::legacy(budget, type_offset))?); - Some(result) + let shape = if context.config.version() == Version::Two { + Shape::indented(offset + last_line_width(&result), context.config) } else { - unreachable!(); + generics_shape_from_config( + context.config, + Shape::indented(offset + last_line_width(&result), context.config), + 0, + )? + }; + let generics_str = rewrite_generics(context, "impl", generics, shape)?; + result.push_str(&generics_str); + result.push_str(format_constness_right(constness)); + + let polarity_str = match polarity { + ast::ImplPolarity::Negative(_) => "!", + ast::ImplPolarity::Positive => "", + }; + + let polarity_overhead; + let trait_ref_overhead; + if let Some(ref trait_ref) = *trait_ref { + let result_len = last_line_width(&result); + result.push_str(&rewrite_trait_ref( + context, + trait_ref, + offset, + polarity_str, + result_len, + )?); + polarity_overhead = 0; // already written + trait_ref_overhead = " for".len(); + } else { + polarity_overhead = polarity_str.len(); + trait_ref_overhead = 0; } + + // Try to put the self type in a single line. + let curly_brace_overhead = if generics.where_clause.predicates.is_empty() { + // If there is no where-clause adapt budget for type formatting to take space and curly + // brace into account. + match context.config.brace_style() { + BraceStyle::AlwaysNextLine => 0, + _ => 2, + } + } else { + 0 + }; + let used_space = + last_line_width(&result) + polarity_overhead + trait_ref_overhead + curly_brace_overhead; + // 1 = space before the type. + let budget = context.budget(used_space + 1); + if let Some(self_ty_str) = self_ty.rewrite(context, Shape::legacy(budget, offset)) { + if !self_ty_str.contains('\n') { + if trait_ref.is_some() { + result.push_str(" for "); + } else { + result.push(' '); + result.push_str(polarity_str); + } + result.push_str(&self_ty_str); + return Some(result); + } + } + + // Couldn't fit the self type on a single line, put it on a new line. + result.push('\n'); + // Add indentation of one additional tab. + let new_line_offset = offset.block_indent(context.config); + result.push_str(&new_line_offset.to_string(context.config)); + if trait_ref.is_some() { + result.push_str("for "); + } else { + result.push_str(polarity_str); + } + let budget = context.budget(last_line_width(&result) + polarity_overhead); + let type_offset = match context.config.indent_style() { + IndentStyle::Visual => new_line_offset + trait_ref_overhead, + IndentStyle::Block => new_line_offset, + }; + result.push_str(&*self_ty.rewrite(context, Shape::legacy(budget, type_offset))?); + Some(result) } fn rewrite_trait_ref( diff --git a/src/visitor.rs b/src/visitor.rs index 527042d098a1..e4a7be742abc 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -485,9 +485,9 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { if should_visit_node_again { match item.kind { ast::ItemKind::Use(ref tree) => self.format_import(item, tree), - ast::ItemKind::Impl { .. } => { + ast::ItemKind::Impl(ref iimpl) => { let block_indent = self.block_indent; - let rw = self.with_context(|ctx| format_impl(ctx, item, block_indent)); + let rw = self.with_context(|ctx| format_impl(ctx, item, iimpl, block_indent)); self.push_rewrite(item.span, rw); } ast::ItemKind::Trait(..) => { From 0023abfb2c826d8a9ff74146ece513b22b86fdce Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Thu, 18 Nov 2021 12:38:34 -0600 Subject: [PATCH 097/401] tests: add cases for type alias issues --- tests/source/issue_4823.rs | 5 +++++ tests/source/issue_5027.rs | 7 +++++++ tests/source/issue_5086.rs | 2 ++ tests/target/issue_4823.rs | 5 +++++ tests/target/issue_5027.rs | 17 +++++++++++++++++ tests/target/issue_5086.rs | 2 ++ 6 files changed, 38 insertions(+) create mode 100644 tests/source/issue_4823.rs create mode 100644 tests/source/issue_5027.rs create mode 100644 tests/source/issue_5086.rs create mode 100644 tests/target/issue_4823.rs create mode 100644 tests/target/issue_5027.rs create mode 100644 tests/target/issue_5086.rs diff --git a/tests/source/issue_4823.rs b/tests/source/issue_4823.rs new file mode 100644 index 000000000000..a008dd3d8381 --- /dev/null +++ b/tests/source/issue_4823.rs @@ -0,0 +1,5 @@ +macro_rules! m { +() => { +type Type; +}; +} diff --git a/tests/source/issue_5027.rs b/tests/source/issue_5027.rs new file mode 100644 index 000000000000..67beeb23b711 --- /dev/null +++ b/tests/source/issue_5027.rs @@ -0,0 +1,7 @@ +// rustfmt-version: Two + +pub type Iter<'a, D> = impl DoubleEndedIterator)>+ ExactSizeIterator+ 'a; + +trait FOo {pub type Iter<'a, D> = impl DoubleEndedIterator)>+ ExactSizeIterator+ 'a;} + +impl Bar {pub type Iter<'a, D> = impl DoubleEndedIterator)>+ ExactSizeIterator+ 'a;} \ No newline at end of file diff --git a/tests/source/issue_5086.rs b/tests/source/issue_5086.rs new file mode 100644 index 000000000000..1644c9d2ccbb --- /dev/null +++ b/tests/source/issue_5086.rs @@ -0,0 +1,2 @@ +#[cfg(any())] + type Type : Bound ; \ No newline at end of file diff --git a/tests/target/issue_4823.rs b/tests/target/issue_4823.rs new file mode 100644 index 000000000000..de17467c0efa --- /dev/null +++ b/tests/target/issue_4823.rs @@ -0,0 +1,5 @@ +macro_rules! m { + () => { + type Type; + }; +} diff --git a/tests/target/issue_5027.rs b/tests/target/issue_5027.rs new file mode 100644 index 000000000000..26d771720b6c --- /dev/null +++ b/tests/target/issue_5027.rs @@ -0,0 +1,17 @@ +// rustfmt-version: Two + +pub type Iter<'a, D> = impl DoubleEndedIterator)> + + ExactSizeIterator + + 'a; + +trait FOo { + pub type Iter<'a, D> = impl DoubleEndedIterator)> + + ExactSizeIterator + + 'a; +} + +impl Bar { + type Iter<'a, D> = impl DoubleEndedIterator)> + + ExactSizeIterator + + 'a; +} diff --git a/tests/target/issue_5086.rs b/tests/target/issue_5086.rs new file mode 100644 index 000000000000..7a0be06f7917 --- /dev/null +++ b/tests/target/issue_5086.rs @@ -0,0 +1,2 @@ +#[cfg(any())] +type Type: Bound; From 2c442ccf25a98c9e8f0a972d94f10ef44b7b09bf Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Thu, 18 Nov 2021 12:39:38 -0600 Subject: [PATCH 098/401] fix: correct some type alias issues --- src/items.rs | 49 ++++++++++++++++++++++++------------------------- src/types.rs | 8 ++++++++ 2 files changed, 32 insertions(+), 25 deletions(-) diff --git a/src/items.rs b/src/items.rs index 849e6c06218f..63c32e012df7 100644 --- a/src/items.rs +++ b/src/items.rs @@ -28,6 +28,7 @@ use crate::shape::{Indent, Shape}; use crate::source_map::{LineRangeUtils, SpanUtils}; use crate::spanned::Spanned; use crate::stmt::Stmt; +use crate::types::opaque_ty; use crate::utils::*; use crate::vertical::rewrite_with_alignment; use crate::visitor::FmtVisitor; @@ -581,13 +582,10 @@ impl<'a> FmtVisitor<'a> { if self.get_context().config.reorder_impl_items() { type TyOpt = Option>; use crate::ast::AssocItemKind::*; - let is_type = |ty: &TyOpt| { - ty.as_ref() - .map_or(true, |t| !matches!(t.kind, ast::TyKind::ImplTrait(..))) - }; - let is_opaque = |ty: &TyOpt| !is_type(ty); - let both_type = |left: &TyOpt, right: &TyOpt| is_type(left) && is_type(right); - let both_opaque = |left: &TyOpt, right: &TyOpt| is_opaque(left) && is_opaque(right); + let is_type = |ty: &TyOpt| opaque_ty(ty).is_none(); + let is_opaque = |ty: &TyOpt| opaque_ty(ty).is_some(); + let both_type = |l: &TyOpt, r: &TyOpt| is_type(l) && is_type(r); + let both_opaque = |l: &TyOpt, r: &TyOpt| is_opaque(l) && is_opaque(r); let need_empty_line = |a: &ast::AssocItemKind, b: &ast::AssocItemKind| match (a, b) { (TyAlias(lty), TyAlias(rty)) if both_type(<y.ty, &rty.ty) || both_opaque(<y.ty, &rty.ty) => @@ -1508,43 +1506,38 @@ pub(crate) fn rewrite_type_alias<'a, 'b>( ref bounds, ref ty, } = *ty_alias_kind; - let ty_opt = ty.as_ref().map(|t| &**t); + let ty_opt = ty.as_ref(); let (ident, vis) = match visitor_kind { Item(i) => (i.ident, &i.vis), AssocTraitItem(i) | AssocImplItem(i) => (i.ident, &i.vis), ForeignItem(i) => (i.ident, &i.vis), }; let rw_info = &TyAliasRewriteInfo(context, indent, generics, ident, span); - + let op_ty = opaque_ty(ty); // Type Aliases are formatted slightly differently depending on the context // in which they appear, whether they are opaque, and whether they are associated. // https://rustc-dev-guide.rust-lang.org/opaque-types-type-alias-impl-trait.html // https://github.com/rust-dev-tools/fmt-rfcs/blob/master/guide/items.md#type-aliases - match (visitor_kind, ty_opt) { - (Item(_), None) => { - let op_ty = OpaqueType { bounds }; - rewrite_ty(rw_info, Some(bounds), Some(&op_ty), vis) + match (visitor_kind, &op_ty) { + (Item(_) | AssocTraitItem(_) | ForeignItem(_), Some(ref op_bounds)) => { + let op = OpaqueType { bounds: op_bounds }; + rewrite_ty(rw_info, Some(bounds), Some(&op), vis) + } + (Item(_) | AssocTraitItem(_) | ForeignItem(_), None) => { + rewrite_ty(rw_info, Some(bounds), ty_opt, vis) } - (Item(_), Some(ty)) => rewrite_ty(rw_info, Some(bounds), Some(&*ty), vis), (AssocImplItem(_), _) => { - let result = if let Some(ast::Ty { - kind: ast::TyKind::ImplTrait(_, ref bounds), - .. - }) = ty_opt - { - let op_ty = OpaqueType { bounds }; - rewrite_ty(rw_info, None, Some(&op_ty), &DEFAULT_VISIBILITY) + let result = if let Some(ref op_bounds) = op_ty { + let op = OpaqueType { bounds: op_bounds }; + rewrite_ty(rw_info, Some(bounds), Some(&op), &DEFAULT_VISIBILITY) } else { - rewrite_ty(rw_info, None, ty.as_ref(), vis) + rewrite_ty(rw_info, Some(bounds), ty_opt, vis) }?; match defaultness { ast::Defaultness::Default(..) => Some(format!("default {}", result)), _ => Some(result), } } - (AssocTraitItem(_), _) | (ForeignItem(_), _) => { - rewrite_ty(rw_info, Some(bounds), ty.as_ref(), vis) - } } } @@ -1867,6 +1860,12 @@ fn rewrite_static( Some(format!("{}{};", prefix, ty_str)) } } + +// FIXME(calebcartwright) - This is a hack around a bug in the handling of TyKind::ImplTrait. +// This should be removed once that bug is resolved, with the type alias formatting using the +// defined Ty for the RHS directly. +// https://github.com/rust-lang/rustfmt/issues/4373 +// https://github.com/rust-lang/rustfmt/issues/5027 struct OpaqueType<'a> { bounds: &'a ast::GenericBounds, } diff --git a/src/types.rs b/src/types.rs index 9ea90c5e46dd..891609783b61 100644 --- a/src/types.rs +++ b/src/types.rs @@ -2,6 +2,7 @@ use std::iter::ExactSizeIterator; use std::ops::Deref; use rustc_ast::ast::{self, FnRetTy, Mutability}; +use rustc_ast::ptr; use rustc_span::{symbol::kw, BytePos, Pos, Span}; use crate::comment::{combine_strs_with_missing_comments, contains_comment}; @@ -1031,6 +1032,13 @@ fn join_bounds_inner( } } +pub(crate) fn opaque_ty(ty: &Option>) -> Option<&ast::GenericBounds> { + ty.as_ref().and_then(|t| match &t.kind { + ast::TyKind::ImplTrait(_, bounds) => Some(bounds), + _ => None, + }) +} + pub(crate) fn can_be_overflowed_type( context: &RewriteContext<'_>, ty: &ast::Ty, From 196e6765048b8306208ac88d743ab02e8faf4b6b Mon Sep 17 00:00:00 2001 From: mujpao Date: Thu, 18 Nov 2021 17:06:49 -0800 Subject: [PATCH 099/401] Preserve normalized comments after last list item --- src/lists.rs | 13 +- .../wrap-comments-not-normalized.rs | 108 ++++++++++++++++ tests/source/issue-4909/wrap-comments-true.rs | 109 ++++++++++++++++ .../target/issue-4909/wrap-comments-false.rs | 72 +++++++++++ .../wrap-comments-not-normalized.rs | 118 +++++++++++++++++ tests/target/issue-4909/wrap-comments-true.rs | 119 ++++++++++++++++++ 6 files changed, 535 insertions(+), 4 deletions(-) create mode 100644 tests/source/issue-4909/wrap-comments-not-normalized.rs create mode 100644 tests/source/issue-4909/wrap-comments-true.rs create mode 100644 tests/target/issue-4909/wrap-comments-false.rs create mode 100644 tests/target/issue-4909/wrap-comments-not-normalized.rs create mode 100644 tests/target/issue-4909/wrap-comments-true.rs diff --git a/src/lists.rs b/src/lists.rs index d341ec8e6b0e..3515dd172510 100644 --- a/src/lists.rs +++ b/src/lists.rs @@ -444,10 +444,15 @@ where let offset = formatting.shape.indent + overhead; let comment_shape = Shape::legacy(width, offset); - // Use block-style only for the last item or multiline comments. - let block_style = !formatting.ends_with_newline && last - || comment.trim().contains('\n') - || comment.trim().len() > width; + let block_style = if !formatting.ends_with_newline && last { + true + } else if starts_with_newline(comment) { + false + } else if comment.trim().contains('\n') || comment.trim().len() > width { + true + } else { + false + }; rewrite_comment( comment.trim_start(), diff --git a/tests/source/issue-4909/wrap-comments-not-normalized.rs b/tests/source/issue-4909/wrap-comments-not-normalized.rs new file mode 100644 index 000000000000..cd8de2707f16 --- /dev/null +++ b/tests/source/issue-4909/wrap-comments-not-normalized.rs @@ -0,0 +1,108 @@ +// rustfmt-wrap_comments: true + +pub enum E { + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + Variant1, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + Variant2, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +} + +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 +} + +pub enum E3 { + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + Variant1, + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + Variant2, + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + +} + +pub struct S { + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + some_field: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + last_field: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +} + +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 +} + +pub struct S3 { + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + some_field: usize, + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + last_field: usize, + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions +} + +fn foo( + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + a: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + b: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +) -> usize { + 5 +} + +fn foo2(// Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +) -> usize { + 5 +} + +fn foo3( + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + a: usize, + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + b: usize, + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + +) -> usize { + 5 +} + +fn main() { + let v = vec![ + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + 1, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + 2, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + ]; + + let v2: Vec = vec![ + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + ]; + + let v3 = vec![ + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + 1, + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + 2, + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + ]; +} diff --git a/tests/source/issue-4909/wrap-comments-true.rs b/tests/source/issue-4909/wrap-comments-true.rs new file mode 100644 index 000000000000..f18d8d686e1f --- /dev/null +++ b/tests/source/issue-4909/wrap-comments-true.rs @@ -0,0 +1,109 @@ +// rustfmt-normalize_comments: true +// rustfmt-wrap_comments: true + +pub enum E { + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + Variant1, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + Variant2, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +} + +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 +} + +pub enum E3 { + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + Variant1, + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + Variant2, + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + +} + +pub struct S { + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + some_field: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + last_field: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +} + +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 +} + +pub struct S3 { + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + some_field: usize, + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + last_field: usize, + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions +} + +fn foo( + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + a: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + b: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +) -> usize { + 5 +} + +fn foo2(// Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +) -> usize { + 5 +} + +fn foo3( + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + a: usize, + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + b: usize, + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + +) -> usize { + 5 +} + +fn main() { + let v = vec![ + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + 1, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + 2, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + ]; + + let v2: Vec = vec![ + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + ]; + + let v3 = vec![ + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + 1, + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + 2, + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + ]; +} diff --git a/tests/target/issue-4909/wrap-comments-false.rs b/tests/target/issue-4909/wrap-comments-false.rs new file mode 100644 index 000000000000..a8ead584f445 --- /dev/null +++ b/tests/target/issue-4909/wrap-comments-false.rs @@ -0,0 +1,72 @@ +// rustfmt-normalize_comments: true + +pub enum E { + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + Variant1, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + Variant2, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +} + +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 +} + +pub struct S { + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + some_field: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + last_field: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +} + +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 +} + +fn foo( + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + a: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + b: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +) -> usize { + 5 +} + +fn foo2(// Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +) -> usize { + 5 +} + +fn main() { + let v = vec![ + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + 1, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + 2, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + ]; + + let v2: Vec = vec![ + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + ]; +} diff --git a/tests/target/issue-4909/wrap-comments-not-normalized.rs b/tests/target/issue-4909/wrap-comments-not-normalized.rs new file mode 100644 index 000000000000..2a3d803b3b1e --- /dev/null +++ b/tests/target/issue-4909/wrap-comments-not-normalized.rs @@ -0,0 +1,118 @@ +// rustfmt-wrap_comments: true + +pub enum E { + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + Variant1, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + Variant2, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +} + +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 +} + +pub enum E3 { + // Expand as needed, numbers should be ascending according to the stage through the inclusion + // pipeline, or according to the descriptions + Variant1, + // Expand as needed, numbers should be ascending according to the stage through the inclusion + // pipeline, or according to the descriptions + Variant2, + // Expand as needed, numbers should be ascending according to the stage through the inclusion + // pipeline, or according to the descriptions +} + +pub struct S { + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + some_field: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + last_field: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +} + +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 +} + +pub struct S3 { + // Expand as needed, numbers should be ascending according to the stage through the inclusion + // pipeline, or according to the descriptions + some_field: usize, + // Expand as needed, numbers should be ascending according to the stage through the inclusion + // pipeline, or according to the descriptions + last_field: usize, + // Expand as needed, numbers should be ascending according to the stage through the inclusion + // pipeline, or according to the descriptions +} + +fn foo( + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + a: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + b: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +) -> usize { + 5 +} + +fn foo2(// Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +) -> usize { + 5 +} + +fn foo3( + // Expand as needed, numbers should be ascending according to the stage through the inclusion + // pipeline, or according to the descriptions + a: usize, + // Expand as needed, numbers should be ascending according to the stage through the inclusion + // pipeline, or according to the descriptions + b: usize, + // Expand as needed, numbers should be ascending according to the stage through the inclusion + // pipeline, or according to the descriptions +) -> usize { + 5 +} + +fn main() { + let v = vec![ + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + 1, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + 2, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + ]; + + let v2: Vec = vec![ + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + ]; + + let v3 = vec![ + // Expand as needed, numbers should be ascending according to the stage through the + // inclusion pipeline, or according to the descriptions + 1, + // Expand as needed, numbers should be ascending according to the stage through the + // inclusion pipeline, or according to the descriptions + 2, + // Expand as needed, numbers should be ascending according to the stage through the + // inclusion pipeline, or according to the descriptions + ]; +} diff --git a/tests/target/issue-4909/wrap-comments-true.rs b/tests/target/issue-4909/wrap-comments-true.rs new file mode 100644 index 000000000000..5376962a2ee4 --- /dev/null +++ b/tests/target/issue-4909/wrap-comments-true.rs @@ -0,0 +1,119 @@ +// rustfmt-normalize_comments: true +// rustfmt-wrap_comments: true + +pub enum E { + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + Variant1, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + Variant2, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +} + +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 +} + +pub enum E3 { + // Expand as needed, numbers should be ascending according to the stage through the inclusion + // pipeline, or according to the descriptions + Variant1, + // Expand as needed, numbers should be ascending according to the stage through the inclusion + // pipeline, or according to the descriptions + Variant2, + // Expand as needed, numbers should be ascending according to the stage through the inclusion + // pipeline, or according to the descriptions +} + +pub struct S { + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + some_field: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + last_field: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +} + +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 +} + +pub struct S3 { + // Expand as needed, numbers should be ascending according to the stage through the inclusion + // pipeline, or according to the descriptions + some_field: usize, + // Expand as needed, numbers should be ascending according to the stage through the inclusion + // pipeline, or according to the descriptions + last_field: usize, + // Expand as needed, numbers should be ascending according to the stage through the inclusion + // pipeline, or according to the descriptions +} + +fn foo( + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + a: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + b: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +) -> usize { + 5 +} + +fn foo2(// Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +) -> usize { + 5 +} + +fn foo3( + // Expand as needed, numbers should be ascending according to the stage through the inclusion + // pipeline, or according to the descriptions + a: usize, + // Expand as needed, numbers should be ascending according to the stage through the inclusion + // pipeline, or according to the descriptions + b: usize, + // Expand as needed, numbers should be ascending according to the stage through the inclusion + // pipeline, or according to the descriptions +) -> usize { + 5 +} + +fn main() { + let v = vec![ + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + 1, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + 2, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + ]; + + let v2: Vec = vec![ + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + ]; + + let v3 = vec![ + // Expand as needed, numbers should be ascending according to the stage through the + // inclusion pipeline, or according to the descriptions + 1, + // Expand as needed, numbers should be ascending according to the stage through the + // inclusion pipeline, or according to the descriptions + 2, + // Expand as needed, numbers should be ascending according to the stage through the + // inclusion pipeline, or according to the descriptions + ]; +} From 826eba8984690b7c23aab8604b6a85587bb93edb Mon Sep 17 00:00:00 2001 From: Johannes Linke Date: Sat, 20 Nov 2021 02:22:50 +0100 Subject: [PATCH 100/401] Add a few missing tracking issues in Configurations.md (#5084) * Add a few missing tracking issues in Configurations.md * fix: tracking issue for imports_granularity stabilization Co-authored-by: Caleb Cartwright --- Configurations.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Configurations.md b/Configurations.md index 4a281251f227..a89fbe863e65 100644 --- a/Configurations.md +++ b/Configurations.md @@ -933,7 +933,7 @@ if any of the first five lines contains `@generated` marker. - **Default value**: `false` - **Possible values**: `true`, `false` -- **Stable**: No +- **Stable**: No (tracking issue: [#5080](https://github.com/rust-lang/rustfmt/issues/5080)) ## `format_macro_matchers` @@ -1064,7 +1064,7 @@ Control the case of the letters in hexadecimal literal values - **Default value**: `Preserve` - **Possible values**: `Upper`, `Lower` -- **Stable**: No +- **Stable**: No (tracking issue: [#5081](https://github.com/rust-lang/rustfmt/issues/5081)) ## `hide_parse_errors` @@ -1701,7 +1701,7 @@ How imports should be grouped into `use` statements. Imports will be merged or s - **Default value**: `Preserve` - **Possible values**: `Preserve`, `Crate`, `Module`, `Item`, `One` -- **Stable**: No +- **Stable**: No (tracking issue: [#4991](https://github.com/rust-lang/rustfmt/issues/4991)) #### `Preserve` (default): @@ -2063,7 +2063,7 @@ Controls the strategy for how imports are grouped together. - **Default value**: `Preserve` - **Possible values**: `Preserve`, `StdExternalCrate`, `One` -- **Stable**: No +- **Stable**: No (tracking issue: [#5083](https://github.com/rust-lang/rustfmt/issues/5083)) #### `Preserve` (default): From 4389a4ce49e737915af1f1b27b96b05598a4e388 Mon Sep 17 00:00:00 2001 From: Dom Date: Fri, 19 Nov 2021 20:15:33 +0100 Subject: [PATCH 101/401] fix: do not wrap reference-style doc links Prevents wrap_comments from incorrectly wrapping reference-style doc links. --- src/comment.rs | 19 ++++++++++++++++++- tests/target/issue-5095.rs | 27 +++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 tests/target/issue-5095.rs diff --git a/src/comment.rs b/src/comment.rs index 7b76c232937d..830d2b50aad8 100644 --- a/src/comment.rs +++ b/src/comment.rs @@ -3,6 +3,8 @@ use std::{self, borrow::Cow, iter}; use itertools::{multipeek, MultiPeek}; +use lazy_static::lazy_static; +use regex::Regex; use rustc_span::Span; use crate::config::Config; @@ -15,6 +17,17 @@ use crate::utils::{ }; use crate::{ErrorKind, FormattingError}; +lazy_static! { + /// A regex matching reference doc links. + /// + /// ```markdown + /// /// An [example]. + /// /// + /// /// [example]: this::is::a::link + /// ``` + static ref REFERENCE_LINK_URL: Regex = Regex::new(r"^\[.+\]\s?:").unwrap(); +} + fn is_custom_comment(comment: &str) -> bool { if !comment.starts_with("//") { false @@ -842,7 +855,11 @@ fn trim_custom_comment_prefix(s: &str) -> String { /// Returns `true` if the given string MAY include URLs or alike. fn has_url(s: &str) -> bool { // This function may return false positive, but should get its job done in most cases. - s.contains("https://") || s.contains("http://") || s.contains("ftp://") || s.contains("file://") + s.contains("https://") + || s.contains("http://") + || s.contains("ftp://") + || s.contains("file://") + || REFERENCE_LINK_URL.is_match(s) } /// Given the span, rewrite the missing comment inside it if available. diff --git a/tests/target/issue-5095.rs b/tests/target/issue-5095.rs new file mode 100644 index 000000000000..6981a65808c9 --- /dev/null +++ b/tests/target/issue-5095.rs @@ -0,0 +1,27 @@ +// rustfmt-wrap_comments: true + +pub mod a_long_name { + pub mod b_long_name { + pub mod c_long_name { + pub mod d_long_name { + pub mod e_long_name { + pub struct Bananas; + impl Bananas { + pub fn fantastic() {} + } + + pub mod f_long_name { + pub struct Apples; + } + } + } + } + } +} + +/// Check out [my other struct] ([`Bananas`]) and [the method it has]. +/// +/// [my other struct]: a_long_name::b_long_name::c_long_name::d_long_name::e_long_name::f_long_name::Apples +/// [`Bananas`]: a_long_name::b_long_name::c_long_name::d_long_name::e_long_name::Bananas::fantastic() +/// [the method it has]: a_long_name::b_long_name::c_long_name::d_long_name::e_long_name::Bananas::fantastic() +pub struct A; From 243cec7a0b9cc0ba3fba6e5ed849b0635f3496db Mon Sep 17 00:00:00 2001 From: Fabian Keller Date: Wed, 24 Nov 2021 10:38:55 +0100 Subject: [PATCH 102/401] Update README.md --- README.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index b3d21e6fb87c..b3a968f0c043 100644 --- a/README.md +++ b/README.md @@ -48,12 +48,11 @@ cargo +nightly fmt ## Limitations Rustfmt tries to work on as much Rust code as possible. Sometimes, the code -doesn't even need to compile! As we approach a 1.0 release we are also looking -to limit areas of instability; in particular, post-1.0, the formatting of most -code should not change as Rustfmt improves. However, there are some things that -Rustfmt can't do or can't do well (and thus where formatting might change -significantly, even post-1.0). We would like to reduce the list of limitations -over time. +doesn't even need to compile! In general, we are looking to limit areas of +instability; in particular, post-1.0, the formatting of most code should not +change as Rustfmt improves. However, there are some things that Rustfmt can't +do or can't do well (and thus where formatting might change significantly, +even post-1.0). We would like to reduce the list of limitations over time. The following list enumerates areas where Rustfmt does not work or where the stability guarantees do not apply (we don't make a distinction between the two From ea042b90c9b66cf5431acddd29e54468c05ea65b Mon Sep 17 00:00:00 2001 From: mujpao Date: Wed, 24 Nov 2021 15:05:23 -0800 Subject: [PATCH 103/401] Add more tests for comments in lists --- .../wrap-comments-not-normalized.rs | 21 ++++ .../wrap-comments-true.rs | 21 ++++ .../comments-in-lists/format-doc-comments.rs | 96 +++++++++++++++++++ .../wrap-comments-false.rs | 13 +++ .../wrap-comments-not-normalized.rs | 24 +++++ .../wrap-comments-true.rs | 24 +++++ 6 files changed, 199 insertions(+) rename tests/source/{issue-4909 => comments-in-lists}/wrap-comments-not-normalized.rs (81%) rename tests/source/{issue-4909 => comments-in-lists}/wrap-comments-true.rs (81%) create mode 100644 tests/target/comments-in-lists/format-doc-comments.rs rename tests/target/{issue-4909 => comments-in-lists}/wrap-comments-false.rs (82%) rename tests/target/{issue-4909 => comments-in-lists}/wrap-comments-not-normalized.rs (81%) rename tests/target/{issue-4909 => comments-in-lists}/wrap-comments-true.rs (81%) diff --git a/tests/source/issue-4909/wrap-comments-not-normalized.rs b/tests/source/comments-in-lists/wrap-comments-not-normalized.rs similarity index 81% rename from tests/source/issue-4909/wrap-comments-not-normalized.rs rename to tests/source/comments-in-lists/wrap-comments-not-normalized.rs index cd8de2707f16..b96c02802d69 100644 --- a/tests/source/issue-4909/wrap-comments-not-normalized.rs +++ b/tests/source/comments-in-lists/wrap-comments-not-normalized.rs @@ -1,5 +1,6 @@ // rustfmt-wrap_comments: true +// https://github.com/rust-lang/rustfmt/issues/4909 pub enum E { // Expand as needed, numbers should be ascending according to the stage // through the inclusion pipeline, or according to the descriptions @@ -105,4 +106,24 @@ fn main() { 2, // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions ]; + + // https://github.com/rust-lang/rustfmt/issues/4430 + match a { + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + b => c, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + d => e, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + } + + match a { + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + b => c, + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + d => e, + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + } } diff --git a/tests/source/issue-4909/wrap-comments-true.rs b/tests/source/comments-in-lists/wrap-comments-true.rs similarity index 81% rename from tests/source/issue-4909/wrap-comments-true.rs rename to tests/source/comments-in-lists/wrap-comments-true.rs index f18d8d686e1f..360b838520ed 100644 --- a/tests/source/issue-4909/wrap-comments-true.rs +++ b/tests/source/comments-in-lists/wrap-comments-true.rs @@ -1,6 +1,7 @@ // rustfmt-normalize_comments: true // rustfmt-wrap_comments: true +// https://github.com/rust-lang/rustfmt/issues/4909 pub enum E { // Expand as needed, numbers should be ascending according to the stage // through the inclusion pipeline, or according to the descriptions @@ -106,4 +107,24 @@ fn main() { 2, // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions ]; + + // https://github.com/rust-lang/rustfmt/issues/4430 + match a { + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + b => c, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + d => e, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + } + + match a { + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + b => c, + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + d => e, + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + } } diff --git a/tests/target/comments-in-lists/format-doc-comments.rs b/tests/target/comments-in-lists/format-doc-comments.rs new file mode 100644 index 000000000000..be31bf0a3319 --- /dev/null +++ b/tests/target/comments-in-lists/format-doc-comments.rs @@ -0,0 +1,96 @@ +// rustfmt-format_code_in_doc_comments: true + +// https://github.com/rust-lang/rustfmt/issues/4420 +enum Minimal { + Example, + //[thisisremoved thatsleft + // canbeanything +} + +struct Minimal2 { + Example: usize, + //[thisisremoved thatsleft + // canbeanything +} + +pub enum E { + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + Variant1, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + Variant2, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +} + +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 +} + +pub struct S { + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + some_field: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + last_field: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +} + +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 +} + +fn foo( + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + a: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + b: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +) -> usize { + 5 +} + +fn foo2(// Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +) -> usize { + 5 +} + +fn main() { + let v = vec![ + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + 1, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + 2, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + ]; + + let v2: Vec = vec![ + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + ]; + + match a { + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + b => c, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + d => e, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + } +} diff --git a/tests/target/issue-4909/wrap-comments-false.rs b/tests/target/comments-in-lists/wrap-comments-false.rs similarity index 82% rename from tests/target/issue-4909/wrap-comments-false.rs rename to tests/target/comments-in-lists/wrap-comments-false.rs index a8ead584f445..80aea59d1b52 100644 --- a/tests/target/issue-4909/wrap-comments-false.rs +++ b/tests/target/comments-in-lists/wrap-comments-false.rs @@ -1,5 +1,6 @@ // rustfmt-normalize_comments: true +// https://github.com/rust-lang/rustfmt/issues/4909 pub enum E { // Expand as needed, numbers should be ascending according to the stage // through the inclusion pipeline, or according to the descriptions @@ -69,4 +70,16 @@ fn main() { // Expand as needed, numbers should be ascending according to the stage // through the inclusion pipeline, or according to the descriptions ]; + + // https://github.com/rust-lang/rustfmt/issues/4430 + match a { + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + b => c, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + d => e, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + } } diff --git a/tests/target/issue-4909/wrap-comments-not-normalized.rs b/tests/target/comments-in-lists/wrap-comments-not-normalized.rs similarity index 81% rename from tests/target/issue-4909/wrap-comments-not-normalized.rs rename to tests/target/comments-in-lists/wrap-comments-not-normalized.rs index 2a3d803b3b1e..52315f470e4b 100644 --- a/tests/target/issue-4909/wrap-comments-not-normalized.rs +++ b/tests/target/comments-in-lists/wrap-comments-not-normalized.rs @@ -1,5 +1,6 @@ // rustfmt-wrap_comments: true +// https://github.com/rust-lang/rustfmt/issues/4909 pub enum E { // Expand as needed, numbers should be ascending according to the stage // through the inclusion pipeline, or according to the descriptions @@ -115,4 +116,27 @@ fn main() { // Expand as needed, numbers should be ascending according to the stage through the // inclusion pipeline, or according to the descriptions ]; + + // https://github.com/rust-lang/rustfmt/issues/4430 + match a { + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + b => c, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + d => e, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + } + + match a { + // Expand as needed, numbers should be ascending according to the stage through the + // inclusion pipeline, or according to the descriptions + b => c, + // Expand as needed, numbers should be ascending according to the stage through the + // inclusion pipeline, or according to the descriptions + d => e, + // Expand as needed, numbers should be ascending according to the stage through the + // inclusion pipeline, or according to the descriptions + } } diff --git a/tests/target/issue-4909/wrap-comments-true.rs b/tests/target/comments-in-lists/wrap-comments-true.rs similarity index 81% rename from tests/target/issue-4909/wrap-comments-true.rs rename to tests/target/comments-in-lists/wrap-comments-true.rs index 5376962a2ee4..e0bfcf0b5007 100644 --- a/tests/target/issue-4909/wrap-comments-true.rs +++ b/tests/target/comments-in-lists/wrap-comments-true.rs @@ -1,6 +1,7 @@ // rustfmt-normalize_comments: true // rustfmt-wrap_comments: true +// https://github.com/rust-lang/rustfmt/issues/4909 pub enum E { // Expand as needed, numbers should be ascending according to the stage // through the inclusion pipeline, or according to the descriptions @@ -116,4 +117,27 @@ fn main() { // Expand as needed, numbers should be ascending according to the stage through the // inclusion pipeline, or according to the descriptions ]; + + // https://github.com/rust-lang/rustfmt/issues/4430 + match a { + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + b => c, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + d => e, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + } + + match a { + // Expand as needed, numbers should be ascending according to the stage through the + // inclusion pipeline, or according to the descriptions + b => c, + // Expand as needed, numbers should be ascending according to the stage through the + // inclusion pipeline, or according to the descriptions + d => e, + // Expand as needed, numbers should be ascending according to the stage through the + // inclusion pipeline, or according to the descriptions + } } From 67fd9ec3002d269c272f68fa0e34a1cb2ca5fd08 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Wed, 24 Nov 2021 03:35:01 -0500 Subject: [PATCH 104/401] Run Windows, Linux, and Mac CI tests with nightly and stable channels --- .github/workflows/linux.yml | 5 ++++- .github/workflows/mac.yml | 5 ++++- .github/workflows/windows.yml | 5 ++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 6eaae69c7080..db4979416421 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -8,7 +8,9 @@ on: jobs: test: runs-on: ubuntu-latest - name: (${{ matrix.target }}, nightly) + name: (${{ matrix.target }}, ${{ matrix.cfg_release_channel }}) + env: + CFG_RELEASE_CHANNEL: ${{ matrix.cfg_release_channel }} strategy: # https://help.github.com/en/actions/getting-started-with-github-actions/about-github-actions#usage-limits # There's a limit of 60 concurrent jobs across all repos in the rust-lang organization. @@ -20,6 +22,7 @@ jobs: target: [ x86_64-unknown-linux-gnu, ] + cfg_release_channel: [nightly, stable] steps: - name: checkout diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml index 79e4f69163e0..55e1cc9539b8 100644 --- a/.github/workflows/mac.yml +++ b/.github/workflows/mac.yml @@ -10,13 +10,16 @@ jobs: # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/virtual-environments-for-github-hosted-runners#supported-runners-and-hardware-resources # macOS Catalina 10.15 runs-on: macos-latest - name: (${{ matrix.target }}, nightly) + name: (${{ matrix.target }}, ${{ matrix.cfg_release_channel }}) + env: + CFG_RELEASE_CHANNEL: ${{ matrix.cfg_release_channel }} strategy: fail-fast: false matrix: target: [ x86_64-apple-darwin, ] + cfg_release_channel: [nightly, stable] steps: - name: checkout diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index c05e8d4896ac..dcb08b5412ea 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -8,7 +8,9 @@ on: jobs: test: runs-on: windows-latest - name: (${{ matrix.target }}, nightly) + name: (${{ matrix.target }}, ${{ matrix.cfg_release_channel }}) + env: + CFG_RELEASE_CHANNEL: ${{ matrix.cfg_release_channel }} strategy: # https://help.github.com/en/actions/getting-started-with-github-actions/about-github-actions#usage-limits # There's a limit of 60 concurrent jobs across all repos in the rust-lang organization. @@ -23,6 +25,7 @@ jobs: x86_64-pc-windows-gnu, x86_64-pc-windows-msvc, ] + cfg_release_channel: [nightly, stable] steps: # The Windows runners have autocrlf enabled by default From a21f1b6c2a5734f39a1efe3fa84d6475843d14fe Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Sat, 27 Nov 2021 17:14:15 -0500 Subject: [PATCH 105/401] Conditionally compile tests based on CFG_RELEASE_CHANNEL env var Adds the ``nightly_only_test`` and ``stable_only_test`` attribute macros that prevent or allow certain tests to compile on nightly and stable respectively. This is achieved through conditionally outputting the tests TokenStream. If CFG_RELEASE_CHANNEL is not set, it's assumed that we're running in a nightly environment. To mark a test as nightly only: #[nightly_only_test] #[test] fn only_run_on_nightly() { ... } To mark a test a stable only: #[stable_only_test] #[test] fn only_run_on_stable() { ... } --- config_proc_macro/src/lib.rs | 42 ++++++++++++++++++ src/config/mod.rs | 82 +++++++++++++++--------------------- src/syntux/session.rs | 15 +++---- src/test/mod.rs | 13 +++--- 4 files changed, 86 insertions(+), 66 deletions(-) diff --git a/config_proc_macro/src/lib.rs b/config_proc_macro/src/lib.rs index 78e7e098ed9e..513018213192 100644 --- a/config_proc_macro/src/lib.rs +++ b/config_proc_macro/src/lib.rs @@ -8,6 +8,8 @@ mod item_enum; mod item_struct; mod utils; +use std::str::FromStr; + use proc_macro::TokenStream; use syn::parse_macro_input; @@ -23,3 +25,43 @@ pub fn config_type(_args: TokenStream, input: TokenStream) -> TokenStream { TokenStream::from(output) } + +/// Used to conditionally output the TokenStream for tests that need to be run on nightly only. +/// +/// ```rust +/// #[nightly_only_test] +/// #[test] +/// fn test_needs_nightly_rustfmt() { +/// assert!(true); +/// } +/// ``` +#[proc_macro_attribute] +pub fn nightly_only_test(_args: TokenStream, input: TokenStream) -> TokenStream { + // if CFG_RELEASE_CHANNEL is not set we default to nightly, hence why the default is true + if option_env!("CFG_RELEASE_CHANNEL").map_or(true, |c| c == "nightly" || c == "dev") { + input + } else { + // output an empty token stream if CFG_RELEASE_CHANNEL is not set to "nightly" or "dev" + TokenStream::from_str("").unwrap() + } +} + +/// Used to conditionally output the TokenStream for tests that need to be run on stable only. +/// +/// ```rust +/// #[stable_only_test] +/// #[test] +/// fn test_needs_stable_rustfmt() { +/// assert!(true); +/// } +/// ``` +#[proc_macro_attribute] +pub fn stable_only_test(_args: TokenStream, input: TokenStream) -> TokenStream { + // if CFG_RELEASE_CHANNEL is not set we default to nightly, hence why the default is false + if option_env!("CFG_RELEASE_CHANNEL").map_or(false, |c| c == "stable") { + input + } else { + // output an empty token stream if CFG_RELEASE_CHANNEL is not set or is not 'stable' + TokenStream::from_str("").unwrap() + } +} diff --git a/src/config/mod.rs b/src/config/mod.rs index c5419d860c94..5dbe532ac388 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -405,6 +405,8 @@ mod test { use super::*; use std::str; + use rustfmt_config_proc_macro::{nightly_only_test, stable_only_test}; + #[allow(dead_code)] mod mock { use super::super::*; @@ -525,21 +527,17 @@ mod test { assert!(config.license_template.is_none()); } + #[nightly_only_test] #[test] fn test_valid_license_template_path() { - if !crate::is_nightly_channel!() { - return; - } let toml = r#"license_template_path = "tests/license-template/lt.txt""#; let config = Config::from_toml(toml, Path::new("")).unwrap(); assert!(config.license_template.is_some()); } + #[nightly_only_test] #[test] fn test_override_existing_license_with_no_license() { - if !crate::is_nightly_channel!() { - return; - } let toml = r#"license_template_path = "tests/license-template/lt.txt""#; let mut config = Config::from_toml(toml, Path::new("")).unwrap(); assert!(config.license_template.is_some()); @@ -634,48 +632,42 @@ make_backup = false assert_eq!(&toml, &default_config); } - // FIXME(#2183): these tests cannot be run in parallel because they use env vars. - // #[test] - // fn test_as_not_nightly_channel() { - // let mut config = Config::default(); - // assert_eq!(config.was_set().unstable_features(), false); - // config.set().unstable_features(true); - // assert_eq!(config.was_set().unstable_features(), false); - // } + #[stable_only_test] + #[test] + fn test_as_not_nightly_channel() { + let mut config = Config::default(); + assert_eq!(config.was_set().unstable_features(), false); + config.set().unstable_features(true); + assert_eq!(config.was_set().unstable_features(), false); + } - // #[test] - // fn test_as_nightly_channel() { - // let v = ::std::env::var("CFG_RELEASE_CHANNEL").unwrap_or(String::from("")); - // ::std::env::set_var("CFG_RELEASE_CHANNEL", "nightly"); - // let mut config = Config::default(); - // config.set().unstable_features(true); - // assert_eq!(config.was_set().unstable_features(), false); - // config.set().unstable_features(true); - // assert_eq!(config.unstable_features(), true); - // ::std::env::set_var("CFG_RELEASE_CHANNEL", v); - // } + #[nightly_only_test] + #[test] + fn test_as_nightly_channel() { + let mut config = Config::default(); + config.set().unstable_features(true); + // When we don't set the config from toml or command line options it + // doesn't get marked as set by the user. + assert_eq!(config.was_set().unstable_features(), false); + config.set().unstable_features(true); + assert_eq!(config.unstable_features(), true); + } - // #[test] - // fn test_unstable_from_toml() { - // let mut config = Config::from_toml("unstable_features = true").unwrap(); - // assert_eq!(config.was_set().unstable_features(), false); - // let v = ::std::env::var("CFG_RELEASE_CHANNEL").unwrap_or(String::from("")); - // ::std::env::set_var("CFG_RELEASE_CHANNEL", "nightly"); - // config = Config::from_toml("unstable_features = true").unwrap(); - // assert_eq!(config.was_set().unstable_features(), true); - // assert_eq!(config.unstable_features(), true); - // ::std::env::set_var("CFG_RELEASE_CHANNEL", v); - // } + #[nightly_only_test] + #[test] + fn test_unstable_from_toml() { + let config = Config::from_toml("unstable_features = true", Path::new("")).unwrap(); + assert_eq!(config.was_set().unstable_features(), true); + assert_eq!(config.unstable_features(), true); + } #[cfg(test)] mod deprecated_option_merge_imports { use super::*; + #[nightly_only_test] #[test] fn test_old_option_set() { - if !crate::is_nightly_channel!() { - return; - } let toml = r#" unstable_features = true merge_imports = true @@ -684,11 +676,9 @@ make_backup = false assert_eq!(config.imports_granularity(), ImportGranularity::Crate); } + #[nightly_only_test] #[test] fn test_both_set() { - if !crate::is_nightly_channel!() { - return; - } let toml = r#" unstable_features = true merge_imports = true @@ -698,11 +688,9 @@ make_backup = false assert_eq!(config.imports_granularity(), ImportGranularity::Preserve); } + #[nightly_only_test] #[test] fn test_new_overridden() { - if !crate::is_nightly_channel!() { - return; - } let toml = r#" unstable_features = true merge_imports = true @@ -712,11 +700,9 @@ make_backup = false assert_eq!(config.imports_granularity(), ImportGranularity::Preserve); } + #[nightly_only_test] #[test] fn test_old_overridden() { - if !crate::is_nightly_channel!() { - return; - } let toml = r#" unstable_features = true imports_granularity = "Module" diff --git a/src/syntux/session.rs b/src/syntux/session.rs index cdb4893d443b..dd7c7352686e 100644 --- a/src/syntux/session.rs +++ b/src/syntux/session.rs @@ -286,10 +286,11 @@ impl LineRangeUtils for ParseSess { mod tests { use super::*; + use rustfmt_config_proc_macro::nightly_only_test; + mod emitter { use super::*; use crate::config::IgnoreList; - use crate::is_nightly_channel; use crate::utils::mk_sp; use rustc_span::{FileName as SourceMapFileName, MultiSpan, RealFileName, DUMMY_SP}; use std::path::PathBuf; @@ -371,11 +372,9 @@ mod tests { assert_eq!(can_reset_errors.load(Ordering::Acquire), false); } + #[nightly_only_test] #[test] fn handles_recoverable_parse_error_in_ignored_file() { - if !is_nightly_channel!() { - return; - } let num_emitted_errors = Lrc::new(AtomicU32::new(0)); let can_reset_errors = Lrc::new(AtomicBool::new(false)); let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#); @@ -398,11 +397,9 @@ mod tests { assert_eq!(can_reset_errors.load(Ordering::Acquire), true); } + #[nightly_only_test] #[test] fn handles_recoverable_parse_error_in_non_ignored_file() { - if !is_nightly_channel!() { - return; - } let num_emitted_errors = Lrc::new(AtomicU32::new(0)); let can_reset_errors = Lrc::new(AtomicBool::new(false)); let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty())); @@ -424,11 +421,9 @@ mod tests { assert_eq!(can_reset_errors.load(Ordering::Acquire), false); } + #[nightly_only_test] #[test] fn handles_mix_of_recoverable_parse_error() { - if !is_nightly_channel!() { - return; - } let num_emitted_errors = Lrc::new(AtomicU32::new(0)); let can_reset_errors = Lrc::new(AtomicBool::new(false)); let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty())); diff --git a/src/test/mod.rs b/src/test/mod.rs index e2620508c340..cceb28dfea6d 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -15,6 +15,8 @@ use crate::rustfmt_diff::{make_diff, print_diff, DiffLine, Mismatch, ModifiedChu use crate::source_file; use crate::{is_nightly_channel, FormatReport, FormatReportFormatterBuilder, Input, Session}; +use rustfmt_config_proc_macro::nightly_only_test; + mod configuration_snippet; mod mod_resolver; mod parser; @@ -307,14 +309,11 @@ fn assert_output(source: &Path, expected_filename: &Path) { // Idempotence tests. Files in tests/target are checked to be unaltered by // rustfmt. +#[nightly_only_test] #[test] fn idempotence_tests() { init_log(); run_test_with(&TestSetting::default(), || { - // these tests require nightly - if !is_nightly_channel!() { - return; - } // Get all files in the tests/target directory. let files = get_test_files(Path::new("tests/target"), true); let (_reports, count, fails) = check_files(files, &None); @@ -332,13 +331,11 @@ fn idempotence_tests() { // Run rustfmt on itself. This operation must be idempotent. We also check that // no warnings are emitted. +// Issue-3443: these tests require nightly +#[nightly_only_test] #[test] fn self_tests() { init_log(); - // Issue-3443: these tests require nightly - if !is_nightly_channel!() { - return; - } let mut files = get_test_files(Path::new("tests"), false); let bin_directories = vec!["cargo-fmt", "git-rustfmt", "bin", "format-diff"]; for dir in bin_directories { From 0fc846f979bc20d556cc07177b384094a421c54c Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Sun, 28 Nov 2021 15:22:49 -0600 Subject: [PATCH 106/401] refactor: maintain more AST info when formatting a RHS --- src/expr.rs | 57 +++++++++++++++++++++++++++++++++++++++++++-------- src/items.rs | 38 +++++++++++++++++++++++++++------- src/macros.rs | 5 +++-- src/types.rs | 5 +++-- 4 files changed, 86 insertions(+), 19 deletions(-) diff --git a/src/expr.rs b/src/expr.rs index 58942e442de0..5fd86c1a4ead 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -196,9 +196,10 @@ pub(crate) fn format_expr( capture, is_async, movability, fn_decl, body, expr.span, context, shape, ) } - ast::ExprKind::Try(..) | ast::ExprKind::Field(..) | ast::ExprKind::MethodCall(..) => { - rewrite_chain(expr, context, shape) - } + ast::ExprKind::Try(..) + | ast::ExprKind::Field(..) + | ast::ExprKind::MethodCall(..) + | ast::ExprKind::Await(_) => rewrite_chain(expr, context, shape), ast::ExprKind::MacCall(ref mac) => { rewrite_macro(mac, None, context, shape, MacroPosition::Expression).or_else(|| { wrap_str( @@ -377,7 +378,6 @@ pub(crate) fn format_expr( )) } } - ast::ExprKind::Await(_) => rewrite_chain(expr, context, shape), ast::ExprKind::Underscore => Some("_".to_owned()), ast::ExprKind::Err => None, }; @@ -829,6 +829,7 @@ impl<'a> ControlFlow<'a> { &format!("{}{}{}", matcher, pat_string, self.connector), expr, cond_shape, + &RhsAssignKind::Expr(&expr.kind, expr.span), RhsTactics::Default, comments_span, true, @@ -1839,6 +1840,34 @@ fn rewrite_unary_op( rewrite_unary_prefix(context, ast::UnOp::to_string(op), expr, shape) } +pub(crate) enum RhsAssignKind<'ast> { + Expr(&'ast ast::ExprKind, Span), + Bounds, + Ty, +} + +impl<'ast> RhsAssignKind<'ast> { + // TODO(calebcartwright) + // Preemptive addition for handling RHS with chains, not yet utilized. + // It may make more sense to construct the chain first and then check + // whether there are actually chain elements. + #[allow(dead_code)] + fn is_chain(&self) -> bool { + match self { + RhsAssignKind::Expr(kind, _) => { + matches!( + kind, + ast::ExprKind::Try(..) + | ast::ExprKind::Field(..) + | ast::ExprKind::MethodCall(..) + | ast::ExprKind::Await(_) + ) + } + _ => false, + } + } +} + fn rewrite_assignment( context: &RewriteContext<'_>, lhs: &ast::Expr, @@ -1855,7 +1884,13 @@ fn rewrite_assignment( let lhs_shape = shape.sub_width(operator_str.len() + 1)?; let lhs_str = format!("{} {}", lhs.rewrite(context, lhs_shape)?, operator_str); - rewrite_assign_rhs(context, lhs_str, rhs, shape) + rewrite_assign_rhs( + context, + lhs_str, + rhs, + &RhsAssignKind::Expr(&rhs.kind, rhs.span), + shape, + ) } /// Controls where to put the rhs. @@ -1876,9 +1911,10 @@ pub(crate) fn rewrite_assign_rhs, R: Rewrite>( context: &RewriteContext<'_>, lhs: S, ex: &R, + rhs_kind: &RhsAssignKind<'_>, shape: Shape, ) -> Option { - rewrite_assign_rhs_with(context, lhs, ex, shape, RhsTactics::Default) + rewrite_assign_rhs_with(context, lhs, ex, shape, rhs_kind, RhsTactics::Default) } pub(crate) fn rewrite_assign_rhs_expr( @@ -1886,6 +1922,7 @@ pub(crate) fn rewrite_assign_rhs_expr( lhs: &str, ex: &R, shape: Shape, + rhs_kind: &RhsAssignKind<'_>, rhs_tactics: RhsTactics, ) -> Option { let last_line_width = last_line_width(lhs).saturating_sub(if lhs.contains('\n') { @@ -1910,6 +1947,7 @@ pub(crate) fn rewrite_assign_rhs_expr( ex, orig_shape, ex.rewrite(context, orig_shape), + rhs_kind, rhs_tactics, has_rhs_comment, ) @@ -1920,10 +1958,11 @@ pub(crate) fn rewrite_assign_rhs_with, R: Rewrite>( lhs: S, ex: &R, shape: Shape, + rhs_kind: &RhsAssignKind<'_>, rhs_tactics: RhsTactics, ) -> Option { let lhs = lhs.into(); - let rhs = rewrite_assign_rhs_expr(context, &lhs, ex, shape, rhs_tactics)?; + let rhs = rewrite_assign_rhs_expr(context, &lhs, ex, shape, rhs_kind, rhs_tactics)?; Some(lhs + &rhs) } @@ -1932,6 +1971,7 @@ pub(crate) fn rewrite_assign_rhs_with_comments, R: Rewrite>( lhs: S, ex: &R, shape: Shape, + rhs_kind: &RhsAssignKind<'_>, rhs_tactics: RhsTactics, between_span: Span, allow_extend: bool, @@ -1943,7 +1983,7 @@ pub(crate) fn rewrite_assign_rhs_with_comments, R: Rewrite>( } else { shape }; - let rhs = rewrite_assign_rhs_expr(context, &lhs, ex, shape, rhs_tactics)?; + let rhs = rewrite_assign_rhs_expr(context, &lhs, ex, shape, rhs_kind, rhs_tactics)?; if contains_comment { let rhs = rhs.trim_start(); @@ -1958,6 +1998,7 @@ fn choose_rhs( expr: &R, shape: Shape, orig_rhs: Option, + _rhs_kind: &RhsAssignKind<'_>, rhs_tactics: RhsTactics, has_rhs_comment: bool, ) -> Option { diff --git a/src/items.rs b/src/items.rs index 63c32e012df7..3fe827ce696d 100644 --- a/src/items.rs +++ b/src/items.rs @@ -18,7 +18,7 @@ use crate::config::lists::*; use crate::config::{BraceStyle, Config, IndentStyle, Version}; use crate::expr::{ is_empty_block, is_simple_block_stmt, rewrite_assign_rhs, rewrite_assign_rhs_with, - rewrite_assign_rhs_with_comments, RhsTactics, + rewrite_assign_rhs_with_comments, RhsAssignKind, RhsTactics, }; use crate::lists::{definitive_tactic, itemize_list, write_list, ListFormatting, Separator}; use crate::macros::{rewrite_macro, MacroPosition}; @@ -116,7 +116,13 @@ impl Rewrite for ast::Local { // 1 = trailing semicolon; let nested_shape = shape.sub_width(1)?; - result = rewrite_assign_rhs(context, result, init, nested_shape)?; + result = rewrite_assign_rhs( + context, + result, + init, + &RhsAssignKind::Expr(&init.kind, init.span), + nested_shape, + )?; // todo else } @@ -564,11 +570,13 @@ impl<'a> FmtVisitor<'a> { let variant_body = if let Some(ref expr) = field.disr_expr { let lhs = format!("{:1$} =", variant_body, pad_discrim_ident_to); + let ex = &*expr.value; rewrite_assign_rhs_with( &context, lhs, - &*expr.value, + ex, shape, + &RhsAssignKind::Expr(&ex.kind, ex.span), RhsTactics::AllowOverflow, )? } else { @@ -1033,6 +1041,7 @@ pub(crate) fn format_trait( result + ":", bounds, shape, + &RhsAssignKind::Bounds, RhsTactics::ForceNextLineWithoutIndent, )?; } @@ -1213,7 +1222,14 @@ pub(crate) fn format_trait_alias( generic_bounds, generics, }; - rewrite_assign_rhs(context, lhs, &trait_alias_bounds, shape.sub_width(1)?).map(|s| s + ";") + rewrite_assign_rhs( + context, + lhs, + &trait_alias_bounds, + &RhsAssignKind::Bounds, + shape.sub_width(1)?, + ) + .map(|s| s + ";") } fn format_unit_struct( @@ -1630,7 +1646,7 @@ fn rewrite_ty( // 1 = `;` let shape = Shape::indented(indent, context.config).sub_width(1)?; - rewrite_assign_rhs(context, lhs, &*ty, shape).map(|s| s + ";") + rewrite_assign_rhs(context, lhs, &*ty, &RhsAssignKind::Ty, shape).map(|s| s + ";") } else { Some(format!("{};", result)) } @@ -1720,7 +1736,7 @@ pub(crate) fn rewrite_struct_field( let is_prefix_empty = prefix.is_empty(); // We must use multiline. We are going to put attributes and a field on different lines. - let field_str = rewrite_assign_rhs(context, prefix, &*field.ty, shape)?; + let field_str = rewrite_assign_rhs(context, prefix, &*field.ty, &RhsAssignKind::Ty, shape)?; // Remove a leading white-space from `rewrite_assign_rhs()` when rewriting a tuple struct. let field_str = if is_prefix_empty { field_str.trim_start() @@ -1850,6 +1866,7 @@ fn rewrite_static( &lhs, &**expr, Shape::legacy(remaining_width, offset.block_only()), + &RhsAssignKind::Expr(&expr.kind, expr.span), RhsTactics::Default, comments_span, true, @@ -3147,7 +3164,14 @@ impl Rewrite for ast::ForeignItem { rewrite_ident(context, self.ident) ); // 1 = ; - rewrite_assign_rhs(context, prefix, &**ty, shape.sub_width(1)?).map(|s| s + ";") + rewrite_assign_rhs( + context, + prefix, + &**ty, + &RhsAssignKind::Ty, + shape.sub_width(1)?, + ) + .map(|s| s + ";") } ast::ForeignItemKind::TyAlias(ref ty_alias) => { let (kind, span) = (&ItemVisitorKind::ForeignItem(&self), self.span); diff --git a/src/macros.rs b/src/macros.rs index ef747638e33e..a52568be9eac 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -27,7 +27,7 @@ use crate::comment::{ contains_comment, CharClasses, FindUncommented, FullCodeCharKind, LineClasses, }; use crate::config::lists::*; -use crate::expr::rewrite_array; +use crate::expr::{rewrite_array, rewrite_assign_rhs, RhsAssignKind}; use crate::lists::{itemize_list, write_list, ListFormatting}; use crate::overflow; use crate::rewrite::{Rewrite, RewriteContext}; @@ -1468,10 +1468,11 @@ fn format_lazy_static( id, ty.rewrite(context, nested_shape)? )); - result.push_str(&crate::expr::rewrite_assign_rhs( + result.push_str(&rewrite_assign_rhs( context, stmt, &*expr, + &RhsAssignKind::Expr(&expr.kind, expr.span), nested_shape.sub_width(1)?, )?); result.push(';'); diff --git a/src/types.rs b/src/types.rs index 891609783b61..88f5dc432451 100644 --- a/src/types.rs +++ b/src/types.rs @@ -10,6 +10,7 @@ use crate::config::lists::*; use crate::config::{IndentStyle, TypeDensity, Version}; use crate::expr::{ format_expr, rewrite_assign_rhs, rewrite_call, rewrite_tuple, rewrite_unary_prefix, ExprType, + RhsAssignKind, }; use crate::lists::{ definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator, @@ -430,7 +431,7 @@ impl Rewrite for ast::WherePredicate { format!("{}{}", type_str, colon) }; - rewrite_assign_rhs(context, lhs, bounds, shape)? + rewrite_assign_rhs(context, lhs, bounds, &RhsAssignKind::Bounds, shape)? } ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate { ref lifetime, @@ -443,7 +444,7 @@ impl Rewrite for ast::WherePredicate { .. }) => { let lhs_ty_str = lhs_ty.rewrite(context, shape).map(|lhs| lhs + " =")?; - rewrite_assign_rhs(context, lhs_ty_str, &**rhs_ty, shape)? + rewrite_assign_rhs(context, lhs_ty_str, &**rhs_ty, &RhsAssignKind::Ty, shape)? } }; From 1f28683ffacebc91b3bc47b94c56690633226ff9 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Sun, 28 Nov 2021 23:39:31 -0500 Subject: [PATCH 107/401] Update nightly only test with #[nightly_only_test] attribute --- src/ignore_path.rs | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/ignore_path.rs b/src/ignore_path.rs index d8974e12b8f5..7738eee0a760 100644 --- a/src/ignore_path.rs +++ b/src/ignore_path.rs @@ -37,21 +37,17 @@ mod test { 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() { - match option_env!("CFG_RELEASE_CHANNEL") { - // this test requires nightly - None | Some("nightly") => { - 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(); + 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(); - assert!(ignore_path_set.is_match(&FileName::Real(PathBuf::from("src/foo.rs")))); - assert!(ignore_path_set.is_match(&FileName::Real(PathBuf::from("bar_dir/baz.rs")))); - assert!(!ignore_path_set.is_match(&FileName::Real(PathBuf::from("src/bar.rs")))); - } - _ => (), - }; + assert!(ignore_path_set.is_match(&FileName::Real(PathBuf::from("src/foo.rs")))); + assert!(ignore_path_set.is_match(&FileName::Real(PathBuf::from("bar_dir/baz.rs")))); + assert!(!ignore_path_set.is_match(&FileName::Real(PathBuf::from("src/bar.rs")))); } } From ec46ffd981d3d50572f1ad3f6033a7c33d27033f Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Fri, 19 Nov 2021 18:52:52 -0500 Subject: [PATCH 108/401] Determine when new comment lines are needed for itemized blocks Fixes 5088 Previously, rustfmt would add a new comment line anytime it reformatted an itemized block within a comment when ``wrap_comments=true``. This would lead to rustfmt adding empty comments with trailing whitespace. Now, new comment lines are only added if the original comment spanned multiple lines, if the comment needs to be wrapped, or if the comment originally started with an empty comment line. --- src/comment.rs | 43 +++++++++++++--- ..._nested_long_comment_wrap_comments_true.rs | 33 +++++++++++++ ..._long_itemized_block_wrap_comments_true.rs | 19 +++++++ .../very_long_comment_wrap_comments_true.rs | 13 +++++ ...nested_long_comment_wrap_comments_false.rs | 33 +++++++++++++ ..._nested_long_comment_wrap_comments_true.rs | 49 +++++++++++++++++++ ...line_itemized_block_wrap_comments_false.rs | 17 +++++++ ..._line_itemized_block_wrap_comments_true.rs | 17 +++++++ ...with_itemized_block_wrap_comments_false.rs | 37 ++++++++++++++ ..._with_itemized_block_wrap_comments_true.rs | 37 ++++++++++++++ ...line_itemized_block_wrap_comments_false.rs | 9 ++++ ..._line_itemized_block_wrap_comments_true.rs | 9 ++++ ...long_itemized_block_wrap_comments_false.rs | 19 +++++++ ..._long_itemized_block_wrap_comments_true.rs | 27 ++++++++++ ..._with_empty_comment_wrap_comments_false.rs | 17 +++++++ ...t_with_empty_comment_wrap_comments_true.rs | 17 +++++++ .../very_long_comment_wrap_comments_false.rs | 13 +++++ .../very_long_comment_wrap_comments_true.rs | 21 ++++++++ 18 files changed, 424 insertions(+), 6 deletions(-) create mode 100644 tests/source/issue-5088/deeply_nested_long_comment_wrap_comments_true.rs create mode 100644 tests/source/issue-5088/start_with_empty_comment_very_long_itemized_block_wrap_comments_true.rs create mode 100644 tests/source/issue-5088/very_long_comment_wrap_comments_true.rs create mode 100644 tests/target/issue-5088/deeply_nested_long_comment_wrap_comments_false.rs create mode 100644 tests/target/issue-5088/deeply_nested_long_comment_wrap_comments_true.rs create mode 100644 tests/target/issue-5088/multi_line_itemized_block_wrap_comments_false.rs create mode 100644 tests/target/issue-5088/multi_line_itemized_block_wrap_comments_true.rs create mode 100644 tests/target/issue-5088/multi_line_text_with_itemized_block_wrap_comments_false.rs create mode 100644 tests/target/issue-5088/multi_line_text_with_itemized_block_wrap_comments_true.rs create mode 100644 tests/target/issue-5088/single_line_itemized_block_wrap_comments_false.rs create mode 100644 tests/target/issue-5088/single_line_itemized_block_wrap_comments_true.rs create mode 100644 tests/target/issue-5088/start_with_empty_comment_very_long_itemized_block_wrap_comments_false.rs create mode 100644 tests/target/issue-5088/start_with_empty_comment_very_long_itemized_block_wrap_comments_true.rs create mode 100644 tests/target/issue-5088/start_with_empty_comment_wrap_comments_false.rs create mode 100644 tests/target/issue-5088/start_with_empty_comment_wrap_comments_true.rs create mode 100644 tests/target/issue-5088/very_long_comment_wrap_comments_false.rs create mode 100644 tests/target/issue-5088/very_long_comment_wrap_comments_true.rs diff --git a/src/comment.rs b/src/comment.rs index 830d2b50aad8..0f850b9b2f2f 100644 --- a/src/comment.rs +++ b/src/comment.rs @@ -519,6 +519,7 @@ struct CommentRewrite<'a> { opener: String, closer: String, line_start: String, + style: CommentStyle<'a>, } impl<'a> CommentRewrite<'a> { @@ -528,10 +529,14 @@ impl<'a> CommentRewrite<'a> { shape: Shape, config: &'a Config, ) -> CommentRewrite<'a> { - let (opener, closer, line_start) = if block_style { - CommentStyle::SingleBullet.to_str_tuplet() + let ((opener, closer, line_start), style) = if block_style { + ( + CommentStyle::SingleBullet.to_str_tuplet(), + CommentStyle::SingleBullet, + ) } else { - comment_style(orig, config.normalize_comments()).to_str_tuplet() + let style = comment_style(orig, config.normalize_comments()); + (style.to_str_tuplet(), style) }; let max_width = shape @@ -564,6 +569,7 @@ impl<'a> CommentRewrite<'a> { opener: opener.to_owned(), closer: closer.to_owned(), line_start: line_start.to_owned(), + style, }; cr.result.push_str(opener); cr @@ -583,6 +589,15 @@ impl<'a> CommentRewrite<'a> { result } + /// Check if any characters were written to the result buffer after the start of the comment. + /// when calling [`CommentRewrite::new()`] the result buffer is initiazlied with the opening + /// characters for the comment. + fn buffer_contains_comment(&self) -> bool { + // if self.result.len() < self.opener.len() then an empty comment is in the buffer + // if self.result.len() > self.opener.len() then a non empty comment is in the buffer + self.result.len() != self.opener.len() + } + fn finish(mut self) -> String { if !self.code_block_buffer.is_empty() { // There is a code block that is not properly enclosed by backticks. @@ -598,7 +613,12 @@ impl<'a> CommentRewrite<'a> { // the last few lines are part of an itemized block self.fmt.shape = Shape::legacy(self.max_width, self.fmt_indent); let item_fmt = ib.create_string_format(&self.fmt); - self.result.push_str(&self.comment_line_separator); + + // only push a comment_line_separator for ItemizedBlocks if the comment is not empty + if self.buffer_contains_comment() { + self.result.push_str(&self.comment_line_separator); + } + self.result.push_str(&ib.opener); match rewrite_string( &ib.trimmed_block_as_string(), @@ -632,7 +652,13 @@ impl<'a> CommentRewrite<'a> { line: &'a str, has_leading_whitespace: bool, ) -> bool { - let is_last = i == count_newlines(orig); + let num_newlines = count_newlines(orig); + let is_last = i == num_newlines; + let needs_new_comment_line = if self.style.is_block_comment() { + num_newlines > 0 || self.buffer_contains_comment() + } else { + self.buffer_contains_comment() + }; if let Some(ref mut ib) = self.item_block { if ib.add_line(line) { @@ -641,7 +667,12 @@ impl<'a> CommentRewrite<'a> { self.is_prev_line_multi_line = false; self.fmt.shape = Shape::legacy(self.max_width, self.fmt_indent); let item_fmt = ib.create_string_format(&self.fmt); - self.result.push_str(&self.comment_line_separator); + + // only push a comment_line_separator if we need to start a new comment line + if needs_new_comment_line { + self.result.push_str(&self.comment_line_separator); + } + self.result.push_str(&ib.opener); match rewrite_string( &ib.trimmed_block_as_string(), diff --git a/tests/source/issue-5088/deeply_nested_long_comment_wrap_comments_true.rs b/tests/source/issue-5088/deeply_nested_long_comment_wrap_comments_true.rs new file mode 100644 index 000000000000..09f68cae4240 --- /dev/null +++ b/tests/source/issue-5088/deeply_nested_long_comment_wrap_comments_true.rs @@ -0,0 +1,33 @@ +// rustfmt-wrap_comments: true + +fn main() { + { + { + { + { + { + { + { + { + { + { + { + // - aaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa bbbbbbbbbb bbbbbbbbb bbbbbbbbb ccc cccccccccc ccccccc cccccccc + + // * aaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa bbbbbbbbbb bbbbbbbbb bbbbbbbbb ccc cccccccccc ccccccc cccccccc + + /* - aaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa bbbbbbbbbb bbbbbbbbb bbbbbbbbb ccc cccccccccc ccccccc cccccccc */ + + /* * aaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa bbbbbbbbbb bbbbbbbbb bbbbbbbbb ccc cccccccccc ccccccc cccccccc */ + }; + }; + }; + }; + }; + }; + }; + }; + }; + }; + }; +} diff --git a/tests/source/issue-5088/start_with_empty_comment_very_long_itemized_block_wrap_comments_true.rs b/tests/source/issue-5088/start_with_empty_comment_very_long_itemized_block_wrap_comments_true.rs new file mode 100644 index 000000000000..75f748000f9b --- /dev/null +++ b/tests/source/issue-5088/start_with_empty_comment_very_long_itemized_block_wrap_comments_true.rs @@ -0,0 +1,19 @@ +// rustfmt-wrap_comments: true + +// +// - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +// - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + +// +// * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +// * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + +/* + * - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/ +/* + * - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/ + +/* + * * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/ +/* + * * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/ diff --git a/tests/source/issue-5088/very_long_comment_wrap_comments_true.rs b/tests/source/issue-5088/very_long_comment_wrap_comments_true.rs new file mode 100644 index 000000000000..00437f00216b --- /dev/null +++ b/tests/source/issue-5088/very_long_comment_wrap_comments_true.rs @@ -0,0 +1,13 @@ +// rustfmt-wrap_comments: true + +// - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +// - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + +// * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +// * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + +/* - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/ +/* - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/ + +/* * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/ +/* * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/ diff --git a/tests/target/issue-5088/deeply_nested_long_comment_wrap_comments_false.rs b/tests/target/issue-5088/deeply_nested_long_comment_wrap_comments_false.rs new file mode 100644 index 000000000000..f4801de01848 --- /dev/null +++ b/tests/target/issue-5088/deeply_nested_long_comment_wrap_comments_false.rs @@ -0,0 +1,33 @@ +// rustfmt-wrap_comments: false + +fn main() { + { + { + { + { + { + { + { + { + { + { + { + // - aaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa bbbbbbbbbb bbbbbbbbb bbbbbbbbb ccc cccccccccc ccccccc cccccccc + + // * aaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa bbbbbbbbbb bbbbbbbbb bbbbbbbbb ccc cccccccccc ccccccc cccccccc + + /* - aaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa bbbbbbbbbb bbbbbbbbb bbbbbbbbb ccc cccccccccc ccccccc cccccccc */ + + /* * aaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa bbbbbbbbbb bbbbbbbbb bbbbbbbbb ccc cccccccccc ccccccc cccccccc */ + }; + }; + }; + }; + }; + }; + }; + }; + }; + }; + }; +} diff --git a/tests/target/issue-5088/deeply_nested_long_comment_wrap_comments_true.rs b/tests/target/issue-5088/deeply_nested_long_comment_wrap_comments_true.rs new file mode 100644 index 000000000000..b289c9f859e0 --- /dev/null +++ b/tests/target/issue-5088/deeply_nested_long_comment_wrap_comments_true.rs @@ -0,0 +1,49 @@ +// rustfmt-wrap_comments: true + +fn main() { + { + { + { + { + { + { + { + { + { + { + { + // - aaaa aaaaaaaaa aaaaaaaaa + // aaaaaaaaa aaaaaaaaa + // bbbbbbbbbb bbbbbbbbb + // bbbbbbbbb ccc cccccccccc + // ccccccc cccccccc + + // * aaaa aaaaaaaaa aaaaaaaaa + // aaaaaaaaa aaaaaaaaa + // bbbbbbbbbb bbbbbbbbb + // bbbbbbbbb ccc cccccccccc + // ccccccc cccccccc + + /* - aaaa aaaaaaaaa aaaaaaaaa + * aaaaaaaaa aaaaaaaaa + * bbbbbbbbbb bbbbbbbbb + * bbbbbbbbb ccc cccccccccc + * ccccccc cccccccc */ + + /* * aaaa aaaaaaaaa aaaaaaaaa + * aaaaaaaaa aaaaaaaaa + * bbbbbbbbbb bbbbbbbbb + * bbbbbbbbb ccc cccccccccc + * ccccccc cccccccc */ + }; + }; + }; + }; + }; + }; + }; + }; + }; + }; + }; +} diff --git a/tests/target/issue-5088/multi_line_itemized_block_wrap_comments_false.rs b/tests/target/issue-5088/multi_line_itemized_block_wrap_comments_false.rs new file mode 100644 index 000000000000..60beed1b0482 --- /dev/null +++ b/tests/target/issue-5088/multi_line_itemized_block_wrap_comments_false.rs @@ -0,0 +1,17 @@ +// rustfmt-wrap_comments: false + +// - some itemized block 1 +// - some itemized block 2 + +// * some itemized block 3 +// * some itemized block 4 + +/* + * - some itemized block 5 + * - some itemized block 6 + */ + +/* + * * some itemized block 7 + * * some itemized block 8 + */ diff --git a/tests/target/issue-5088/multi_line_itemized_block_wrap_comments_true.rs b/tests/target/issue-5088/multi_line_itemized_block_wrap_comments_true.rs new file mode 100644 index 000000000000..84fba4b7c198 --- /dev/null +++ b/tests/target/issue-5088/multi_line_itemized_block_wrap_comments_true.rs @@ -0,0 +1,17 @@ +// rustfmt-wrap_comments: true + +// - some itemized block 1 +// - some itemized block 2 + +// * some itemized block 3 +// * some itemized block 4 + +/* + * - some itemized block 5 + * - some itemized block 6 + */ + +/* + * * some itemized block 7 + * * some itemized block 8 + */ diff --git a/tests/target/issue-5088/multi_line_text_with_itemized_block_wrap_comments_false.rs b/tests/target/issue-5088/multi_line_text_with_itemized_block_wrap_comments_false.rs new file mode 100644 index 000000000000..d1bf44f6c741 --- /dev/null +++ b/tests/target/issue-5088/multi_line_text_with_itemized_block_wrap_comments_false.rs @@ -0,0 +1,37 @@ +// rustfmt-wrap_comments: false + +// Some text +// - some itemized block 1 +// - some itemized block 2 +// Some more text +// - some itemized block 3 +// - some itemized block 4 +// Even more text + +// Some text +// * some itemized block 5 +// * some itemized block 6 +// Some more text +// * some itemized block 7 +// * some itemized block 8 +// Even more text + +/* + * Some text + * - some itemized block 9 + * - some itemized block 10 + * Some more text + * - some itemized block 11 + * - some itemized block 12 + * Even more text + */ + +/* + * Some text + * * some itemized block 13 + * * some itemized block 14 + * Some more text + * * some itemized block 15 + * * some itemized block 16 + * Even more text + */ diff --git a/tests/target/issue-5088/multi_line_text_with_itemized_block_wrap_comments_true.rs b/tests/target/issue-5088/multi_line_text_with_itemized_block_wrap_comments_true.rs new file mode 100644 index 000000000000..f767491f902d --- /dev/null +++ b/tests/target/issue-5088/multi_line_text_with_itemized_block_wrap_comments_true.rs @@ -0,0 +1,37 @@ +// rustfmt-wrap_comments: true + +// Some text +// - some itemized block 1 +// - some itemized block 2 +// Some more text +// - some itemized block 3 +// - some itemized block 4 +// Even more text + +// Some text +// * some itemized block 5 +// * some itemized block 6 +// Some more text +// * some itemized block 7 +// * some itemized block 8 +// Even more text + +/* + * Some text + * - some itemized block 9 + * - some itemized block 10 + * Some more text + * - some itemized block 11 + * - some itemized block 12 + * Even more text + */ + +/* + * Some text + * * some itemized block 13 + * * some itemized block 14 + * Some more text + * * some itemized block 15 + * * some itemized block 16 + * Even more text + */ diff --git a/tests/target/issue-5088/single_line_itemized_block_wrap_comments_false.rs b/tests/target/issue-5088/single_line_itemized_block_wrap_comments_false.rs new file mode 100644 index 000000000000..2cd85c787f97 --- /dev/null +++ b/tests/target/issue-5088/single_line_itemized_block_wrap_comments_false.rs @@ -0,0 +1,9 @@ +// rustfmt-wrap_comments: false + +// - some itemized block 1 + +// * some itemized block 2 + +/* - some itemized block 3 */ + +/* * some itemized block 4 */ diff --git a/tests/target/issue-5088/single_line_itemized_block_wrap_comments_true.rs b/tests/target/issue-5088/single_line_itemized_block_wrap_comments_true.rs new file mode 100644 index 000000000000..e9f343d75d5e --- /dev/null +++ b/tests/target/issue-5088/single_line_itemized_block_wrap_comments_true.rs @@ -0,0 +1,9 @@ +// rustfmt-wrap_comments: true + +// - some itemized block 1 + +// * some itemized block 2 + +/* - some itemized block 3 */ + +/* * some itemized block 4 */ diff --git a/tests/target/issue-5088/start_with_empty_comment_very_long_itemized_block_wrap_comments_false.rs b/tests/target/issue-5088/start_with_empty_comment_very_long_itemized_block_wrap_comments_false.rs new file mode 100644 index 000000000000..97bb7733d189 --- /dev/null +++ b/tests/target/issue-5088/start_with_empty_comment_very_long_itemized_block_wrap_comments_false.rs @@ -0,0 +1,19 @@ +// rustfmt-wrap_comments: false + +// +// - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +// - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + +// +// * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +// * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + +/* + * - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/ +/* + * - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/ + +/* + * * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/ +/* + * * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/ diff --git a/tests/target/issue-5088/start_with_empty_comment_very_long_itemized_block_wrap_comments_true.rs b/tests/target/issue-5088/start_with_empty_comment_very_long_itemized_block_wrap_comments_true.rs new file mode 100644 index 000000000000..c8af8383e058 --- /dev/null +++ b/tests/target/issue-5088/start_with_empty_comment_very_long_itemized_block_wrap_comments_true.rs @@ -0,0 +1,27 @@ +// rustfmt-wrap_comments: true + +// +// - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod +// tempor incididunt ut labore et dolore magna aliqua. +// - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod +// tempor incididunt ut labore et dolore magna aliqua. + +// +// * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod +// tempor incididunt ut labore et dolore magna aliqua. +// * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod +// tempor incididunt ut labore et dolore magna aliqua. + +/* + * - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + * tempor incididunt ut labore et dolore magna aliqua. */ +/* + * - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + * tempor incididunt ut labore et dolore magna aliqua. */ + +/* + * * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + * tempor incididunt ut labore et dolore magna aliqua. */ +/* + * * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + * tempor incididunt ut labore et dolore magna aliqua. */ diff --git a/tests/target/issue-5088/start_with_empty_comment_wrap_comments_false.rs b/tests/target/issue-5088/start_with_empty_comment_wrap_comments_false.rs new file mode 100644 index 000000000000..75cc42c0e66b --- /dev/null +++ b/tests/target/issue-5088/start_with_empty_comment_wrap_comments_false.rs @@ -0,0 +1,17 @@ +// rustfmt-wrap_comments: false + +// +// - some itemized block 1 +// - some itemized block 2 + +// +// * some itemized block 3 +// * some itemized block 4 + +/* + * - some itemized block 5 + * - some itemized block 6 */ + +/* + * * some itemized block 7 + * * some itemized block 8 */ diff --git a/tests/target/issue-5088/start_with_empty_comment_wrap_comments_true.rs b/tests/target/issue-5088/start_with_empty_comment_wrap_comments_true.rs new file mode 100644 index 000000000000..ef2c8f90cd30 --- /dev/null +++ b/tests/target/issue-5088/start_with_empty_comment_wrap_comments_true.rs @@ -0,0 +1,17 @@ +// rustfmt-wrap_comments: true + +// +// - some itemized block 1 +// - some itemized block 2 + +// +// * some itemized block 3 +// * some itemized block 4 + +/* + * - some itemized block 5 + * - some itemized block 6 */ + +/* + * * some itemized block 7 + * * some itemized block 8 */ diff --git a/tests/target/issue-5088/very_long_comment_wrap_comments_false.rs b/tests/target/issue-5088/very_long_comment_wrap_comments_false.rs new file mode 100644 index 000000000000..c826cc5d4da6 --- /dev/null +++ b/tests/target/issue-5088/very_long_comment_wrap_comments_false.rs @@ -0,0 +1,13 @@ +// rustfmt-wrap_comments: false + +// - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +// - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + +// * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +// * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + +/* - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/ +/* - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/ + +/* * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/ +/* * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/ diff --git a/tests/target/issue-5088/very_long_comment_wrap_comments_true.rs b/tests/target/issue-5088/very_long_comment_wrap_comments_true.rs new file mode 100644 index 000000000000..7f764dbd8a22 --- /dev/null +++ b/tests/target/issue-5088/very_long_comment_wrap_comments_true.rs @@ -0,0 +1,21 @@ +// rustfmt-wrap_comments: true + +// - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod +// tempor incididunt ut labore et dolore magna aliqua. +// - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod +// tempor incididunt ut labore et dolore magna aliqua. + +// * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod +// tempor incididunt ut labore et dolore magna aliqua. +// * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod +// tempor incididunt ut labore et dolore magna aliqua. + +/* - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + * tempor incididunt ut labore et dolore magna aliqua. */ +/* - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + * tempor incididunt ut labore et dolore magna aliqua. */ + +/* * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + * tempor incididunt ut labore et dolore magna aliqua. */ +/* * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + * tempor incididunt ut labore et dolore magna aliqua. */ From f40b1d9f1aeabca7a6e28d2d32d8458943111957 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Wed, 24 Nov 2021 18:47:37 -0500 Subject: [PATCH 109/401] Backport: Do not touch module with #![rustfmt::skip] (4297) Although the implementation is slightly different than the original PR, the general idea is the same. After collecting all modules we want to exclude formatting those that contain the #![rustfmt::skip] attribute. --- src/formatting.rs | 52 +++++++++++++++---- src/test/configuration_snippet.rs | 21 +++++--- src/test/mod_resolver.rs | 9 ++++ src/visitor.rs | 13 ++--- .../mod-resolver/skip-files-issue-5065/foo.rs | 5 ++ .../skip-files-issue-5065/foo/bar/baz.rs | 1 + .../skip-files-issue-5065/main.rs | 9 ++++ .../mod-resolver/skip-files-issue-5065/one.rs | 1 + .../target/skip/preserve_trailing_comment.rs | 7 +++ 9 files changed, 93 insertions(+), 25 deletions(-) create mode 100644 tests/mod-resolver/skip-files-issue-5065/foo.rs create mode 100644 tests/mod-resolver/skip-files-issue-5065/foo/bar/baz.rs create mode 100644 tests/mod-resolver/skip-files-issue-5065/main.rs create mode 100644 tests/mod-resolver/skip-files-issue-5065/one.rs create mode 100644 tests/target/skip/preserve_trailing_comment.rs diff --git a/src/formatting.rs b/src/formatting.rs index 7d0facb8f12c..1972b5a87a5e 100644 --- a/src/formatting.rs +++ b/src/formatting.rs @@ -5,6 +5,7 @@ use std::io::{self, Write}; use std::time::{Duration, Instant}; use rustc_ast::ast; +use rustc_ast::AstLike; use rustc_span::Span; use self::newline_style::apply_newline_style; @@ -15,7 +16,7 @@ use crate::issues::BadIssueSeeker; use crate::modules::Module; use crate::syntux::parser::{DirectoryOwnership, Parser, ParserError}; use crate::syntux::session::ParseSess; -use crate::utils::count_newlines; +use crate::utils::{contains_skip, count_newlines}; use crate::visitor::FmtVisitor; use crate::{modules, source_file, ErrorKind, FormatReport, Input, Session}; @@ -58,6 +59,39 @@ impl<'b, T: Write + 'b> Session<'b, T> { } } +/// Determine if a module should be skipped. True if the module should be skipped, false otherwise. +fn should_skip_module( + config: &Config, + context: &FormatContext<'_, T>, + input_is_stdin: bool, + main_file: &FileName, + path: &FileName, + module: &Module<'_>, +) -> bool { + if contains_skip(module.attrs()) { + return true; + } + + if config.skip_children() && path != main_file { + return true; + } + + if !input_is_stdin && context.ignore_file(&path) { + return true; + } + + if !config.format_generated_files() { + let source_file = context.parse_session.span_to_file_contents(module.span); + let src = source_file.src.as_ref().expect("SourceFile without src"); + + if is_generated_file(src) { + return true; + } + } + + false +} + // Format an entire crate (or subset of the module tree). fn format_project( input: Input, @@ -97,7 +131,12 @@ fn format_project( directory_ownership.unwrap_or(DirectoryOwnership::UnownedViaBlock), !input_is_stdin && !config.skip_children(), ) - .visit_crate(&krate)?; + .visit_crate(&krate)? + .into_iter() + .filter(|(path, module)| { + !should_skip_module(config, &context, input_is_stdin, &main_file, path, module) + }) + .collect::>(); timer = timer.done_parsing(); @@ -105,15 +144,6 @@ fn format_project( context.parse_session.set_silent_emitter(); for (path, module) in files { - let source_file = context.parse_session.span_to_file_contents(module.span); - let src = source_file.src.as_ref().expect("SourceFile without src"); - - let should_ignore = (!input_is_stdin && context.ignore_file(&path)) - || (!config.format_generated_files() && is_generated_file(src)); - - if (config.skip_children() && path != main_file) || should_ignore { - continue; - } should_emit_verbose(input_is_stdin, config, || println!("Formatting {}", path)); context.format_file(path, &module, is_macro_def)?; } diff --git a/src/test/configuration_snippet.rs b/src/test/configuration_snippet.rs index ef7dd0ddcd12..92949ab576a6 100644 --- a/src/test/configuration_snippet.rs +++ b/src/test/configuration_snippet.rs @@ -110,14 +110,7 @@ impl ConfigCodeBlock { assert!(self.code_block.is_some() && self.code_block_start.is_some()); // See if code block begins with #![rustfmt::skip]. - let fmt_skip = self - .code_block - .as_ref() - .unwrap() - .lines() - .nth(0) - .unwrap_or("") - == "#![rustfmt::skip]"; + let fmt_skip = self.fmt_skip(); if self.config_name.is_none() && !fmt_skip { write_message(&format!( @@ -138,6 +131,17 @@ impl ConfigCodeBlock { true } + /// True if the code block starts with #![rustfmt::skip] + fn fmt_skip(&self) -> bool { + self.code_block + .as_ref() + .unwrap() + .lines() + .nth(0) + .unwrap_or("") + == "#![rustfmt::skip]" + } + fn has_parsing_errors(&self, session: &Session<'_, T>) -> bool { if session.has_parsing_errors() { write_message(&format!( @@ -251,6 +255,7 @@ fn configuration_snippet_tests() { let blocks = get_code_blocks(); let failures = blocks .iter() + .filter(|block| !block.fmt_skip()) .map(ConfigCodeBlock::formatted_is_idempotent) .fold(0, |acc, r| acc + (!r as u32)); diff --git a/src/test/mod_resolver.rs b/src/test/mod_resolver.rs index ae4a0d0fccb1..ec9ed0f0b8d6 100644 --- a/src/test/mod_resolver.rs +++ b/src/test/mod_resolver.rs @@ -41,3 +41,12 @@ fn out_of_line_nested_inline_within_out_of_line() { ], ); } + +#[test] +fn skip_out_of_line_nested_inline_within_out_of_line() { + // See also https://github.com/rust-lang/rustfmt/issues/5065 + verify_mod_resolution( + "tests/mod-resolver/skip-files-issue-5065/main.rs", + &["tests/mod-resolver/skip-files-issue-5065/one.rs"], + ); +} diff --git a/src/visitor.rs b/src/visitor.rs index e4a7be742abc..ba446200232b 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -948,12 +948,13 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { pub(crate) fn format_separate_mod(&mut self, m: &Module<'_>, end_pos: BytePos) { self.block_indent = Indent::empty(); - if self.visit_attrs(m.attrs(), ast::AttrStyle::Inner) { - self.push_skipped_with_span(m.attrs(), m.span, m.span); - } else { - self.walk_mod_items(&m.items); - self.format_missing_with_indent(end_pos); - } + let skipped = self.visit_attrs(m.attrs(), ast::AttrStyle::Inner); + assert!( + !skipped, + "Skipping module must be handled before reaching this line." + ); + self.walk_mod_items(&m.items); + self.format_missing_with_indent(end_pos); } pub(crate) fn skip_empty_lines(&mut self, end_pos: BytePos) { diff --git a/tests/mod-resolver/skip-files-issue-5065/foo.rs b/tests/mod-resolver/skip-files-issue-5065/foo.rs new file mode 100644 index 000000000000..74889acf0c38 --- /dev/null +++ b/tests/mod-resolver/skip-files-issue-5065/foo.rs @@ -0,0 +1,5 @@ +#![rustfmt::skip] + +mod bar { + + mod baz;} \ No newline at end of file diff --git a/tests/mod-resolver/skip-files-issue-5065/foo/bar/baz.rs b/tests/mod-resolver/skip-files-issue-5065/foo/bar/baz.rs new file mode 100644 index 000000000000..3519b0ee59c8 --- /dev/null +++ b/tests/mod-resolver/skip-files-issue-5065/foo/bar/baz.rs @@ -0,0 +1 @@ +fn baz() { } \ No newline at end of file diff --git a/tests/mod-resolver/skip-files-issue-5065/main.rs b/tests/mod-resolver/skip-files-issue-5065/main.rs new file mode 100644 index 000000000000..3122e4f220f6 --- /dev/null +++ b/tests/mod-resolver/skip-files-issue-5065/main.rs @@ -0,0 +1,9 @@ +#![rustfmt::skip] + +mod foo; +mod one; + +fn main() {println!("Hello, world!"); +} + +// trailing commet diff --git a/tests/mod-resolver/skip-files-issue-5065/one.rs b/tests/mod-resolver/skip-files-issue-5065/one.rs new file mode 100644 index 000000000000..e7eb2c2d64dd --- /dev/null +++ b/tests/mod-resolver/skip-files-issue-5065/one.rs @@ -0,0 +1 @@ +struct One { value: String } \ No newline at end of file diff --git a/tests/target/skip/preserve_trailing_comment.rs b/tests/target/skip/preserve_trailing_comment.rs new file mode 100644 index 000000000000..f85de33257cc --- /dev/null +++ b/tests/target/skip/preserve_trailing_comment.rs @@ -0,0 +1,7 @@ +#![rustfmt::skip] + +fn main() { + println!("Hello, world!"); +} + +// Trailing Comment From 740fb57f5ddacddd9bcf074df701bcc50b46e69f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sun, 12 Dec 2021 20:15:08 +0100 Subject: [PATCH 110/401] clippy fixes --- src/bin/main.rs | 2 +- src/expr.rs | 4 +--- src/formatting.rs | 2 +- src/items.rs | 10 +++++----- src/lists.rs | 4 +--- src/visitor.rs | 12 ++++++------ 6 files changed, 15 insertions(+), 19 deletions(-) diff --git a/src/bin/main.rs b/src/bin/main.rs index 9d2e97c9479f..4d845547cdfe 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -26,7 +26,7 @@ fn main() { let exit_code = match execute(&opts) { Ok(code) => code, Err(e) => { - eprintln!("{}", e.to_string()); + eprintln!("{}", e); 1 } }; diff --git a/src/expr.rs b/src/expr.rs index 5fd86c1a4ead..edd004ac63f0 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -2003,9 +2003,7 @@ fn choose_rhs( has_rhs_comment: bool, ) -> Option { match orig_rhs { - Some(ref new_str) if new_str.is_empty() => { - return Some(String::new()); - } + Some(ref new_str) if new_str.is_empty() => Some(String::new()), Some(ref new_str) if !new_str.contains('\n') && unicode_str_width(new_str) <= shape.width => { diff --git a/src/formatting.rs b/src/formatting.rs index 1972b5a87a5e..b39480a0ef90 100644 --- a/src/formatting.rs +++ b/src/formatting.rs @@ -76,7 +76,7 @@ fn should_skip_module( return true; } - if !input_is_stdin && context.ignore_file(&path) { + if !input_is_stdin && context.ignore_file(path) { return true; } diff --git a/src/items.rs b/src/items.rs index f36bdba26e98..a77b7c108760 100644 --- a/src/items.rs +++ b/src/items.rs @@ -1535,7 +1535,7 @@ pub(crate) fn rewrite_type_alias<'a, 'b>( // https://rustc-dev-guide.rust-lang.org/opaque-types-type-alias-impl-trait.html // https://github.com/rust-dev-tools/fmt-rfcs/blob/master/guide/items.md#type-aliases match (visitor_kind, &op_ty) { - (Item(_) | AssocTraitItem(_) | ForeignItem(_), Some(ref op_bounds)) => { + (Item(_) | AssocTraitItem(_) | ForeignItem(_), Some(op_bounds)) => { let op = OpaqueType { bounds: op_bounds }; rewrite_ty(rw_info, Some(bounds), Some(&op), vis) } @@ -1543,7 +1543,7 @@ pub(crate) fn rewrite_type_alias<'a, 'b>( rewrite_ty(rw_info, Some(bounds), ty_opt, vis) } (AssocImplItem(_), _) => { - let result = if let Some(ref op_bounds) = op_ty { + let result = if let Some(op_bounds) = op_ty { let op = OpaqueType { bounds: op_bounds }; rewrite_ty(rw_info, Some(bounds), Some(&op), &DEFAULT_VISIBILITY) } else { @@ -3124,7 +3124,7 @@ impl Rewrite for ast::ForeignItem { let inner_attrs = inner_attributes(&self.attrs); let fn_ctxt = visit::FnCtxt::Foreign; visitor.visit_fn( - visit::FnKind::Fn(fn_ctxt, self.ident, &sig, &self.vis, Some(body)), + visit::FnKind::Fn(fn_ctxt, self.ident, sig, &self.vis, Some(body)), generics, &sig.decl, self.span, @@ -3137,7 +3137,7 @@ impl Rewrite for ast::ForeignItem { context, shape.indent, self.ident, - &FnSig::from_method_sig(&sig, generics, &self.vis), + &FnSig::from_method_sig(sig, generics, &self.vis), span, FnBraceStyle::None, ) @@ -3166,7 +3166,7 @@ impl Rewrite for ast::ForeignItem { .map(|s| s + ";") } ast::ForeignItemKind::TyAlias(ref ty_alias) => { - let (kind, span) = (&ItemVisitorKind::ForeignItem(&self), self.span); + let (kind, span) = (&ItemVisitorKind::ForeignItem(self), self.span); rewrite_type_alias(ty_alias, context, shape.indent, kind, span) } ast::ForeignItemKind::MacCall(ref mac) => { diff --git a/src/lists.rs b/src/lists.rs index 3515dd172510..7aa0315f18c2 100644 --- a/src/lists.rs +++ b/src/lists.rs @@ -448,10 +448,8 @@ where true } else if starts_with_newline(comment) { false - } else if comment.trim().contains('\n') || comment.trim().len() > width { - true } else { - false + comment.trim().contains('\n') || comment.trim().len() > width }; rewrite_comment( diff --git a/src/visitor.rs b/src/visitor.rs index ba446200232b..1896a4744fe4 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -552,7 +552,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { _ => visit::FnCtxt::Foreign, }; self.visit_fn( - visit::FnKind::Fn(fn_ctxt, item.ident, &sig, &item.vis, Some(body)), + visit::FnKind::Fn(fn_ctxt, item.ident, sig, &item.vis, Some(body)), generics, &sig.decl, item.span, @@ -562,14 +562,14 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { } else { let indent = self.block_indent; let rewrite = self.rewrite_required_fn( - indent, item.ident, &sig, &item.vis, generics, item.span, + indent, item.ident, sig, &item.vis, generics, item.span, ); self.push_rewrite(item.span, rewrite); } } ast::ItemKind::TyAlias(ref ty_alias) => { use ItemVisitorKind::Item; - self.visit_ty_alias_kind(ty_alias, &Item(&item), item.span); + self.visit_ty_alias_kind(ty_alias, &Item(item), item.span); } ast::ItemKind::GlobalAsm(..) => { let snippet = Some(self.snippet(item.span).to_owned()); @@ -619,17 +619,17 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { skip_out_of_file_lines_range_visitor!(self, ai.span); if self.visit_attrs(&ai.attrs, ast::AttrStyle::Outer) { - self.push_skipped_with_span(&ai.attrs.as_slice(), skip_span, skip_span); + self.push_skipped_with_span(ai.attrs.as_slice(), skip_span, skip_span); return; } // TODO(calebcartwright): consider enabling box_patterns feature gate match (&ai.kind, visitor_kind) { (ast::AssocItemKind::Const(..), AssocTraitItem(_)) => { - self.visit_static(&StaticParts::from_trait_item(&ai)) + self.visit_static(&StaticParts::from_trait_item(ai)) } (ast::AssocItemKind::Const(..), AssocImplItem(_)) => { - self.visit_static(&StaticParts::from_impl_item(&ai)) + self.visit_static(&StaticParts::from_impl_item(ai)) } (ast::AssocItemKind::Fn(ref fn_kind), _) => { let ast::Fn { From 57ac92bf1658a576fdc066b82a37aa3a7de2c96b Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Wed, 17 Nov 2021 20:46:48 -0500 Subject: [PATCH 111/401] Prevent duplicate comma when formatting struct pattern with ".." Fixes 5066 When a struct pattern that contained a ".." was formatted, it was assumed that a trailing comma should always be added if the struct fields weren't formatted vertically. Now, a trailing comma is only added if not already included in the reformatted struct fields. --- src/patterns.rs | 7 ++++--- ..._struct_trailing_comma_always_struct_lit_width_0.rs | 10 ++++++++++ ...e_struct_trailing_comma_never_struct_lit_width_0.rs | 10 ++++++++++ .../multi_line_struct_with_trailing_comma_always.rs | 10 ++++++++++ .../multi_line_struct_with_trailing_comma_never.rs | 10 ++++++++++ tests/target/issue-5066/with_trailing_comma_always.rs | 5 +++++ tests/target/issue-5066/with_trailing_comma_never.rs | 5 +++++ 7 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 tests/target/issue-5066/multi_line_struct_trailing_comma_always_struct_lit_width_0.rs create mode 100644 tests/target/issue-5066/multi_line_struct_trailing_comma_never_struct_lit_width_0.rs create mode 100644 tests/target/issue-5066/multi_line_struct_with_trailing_comma_always.rs create mode 100644 tests/target/issue-5066/multi_line_struct_with_trailing_comma_never.rs create mode 100644 tests/target/issue-5066/with_trailing_comma_always.rs create mode 100644 tests/target/issue-5066/with_trailing_comma_never.rs diff --git a/src/patterns.rs b/src/patterns.rs index a80d63201f98..9b74b35f3141 100644 --- a/src/patterns.rs +++ b/src/patterns.rs @@ -318,10 +318,12 @@ fn rewrite_struct_pat( let mut fields_str = write_list(&item_vec, &fmt)?; let one_line_width = h_shape.map_or(0, |shape| shape.width); + let has_trailing_comma = fmt.needs_trailing_separator(); + if ellipsis { if fields_str.contains('\n') || fields_str.len() > one_line_width { // Add a missing trailing comma. - if context.config.trailing_comma() == SeparatorTactic::Never { + if !has_trailing_comma { fields_str.push(','); } fields_str.push('\n'); @@ -329,8 +331,7 @@ fn rewrite_struct_pat( } else { if !fields_str.is_empty() { // there are preceding struct fields being matched on - if tactic == DefinitiveListTactic::Vertical { - // if the tactic is Vertical, write_list already added a trailing , + if has_trailing_comma { fields_str.push(' '); } else { fields_str.push_str(", "); diff --git a/tests/target/issue-5066/multi_line_struct_trailing_comma_always_struct_lit_width_0.rs b/tests/target/issue-5066/multi_line_struct_trailing_comma_always_struct_lit_width_0.rs new file mode 100644 index 000000000000..c7122c676237 --- /dev/null +++ b/tests/target/issue-5066/multi_line_struct_trailing_comma_always_struct_lit_width_0.rs @@ -0,0 +1,10 @@ +// rustfmt-trailing_comma: Always +// rustfmt-struct_lit_single_line: false +// rustfmt-struct_lit_width: 0 + +fn main() { + let Foo { + a, + .. + } = b; +} diff --git a/tests/target/issue-5066/multi_line_struct_trailing_comma_never_struct_lit_width_0.rs b/tests/target/issue-5066/multi_line_struct_trailing_comma_never_struct_lit_width_0.rs new file mode 100644 index 000000000000..68e89c4179f7 --- /dev/null +++ b/tests/target/issue-5066/multi_line_struct_trailing_comma_never_struct_lit_width_0.rs @@ -0,0 +1,10 @@ +// rustfmt-trailing_comma: Never +// rustfmt-struct_lit_single_line: false +// rustfmt-struct_lit_width: 0 + +fn main() { + let Foo { + a, + .. + } = b; +} diff --git a/tests/target/issue-5066/multi_line_struct_with_trailing_comma_always.rs b/tests/target/issue-5066/multi_line_struct_with_trailing_comma_always.rs new file mode 100644 index 000000000000..3368f0703868 --- /dev/null +++ b/tests/target/issue-5066/multi_line_struct_with_trailing_comma_always.rs @@ -0,0 +1,10 @@ +// rustfmt-trailing_comma: Always +// rustfmt-struct_lit_single_line: false + +// There is an issue with how this is formatted. +// formatting should look like ./multi_line_struct_trailing_comma_always_struct_lit_width_0.rs +fn main() { + let Foo { + a, .. + } = b; +} diff --git a/tests/target/issue-5066/multi_line_struct_with_trailing_comma_never.rs b/tests/target/issue-5066/multi_line_struct_with_trailing_comma_never.rs new file mode 100644 index 000000000000..cf63c4c983c4 --- /dev/null +++ b/tests/target/issue-5066/multi_line_struct_with_trailing_comma_never.rs @@ -0,0 +1,10 @@ +// rustfmt-trailing_comma: Never +// rustfmt-struct_lit_single_line: false + +// There is an issue with how this is formatted. +// formatting should look like ./multi_line_struct_trailing_comma_never_struct_lit_width_0.rs +fn main() { + let Foo { + a, .. + } = b; +} diff --git a/tests/target/issue-5066/with_trailing_comma_always.rs b/tests/target/issue-5066/with_trailing_comma_always.rs new file mode 100644 index 000000000000..e20bcec93169 --- /dev/null +++ b/tests/target/issue-5066/with_trailing_comma_always.rs @@ -0,0 +1,5 @@ +// rustfmt-trailing_comma: Always + +fn main() { + let Foo { a, .. } = b; +} diff --git a/tests/target/issue-5066/with_trailing_comma_never.rs b/tests/target/issue-5066/with_trailing_comma_never.rs new file mode 100644 index 000000000000..8b95bb137bca --- /dev/null +++ b/tests/target/issue-5066/with_trailing_comma_never.rs @@ -0,0 +1,5 @@ +// rustfmt-trailing_comma: Never + +fn main() { + let Foo { a, .. } = b; +} From b214938ff3fcd82f568d9eae0b0bda5d528a15ae Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Mon, 20 Dec 2021 11:58:27 -0600 Subject: [PATCH 112/401] chore: bump toolchain --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index 1d2cad667511..c97b5ec6609b 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2021-11-08" +channel = "nightly-2021-12-20" components = ["rustc-dev"] From 40b73d8feec5938a4b6d542840641eceb563930c Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Mon, 20 Dec 2021 17:55:48 -0600 Subject: [PATCH 113/401] refactor: rename syntux mod to parse --- src/formatting.rs | 4 ++-- src/lib.rs | 4 ++-- src/modules.rs | 4 ++-- src/modules/visitor.rs | 4 ++-- src/parse/mod.rs | 2 ++ src/{syntux => parse}/parser.rs | 2 +- src/{syntux => parse}/session.rs | 2 +- src/rewrite.rs | 2 +- src/source_file.rs | 2 +- src/visitor.rs | 2 +- 10 files changed, 15 insertions(+), 13 deletions(-) create mode 100644 src/parse/mod.rs rename src/{syntux => parse}/parser.rs (99%) rename src/{syntux => parse}/session.rs (99%) diff --git a/src/formatting.rs b/src/formatting.rs index b39480a0ef90..67cf1232f66a 100644 --- a/src/formatting.rs +++ b/src/formatting.rs @@ -14,8 +14,8 @@ use crate::config::{Config, FileName, Verbosity}; use crate::formatting::generated::is_generated_file; use crate::issues::BadIssueSeeker; use crate::modules::Module; -use crate::syntux::parser::{DirectoryOwnership, Parser, ParserError}; -use crate::syntux::session::ParseSess; +use crate::parse::parser::{DirectoryOwnership, Parser, ParserError}; +use crate::parse::session::ParseSess; use crate::utils::{contains_skip, count_newlines}; use crate::visitor::FmtVisitor; use crate::{modules, source_file, ErrorKind, FormatReport, Input, Session}; diff --git a/src/lib.rs b/src/lib.rs index 792a1080f0e9..f59ebad97ce8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,8 +40,8 @@ use crate::emitter::Emitter; use crate::formatting::{FormatErrorMap, FormattingError, ReportedErrors, SourceFile}; use crate::issues::Issue; use crate::modules::ModuleResolutionError; +use crate::parse::parser::DirectoryOwnership; use crate::shape::Indent; -use crate::syntux::parser::DirectoryOwnership; use crate::utils::indent_next_line; pub use crate::config::{ @@ -77,6 +77,7 @@ mod missed_spans; pub(crate) mod modules; mod overflow; mod pairs; +mod parse; mod patterns; mod release_channel; mod reorder; @@ -89,7 +90,6 @@ pub(crate) mod source_map; mod spanned; mod stmt; mod string; -mod syntux; #[cfg(test)] mod test; mod types; diff --git a/src/modules.rs b/src/modules.rs index 9d438a80d942..9c964b274e08 100644 --- a/src/modules.rs +++ b/src/modules.rs @@ -12,10 +12,10 @@ use thiserror::Error; use crate::attr::MetaVisitor; use crate::config::FileName; use crate::items::is_mod_decl; -use crate::syntux::parser::{ +use crate::parse::parser::{ Directory, DirectoryOwnership, ModError, ModulePathSuccess, Parser, ParserError, }; -use crate::syntux::session::ParseSess; +use crate::parse::session::ParseSess; use crate::utils::{contains_skip, mk_sp}; mod visitor; diff --git a/src/modules/visitor.rs b/src/modules/visitor.rs index d5acf3f1cbcb..7486a4c4ee15 100644 --- a/src/modules/visitor.rs +++ b/src/modules/visitor.rs @@ -3,8 +3,8 @@ use rustc_ast::visit::Visitor; use rustc_span::Symbol; use crate::attr::MetaVisitor; -use crate::syntux::parser::Parser; -use crate::syntux::session::ParseSess; +use crate::parse::parser::Parser; +use crate::parse::session::ParseSess; pub(crate) struct ModItem { pub(crate) item: ast::Item, diff --git a/src/parse/mod.rs b/src/parse/mod.rs new file mode 100644 index 000000000000..bb7d9ca87d40 --- /dev/null +++ b/src/parse/mod.rs @@ -0,0 +1,2 @@ +pub(crate) mod parser; +pub(crate) mod session; diff --git a/src/syntux/parser.rs b/src/parse/parser.rs similarity index 99% rename from src/syntux/parser.rs rename to src/parse/parser.rs index 23d065c9cc95..024b38cebb62 100644 --- a/src/syntux/parser.rs +++ b/src/parse/parser.rs @@ -11,7 +11,7 @@ use rustc_parse::{ use rustc_span::{sym, symbol::kw, Span}; use crate::attr::first_attr_value_str_by_name; -use crate::syntux::session::ParseSess; +use crate::parse::session::ParseSess; use crate::Input; pub(crate) type DirectoryOwnership = rustc_expand::module::DirOwnership; diff --git a/src/syntux/session.rs b/src/parse/session.rs similarity index 99% rename from src/syntux/session.rs rename to src/parse/session.rs index dd7c7352686e..624fed0d2de2 100644 --- a/src/syntux/session.rs +++ b/src/parse/session.rs @@ -222,7 +222,7 @@ impl ParseSess { } } -// Methods that should be restricted within the syntux module. +// Methods that should be restricted within the parse module. impl ParseSess { pub(super) fn emit_diagnostics(&self, diagnostics: Vec) { for diagnostic in diagnostics { diff --git a/src/rewrite.rs b/src/rewrite.rs index c8abe70141b5..4a3bd129d16f 100644 --- a/src/rewrite.rs +++ b/src/rewrite.rs @@ -7,9 +7,9 @@ use rustc_ast::ptr; use rustc_span::Span; use crate::config::{Config, IndentStyle}; +use crate::parse::session::ParseSess; use crate::shape::Shape; use crate::skip::SkipContext; -use crate::syntux::session::ParseSess; use crate::visitor::SnippetProvider; use crate::FormatReport; diff --git a/src/source_file.rs b/src/source_file.rs index 853336004d8b..56d4ab400383 100644 --- a/src/source_file.rs +++ b/src/source_file.rs @@ -4,7 +4,7 @@ use std::path::Path; use crate::config::FileName; use crate::emitter::{self, Emitter}; -use crate::syntux::session::ParseSess; +use crate::parse::session::ParseSess; use crate::NewlineStyle; #[cfg(test)] diff --git a/src/visitor.rs b/src/visitor.rs index 1896a4744fe4..0177689958aa 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -16,13 +16,13 @@ use crate::items::{ }; use crate::macros::{macro_style, rewrite_macro, rewrite_macro_def, MacroPosition}; use crate::modules::Module; +use crate::parse::session::ParseSess; use crate::rewrite::{Rewrite, RewriteContext}; use crate::shape::{Indent, Shape}; use crate::skip::{is_skip_attr, SkipContext}; use crate::source_map::{LineRangeUtils, SpanUtils}; use crate::spanned::Spanned; use crate::stmt::Stmt; -use crate::syntux::session::ParseSess; use crate::utils::{ self, contains_skip, count_newlines, depr_skip_annotation, format_unsafety, inner_attributes, last_line_width, mk_sp, ptr_vec_to_ref_vec, rewrite_ident, starts_with_newline, stmt_expr, From 9ce5470a8ce0abdfdb5b19c8f9a2b0773f0b6432 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Mon, 20 Dec 2021 18:59:01 -0600 Subject: [PATCH 114/401] refactor: move macro arg parsing to parse mod --- src/macros.rs | 216 ++++------------------------------------ src/parse/macros/mod.rs | 216 ++++++++++++++++++++++++++++++++++++++++ src/parse/mod.rs | 1 + 3 files changed, 234 insertions(+), 199 deletions(-) create mode 100644 src/parse/macros/mod.rs diff --git a/src/macros.rs b/src/macros.rs index a52568be9eac..26a68dfdcf26 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -16,8 +16,6 @@ use rustc_ast::token::{BinOpToken, DelimToken, Token, TokenKind}; use rustc_ast::tokenstream::{Cursor, Spacing, TokenStream, TokenTree}; use rustc_ast::{ast, ptr}; use rustc_ast_pretty::pprust; -use rustc_parse::parser::{ForceCollect, Parser}; -use rustc_parse::{stream_to_parser, MACRO_ARGUMENTS}; use rustc_span::{ symbol::{self, kw}, BytePos, Span, Symbol, DUMMY_SP, @@ -30,6 +28,7 @@ use crate::config::lists::*; use crate::expr::{rewrite_array, rewrite_assign_rhs, RhsAssignKind}; use crate::lists::{itemize_list, write_list, ListFormatting}; use crate::overflow; +use crate::parse::macros::{build_parser, parse_macro_args, ParsedMacroArgs}; use crate::rewrite::{Rewrite, RewriteContext}; use crate::shape::{Indent, Shape}; use crate::source_map::SpanUtils; @@ -60,7 +59,7 @@ pub(crate) enum MacroArg { } impl MacroArg { - fn is_item(&self) -> bool { + pub(crate) fn is_item(&self) -> bool { match self { MacroArg::Item(..) => true, _ => false, @@ -90,61 +89,6 @@ impl Rewrite for MacroArg { } } -fn build_parser<'a>(context: &RewriteContext<'a>, cursor: Cursor) -> Parser<'a> { - stream_to_parser( - context.parse_sess.inner(), - cursor.collect(), - MACRO_ARGUMENTS, - ) -} - -fn parse_macro_arg<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option { - macro_rules! parse_macro_arg { - ($macro_arg:ident, $parser:expr, $f:expr) => { - let mut cloned_parser = (*parser).clone(); - match $parser(&mut cloned_parser) { - Ok(x) => { - if parser.sess.span_diagnostic.has_errors() { - parser.sess.span_diagnostic.reset_err_count(); - } else { - // Parsing succeeded. - *parser = cloned_parser; - return Some(MacroArg::$macro_arg($f(x)?)); - } - } - Err(mut e) => { - e.cancel(); - parser.sess.span_diagnostic.reset_err_count(); - } - } - }; - } - - parse_macro_arg!( - Expr, - |parser: &mut rustc_parse::parser::Parser<'b>| parser.parse_expr(), - |x: ptr::P| Some(x) - ); - parse_macro_arg!( - Ty, - |parser: &mut rustc_parse::parser::Parser<'b>| parser.parse_ty(), - |x: ptr::P| Some(x) - ); - parse_macro_arg!( - Pat, - |parser: &mut rustc_parse::parser::Parser<'b>| parser.parse_pat_no_top_alt(None), - |x: ptr::P| Some(x) - ); - // `parse_item` returns `Option>`. - parse_macro_arg!( - Item, - |parser: &mut rustc_parse::parser::Parser<'b>| parser.parse_item(ForceCollect::No), - |x: Option>| x - ); - - None -} - /// Rewrite macro name without using pretty-printer if possible. fn rewrite_macro_name( context: &RewriteContext<'_>, @@ -232,25 +176,6 @@ pub(crate) fn rewrite_macro( } } -fn check_keyword<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option { - for &keyword in RUST_KW.iter() { - if parser.token.is_keyword(keyword) - && parser.look_ahead(1, |t| { - t.kind == TokenKind::Eof - || t.kind == TokenKind::Comma - || t.kind == TokenKind::CloseDelim(DelimToken::NoDelim) - }) - { - parser.bump(); - return Some(MacroArg::Keyword( - symbol::Ident::with_dummy_span(keyword), - parser.prev_token.span, - )); - } - } - None -} - fn rewrite_macro_inner( mac: &ast::MacCall, extra_ident: Option, @@ -269,8 +194,9 @@ fn rewrite_macro_inner( let original_style = macro_style(mac, context); let macro_name = rewrite_macro_name(context, &mac.path, extra_ident); + let is_forced_bracket = FORCED_BRACKET_MACROS.contains(&¯o_name[..]); - let style = if FORCED_BRACKET_MACROS.contains(&¯o_name[..]) && !is_nested_macro { + let style = if is_forced_bracket && !is_nested_macro { DelimToken::Bracket } else { original_style @@ -294,67 +220,21 @@ fn rewrite_macro_inner( } // Format well-known macros which cannot be parsed as a valid AST. if macro_name == "lazy_static!" && !has_comment { - if let success @ Some(..) = format_lazy_static(context, shape, &ts) { + if let success @ Some(..) = format_lazy_static(context, shape, ts.trees().collect()) { return success; } } - let mut parser = build_parser(context, ts.trees()); - let mut arg_vec = Vec::new(); - let mut vec_with_semi = false; - let mut trailing_comma = false; - - if DelimToken::Brace != style { - loop { - if let Some(arg) = check_keyword(&mut parser) { - arg_vec.push(arg); - } else if let Some(arg) = parse_macro_arg(&mut parser) { - arg_vec.push(arg); - } else { - return return_macro_parse_failure_fallback(context, shape.indent, mac.span()); - } - - match parser.token.kind { - TokenKind::Eof => break, - TokenKind::Comma => (), - TokenKind::Semi => { - // Try to parse `vec![expr; expr]` - if FORCED_BRACKET_MACROS.contains(&¯o_name[..]) { - parser.bump(); - if parser.token.kind != TokenKind::Eof { - match parse_macro_arg(&mut parser) { - Some(arg) => { - arg_vec.push(arg); - parser.bump(); - if parser.token.kind == TokenKind::Eof && arg_vec.len() == 2 { - vec_with_semi = true; - break; - } - } - None => { - return return_macro_parse_failure_fallback( - context, - shape.indent, - mac.span(), - ); - } - } - } - } - return return_macro_parse_failure_fallback(context, shape.indent, mac.span()); - } - _ if arg_vec.last().map_or(false, MacroArg::is_item) => continue, - _ => return return_macro_parse_failure_fallback(context, shape.indent, mac.span()), - } - - parser.bump(); - - if parser.token.kind == TokenKind::Eof { - trailing_comma = true; - break; - } + let ParsedMacroArgs { + args: arg_vec, + vec_with_semi, + trailing_comma, + } = match parse_macro_args(context, ts, style, is_forced_bracket) { + Some(args) => args, + None => { + return return_macro_parse_failure_fallback(context, shape.indent, mac.span()); } - } + }; if !arg_vec.is_empty() && arg_vec.iter().all(MacroArg::is_item) { return rewrite_macro_with_items( @@ -1179,7 +1059,7 @@ pub(crate) fn convert_try_mac( let path = &pprust::path_to_string(&mac.path); if path == "try" || path == "r#try" { let ts = mac.args.inner_tokens(); - let mut parser = build_parser(context, ts.trees()); + let mut parser = build_parser(context, ts); Some(ast::Expr { id: ast::NodeId::root(), // dummy value @@ -1414,10 +1294,10 @@ impl MacroBranch { fn format_lazy_static( context: &RewriteContext<'_>, shape: Shape, - ts: &TokenStream, + ts: TokenStream, ) -> Option { let mut result = String::with_capacity(1024); - let mut parser = build_parser(context, ts.trees()); + let mut parser = build_parser(context, ts); let nested_shape = shape .block_indent(context.config.tab_spaces()) .with_max_width(context.config); @@ -1528,65 +1408,3 @@ fn rewrite_macro_with_items( result.push_str(trailing_semicolon); Some(result) } - -const RUST_KW: [Symbol; 59] = [ - kw::PathRoot, - kw::DollarCrate, - kw::Underscore, - kw::As, - kw::Box, - kw::Break, - kw::Const, - kw::Continue, - kw::Crate, - kw::Else, - kw::Enum, - kw::Extern, - kw::False, - kw::Fn, - kw::For, - kw::If, - kw::Impl, - kw::In, - kw::Let, - kw::Loop, - kw::Match, - kw::Mod, - kw::Move, - kw::Mut, - kw::Pub, - kw::Ref, - kw::Return, - kw::SelfLower, - kw::SelfUpper, - kw::Static, - kw::Struct, - kw::Super, - kw::Trait, - kw::True, - kw::Type, - kw::Unsafe, - kw::Use, - kw::Where, - kw::While, - kw::Abstract, - kw::Become, - kw::Do, - kw::Final, - kw::Macro, - kw::Override, - kw::Priv, - kw::Typeof, - kw::Unsized, - kw::Virtual, - kw::Yield, - kw::Dyn, - kw::Async, - kw::Try, - kw::UnderscoreLifetime, - kw::StaticLifetime, - kw::Auto, - kw::Catch, - kw::Default, - kw::Union, -]; diff --git a/src/parse/macros/mod.rs b/src/parse/macros/mod.rs new file mode 100644 index 000000000000..e7844c9a4dce --- /dev/null +++ b/src/parse/macros/mod.rs @@ -0,0 +1,216 @@ +use rustc_ast::token::{BinOpToken, DelimToken, Token, TokenKind}; +use rustc_ast::tokenstream::{Cursor, Spacing, TokenStream, TokenTree}; +use rustc_ast::{ast, ptr}; +use rustc_parse::parser::{ForceCollect, Parser}; +use rustc_parse::{stream_to_parser, MACRO_ARGUMENTS}; +use rustc_span::{ + symbol::{self, kw}, + BytePos, Span, Symbol, DUMMY_SP, +}; + +use crate::macros::MacroArg; +use crate::rewrite::{Rewrite, RewriteContext}; + +pub(crate) fn build_parser<'a>(context: &RewriteContext<'a>, tokens: TokenStream) -> Parser<'a> { + stream_to_parser(context.parse_sess.inner(), tokens, MACRO_ARGUMENTS) +} + +fn parse_macro_arg<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option { + macro_rules! parse_macro_arg { + ($macro_arg:ident, $parser:expr, $f:expr) => { + let mut cloned_parser = (*parser).clone(); + match $parser(&mut cloned_parser) { + Ok(x) => { + if parser.sess.span_diagnostic.has_errors() { + parser.sess.span_diagnostic.reset_err_count(); + } else { + // Parsing succeeded. + *parser = cloned_parser; + return Some(MacroArg::$macro_arg($f(x)?)); + } + } + Err(mut e) => { + e.cancel(); + parser.sess.span_diagnostic.reset_err_count(); + } + } + }; + } + + parse_macro_arg!( + Expr, + |parser: &mut rustc_parse::parser::Parser<'b>| parser.parse_expr(), + |x: ptr::P| Some(x) + ); + parse_macro_arg!( + Ty, + |parser: &mut rustc_parse::parser::Parser<'b>| parser.parse_ty(), + |x: ptr::P| Some(x) + ); + parse_macro_arg!( + Pat, + |parser: &mut rustc_parse::parser::Parser<'b>| parser.parse_pat_no_top_alt(None), + |x: ptr::P| Some(x) + ); + // `parse_item` returns `Option>`. + parse_macro_arg!( + Item, + |parser: &mut rustc_parse::parser::Parser<'b>| parser.parse_item(ForceCollect::No), + |x: Option>| x + ); + + None +} + +pub(crate) struct ParsedMacroArgs { + pub(crate) vec_with_semi: bool, + pub(crate) trailing_comma: bool, + pub(crate) args: Vec, +} + +fn check_keyword<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option { + for &keyword in RUST_KW.iter() { + if parser.token.is_keyword(keyword) + && parser.look_ahead(1, |t| { + t.kind == TokenKind::Eof + || t.kind == TokenKind::Comma + || t.kind == TokenKind::CloseDelim(DelimToken::NoDelim) + }) + { + parser.bump(); + return Some(MacroArg::Keyword( + symbol::Ident::with_dummy_span(keyword), + parser.prev_token.span, + )); + } + } + None +} + +pub(crate) fn parse_macro_args( + context: &RewriteContext<'_>, + tokens: TokenStream, + style: DelimToken, + forced_bracket: bool, +) -> Option { + let mut parser = build_parser(context, tokens); + let mut args = Vec::new(); + let mut vec_with_semi = false; + let mut trailing_comma = false; + + if DelimToken::Brace != style { + loop { + if let Some(arg) = check_keyword(&mut parser) { + args.push(arg); + } else if let Some(arg) = parse_macro_arg(&mut parser) { + args.push(arg); + } else { + return None; + } + + match parser.token.kind { + TokenKind::Eof => break, + TokenKind::Comma => (), + TokenKind::Semi => { + // Try to parse `vec![expr; expr]` + if forced_bracket { + parser.bump(); + if parser.token.kind != TokenKind::Eof { + match parse_macro_arg(&mut parser) { + Some(arg) => { + args.push(arg); + parser.bump(); + if parser.token.kind == TokenKind::Eof && args.len() == 2 { + vec_with_semi = true; + break; + } + } + None => { + return None; + } + } + } + } + return None; + } + _ if args.last().map_or(false, MacroArg::is_item) => continue, + _ => return None, + } + + parser.bump(); + + if parser.token.kind == TokenKind::Eof { + trailing_comma = true; + break; + } + } + } + + Some(ParsedMacroArgs { + vec_with_semi, + trailing_comma, + args, + }) +} + +const RUST_KW: [Symbol; 59] = [ + kw::PathRoot, + kw::DollarCrate, + kw::Underscore, + kw::As, + kw::Box, + kw::Break, + kw::Const, + kw::Continue, + kw::Crate, + kw::Else, + kw::Enum, + kw::Extern, + kw::False, + kw::Fn, + kw::For, + kw::If, + kw::Impl, + kw::In, + kw::Let, + kw::Loop, + kw::Match, + kw::Mod, + kw::Move, + kw::Mut, + kw::Pub, + kw::Ref, + kw::Return, + kw::SelfLower, + kw::SelfUpper, + kw::Static, + kw::Struct, + kw::Super, + kw::Trait, + kw::True, + kw::Type, + kw::Unsafe, + kw::Use, + kw::Where, + kw::While, + kw::Abstract, + kw::Become, + kw::Do, + kw::Final, + kw::Macro, + kw::Override, + kw::Priv, + kw::Typeof, + kw::Unsized, + kw::Virtual, + kw::Yield, + kw::Dyn, + kw::Async, + kw::Try, + kw::UnderscoreLifetime, + kw::StaticLifetime, + kw::Auto, + kw::Catch, + kw::Default, + kw::Union, +]; diff --git a/src/parse/mod.rs b/src/parse/mod.rs index bb7d9ca87d40..5e88826ea8cc 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -1,2 +1,3 @@ +pub(crate) mod macros; pub(crate) mod parser; pub(crate) mod session; From c8cf454173ec332acf3860863c7d2f088876e40b Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Mon, 20 Dec 2021 19:38:00 -0600 Subject: [PATCH 115/401] refactor: move lazy_static parsing to parse mod --- src/macros.rs | 43 ++++------------------------ src/parse/macros/lazy_static.rs | 50 +++++++++++++++++++++++++++++++++ src/parse/macros/mod.rs | 2 ++ 3 files changed, 58 insertions(+), 37 deletions(-) create mode 100644 src/parse/macros/lazy_static.rs diff --git a/src/macros.rs b/src/macros.rs index 26a68dfdcf26..b14b67f2a0ae 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -28,6 +28,7 @@ use crate::config::lists::*; use crate::expr::{rewrite_array, rewrite_assign_rhs, RhsAssignKind}; use crate::lists::{itemize_list, write_list, ListFormatting}; use crate::overflow; +use crate::parse::macros::lazy_static::parse_lazy_static; use crate::parse::macros::{build_parser, parse_macro_args, ParsedMacroArgs}; use crate::rewrite::{Rewrite, RewriteContext}; use crate::shape::{Indent, Shape}; @@ -1297,7 +1298,6 @@ fn format_lazy_static( ts: TokenStream, ) -> Option { let mut result = String::with_capacity(1024); - let mut parser = build_parser(context, ts); let nested_shape = shape .block_indent(context.config.tab_spaces()) .with_max_width(context.config); @@ -1305,42 +1305,11 @@ fn format_lazy_static( result.push_str("lazy_static! {"); result.push_str(&nested_shape.indent.to_string_with_newline(context.config)); - macro_rules! parse_or { - ($method:ident $(,)* $($arg:expr),* $(,)*) => { - match parser.$method($($arg,)*) { - Ok(val) => { - if parser.sess.span_diagnostic.has_errors() { - parser.sess.span_diagnostic.reset_err_count(); - return None; - } else { - val - } - } - Err(mut err) => { - err.cancel(); - parser.sess.span_diagnostic.reset_err_count(); - return None; - } - } - } - } - - while parser.token.kind != TokenKind::Eof { - // Parse a `lazy_static!` item. - let vis = crate::utils::format_visibility( - context, - &parse_or!(parse_visibility, rustc_parse::parser::FollowedByType::No), - ); - parser.eat_keyword(kw::Static); - parser.eat_keyword(kw::Ref); - let id = parse_or!(parse_ident); - parser.eat(&TokenKind::Colon); - let ty = parse_or!(parse_ty); - parser.eat(&TokenKind::Eq); - let expr = parse_or!(parse_expr); - parser.eat(&TokenKind::Semi); - + let parsed_elems = parse_lazy_static(context, ts)?; + let last = parsed_elems.len() - 1; + for (i, (vis, id, ty, expr)) in parsed_elems.iter().enumerate() { // Rewrite as a static item. + let vis = crate::utils::format_visibility(context, vis); let mut stmt = String::with_capacity(128); stmt.push_str(&format!( "{}static ref {}: {} =", @@ -1356,7 +1325,7 @@ fn format_lazy_static( nested_shape.sub_width(1)?, )?); result.push(';'); - if parser.token.kind != TokenKind::Eof { + if i != last { result.push_str(&nested_shape.indent.to_string_with_newline(context.config)); } } diff --git a/src/parse/macros/lazy_static.rs b/src/parse/macros/lazy_static.rs new file mode 100644 index 000000000000..9c8651aa3faf --- /dev/null +++ b/src/parse/macros/lazy_static.rs @@ -0,0 +1,50 @@ +use rustc_ast::ast; +use rustc_ast::ptr::P; +use rustc_ast::token::TokenKind; +use rustc_ast::tokenstream::TokenStream; +use rustc_span::symbol::{self, kw}; + +use crate::rewrite::RewriteContext; + +pub(crate) fn parse_lazy_static( + context: &RewriteContext<'_>, + ts: TokenStream, +) -> Option, P)>> { + let mut result = vec![]; + let mut parser = super::build_parser(context, ts); + macro_rules! parse_or { + ($method:ident $(,)* $($arg:expr),* $(,)*) => { + match parser.$method($($arg,)*) { + Ok(val) => { + if parser.sess.span_diagnostic.has_errors() { + parser.sess.span_diagnostic.reset_err_count(); + return None; + } else { + val + } + } + Err(mut err) => { + err.cancel(); + parser.sess.span_diagnostic.reset_err_count(); + return None; + } + } + } + } + + while parser.token.kind != TokenKind::Eof { + // Parse a `lazy_static!` item. + let vis = parse_or!(parse_visibility, rustc_parse::parser::FollowedByType::No); + parser.eat_keyword(kw::Static); + parser.eat_keyword(kw::Ref); + let id = parse_or!(parse_ident); + parser.eat(&TokenKind::Colon); + let ty = parse_or!(parse_ty); + parser.eat(&TokenKind::Eq); + let expr = parse_or!(parse_expr); + parser.eat(&TokenKind::Semi); + result.push((vis, id, ty, expr)); + } + + Some(result) +} diff --git a/src/parse/macros/mod.rs b/src/parse/macros/mod.rs index e7844c9a4dce..7115302a479e 100644 --- a/src/parse/macros/mod.rs +++ b/src/parse/macros/mod.rs @@ -11,6 +11,8 @@ use rustc_span::{ use crate::macros::MacroArg; use crate::rewrite::{Rewrite, RewriteContext}; +pub(crate) mod lazy_static; + pub(crate) fn build_parser<'a>(context: &RewriteContext<'a>, tokens: TokenStream) -> Parser<'a> { stream_to_parser(context.parse_sess.inner(), tokens, MACRO_ARGUMENTS) } From 62987562e2fbb6ea83fa20598b9e0c91bee536e3 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Mon, 20 Dec 2021 19:44:23 -0600 Subject: [PATCH 116/401] refactor: extract final rustc_parse touchpoint from macros.rs --- src/macros.rs | 5 ++--- src/parse/macros/mod.rs | 10 +++++++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index b14b67f2a0ae..f29552caf8d8 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -29,7 +29,7 @@ use crate::expr::{rewrite_array, rewrite_assign_rhs, RhsAssignKind}; use crate::lists::{itemize_list, write_list, ListFormatting}; use crate::overflow; use crate::parse::macros::lazy_static::parse_lazy_static; -use crate::parse::macros::{build_parser, parse_macro_args, ParsedMacroArgs}; +use crate::parse::macros::{parse_expr, parse_macro_args, ParsedMacroArgs}; use crate::rewrite::{Rewrite, RewriteContext}; use crate::shape::{Indent, Shape}; use crate::source_map::SpanUtils; @@ -1060,11 +1060,10 @@ pub(crate) fn convert_try_mac( let path = &pprust::path_to_string(&mac.path); if path == "try" || path == "r#try" { let ts = mac.args.inner_tokens(); - let mut parser = build_parser(context, ts); Some(ast::Expr { id: ast::NodeId::root(), // dummy value - kind: ast::ExprKind::Try(parser.parse_expr().ok()?), + kind: ast::ExprKind::Try(parse_expr(context, ts)?), span: mac.span(), // incorrect span, but shouldn't matter too much attrs: ast::AttrVec::new(), tokens: None, diff --git a/src/parse/macros/mod.rs b/src/parse/macros/mod.rs index 7115302a479e..414b72f29213 100644 --- a/src/parse/macros/mod.rs +++ b/src/parse/macros/mod.rs @@ -13,7 +13,7 @@ use crate::rewrite::{Rewrite, RewriteContext}; pub(crate) mod lazy_static; -pub(crate) fn build_parser<'a>(context: &RewriteContext<'a>, tokens: TokenStream) -> Parser<'a> { +fn build_parser<'a>(context: &RewriteContext<'a>, tokens: TokenStream) -> Parser<'a> { stream_to_parser(context.parse_sess.inner(), tokens, MACRO_ARGUMENTS) } @@ -155,6 +155,14 @@ pub(crate) fn parse_macro_args( }) } +pub(crate) fn parse_expr( + context: &RewriteContext<'_>, + tokens: TokenStream, +) -> Option> { + let mut parser = build_parser(context, tokens); + parser.parse_expr().ok() +} + const RUST_KW: [Symbol; 59] = [ kw::PathRoot, kw::DollarCrate, From 97c3e48834c1485168a59473385cb755d76c9287 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Mon, 20 Dec 2021 20:28:55 -0600 Subject: [PATCH 117/401] refactor: encapsulate cfg_if parsing within parse mod --- src/modules/visitor.rs | 4 +- src/parse/macros/cfg_if.rs | 89 ++++++++++++++++++++++++++++++++++++++ src/parse/macros/mod.rs | 8 +++- src/parse/parser.rs | 80 ---------------------------------- 4 files changed, 98 insertions(+), 83 deletions(-) create mode 100644 src/parse/macros/cfg_if.rs diff --git a/src/modules/visitor.rs b/src/modules/visitor.rs index 7486a4c4ee15..ea67977c17a2 100644 --- a/src/modules/visitor.rs +++ b/src/modules/visitor.rs @@ -3,7 +3,7 @@ use rustc_ast::visit::Visitor; use rustc_span::Symbol; use crate::attr::MetaVisitor; -use crate::parse::parser::Parser; +use crate::parse::macros::cfg_if::parse_cfg_if; use crate::parse::session::ParseSess; pub(crate) struct ModItem { @@ -62,7 +62,7 @@ impl<'a, 'ast: 'a> CfgIfVisitor<'a> { } }; - let items = Parser::parse_cfg_if(self.parse_sess, mac)?; + let items = parse_cfg_if(self.parse_sess, mac)?; self.mods .append(&mut items.into_iter().map(|item| ModItem { item }).collect()); diff --git a/src/parse/macros/cfg_if.rs b/src/parse/macros/cfg_if.rs new file mode 100644 index 000000000000..e10fbe64bcdb --- /dev/null +++ b/src/parse/macros/cfg_if.rs @@ -0,0 +1,89 @@ +use std::panic::{catch_unwind, AssertUnwindSafe}; + +use rustc_ast::ast; +use rustc_ast::token::{DelimToken, TokenKind}; +use rustc_parse::parser::ForceCollect; +use rustc_span::symbol::kw; + +use crate::parse::macros::build_stream_parser; +use crate::parse::session::ParseSess; + +pub(crate) fn parse_cfg_if<'a>( + sess: &'a ParseSess, + mac: &'a ast::MacCall, +) -> Result, &'static str> { + match catch_unwind(AssertUnwindSafe(|| parse_cfg_if_inner(sess, mac))) { + Ok(Ok(items)) => Ok(items), + Ok(err @ Err(_)) => err, + Err(..) => Err("failed to parse cfg_if!"), + } +} + +fn parse_cfg_if_inner<'a>( + sess: &'a ParseSess, + mac: &'a ast::MacCall, +) -> Result, &'static str> { + let ts = mac.args.inner_tokens(); + let mut parser = build_stream_parser(sess.inner(), ts); + + let mut items = vec![]; + let mut process_if_cfg = true; + + while parser.token.kind != TokenKind::Eof { + if process_if_cfg { + if !parser.eat_keyword(kw::If) { + return Err("Expected `if`"); + } + // Inner attributes are not actually syntactically permitted here, but we don't + // care about inner vs outer attributes in this position. Our purpose with this + // special case parsing of cfg_if macros is to ensure we can correctly resolve + // imported modules that may have a custom `path` defined. + // + // As such, we just need to advance the parser past the attribute and up to + // to the opening brace. + // See also https://github.com/rust-lang/rust/pull/79433 + parser + .parse_attribute(rustc_parse::parser::attr::InnerAttrPolicy::Permitted) + .map_err(|_| "Failed to parse attributes")?; + } + + if !parser.eat(&TokenKind::OpenDelim(DelimToken::Brace)) { + return Err("Expected an opening brace"); + } + + while parser.token != TokenKind::CloseDelim(DelimToken::Brace) + && parser.token.kind != TokenKind::Eof + { + let item = match parser.parse_item(ForceCollect::No) { + Ok(Some(item_ptr)) => item_ptr.into_inner(), + Ok(None) => continue, + Err(mut err) => { + err.cancel(); + parser.sess.span_diagnostic.reset_err_count(); + return Err( + "Expected item inside cfg_if block, but failed to parse it as an item", + ); + } + }; + if let ast::ItemKind::Mod(..) = item.kind { + items.push(item); + } + } + + if !parser.eat(&TokenKind::CloseDelim(DelimToken::Brace)) { + return Err("Expected a closing brace"); + } + + if parser.eat(&TokenKind::Eof) { + break; + } + + if !parser.eat_keyword(kw::Else) { + return Err("Expected `else`"); + } + + process_if_cfg = parser.token.is_keyword(kw::If); + } + + Ok(items) +} diff --git a/src/parse/macros/mod.rs b/src/parse/macros/mod.rs index 414b72f29213..fbd8c3f90953 100644 --- a/src/parse/macros/mod.rs +++ b/src/parse/macros/mod.rs @@ -3,6 +3,7 @@ use rustc_ast::tokenstream::{Cursor, Spacing, TokenStream, TokenTree}; use rustc_ast::{ast, ptr}; use rustc_parse::parser::{ForceCollect, Parser}; use rustc_parse::{stream_to_parser, MACRO_ARGUMENTS}; +use rustc_session::parse::ParseSess; use rustc_span::{ symbol::{self, kw}, BytePos, Span, Symbol, DUMMY_SP, @@ -11,10 +12,15 @@ use rustc_span::{ use crate::macros::MacroArg; use crate::rewrite::{Rewrite, RewriteContext}; +pub(crate) mod cfg_if; pub(crate) mod lazy_static; +fn build_stream_parser<'a>(sess: &'a ParseSess, tokens: TokenStream) -> Parser<'a> { + stream_to_parser(sess, tokens, MACRO_ARGUMENTS) +} + fn build_parser<'a>(context: &RewriteContext<'a>, tokens: TokenStream) -> Parser<'a> { - stream_to_parser(context.parse_sess.inner(), tokens, MACRO_ARGUMENTS) + build_stream_parser(context.parse_sess.inner(), tokens) } fn parse_macro_arg<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option { diff --git a/src/parse/parser.rs b/src/parse/parser.rs index 024b38cebb62..8efd2bf257b1 100644 --- a/src/parse/parser.rs +++ b/src/parse/parser.rs @@ -175,84 +175,4 @@ impl<'a> Parser<'a> { Err(_) => Err(ParserError::ParsePanicError), } } - - pub(crate) fn parse_cfg_if( - sess: &'a ParseSess, - mac: &'a ast::MacCall, - ) -> Result, &'static str> { - match catch_unwind(AssertUnwindSafe(|| Parser::parse_cfg_if_inner(sess, mac))) { - Ok(Ok(items)) => Ok(items), - Ok(err @ Err(_)) => err, - Err(..) => Err("failed to parse cfg_if!"), - } - } - - fn parse_cfg_if_inner( - sess: &'a ParseSess, - mac: &'a ast::MacCall, - ) -> Result, &'static str> { - let token_stream = mac.args.inner_tokens(); - let mut parser = rustc_parse::stream_to_parser(sess.inner(), token_stream, Some("")); - - let mut items = vec![]; - let mut process_if_cfg = true; - - while parser.token.kind != TokenKind::Eof { - if process_if_cfg { - if !parser.eat_keyword(kw::If) { - return Err("Expected `if`"); - } - // Inner attributes are not actually syntactically permitted here, but we don't - // care about inner vs outer attributes in this position. Our purpose with this - // special case parsing of cfg_if macros is to ensure we can correctly resolve - // imported modules that may have a custom `path` defined. - // - // As such, we just need to advance the parser past the attribute and up to - // to the opening brace. - // See also https://github.com/rust-lang/rust/pull/79433 - parser - .parse_attribute(rustc_parse::parser::attr::InnerAttrPolicy::Permitted) - .map_err(|_| "Failed to parse attributes")?; - } - - if !parser.eat(&TokenKind::OpenDelim(DelimToken::Brace)) { - return Err("Expected an opening brace"); - } - - while parser.token != TokenKind::CloseDelim(DelimToken::Brace) - && parser.token.kind != TokenKind::Eof - { - let item = match parser.parse_item(ForceCollect::No) { - Ok(Some(item_ptr)) => item_ptr.into_inner(), - Ok(None) => continue, - Err(mut err) => { - err.cancel(); - parser.sess.span_diagnostic.reset_err_count(); - return Err( - "Expected item inside cfg_if block, but failed to parse it as an item", - ); - } - }; - if let ast::ItemKind::Mod(..) = item.kind { - items.push(item); - } - } - - if !parser.eat(&TokenKind::CloseDelim(DelimToken::Brace)) { - return Err("Expected a closing brace"); - } - - if parser.eat(&TokenKind::Eof) { - break; - } - - if !parser.eat_keyword(kw::Else) { - return Err("Expected `else`"); - } - - process_if_cfg = parser.token.is_keyword(kw::If); - } - - Ok(items) - } } From 7b8303d47968a0e1fcad4b143e0ff4b5e512f37b Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Mon, 20 Dec 2021 20:33:20 -0600 Subject: [PATCH 118/401] chore: cleanup unused imports --- src/parse/macros/mod.rs | 12 +++++------- src/parse/parser.rs | 9 +++------ 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/parse/macros/mod.rs b/src/parse/macros/mod.rs index fbd8c3f90953..7e12f82af15a 100644 --- a/src/parse/macros/mod.rs +++ b/src/parse/macros/mod.rs @@ -1,16 +1,14 @@ -use rustc_ast::token::{BinOpToken, DelimToken, Token, TokenKind}; -use rustc_ast::tokenstream::{Cursor, Spacing, TokenStream, TokenTree}; +use rustc_ast::token::{DelimToken, TokenKind}; +use rustc_ast::tokenstream::TokenStream; use rustc_ast::{ast, ptr}; use rustc_parse::parser::{ForceCollect, Parser}; use rustc_parse::{stream_to_parser, MACRO_ARGUMENTS}; use rustc_session::parse::ParseSess; -use rustc_span::{ - symbol::{self, kw}, - BytePos, Span, Symbol, DUMMY_SP, -}; +use rustc_span::symbol::{self, kw}; +use rustc_span::Symbol; use crate::macros::MacroArg; -use crate::rewrite::{Rewrite, RewriteContext}; +use crate::rewrite::RewriteContext; pub(crate) mod cfg_if; pub(crate) mod lazy_static; diff --git a/src/parse/parser.rs b/src/parse/parser.rs index 8efd2bf257b1..657217633f4a 100644 --- a/src/parse/parser.rs +++ b/src/parse/parser.rs @@ -1,14 +1,11 @@ use std::panic::{catch_unwind, AssertUnwindSafe}; use std::path::{Path, PathBuf}; -use rustc_ast::token::{DelimToken, TokenKind}; +use rustc_ast::token::TokenKind; use rustc_ast::{ast, ptr}; use rustc_errors::Diagnostic; -use rustc_parse::{ - new_parser_from_file, - parser::{ForceCollect, Parser as RawParser}, -}; -use rustc_span::{sym, symbol::kw, Span}; +use rustc_parse::{new_parser_from_file, parser::Parser as RawParser}; +use rustc_span::{sym, Span}; use crate::attr::first_attr_value_str_by_name; use crate::parse::session::ParseSess; From 0b2fd9b1329a41702ff2db51426003a37c1db510 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 23 Dec 2021 14:23:51 -0800 Subject: [PATCH 119/401] Fix static async closure qualifier order --- src/closures.rs | 14 +++++++------- tests/source/async_block.rs | 16 ++++++++++++++++ tests/target/async_block.rs | 10 ++++++++++ 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/src/closures.rs b/src/closures.rs index 34d73a77fd3d..e688db1c39d7 100644 --- a/src/closures.rs +++ b/src/closures.rs @@ -236,21 +236,21 @@ fn rewrite_closure_fn_decl( context: &RewriteContext<'_>, shape: Shape, ) -> Option<(String, usize)> { + let immovable = if movability == ast::Movability::Static { + "static " + } else { + "" + }; let is_async = if asyncness.is_async() { "async " } else { "" }; let mover = if capture == ast::CaptureBy::Value { "move " } else { "" }; - let immovable = if movability == ast::Movability::Static { - "static " - } else { - "" - }; // 4 = "|| {".len(), which is overconservative when the closure consists of // a single expression. let nested_shape = shape - .shrink_left(is_async.len() + mover.len() + immovable.len())? + .shrink_left(immovable.len() + is_async.len() + mover.len())? .sub_width(4)?; // 1 = | @@ -288,7 +288,7 @@ fn rewrite_closure_fn_decl( .tactic(tactic) .preserve_newline(true); let list_str = write_list(&item_vec, &fmt)?; - let mut prefix = format!("{}{}{}|{}|", is_async, immovable, mover, list_str); + let mut prefix = format!("{}{}{}|{}|", immovable, is_async, mover, list_str); if !ret_str.is_empty() { if prefix.contains('\n') { diff --git a/tests/source/async_block.rs b/tests/source/async_block.rs index 3de51a084d26..18cb4fb5f5cc 100644 --- a/tests/source/async_block.rs +++ b/tests/source/async_block.rs @@ -32,4 +32,20 @@ fn baz() { Ok(()) }, ); + + spawn( + a, + static async || { + action(); + Ok(()) + }, + ); + + spawn( + a, + static async move || { + action(); + Ok(()) + }, + ); } diff --git a/tests/target/async_block.rs b/tests/target/async_block.rs index 258dd937a6f5..137d849c9ee0 100644 --- a/tests/target/async_block.rs +++ b/tests/target/async_block.rs @@ -22,4 +22,14 @@ fn baz() { action(); Ok(()) }); + + spawn(a, static async || { + action(); + Ok(()) + }); + + spawn(a, static async move || { + action(); + Ok(()) + }); } From 76eb077fb298a01137c878a9f10854b44c5edf37 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Thu, 23 Dec 2021 20:22:09 -0500 Subject: [PATCH 120/401] Retain qualified path when rewriting struct literals Fixes 5151 Details about the qualified path are now passed along so that rustfmt can include them when formatting struct literals. --- src/expr.rs | 19 ++++++++++++++++--- tests/target/issue-5151/minimum_example.rs | 16 ++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 tests/target/issue-5151/minimum_example.rs diff --git a/src/expr.rs b/src/expr.rs index edd004ac63f0..c9c8852cd3b5 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -108,9 +108,21 @@ pub(crate) fn format_expr( ast::ExprKind::Unary(op, ref subexpr) => rewrite_unary_op(context, op, subexpr, shape), ast::ExprKind::Struct(ref struct_expr) => { let ast::StructExpr { - fields, path, rest, .. + qself, + fields, + path, + rest, } = &**struct_expr; - rewrite_struct_lit(context, path, fields, rest, &expr.attrs, expr.span, shape) + rewrite_struct_lit( + context, + path, + qself.as_ref(), + fields, + rest, + &expr.attrs, + expr.span, + shape, + ) } ast::ExprKind::Tup(ref items) => { rewrite_tuple(context, items.iter(), expr.span, shape, items.len() == 1) @@ -1511,6 +1523,7 @@ fn struct_lit_can_be_aligned(fields: &[ast::ExprField], has_base: bool) -> bool fn rewrite_struct_lit<'a>( context: &RewriteContext<'_>, path: &ast::Path, + qself: Option<&ast::QSelf>, fields: &'a [ast::ExprField], struct_rest: &ast::StructRest, attrs: &[ast::Attribute], @@ -1527,7 +1540,7 @@ fn rewrite_struct_lit<'a>( // 2 = " {".len() let path_shape = shape.sub_width(2)?; - let path_str = rewrite_path(context, PathContext::Expr, None, path, path_shape)?; + let path_str = rewrite_path(context, PathContext::Expr, qself, path, path_shape)?; let has_base_or_rest = match struct_rest { ast::StructRest::None if fields.is_empty() => return Some(format!("{} {{}}", path_str)), diff --git a/tests/target/issue-5151/minimum_example.rs b/tests/target/issue-5151/minimum_example.rs new file mode 100644 index 000000000000..2ed3d936e32a --- /dev/null +++ b/tests/target/issue-5151/minimum_example.rs @@ -0,0 +1,16 @@ +#![feature(more_qualified_paths)] + +struct Struct {} + +trait Trait { + type Type; +} + +impl Trait for Struct { + type Type = Self; +} + +fn main() { + // keep the qualified path details + let _ = ::Type {}; +} From e8afb62c71f5c789c554e23c835efc019cb42685 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Mon, 27 Dec 2021 17:42:48 -0600 Subject: [PATCH 121/401] chore: reduce some vis. for updated unreachable_pub lint --- src/config/file_lines.rs | 8 ++++---- src/config/options.rs | 14 +++++++------- src/rustfmt_diff.rs | 10 +++++----- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/config/file_lines.rs b/src/config/file_lines.rs index 4b799780d85d..7b498dc46b32 100644 --- a/src/config/file_lines.rs +++ b/src/config/file_lines.rs @@ -13,9 +13,9 @@ use thiserror::Error; /// A range of lines in a file, inclusive of both ends. pub struct LineRange { - pub file: Lrc, - pub lo: usize, - pub hi: usize, + pub(crate) file: Lrc, + pub(crate) lo: usize, + pub(crate) hi: usize, } /// Defines the name of an input - either a file or stdin. @@ -75,7 +75,7 @@ impl Serialize for FileName { } impl LineRange { - pub fn file_name(&self) -> FileName { + pub(crate) fn file_name(&self) -> FileName { self.file.name.clone().into() } } diff --git a/src/config/options.rs b/src/config/options.rs index bce9e5d07f26..d857c29be29c 100644 --- a/src/config/options.rs +++ b/src/config/options.rs @@ -218,24 +218,24 @@ pub enum Verbosity { pub struct WidthHeuristics { // Maximum width of the args of a function call before falling back // to vertical formatting. - pub fn_call_width: usize, + pub(crate) fn_call_width: usize, // Maximum width of the args of a function-like attributes before falling // back to vertical formatting. - pub attr_fn_like_width: usize, + pub(crate) attr_fn_like_width: usize, // Maximum width in the body of a struct lit before falling back to // vertical formatting. - pub struct_lit_width: usize, + pub(crate) struct_lit_width: usize, // Maximum width in the body of a struct variant before falling back // to vertical formatting. - pub struct_variant_width: usize, + pub(crate) struct_variant_width: usize, // Maximum width of an array literal before falling back to vertical // formatting. - pub array_width: usize, + pub(crate) array_width: usize, // Maximum length of a chain to fit on a single line. - pub chain_width: usize, + pub(crate) chain_width: usize, // Maximum line length for single line if-else expressions. A value // of zero means always break if-else expressions. - pub single_line_if_else_max_width: usize, + pub(crate) single_line_if_else_max_width: usize, } impl fmt::Display for WidthHeuristics { diff --git a/src/rustfmt_diff.rs b/src/rustfmt_diff.rs index a394ce07398e..1724a0f87bf7 100644 --- a/src/rustfmt_diff.rs +++ b/src/rustfmt_diff.rs @@ -6,20 +6,20 @@ use std::io::Write; use crate::config::{Color, Config, Verbosity}; #[derive(Debug, PartialEq)] -pub enum DiffLine { +pub(crate) enum DiffLine { Context(String), Expected(String), Resulting(String), } #[derive(Debug, PartialEq)] -pub struct Mismatch { +pub(crate) struct Mismatch { /// The line number in the formatted version. - pub line_number: u32, + pub(crate) line_number: u32, /// The line number in the original version. - pub line_number_orig: u32, + pub(crate) line_number_orig: u32, /// The set of lines (context and old/new) in the mismatch. - pub lines: Vec, + pub(crate) lines: Vec, } impl Mismatch { From 50bbb43dab5af3b42338bd6a9748da9a27136bf2 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Tue, 28 Dec 2021 19:23:31 -0600 Subject: [PATCH 122/401] chore: bump toolchain --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index c97b5ec6609b..d4cdcec2018a 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2021-12-20" +channel = "nightly-2021-12-29" components = ["rustc-dev"] From f935f0cf89f53a9df3a443b9cc09bb1bcc33064b Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Tue, 28 Dec 2021 19:23:51 -0600 Subject: [PATCH 123/401] feat: support parsing asm! args --- src/lib.rs | 1 + src/parse/macros/asm.rs | 11 +++++++++++ src/parse/macros/mod.rs | 1 + 3 files changed, 13 insertions(+) create mode 100644 src/parse/macros/asm.rs diff --git a/src/lib.rs b/src/lib.rs index f59ebad97ce8..ad23b16e02ec 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,6 +15,7 @@ extern crate log; // N.B. these crates are loaded from the sysroot, so they need extern crate. extern crate rustc_ast; extern crate rustc_ast_pretty; +extern crate rustc_builtin_macros; extern crate rustc_data_structures; extern crate rustc_errors; extern crate rustc_expand; diff --git a/src/parse/macros/asm.rs b/src/parse/macros/asm.rs new file mode 100644 index 000000000000..cc9fb5072ce1 --- /dev/null +++ b/src/parse/macros/asm.rs @@ -0,0 +1,11 @@ +use rustc_ast::ast; +use rustc_builtin_macros::asm::{parse_asm_args, AsmArgs}; + +use crate::rewrite::RewriteContext; + +#[allow(dead_code)] +pub(crate) fn parse_asm(context: &RewriteContext<'_>, mac: &ast::MacCall) -> Option { + let ts = mac.args.inner_tokens(); + let mut parser = super::build_parser(context, ts); + parse_asm_args(&mut parser, context.parse_sess.inner(), mac.span(), false).ok() +} diff --git a/src/parse/macros/mod.rs b/src/parse/macros/mod.rs index 7e12f82af15a..2e9ce1d35f40 100644 --- a/src/parse/macros/mod.rs +++ b/src/parse/macros/mod.rs @@ -10,6 +10,7 @@ use rustc_span::Symbol; use crate::macros::MacroArg; use crate::rewrite::RewriteContext; +pub(crate) mod asm; pub(crate) mod cfg_if; pub(crate) mod lazy_static; From 4a053f206fd6799a25823c307f7d7f9d897be118 Mon Sep 17 00:00:00 2001 From: David Lattimore Date: Thu, 30 Dec 2021 10:13:45 +1100 Subject: [PATCH 124/401] Do not flatten match arm block with leading attributes This is a backport of #4124. Fixes #4109 --- src/matches.rs | 6 +++++- tests/source/match.rs | 19 +++++++++++++++++++ tests/target/match.rs | 19 +++++++++++++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/matches.rs b/src/matches.rs index 22d23fc1cdba..85d9c5d2b9bb 100644 --- a/src/matches.rs +++ b/src/matches.rs @@ -322,7 +322,11 @@ fn flatten_arm_body<'a>( if let Some(block) = block_can_be_flattened(context, body) { if let ast::StmtKind::Expr(ref expr) = block.stmts[0].kind { if let ast::ExprKind::Block(..) = expr.kind { - flatten_arm_body(context, expr, None) + if expr.attrs.is_empty() { + flatten_arm_body(context, expr, None) + } else { + (true, body) + } } else { let cond_becomes_muti_line = opt_shape .and_then(|shape| rewrite_cond(context, expr, shape)) diff --git a/tests/source/match.rs b/tests/source/match.rs index da20517203a6..b5dc9957a2ca 100644 --- a/tests/source/match.rs +++ b/tests/source/match.rs @@ -568,3 +568,22 @@ fn issue_3774() { } } } + +// #4109 +fn issue_4109() { + match () { + _ => { +#[cfg(debug_assertions)] +{ +println!("Foo"); +} +} +} + +match () { +_ => { +#[allow(unsafe_code)] +unsafe {} +} +} +} diff --git a/tests/target/match.rs b/tests/target/match.rs index e2c522bea10b..1bf3fb758ee8 100644 --- a/tests/target/match.rs +++ b/tests/target/match.rs @@ -608,3 +608,22 @@ fn issue_3774() { } } } + +// #4109 +fn issue_4109() { + match () { + _ => { + #[cfg(debug_assertions)] + { + println!("Foo"); + } + } + } + + match () { + _ => { + #[allow(unsafe_code)] + unsafe {} + } + } +} From 737e6f704671dbf30e81dababa0133b585c03b48 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Sun, 19 Dec 2021 21:27:25 -0500 Subject: [PATCH 125/401] Improve out of line module resolution Fixes 5119 When the directory structure was laid out as follows: ``` dir |---mod_a | |---sub_mod_1.rs | |---sub_mod_2.rs |---mod_a.rs ``` And ``mod_a.rs`` contains the following content: ```rust mod mod_a { mod sub_mod_1; mod sub_mod_2; } ``` rustfmt previously tried to find ``sub_mod_1.rs`` and ``sub_mod_2.rs`` in ``./mod_a/mod_a/``. This directory does not exist and this caused rustfmt to fail with the error message: Error writing files: failed to resolve mod Now, both ``sub_mod_1.rs`` and ``sub_mod_2.rs`` are resolved correctly and found at ``mod_a/sub_mod_1.rs`` and ``mod_a/sub_mod_2.rs``. --- src/modules.rs | 9 ++++++- src/test/mod_resolver.rs | 14 +++++++++++ tests/cargo-fmt/main.rs | 24 +++++++++++++++++++ .../test-submodule-issue-5119/Cargo.toml | 8 +++++++ .../test-submodule-issue-5119/src/lib.rs | 7 ++++++ .../test-submodule-issue-5119/tests/test1.rs | 8 +++++++ .../tests/test1/sub1.rs | 6 +++++ .../tests/test1/sub2.rs | 6 +++++ .../tests/test1/sub3/mod.rs | 1 + .../tests/test1/sub3/sub4.rs | 0 10 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 tests/mod-resolver/test-submodule-issue-5119/Cargo.toml create mode 100644 tests/mod-resolver/test-submodule-issue-5119/src/lib.rs create mode 100644 tests/mod-resolver/test-submodule-issue-5119/tests/test1.rs create mode 100644 tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub1.rs create mode 100644 tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub2.rs create mode 100644 tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/mod.rs create mode 100644 tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/sub4.rs diff --git a/src/modules.rs b/src/modules.rs index 9c964b274e08..70b937b02836 100644 --- a/src/modules.rs +++ b/src/modules.rs @@ -458,6 +458,7 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> { self.directory.path.push(path.as_str()); self.directory.ownership = DirectoryOwnership::Owned { relative: None }; } else { + let id = id.as_str(); // We have to push on the current module name in the case of relative // paths in order to ensure that any additional module paths from inline // `mod x { ... }` come after the relative extension. @@ -468,9 +469,15 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> { if let Some(ident) = relative.take() { // remove the relative offset self.directory.path.push(ident.as_str()); + + // In the case where there is an x.rs and an ./x directory we want + // to prevent adding x twice. For example, ./x/x + if self.directory.path.exists() && !self.directory.path.join(id).exists() { + return; + } } } - self.directory.path.push(id.as_str()); + self.directory.path.push(id); } } diff --git a/src/test/mod_resolver.rs b/src/test/mod_resolver.rs index ec9ed0f0b8d6..fcff6d14e6fa 100644 --- a/src/test/mod_resolver.rs +++ b/src/test/mod_resolver.rs @@ -50,3 +50,17 @@ fn skip_out_of_line_nested_inline_within_out_of_line() { &["tests/mod-resolver/skip-files-issue-5065/one.rs"], ); } + +#[test] +fn fmt_out_of_line_test_modules() { + // See also https://github.com/rust-lang/rustfmt/issues/5119 + verify_mod_resolution( + "tests/mod-resolver/test-submodule-issue-5119/tests/test1.rs", + &[ + "tests/mod-resolver/test-submodule-issue-5119/tests/test1.rs", + "tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub1.rs", + "tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub2.rs", + "tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/sub4.rs", + ], + ) +} diff --git a/tests/cargo-fmt/main.rs b/tests/cargo-fmt/main.rs index 5493b09e4aa6..bf81f253f691 100644 --- a/tests/cargo-fmt/main.rs +++ b/tests/cargo-fmt/main.rs @@ -2,6 +2,7 @@ use std::env; use std::process::Command; +use std::path::Path; /// Run the cargo-fmt executable and return its output. fn cargo_fmt(args: &[&str]) -> (String, String) { @@ -71,3 +72,26 @@ fn rustfmt_help() { assert_that!(&["--", "-h"], contains("Format Rust code")); assert_that!(&["--", "--help=config"], contains("Configuration Options:")); } + +#[test] +fn cargo_fmt_out_of_line_test_modules() { + // See also https://github.com/rust-lang/rustfmt/issues/5119 + let expected_modified_files = [ + "tests/mod-resolver/test-submodule-issue-5119/src/lib.rs", + "tests/mod-resolver/test-submodule-issue-5119/tests/test1.rs", + "tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub1.rs", + "tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub2.rs", + "tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/sub4.rs", + ]; + let args = [ + "-v", + "--check", + "--manifest-path", + "tests/mod-resolver/test-submodule-issue-5119/Cargo.toml", + ]; + let (stdout, _) = cargo_fmt(&args); + for file in expected_modified_files { + let path = Path::new(file).canonicalize().unwrap(); + assert!(stdout.contains(&format!("Diff in {}", path.display()))) + } +} diff --git a/tests/mod-resolver/test-submodule-issue-5119/Cargo.toml b/tests/mod-resolver/test-submodule-issue-5119/Cargo.toml new file mode 100644 index 000000000000..0993f1279599 --- /dev/null +++ b/tests/mod-resolver/test-submodule-issue-5119/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "rustfmt-test-submodule-issue" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/mod-resolver/test-submodule-issue-5119/src/lib.rs b/tests/mod-resolver/test-submodule-issue-5119/src/lib.rs new file mode 100644 index 000000000000..3f7ddba8a288 --- /dev/null +++ b/tests/mod-resolver/test-submodule-issue-5119/src/lib.rs @@ -0,0 +1,7 @@ +pub fn foo() -> i32 { +3 +} + +pub fn bar() -> i32 { +4 +} diff --git a/tests/mod-resolver/test-submodule-issue-5119/tests/test1.rs b/tests/mod-resolver/test-submodule-issue-5119/tests/test1.rs new file mode 100644 index 000000000000..da4e86169ad9 --- /dev/null +++ b/tests/mod-resolver/test-submodule-issue-5119/tests/test1.rs @@ -0,0 +1,8 @@ +mod test1 { +#[cfg(unix)] +mod sub1; +#[cfg(not(unix))] +mod sub2; + +mod sub3; +} diff --git a/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub1.rs b/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub1.rs new file mode 100644 index 000000000000..b760ba23cd27 --- /dev/null +++ b/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub1.rs @@ -0,0 +1,6 @@ +use rustfmt_test_submodule_issue::foo; + +#[test] +fn test_foo() { +assert_eq!(3, foo()); +} diff --git a/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub2.rs b/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub2.rs new file mode 100644 index 000000000000..4fd8286eac40 --- /dev/null +++ b/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub2.rs @@ -0,0 +1,6 @@ +use rustfmt_test_submodule_issue::bar; + +#[test] +fn test_bar() { +assert_eq!(4, bar()); +} diff --git a/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/mod.rs b/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/mod.rs new file mode 100644 index 000000000000..e029785bc245 --- /dev/null +++ b/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/mod.rs @@ -0,0 +1 @@ +mod sub4; diff --git a/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/sub4.rs b/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/sub4.rs new file mode 100644 index 000000000000..e69de29bb2d1 From 93b7de5b0110a1ab79714c3dfd009a4aae3e34b1 Mon Sep 17 00:00:00 2001 From: Chris Emerson Date: Wed, 6 Nov 2019 10:15:34 +0000 Subject: [PATCH 126/401] Make `--check` work when running from stdin. (#3896) # Conflicts: # src/bin/main.rs --- src/bin/main.rs | 31 ++++++----- src/emitter/checkstyle.rs | 12 ++-- src/emitter/json.rs | 20 +++++-- src/test/mod.rs | 93 +++++++++++++++++++++++++++++++ tests/writemode/source/stdin.rs | 6 ++ tests/writemode/target/stdin.json | 1 + tests/writemode/target/stdin.xml | 2 + 7 files changed, 139 insertions(+), 26 deletions(-) create mode 100644 tests/writemode/source/stdin.rs create mode 100644 tests/writemode/target/stdin.json create mode 100644 tests/writemode/target/stdin.xml diff --git a/src/bin/main.rs b/src/bin/main.rs index 4d845547cdfe..6f5b09fc86ad 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -74,14 +74,10 @@ pub enum OperationError { /// An io error during reading or writing. #[error("{0}")] IoError(IoError), - /// Attempt to use --check with stdin, which isn't currently - /// supported. - #[error("The `--check` option is not supported with standard input.")] - CheckWithStdin, - /// Attempt to use --emit=json with stdin, which isn't currently - /// supported. - #[error("Using `--emit` other than stdout is not supported with standard input.")] - EmitWithStdin, + /// Attempt to use --emit with a mode which is not currently + /// supported with stdandard input. + #[error("Emit mode {0} not supported with standard output.")] + StdinBadEmit(EmitMode), } impl From for OperationError { @@ -255,15 +251,20 @@ fn format_string(input: String, options: GetOptsOptions) -> Result { let (mut config, _) = load_config(Some(Path::new(".")), Some(options.clone()))?; if options.check { - return Err(OperationError::CheckWithStdin.into()); - } - if let Some(emit_mode) = options.emit_mode { - if emit_mode != EmitMode::Stdout { - return Err(OperationError::EmitWithStdin.into()); + config.set().emit_mode(EmitMode::Diff); + } else { + match options.emit_mode { + // Emit modes which work with standard input + // None means default, which is Stdout. + None | Some(EmitMode::Stdout) | Some(EmitMode::Checkstyle) | Some(EmitMode::Json) => {} + Some(emit_mode) => { + return Err(OperationError::StdinBadEmit(emit_mode).into()); + } } + config + .set() + .emit_mode(options.emit_mode.unwrap_or(EmitMode::Stdout)); } - // emit mode is always Stdout for Stdin. - config.set().emit_mode(EmitMode::Stdout); config.set().verbose(Verbosity::Quiet); // parse file_lines diff --git a/src/emitter/checkstyle.rs b/src/emitter/checkstyle.rs index 76f2527db3da..545b259979d9 100644 --- a/src/emitter/checkstyle.rs +++ b/src/emitter/checkstyle.rs @@ -2,7 +2,6 @@ use self::xml::XmlEscaped; use super::*; use crate::rustfmt_diff::{make_diff, DiffLine, Mismatch}; use std::io::{self, Write}; -use std::path::Path; mod xml; @@ -30,7 +29,6 @@ impl Emitter for CheckstyleEmitter { }: FormattedFile<'_>, ) -> Result { const CONTEXT_SIZE: usize = 0; - let filename = ensure_real_path(filename); let diff = make_diff(original_text, formatted_text, CONTEXT_SIZE); output_checkstyle_file(output, filename, diff)?; Ok(EmitterResult::default()) @@ -39,13 +37,13 @@ impl Emitter for CheckstyleEmitter { pub(crate) fn output_checkstyle_file( mut writer: T, - filename: &Path, + filename: &FileName, diff: Vec, ) -> Result<(), io::Error> where T: Write, { - write!(writer, r#""#, filename.display())?; + write!(writer, r#""#, filename)?; for mismatch in diff { let begin_line = mismatch.line_number; let mut current_line; @@ -77,7 +75,11 @@ mod tests { fn emits_empty_record_on_file_with_no_mismatches() { let file_name = "src/well_formatted.rs"; let mut writer = Vec::new(); - let _ = output_checkstyle_file(&mut writer, &PathBuf::from(file_name), vec![]); + let _ = output_checkstyle_file( + &mut writer, + &FileName::Real(PathBuf::from(file_name)), + vec![], + ); assert_eq!( &writer[..], format!(r#""#, file_name).as_bytes() diff --git a/src/emitter/json.rs b/src/emitter/json.rs index 269dd2d4daf5..4d6f972c5e3d 100644 --- a/src/emitter/json.rs +++ b/src/emitter/json.rs @@ -3,7 +3,6 @@ use crate::rustfmt_diff::{make_diff, DiffLine, Mismatch}; use serde::Serialize; use serde_json::to_string as to_json_string; use std::io::{self, Write}; -use std::path::Path; #[derive(Debug, Default)] pub(crate) struct JsonEmitter { @@ -47,7 +46,6 @@ impl Emitter for JsonEmitter { }: FormattedFile<'_>, ) -> Result { const CONTEXT_SIZE: usize = 0; - let filename = ensure_real_path(filename); let diff = make_diff(original_text, formatted_text, CONTEXT_SIZE); let has_diff = !diff.is_empty(); @@ -62,7 +60,7 @@ impl Emitter for JsonEmitter { fn output_json_file( mut writer: T, - filename: &Path, + filename: &FileName, diff: Vec, num_emitted_files: u32, ) -> Result<(), io::Error> @@ -106,7 +104,7 @@ where }); } let json = to_json_string(&MismatchedFile { - name: String::from(filename.to_str().unwrap()), + name: format!("{}", filename), mismatches, })?; let prefix = if num_emitted_files > 0 { "," } else { "" }; @@ -148,7 +146,12 @@ mod tests { let mut writer = Vec::new(); let exp_json = to_json_string(&mismatched_file).unwrap(); - let _ = output_json_file(&mut writer, &PathBuf::from(file), vec![mismatch], 0); + let _ = output_json_file( + &mut writer, + &FileName::Real(PathBuf::from(file)), + vec![mismatch], + 0, + ); assert_eq!(&writer[..], format!("{}", exp_json).as_bytes()); } @@ -188,7 +191,12 @@ mod tests { let mut writer = Vec::new(); let exp_json = to_json_string(&mismatched_file).unwrap(); - let _ = output_json_file(&mut writer, &PathBuf::from(file), vec![mismatch], 0); + let _ = output_json_file( + &mut writer, + &FileName::Real(PathBuf::from(file)), + vec![mismatch], + 0, + ); assert_eq!(&writer[..], format!("{}", exp_json).as_bytes()); } diff --git a/src/test/mod.rs b/src/test/mod.rs index cceb28dfea6d..e1a7972ec829 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -307,6 +307,52 @@ fn assert_output(source: &Path, expected_filename: &Path) { } } +// Helper function for comparing the results of rustfmt +// to a known output generated by one of the write modes. +fn assert_stdin_output( + source: &Path, + expected_filename: &Path, + emit_mode: EmitMode, + has_diff: bool, +) { + let mut config = Config::default(); + config.set().newline_style(NewlineStyle::Unix); + config.set().emit_mode(emit_mode); + + let mut source_file = fs::File::open(&source).expect("couldn't open source"); + let mut source_text = String::new(); + source_file + .read_to_string(&mut source_text) + .expect("Failed reading target"); + let input = Input::Text(source_text); + + // Populate output by writing to a vec. + let mut buf: Vec = vec![]; + { + let mut session = Session::new(config, Some(&mut buf)); + session.format(input).unwrap(); + let errors = ReportedErrors { + has_diff: has_diff, + ..Default::default() + }; + assert_eq!(session.errors, errors); + } + + let mut expected_file = fs::File::open(&expected_filename).expect("couldn't open target"); + let mut expected_text = String::new(); + expected_file + .read_to_string(&mut expected_text) + .expect("Failed reading target"); + + let output = String::from_utf8(buf).unwrap(); + let compare = make_diff(&expected_text, &output, DIFF_CONTEXT_SIZE); + if !compare.is_empty() { + let mut failures = HashMap::new(); + failures.insert(source.to_owned(), compare); + print_mismatches_default_message(failures); + panic!("Text does not match expected output"); + } +} // Idempotence tests. Files in tests/target are checked to be unaltered by // rustfmt. #[nightly_only_test] @@ -463,6 +509,30 @@ fn stdin_works_with_modified_lines() { assert_eq!(buf, output.as_bytes()); } +/// Ensures that `EmitMode::Json` works with input from `stdin`. +#[test] +fn stdin_works_with_json() { + init_log(); + assert_stdin_output( + Path::new("tests/writemode/source/stdin.rs"), + Path::new("tests/writemode/target/stdin.json"), + EmitMode::Json, + true, + ); +} + +/// Ensures that `EmitMode::Checkstyle` works with input from `stdin`. +#[test] +fn stdin_works_with_checkstyle() { + init_log(); + assert_stdin_output( + Path::new("tests/writemode/source/stdin.rs"), + Path::new("tests/writemode/target/stdin.xml"), + EmitMode::Checkstyle, + false, + ); +} + #[test] fn stdin_disable_all_formatting_test() { init_log(); @@ -896,3 +966,26 @@ fn verify_check_works() { .status() .expect("run with check option failed"); } + +#[test] +fn verify_check_works_with_stdin() { + init_log(); + + let mut child = Command::new(rustfmt().to_str().unwrap()) + .arg("--check") + .stdin(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .expect("run with check option failed"); + + { + let stdin = child.stdin.as_mut().expect("Failed to open stdin"); + stdin + .write_all("fn main() {}\n".as_bytes()) + .expect("Failed to write to rustfmt --check"); + } + let output = child + .wait_with_output() + .expect("Failed to wait on rustfmt child"); + assert!(output.status.success()); +} diff --git a/tests/writemode/source/stdin.rs b/tests/writemode/source/stdin.rs new file mode 100644 index 000000000000..06f8a0c288d7 --- /dev/null +++ b/tests/writemode/source/stdin.rs @@ -0,0 +1,6 @@ + +fn + some( ) +{ +} +fn main () {} diff --git a/tests/writemode/target/stdin.json b/tests/writemode/target/stdin.json new file mode 100644 index 000000000000..20e38f57f4a7 --- /dev/null +++ b/tests/writemode/target/stdin.json @@ -0,0 +1 @@ +[{"name":"stdin","mismatches":[{"original_begin_line":1,"original_end_line":6,"expected_begin_line":1,"expected_end_line":2,"original":"\nfn\n some( )\n{\n}\nfn main () {}","expected":"fn some() {}\nfn main() {}"}]}] \ No newline at end of file diff --git a/tests/writemode/target/stdin.xml b/tests/writemode/target/stdin.xml new file mode 100644 index 000000000000..e70708338f56 --- /dev/null +++ b/tests/writemode/target/stdin.xml @@ -0,0 +1,2 @@ + + From 34263cd6bdb29df23258a96ea18fe986efe8dad0 Mon Sep 17 00:00:00 2001 From: Chris Emerson Date: Tue, 12 Nov 2019 02:55:04 +0000 Subject: [PATCH 127/401] Fix --check -l with stdin. (#3910) * Fix some possible panics when using `--check` with stdin. One case which doesn't work is when there are only line ending fixes; with stdin rustfmt is unable to detect the difference as it stores the input with Unix line endings. * Add test for `rustfmt --check -l` with stdin. --- src/emitter/diff.rs | 5 ++--- src/test/mod.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/emitter/diff.rs b/src/emitter/diff.rs index 7264ad8bbf36..5e1f13446560 100644 --- a/src/emitter/diff.rs +++ b/src/emitter/diff.rs @@ -28,7 +28,7 @@ impl Emitter for DiffEmitter { if has_diff { if self.config.print_misformatted_file_names() { - writeln!(output, "{}", ensure_real_path(filename).display())?; + writeln!(output, "{}", filename)?; } else { print_diff( mismatch, @@ -40,8 +40,7 @@ impl Emitter for DiffEmitter { // This occurs when the only difference between the original and formatted values // is the newline style. This happens because The make_diff function compares the // original and formatted values line by line, independent of line endings. - let file_path = ensure_real_path(filename); - writeln!(output, "Incorrect newline style in {}", file_path.display())?; + writeln!(output, "Incorrect newline style in {}", filename)?; return Ok(EmitterResult { has_diff: true }); } diff --git a/src/test/mod.rs b/src/test/mod.rs index e1a7972ec829..db1cf88479cf 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -989,3 +989,29 @@ fn verify_check_works_with_stdin() { .expect("Failed to wait on rustfmt child"); assert!(output.status.success()); } + +#[test] +fn verify_check_l_works_with_stdin() { + init_log(); + + let mut child = Command::new(rustfmt().to_str().unwrap()) + .arg("--check") + .arg("-l") + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .expect("run with check option failed"); + + { + let stdin = child.stdin.as_mut().expect("Failed to open stdin"); + stdin + .write_all("fn main()\n{}\n".as_bytes()) + .expect("Failed to write to rustfmt --check"); + } + let output = child + .wait_with_output() + .expect("Failed to wait on rustfmt child"); + assert!(output.status.success()); + assert_eq!(std::str::from_utf8(&output.stdout).unwrap(), "stdin\n"); +} From 776baf93f8fd1e2f3a66da4c2a1dd92114cef9c6 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Mon, 9 Dec 2019 04:41:16 -0600 Subject: [PATCH 128/401] refactor: update json emitter to better handle errors (#3953) --- src/emitter/json.rs | 169 +++++++++++++---------------- tests/writemode/target/output.json | 2 +- tests/writemode/target/stdin.json | 2 +- 3 files changed, 80 insertions(+), 93 deletions(-) diff --git a/src/emitter/json.rs b/src/emitter/json.rs index 4d6f972c5e3d..7c0f862cbc63 100644 --- a/src/emitter/json.rs +++ b/src/emitter/json.rs @@ -6,10 +6,10 @@ use std::io::{self, Write}; #[derive(Debug, Default)] pub(crate) struct JsonEmitter { - num_files: u32, + mismatched_files: Vec, } -#[derive(Debug, Default, Serialize)] +#[derive(Debug, Default, PartialEq, Serialize)] struct MismatchedBlock { original_begin_line: u32, original_end_line: u32, @@ -19,26 +19,20 @@ struct MismatchedBlock { expected: String, } -#[derive(Debug, Default, Serialize)] +#[derive(Debug, Default, PartialEq, Serialize)] struct MismatchedFile { name: String, mismatches: Vec, } impl Emitter for JsonEmitter { - fn emit_header(&self, output: &mut dyn Write) -> Result<(), io::Error> { - write!(output, "[")?; - Ok(()) - } - fn emit_footer(&self, output: &mut dyn Write) -> Result<(), io::Error> { - write!(output, "]")?; - Ok(()) + writeln!(output, "{}", &to_json_string(&self.mismatched_files)?) } fn emit_formatted_file( &mut self, - output: &mut dyn Write, + _output: &mut dyn Write, FormattedFile { filename, original_text, @@ -50,66 +44,61 @@ impl Emitter for JsonEmitter { let has_diff = !diff.is_empty(); if has_diff { - output_json_file(output, filename, diff, self.num_files)?; - self.num_files += 1; + self.add_misformatted_file(filename, diff)?; } Ok(EmitterResult { has_diff }) } } -fn output_json_file( - mut writer: T, - filename: &FileName, - diff: Vec, - num_emitted_files: u32, -) -> Result<(), io::Error> -where - T: Write, -{ - let mut mismatches = vec![]; - for mismatch in diff { - let original_begin_line = mismatch.line_number_orig; - let expected_begin_line = mismatch.line_number; - let mut original_end_line = original_begin_line; - let mut expected_end_line = expected_begin_line; - let mut original_line_counter = 0; - let mut expected_line_counter = 0; - let mut original_lines = vec![]; - let mut expected_lines = vec![]; +impl JsonEmitter { + fn add_misformatted_file( + &mut self, + filename: &FileName, + diff: Vec, + ) -> Result<(), io::Error> { + let mut mismatches = vec![]; + for mismatch in diff { + let original_begin_line = mismatch.line_number_orig; + let expected_begin_line = mismatch.line_number; + let mut original_end_line = original_begin_line; + let mut expected_end_line = expected_begin_line; + let mut original_line_counter = 0; + let mut expected_line_counter = 0; + let mut original_lines = vec![]; + let mut expected_lines = vec![]; - for line in mismatch.lines { - match line { - DiffLine::Expected(msg) => { - expected_end_line = expected_begin_line + expected_line_counter; - expected_line_counter += 1; - expected_lines.push(msg) + for line in mismatch.lines { + match line { + DiffLine::Expected(msg) => { + expected_end_line = expected_begin_line + expected_line_counter; + expected_line_counter += 1; + expected_lines.push(msg) + } + DiffLine::Resulting(msg) => { + original_end_line = original_begin_line + original_line_counter; + original_line_counter += 1; + original_lines.push(msg) + } + DiffLine::Context(_) => continue, } - DiffLine::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: original_lines.join("\n"), + expected: expected_lines.join("\n"), + }); + } + self.mismatched_files.push(MismatchedFile { + name: format!("{}", filename), + mismatches, }); + Ok(()) } - let json = to_json_string(&MismatchedFile { - name: format!("{}", filename), - mismatches, - })?; - let prefix = if num_emitted_files > 0 { "," } else { "" }; - write!(writer, "{}{}", prefix, &json)?; - Ok(()) } #[cfg(test)] @@ -120,6 +109,9 @@ mod tests { #[test] fn expected_line_range_correct_when_single_line_split() { + let mut emitter = JsonEmitter { + mismatched_files: vec![], + }; let file = "foo/bar.rs"; let mismatched_file = MismatchedFile { name: String::from(file), @@ -144,19 +136,19 @@ mod tests { ], }; - let mut writer = Vec::new(); - let exp_json = to_json_string(&mismatched_file).unwrap(); - let _ = output_json_file( - &mut writer, - &FileName::Real(PathBuf::from(file)), - vec![mismatch], - 0, - ); - assert_eq!(&writer[..], format!("{}", exp_json).as_bytes()); + let _ = emitter + .add_misformatted_file(&FileName::Real(PathBuf::from(file)), vec![mismatch]) + .unwrap(); + + assert_eq!(emitter.mismatched_files.len(), 1); + assert_eq!(emitter.mismatched_files[0], mismatched_file); } #[test] fn context_lines_ignored() { + let mut emitter = JsonEmitter { + mismatched_files: vec![], + }; let file = "src/lib.rs"; let mismatched_file = MismatchedFile { name: String::from(file), @@ -189,15 +181,12 @@ mod tests { ], }; - let mut writer = Vec::new(); - let exp_json = to_json_string(&mismatched_file).unwrap(); - let _ = output_json_file( - &mut writer, - &FileName::Real(PathBuf::from(file)), - vec![mismatch], - 0, - ); - assert_eq!(&writer[..], format!("{}", exp_json).as_bytes()); + let _ = emitter + .add_misformatted_file(&FileName::Real(PathBuf::from(file)), vec![mismatch]) + .unwrap(); + + assert_eq!(emitter.mismatched_files.len(), 1); + assert_eq!(emitter.mismatched_files[0], mismatched_file); } #[test] @@ -217,7 +206,7 @@ mod tests { .unwrap(); let _ = emitter.emit_footer(&mut writer); assert_eq!(result.has_diff, false); - assert_eq!(&writer[..], "[]".as_bytes()); + assert_eq!(&writer[..], "[]\n".as_bytes()); } #[test] @@ -263,7 +252,7 @@ mod tests { ) .unwrap(); let _ = emitter.emit_footer(&mut writer); - let exp_json = to_json_string(&MismatchedFile { + let exp_json = to_json_string(&vec![MismatchedFile { name: String::from(file_name), mismatches: vec![ MismatchedBlock { @@ -287,10 +276,10 @@ mod tests { ), }, ], - }) + }]) .unwrap(); assert_eq!(result.has_diff, true); - assert_eq!(&writer[..], format!("[{}]", exp_json).as_bytes()); + assert_eq!(&writer[..], format!("{}\n", exp_json).as_bytes()); } #[test] @@ -325,7 +314,7 @@ mod tests { ) .unwrap(); let _ = emitter.emit_footer(&mut writer); - let exp_bin_json = to_json_string(&MismatchedFile { + let exp_bin = MismatchedFile { name: String::from(bin_file), mismatches: vec![MismatchedBlock { original_begin_line: 2, @@ -335,9 +324,9 @@ mod tests { original: String::from("println!(\"Hello, world!\");"), expected: String::from(" println!(\"Hello, world!\");"), }], - }) - .unwrap(); - let exp_lib_json = to_json_string(&MismatchedFile { + }; + + let exp_lib = MismatchedFile { name: String::from(lib_file), mismatches: vec![MismatchedBlock { original_begin_line: 2, @@ -347,11 +336,9 @@ mod tests { original: String::from("println!(\"Greetings!\");"), expected: String::from(" println!(\"Greetings!\");"), }], - }) - .unwrap(); - assert_eq!( - &writer[..], - format!("[{},{}]", exp_bin_json, exp_lib_json).as_bytes() - ); + }; + + let exp_json = to_json_string(&vec![exp_bin, exp_lib]).unwrap(); + assert_eq!(&writer[..], format!("{}\n", exp_json).as_bytes()); } } diff --git a/tests/writemode/target/output.json b/tests/writemode/target/output.json index b5f327b0a1ca..acb33dea7ef3 100644 --- a/tests/writemode/target/output.json +++ b/tests/writemode/target/output.json @@ -1 +1 @@ -[{"name":"tests/writemode/source/json.rs","mismatches":[{"original_begin_line":5,"original_end_line":7,"expected_begin_line":5,"expected_end_line":5,"original":"fn foo_expr() {\n 1\n}","expected":"fn foo_expr() { 1 }"},{"original_begin_line":9,"original_end_line":11,"expected_begin_line":7,"expected_end_line":7,"original":"fn foo_stmt() {\n foo();\n}","expected":"fn foo_stmt() { foo(); }"},{"original_begin_line":13,"original_end_line":15,"expected_begin_line":9,"expected_end_line":9,"original":"fn foo_decl_local() {\n let z = 5;\n }","expected":"fn foo_decl_local() { let z = 5; }"},{"original_begin_line":17,"original_end_line":19,"expected_begin_line":11,"expected_end_line":11,"original":"fn foo_decl_item(x: &mut i32) {\n x = 3;\n}","expected":"fn foo_decl_item(x: &mut i32) { x = 3; }"},{"original_begin_line":21,"original_end_line":21,"expected_begin_line":13,"expected_end_line":13,"original":" fn empty() {","expected":"fn empty() {}"},{"original_begin_line":23,"original_end_line":23,"expected_begin_line":15,"expected_end_line":15,"original":"}","expected":"fn foo_return() -> String { \"yay\" }"},{"original_begin_line":25,"original_end_line":29,"expected_begin_line":17,"expected_end_line":20,"original":"fn foo_return() -> String {\n \"yay\"\n}\n\nfn foo_where() -> T where T: Sync {","expected":"fn foo_where() -> T\nwhere\n T: Sync,\n{"},{"original_begin_line":64,"original_end_line":66,"expected_begin_line":55,"expected_end_line":55,"original":"fn lots_of_space () {\n 1 \n}","expected":"fn lots_of_space() { 1 }"},{"original_begin_line":71,"original_end_line":72,"expected_begin_line":60,"expected_end_line":60,"original":" fn dummy(&self) {\n }","expected":" fn dummy(&self) {}"},{"original_begin_line":75,"original_end_line":75,"expected_begin_line":63,"expected_end_line":64,"original":"trait CoolerTypes { fn dummy(&self) { ","expected":"trait CoolerTypes {\n fn dummy(&self) {}"},{"original_begin_line":77,"original_end_line":77,"expected_begin_line":66,"expected_end_line":66,"original":"}","expected":""},{"original_begin_line":79,"original_end_line":79,"expected_begin_line":67,"expected_end_line":70,"original":"fn Foo() where T: Bar {","expected":"fn Foo()\nwhere\n T: Bar,\n{"}]}] \ No newline at end of file +[{"name":"tests/writemode/source/json.rs","mismatches":[{"original_begin_line":5,"original_end_line":7,"expected_begin_line":5,"expected_end_line":5,"original":"fn foo_expr() {\n 1\n}","expected":"fn foo_expr() { 1 }"},{"original_begin_line":9,"original_end_line":11,"expected_begin_line":7,"expected_end_line":7,"original":"fn foo_stmt() {\n foo();\n}","expected":"fn foo_stmt() { foo(); }"},{"original_begin_line":13,"original_end_line":15,"expected_begin_line":9,"expected_end_line":9,"original":"fn foo_decl_local() {\n let z = 5;\n }","expected":"fn foo_decl_local() { let z = 5; }"},{"original_begin_line":17,"original_end_line":19,"expected_begin_line":11,"expected_end_line":11,"original":"fn foo_decl_item(x: &mut i32) {\n x = 3;\n}","expected":"fn foo_decl_item(x: &mut i32) { x = 3; }"},{"original_begin_line":21,"original_end_line":21,"expected_begin_line":13,"expected_end_line":13,"original":" fn empty() {","expected":"fn empty() {}"},{"original_begin_line":23,"original_end_line":23,"expected_begin_line":15,"expected_end_line":15,"original":"}","expected":"fn foo_return() -> String { \"yay\" }"},{"original_begin_line":25,"original_end_line":29,"expected_begin_line":17,"expected_end_line":20,"original":"fn foo_return() -> String {\n \"yay\"\n}\n\nfn foo_where() -> T where T: Sync {","expected":"fn foo_where() -> T\nwhere\n T: Sync,\n{"},{"original_begin_line":64,"original_end_line":66,"expected_begin_line":55,"expected_end_line":55,"original":"fn lots_of_space () {\n 1 \n}","expected":"fn lots_of_space() { 1 }"},{"original_begin_line":71,"original_end_line":72,"expected_begin_line":60,"expected_end_line":60,"original":" fn dummy(&self) {\n }","expected":" fn dummy(&self) {}"},{"original_begin_line":75,"original_end_line":75,"expected_begin_line":63,"expected_end_line":64,"original":"trait CoolerTypes { fn dummy(&self) { ","expected":"trait CoolerTypes {\n fn dummy(&self) {}"},{"original_begin_line":77,"original_end_line":77,"expected_begin_line":66,"expected_end_line":66,"original":"}","expected":""},{"original_begin_line":79,"original_end_line":79,"expected_begin_line":67,"expected_end_line":70,"original":"fn Foo() where T: Bar {","expected":"fn Foo()\nwhere\n T: Bar,\n{"}]}] diff --git a/tests/writemode/target/stdin.json b/tests/writemode/target/stdin.json index 20e38f57f4a7..ae6796863e5e 100644 --- a/tests/writemode/target/stdin.json +++ b/tests/writemode/target/stdin.json @@ -1 +1 @@ -[{"name":"stdin","mismatches":[{"original_begin_line":1,"original_end_line":6,"expected_begin_line":1,"expected_end_line":2,"original":"\nfn\n some( )\n{\n}\nfn main () {}","expected":"fn some() {}\nfn main() {}"}]}] \ No newline at end of file +[{"name":"stdin","mismatches":[{"original_begin_line":1,"original_end_line":6,"expected_begin_line":1,"expected_end_line":2,"original":"\nfn\n some( )\n{\n}\nfn main () {}","expected":"fn some() {}\nfn main() {}"}]}] From 894a3c0e7745ece39fcee89d38389a789cfe520a Mon Sep 17 00:00:00 2001 From: Tim Date: Tue, 23 Jun 2020 01:20:29 +0100 Subject: [PATCH 129/401] Fix newlines in JSON output (#4262) * Fix newlines in JSON output This changes the JSON output to be more consistent about where newlines are included. Previously it only included them between lines in a multiline diff. That meant single line changes were treated a bit weirdly. This changes it to append a newline to every line. When feeding the results into `arc lint` this behaves correctly. I have only done limited testing though, in particular there's a possibility it might not work with files with `\r\n` endings (though that would have been the case before too). Fixes #4259 * Update tests # Conflicts: # tests/writemode/target/output.json --- src/emitter/json.rs | 38 ++++++++++++++++-------------- tests/writemode/target/output.json | 2 +- tests/writemode/target/stdin.json | 2 +- 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/emitter/json.rs b/src/emitter/json.rs index 7c0f862cbc63..c7f68d4675a6 100644 --- a/src/emitter/json.rs +++ b/src/emitter/json.rs @@ -65,20 +65,22 @@ impl JsonEmitter { let mut expected_end_line = expected_begin_line; let mut original_line_counter = 0; let mut expected_line_counter = 0; - let mut original_lines = vec![]; - let mut expected_lines = vec![]; + let mut original = String::new(); + let mut expected = String::new(); for line in mismatch.lines { match line { DiffLine::Expected(msg) => { expected_end_line = expected_begin_line + expected_line_counter; expected_line_counter += 1; - expected_lines.push(msg) + expected.push_str(&msg); + expected.push('\n'); } DiffLine::Resulting(msg) => { original_end_line = original_begin_line + original_line_counter; original_line_counter += 1; - original_lines.push(msg) + original.push_str(&msg); + original.push('\n'); } DiffLine::Context(_) => continue, } @@ -89,8 +91,8 @@ impl JsonEmitter { original_end_line, expected_begin_line, expected_end_line, - original: original_lines.join("\n"), - expected: expected_lines.join("\n"), + original, + expected, }); } self.mismatched_files.push(MismatchedFile { @@ -120,8 +122,8 @@ mod tests { original_end_line: 79, expected_begin_line: 79, expected_end_line: 82, - original: String::from("fn Foo() where T: Bar {"), - expected: String::from("fn Foo()\nwhere\n T: Bar,\n{"), + original: String::from("fn Foo() where T: Bar {\n"), + expected: String::from("fn Foo()\nwhere\n T: Bar,\n{\n"), }], }; let mismatch = Mismatch { @@ -158,10 +160,10 @@ mod tests { expected_begin_line: 5, expected_end_line: 5, original: String::from( - "fn foo(_x: &u64) -> Option<&(dyn::std::error::Error + 'static)> {", + "fn foo(_x: &u64) -> Option<&(dyn::std::error::Error + 'static)> {\n", ), expected: String::from( - "fn foo(_x: &u64) -> Option<&(dyn ::std::error::Error + 'static)> {", + "fn foo(_x: &u64) -> Option<&(dyn ::std::error::Error + 'static)> {\n", ), }], }; @@ -260,8 +262,8 @@ mod tests { original_end_line: 2, expected_begin_line: 2, expected_end_line: 2, - original: String::from("println!(\"Hello, world!\");"), - expected: String::from(" println!(\"Hello, world!\");"), + original: String::from("println!(\"Hello, world!\");\n"), + expected: String::from(" println!(\"Hello, world!\");\n"), }, MismatchedBlock { original_begin_line: 7, @@ -269,10 +271,10 @@ mod tests { expected_begin_line: 7, expected_end_line: 10, original: String::from( - "#[test]\nfn it_works() {\n assert_eq!(2 + 2, 4);\n}", + "#[test]\nfn it_works() {\n assert_eq!(2 + 2, 4);\n}\n", ), expected: String::from( - " #[test]\n fn it_works() {\n assert_eq!(2 + 2, 4);\n }", + " #[test]\n fn it_works() {\n assert_eq!(2 + 2, 4);\n }\n", ), }, ], @@ -321,8 +323,8 @@ mod tests { original_end_line: 2, expected_begin_line: 2, expected_end_line: 2, - original: String::from("println!(\"Hello, world!\");"), - expected: String::from(" println!(\"Hello, world!\");"), + original: String::from("println!(\"Hello, world!\");\n"), + expected: String::from(" println!(\"Hello, world!\");\n"), }], }; @@ -333,8 +335,8 @@ mod tests { original_end_line: 2, expected_begin_line: 2, expected_end_line: 2, - original: String::from("println!(\"Greetings!\");"), - expected: String::from(" println!(\"Greetings!\");"), + original: String::from("println!(\"Greetings!\");\n"), + expected: String::from(" println!(\"Greetings!\");\n"), }], }; diff --git a/tests/writemode/target/output.json b/tests/writemode/target/output.json index acb33dea7ef3..d8b5467ee91c 100644 --- a/tests/writemode/target/output.json +++ b/tests/writemode/target/output.json @@ -1 +1 @@ -[{"name":"tests/writemode/source/json.rs","mismatches":[{"original_begin_line":5,"original_end_line":7,"expected_begin_line":5,"expected_end_line":5,"original":"fn foo_expr() {\n 1\n}","expected":"fn foo_expr() { 1 }"},{"original_begin_line":9,"original_end_line":11,"expected_begin_line":7,"expected_end_line":7,"original":"fn foo_stmt() {\n foo();\n}","expected":"fn foo_stmt() { foo(); }"},{"original_begin_line":13,"original_end_line":15,"expected_begin_line":9,"expected_end_line":9,"original":"fn foo_decl_local() {\n let z = 5;\n }","expected":"fn foo_decl_local() { let z = 5; }"},{"original_begin_line":17,"original_end_line":19,"expected_begin_line":11,"expected_end_line":11,"original":"fn foo_decl_item(x: &mut i32) {\n x = 3;\n}","expected":"fn foo_decl_item(x: &mut i32) { x = 3; }"},{"original_begin_line":21,"original_end_line":21,"expected_begin_line":13,"expected_end_line":13,"original":" fn empty() {","expected":"fn empty() {}"},{"original_begin_line":23,"original_end_line":23,"expected_begin_line":15,"expected_end_line":15,"original":"}","expected":"fn foo_return() -> String { \"yay\" }"},{"original_begin_line":25,"original_end_line":29,"expected_begin_line":17,"expected_end_line":20,"original":"fn foo_return() -> String {\n \"yay\"\n}\n\nfn foo_where() -> T where T: Sync {","expected":"fn foo_where() -> T\nwhere\n T: Sync,\n{"},{"original_begin_line":64,"original_end_line":66,"expected_begin_line":55,"expected_end_line":55,"original":"fn lots_of_space () {\n 1 \n}","expected":"fn lots_of_space() { 1 }"},{"original_begin_line":71,"original_end_line":72,"expected_begin_line":60,"expected_end_line":60,"original":" fn dummy(&self) {\n }","expected":" fn dummy(&self) {}"},{"original_begin_line":75,"original_end_line":75,"expected_begin_line":63,"expected_end_line":64,"original":"trait CoolerTypes { fn dummy(&self) { ","expected":"trait CoolerTypes {\n fn dummy(&self) {}"},{"original_begin_line":77,"original_end_line":77,"expected_begin_line":66,"expected_end_line":66,"original":"}","expected":""},{"original_begin_line":79,"original_end_line":79,"expected_begin_line":67,"expected_end_line":70,"original":"fn Foo() where T: Bar {","expected":"fn Foo()\nwhere\n T: Bar,\n{"}]}] +[{"name":"tests/writemode/source/json.rs","mismatches":[{"original_begin_line":5,"original_end_line":7,"expected_begin_line":5,"expected_end_line":5,"original":"fn foo_expr() {\n 1\n}\n","expected":"fn foo_expr() { 1 }\n"},{"original_begin_line":9,"original_end_line":11,"expected_begin_line":7,"expected_end_line":7,"original":"fn foo_stmt() {\n foo();\n}\n","expected":"fn foo_stmt() { foo(); }\n"},{"original_begin_line":13,"original_end_line":15,"expected_begin_line":9,"expected_end_line":9,"original":"fn foo_decl_local() {\n let z = 5;\n }\n","expected":"fn foo_decl_local() { let z = 5; }\n"},{"original_begin_line":17,"original_end_line":19,"expected_begin_line":11,"expected_end_line":11,"original":"fn foo_decl_item(x: &mut i32) {\n x = 3;\n}\n","expected":"fn foo_decl_item(x: &mut i32) { x = 3; }\n"},{"original_begin_line":21,"original_end_line":21,"expected_begin_line":13,"expected_end_line":13,"original":" fn empty() {\n","expected":"fn empty() {}\n"},{"original_begin_line":23,"original_end_line":23,"expected_begin_line":15,"expected_end_line":15,"original":"}\n","expected":"fn foo_return() -> String { \"yay\" }\n"},{"original_begin_line":25,"original_end_line":29,"expected_begin_line":17,"expected_end_line":20,"original":"fn foo_return() -> String {\n \"yay\"\n}\n\nfn foo_where() -> T where T: Sync {\n","expected":"fn foo_where() -> T\nwhere\n T: Sync,\n{\n"},{"original_begin_line":64,"original_end_line":66,"expected_begin_line":55,"expected_end_line":55,"original":"fn lots_of_space () {\n 1 \n}\n","expected":"fn lots_of_space() { 1 }\n"},{"original_begin_line":71,"original_end_line":72,"expected_begin_line":60,"expected_end_line":60,"original":" fn dummy(&self) {\n }\n","expected":" fn dummy(&self) {}\n"},{"original_begin_line":75,"original_end_line":75,"expected_begin_line":63,"expected_end_line":64,"original":"trait CoolerTypes { fn dummy(&self) { \n","expected":"trait CoolerTypes {\n fn dummy(&self) {}\n"},{"original_begin_line":77,"original_end_line":77,"expected_begin_line":66,"expected_end_line":66,"original":"}\n","expected":""},{"original_begin_line":79,"original_end_line":79,"expected_begin_line":67,"expected_end_line":70,"original":"fn Foo() where T: Bar {\n","expected":"fn Foo()\nwhere\n T: Bar,\n{\n"}]}] diff --git a/tests/writemode/target/stdin.json b/tests/writemode/target/stdin.json index ae6796863e5e..6f5d5bfb8ca2 100644 --- a/tests/writemode/target/stdin.json +++ b/tests/writemode/target/stdin.json @@ -1 +1 @@ -[{"name":"stdin","mismatches":[{"original_begin_line":1,"original_end_line":6,"expected_begin_line":1,"expected_end_line":2,"original":"\nfn\n some( )\n{\n}\nfn main () {}","expected":"fn some() {}\nfn main() {}"}]}] +[{"name":"stdin","mismatches":[{"original_begin_line":1,"original_end_line":6,"expected_begin_line":1,"expected_end_line":2,"original":"\nfn\n some( )\n{\n}\nfn main () {}\n","expected":"fn some() {}\nfn main() {}\n"}]}] From 34d374ee5d23951d166ca3f51d477ddf6526a2fa Mon Sep 17 00:00:00 2001 From: Seiichi Uchida Date: Fri, 3 Jul 2020 11:13:16 +0900 Subject: [PATCH 130/401] Use when emitting stdin as filename (#4298) # Conflicts: # src/config/file_lines.rs # src/rustfmt/main.rs # src/test/mod.rs --- src/config/file_lines.rs | 2 +- src/test/mod.rs | 6 +++--- tests/writemode/target/stdin.json | 2 +- tests/writemode/target/stdin.xml | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/config/file_lines.rs b/src/config/file_lines.rs index 7b498dc46b32..e4e51a3f3b40 100644 --- a/src/config/file_lines.rs +++ b/src/config/file_lines.rs @@ -39,7 +39,7 @@ impl fmt::Display for FileName { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { FileName::Real(p) => write!(f, "{}", p.to_str().unwrap()), - FileName::Stdin => write!(f, "stdin"), + FileName::Stdin => write!(f, ""), } } } diff --git a/src/test/mod.rs b/src/test/mod.rs index db1cf88479cf..2d5a8f22053f 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -466,9 +466,9 @@ fn stdin_formatting_smoke_test() { } #[cfg(not(windows))] - assert_eq!(buf, "stdin:\n\nfn main() {}\n".as_bytes()); + assert_eq!(buf, ":\n\nfn main() {}\n".as_bytes()); #[cfg(windows)] - assert_eq!(buf, "stdin:\n\nfn main() {}\r\n".as_bytes()); + assert_eq!(buf, ":\n\nfn main() {}\r\n".as_bytes()); } #[test] @@ -1013,5 +1013,5 @@ fn verify_check_l_works_with_stdin() { .wait_with_output() .expect("Failed to wait on rustfmt child"); assert!(output.status.success()); - assert_eq!(std::str::from_utf8(&output.stdout).unwrap(), "stdin\n"); + assert_eq!(std::str::from_utf8(&output.stdout).unwrap(), "\n"); } diff --git a/tests/writemode/target/stdin.json b/tests/writemode/target/stdin.json index 6f5d5bfb8ca2..dbf2c4863229 100644 --- a/tests/writemode/target/stdin.json +++ b/tests/writemode/target/stdin.json @@ -1 +1 @@ -[{"name":"stdin","mismatches":[{"original_begin_line":1,"original_end_line":6,"expected_begin_line":1,"expected_end_line":2,"original":"\nfn\n some( )\n{\n}\nfn main () {}\n","expected":"fn some() {}\nfn main() {}\n"}]}] +[{"name":"","mismatches":[{"original_begin_line":1,"original_end_line":6,"expected_begin_line":1,"expected_end_line":2,"original":"\nfn\n some( )\n{\n}\nfn main () {}\n","expected":"fn some() {}\nfn main() {}\n"}]}] diff --git a/tests/writemode/target/stdin.xml b/tests/writemode/target/stdin.xml index e70708338f56..a7301bbc553c 100644 --- a/tests/writemode/target/stdin.xml +++ b/tests/writemode/target/stdin.xml @@ -1,2 +1,2 @@ - - + + From 5056f4cfb311a084420f1828cd58af94d143f5e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Wed, 5 Jan 2022 21:19:36 +0100 Subject: [PATCH 131/401] some minor clippy fixes --- src/cargo-fmt/main.rs | 3 +-- src/format_report_formatter.rs | 1 + src/macros.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cargo-fmt/main.rs b/src/cargo-fmt/main.rs index 759b21218c35..8cb7b4585ecb 100644 --- a/src/cargo-fmt/main.rs +++ b/src/cargo-fmt/main.rs @@ -387,8 +387,7 @@ fn get_targets_root_only( .unwrap_or_default() == current_dir_manifest }) - .map(|p| p.targets) - .flatten() + .flat_map(|p| p.targets) .collect(), }; diff --git a/src/format_report_formatter.rs b/src/format_report_formatter.rs index c820259256c4..90406cdb95e2 100644 --- a/src/format_report_formatter.rs +++ b/src/format_report_formatter.rs @@ -20,6 +20,7 @@ impl<'a> FormatReportFormatterBuilder<'a> { } /// Enables colors and formatting in the output. + #[must_use] pub fn enable_colors(self, enable_colors: bool) -> Self { Self { enable_colors, diff --git a/src/macros.rs b/src/macros.rs index f29552caf8d8..fdbe3374615c 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -152,7 +152,7 @@ pub(crate) fn rewrite_macro( ) -> Option { let should_skip = context .skip_context - .skip_macro(&context.snippet(mac.path.span).to_owned()); + .skip_macro(context.snippet(mac.path.span)); if should_skip { None } else { From 9e1973f1d9b94914c3e5539f3ed992c19389b4a7 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Sun, 23 Jan 2022 11:18:17 -0600 Subject: [PATCH 132/401] chore: bump toolchain, update test --- rust-toolchain | 2 +- src/test/mod.rs | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/rust-toolchain b/rust-toolchain index d4cdcec2018a..d8bf02aec85e 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2021-12-29" +channel = "nightly-2022-01-23" components = ["rustc-dev"] diff --git a/src/test/mod.rs b/src/test/mod.rs index c399512ba7e3..ab966d4a3607 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -572,7 +572,10 @@ fn stdin_generated_files_issue_5172() { assert!(session.has_no_errors()); } // N.B. this should be changed once `format_generated_files` is supported with stdin - assert_eq!(buf, "stdin:\n\n//@generated\nfn main() {}\n".as_bytes()); + assert_eq!( + String::from_utf8(buf).unwrap(), + ":\n\n//@generated\nfn main() {}\n", + ); } #[test] From b4a4bf0bf8a16913b9f16f2aee7030065ff00931 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szymon=20Giba=C5=82a?= Date: Sat, 29 Jan 2022 05:55:47 +0100 Subject: [PATCH 133/401] Fix formatting of comments in empty structs (#5171) * Fix formatting of comments in empty structs * Add tests * Add single line tests * Fix block comments * Revert changes of test source files --- src/items.rs | 10 +- tests/source/issue_4854.rs | 113 +++++++++++++++++ .../comments-in-lists/format-doc-comments.rs | 10 +- .../comments-in-lists/wrap-comments-false.rs | 10 +- .../wrap-comments-not-normalized.rs | 8 +- .../comments-in-lists/wrap-comments-true.rs | 8 +- tests/target/issue_4854.rs | 115 ++++++++++++++++++ 7 files changed, 251 insertions(+), 23 deletions(-) create mode 100644 tests/source/issue_4854.rs create mode 100644 tests/target/issue_4854.rs diff --git a/src/items.rs b/src/items.rs index babc56f86edc..4c7a33c86d2f 100644 --- a/src/items.rs +++ b/src/items.rs @@ -1374,17 +1374,21 @@ fn format_empty_struct_or_tuple( result.push_str(&offset.to_string_with_newline(context.config)) } result.push_str(opener); - match rewrite_missing_comment(span, Shape::indented(offset, context.config), context) { + + // indented shape for proper indenting of multi-line comments + let shape = Shape::indented(offset.block_indent(context.config), context.config); + match rewrite_missing_comment(span, shape, context) { Some(ref s) if s.is_empty() => (), Some(ref s) => { - if !is_single_line(s) || first_line_contains_single_line_comment(s) { + let is_multi_line = !is_single_line(s); + if is_multi_line || first_line_contains_single_line_comment(s) { let nested_indent_str = offset .block_indent(context.config) .to_string_with_newline(context.config); result.push_str(&nested_indent_str); } result.push_str(s); - if last_line_contains_single_line_comment(s) { + if is_multi_line || last_line_contains_single_line_comment(s) { result.push_str(&offset.to_string_with_newline(context.config)); } } diff --git a/tests/source/issue_4854.rs b/tests/source/issue_4854.rs new file mode 100644 index 000000000000..35d6e21affee --- /dev/null +++ b/tests/source/issue_4854.rs @@ -0,0 +1,113 @@ +struct Struct { + // Multiline comment + // should be formatted + // properly. +} + +struct Struct2 { + // This formatting +// Should be changed +} + +struct Struct3( + // This + // is + // correct +); + +struct Struct4( + // This +// is +// not +// correct +); + +struct Struct5 { + /* + Comment block + with many lines. + */ +} + +struct Struct6( + /* + Comment block + with many lines. + */ +); + +struct Struct7 { + /* +Invalid +format +*/ +} + +struct Struct8( + /* +Invalid +format +*/ +); + +struct Struct9 { /* bar */ } + +struct Struct10 { /* bar +baz +*/ } + +mod module { + struct Struct { + // Multiline comment + // should be formatted + // properly. + } + + struct Struct2 { + // This formatting +// Should be changed + } + + struct Struct3( + // This + // is + // correct + ); + + struct Struct4( + // This + // is + // not +// correct + ); + + struct Struct5 { + /* + Comment block + with many lines. + */ + } + + struct Struct6( + /* + Comment block + with many lines. + */ + ); + + struct Struct7 { + /* +Invalid +format +*/ + } + + struct Struct8( + /* +Invalid +format +*/ + ); + + struct Struct9 { /* bar */ } +} diff --git a/tests/target/comments-in-lists/format-doc-comments.rs b/tests/target/comments-in-lists/format-doc-comments.rs index be31bf0a3319..be4b7a8c42e3 100644 --- a/tests/target/comments-in-lists/format-doc-comments.rs +++ b/tests/target/comments-in-lists/format-doc-comments.rs @@ -25,9 +25,8 @@ pub enum E { } pub enum E2 { - // This can be changed once https://github.com/rust-lang/rustfmt/issues/4854 is fixed -// Expand as needed, numbers should be ascending according to the stage -// through the inclusion pipeline, or according to the descriptions + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions } pub struct S { @@ -42,9 +41,8 @@ pub struct S { } pub struct S2 { - // This can be changed once https://github.com/rust-lang/rustfmt/issues/4854 is fixed -// Expand as needed, numbers should be ascending according to the stage -// through the inclusion pipeline, or according to the descriptions + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions } fn foo( diff --git a/tests/target/comments-in-lists/wrap-comments-false.rs b/tests/target/comments-in-lists/wrap-comments-false.rs index 80aea59d1b52..db4da6223721 100644 --- a/tests/target/comments-in-lists/wrap-comments-false.rs +++ b/tests/target/comments-in-lists/wrap-comments-false.rs @@ -13,9 +13,8 @@ pub enum E { } pub enum E2 { - // This can be changed once https://github.com/rust-lang/rustfmt/issues/4854 is fixed -// Expand as needed, numbers should be ascending according to the stage -// through the inclusion pipeline, or according to the descriptions + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions } pub struct S { @@ -30,9 +29,8 @@ pub struct S { } pub struct S2 { - // This can be changed once https://github.com/rust-lang/rustfmt/issues/4854 is fixed -// Expand as needed, numbers should be ascending according to the stage -// through the inclusion pipeline, or according to the descriptions + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions } fn foo( diff --git a/tests/target/comments-in-lists/wrap-comments-not-normalized.rs b/tests/target/comments-in-lists/wrap-comments-not-normalized.rs index 52315f470e4b..9b9147eb1247 100644 --- a/tests/target/comments-in-lists/wrap-comments-not-normalized.rs +++ b/tests/target/comments-in-lists/wrap-comments-not-normalized.rs @@ -14,8 +14,8 @@ pub enum E { pub enum E2 { // This can be changed once https://github.com/rust-lang/rustfmt/issues/4854 is fixed -// Expand as needed, numbers should be ascending according to the stage -// through the inclusion pipeline, or according to the descriptions + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions } pub enum E3 { @@ -42,8 +42,8 @@ pub struct S { pub struct S2 { // This can be changed once https://github.com/rust-lang/rustfmt/issues/4854 is fixed -// Expand as needed, numbers should be ascending according to the stage -// through the inclusion pipeline, or according to the descriptions + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions } pub struct S3 { diff --git a/tests/target/comments-in-lists/wrap-comments-true.rs b/tests/target/comments-in-lists/wrap-comments-true.rs index e0bfcf0b5007..c1531d22a4a7 100644 --- a/tests/target/comments-in-lists/wrap-comments-true.rs +++ b/tests/target/comments-in-lists/wrap-comments-true.rs @@ -15,8 +15,8 @@ pub enum E { pub enum E2 { // This can be changed once https://github.com/rust-lang/rustfmt/issues/4854 is fixed -// Expand as needed, numbers should be ascending according to the stage -// through the inclusion pipeline, or according to the descriptions + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions } pub enum E3 { @@ -43,8 +43,8 @@ pub struct S { pub struct S2 { // This can be changed once https://github.com/rust-lang/rustfmt/issues/4854 is fixed -// Expand as needed, numbers should be ascending according to the stage -// through the inclusion pipeline, or according to the descriptions + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions } pub struct S3 { diff --git a/tests/target/issue_4854.rs b/tests/target/issue_4854.rs new file mode 100644 index 000000000000..a81c5a5171fb --- /dev/null +++ b/tests/target/issue_4854.rs @@ -0,0 +1,115 @@ +struct Struct { + // Multiline comment + // should be formatted + // properly. +} + +struct Struct2 { + // This formatting + // Should be changed +} + +struct Struct3( + // This + // is + // correct +); + +struct Struct4( + // This + // is + // not + // correct +); + +struct Struct5 { + /* + Comment block + with many lines. + */ +} + +struct Struct6( + /* + Comment block + with many lines. + */ +); + +struct Struct7 { + /* + Invalid + format + */ +} + +struct Struct8( + /* + Invalid + format + */ +); + +struct Struct9 {/* bar */} + +struct Struct10 { + /* bar + baz + */ +} + +mod module { + struct Struct { + // Multiline comment + // should be formatted + // properly. + } + + struct Struct2 { + // This formatting + // Should be changed + } + + struct Struct3( + // This + // is + // correct + ); + + struct Struct4( + // This + // is + // not + // correct + ); + + struct Struct5 { + /* + Comment block + with many lines. + */ + } + + struct Struct6( + /* + Comment block + with many lines. + */ + ); + + struct Struct7 { + /* + Invalid + format + */ + } + + struct Struct8( + /* + Invalid + format + */ + ); + + struct Struct9 {/* bar */} +} From 8b0b213cddb23a9bbe421b717d1a0e5fb3982712 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Thu, 9 Dec 2021 23:17:43 -0500 Subject: [PATCH 134/401] Prevent adding trailing whitespace when rewriting ast::Param Fixes 5125 Previously, a newline was always added, even if the parameter name was not preceded by any param attrs. Now a newline is only added if there were param attrs. --- src/items.rs | 8 ++++++- .../attributes_in_formal_fuction_parameter.rs | 6 +++++ .../long_parameter_in_different_positions.rs | 24 +++++++++++++++++++ tests/target/issue-5125/minimum_example.rs | 6 +++++ .../with_leading_and_inline_comments.rs | 7 ++++++ 5 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 tests/target/issue-5125/attributes_in_formal_fuction_parameter.rs create mode 100644 tests/target/issue-5125/long_parameter_in_different_positions.rs create mode 100644 tests/target/issue-5125/minimum_example.rs create mode 100644 tests/target/issue-5125/with_leading_and_inline_comments.rs diff --git a/src/items.rs b/src/items.rs index 4c7a33c86d2f..007609aba249 100644 --- a/src/items.rs +++ b/src/items.rs @@ -2018,9 +2018,15 @@ impl Rewrite for ast::Param { { result.push_str(&ty_str); } else { + let prev_str = if param_attrs_result.is_empty() { + param_attrs_result + } else { + param_attrs_result + &shape.to_string_with_newline(context.config) + }; + result = combine_strs_with_missing_comments( context, - &(param_attrs_result + &shape.to_string_with_newline(context.config)), + &prev_str, param_name, span, shape, diff --git a/tests/target/issue-5125/attributes_in_formal_fuction_parameter.rs b/tests/target/issue-5125/attributes_in_formal_fuction_parameter.rs new file mode 100644 index 000000000000..5d167932828f --- /dev/null +++ b/tests/target/issue-5125/attributes_in_formal_fuction_parameter.rs @@ -0,0 +1,6 @@ +fn foo( + #[unused] a: >::ForeignType, +) { +} diff --git a/tests/target/issue-5125/long_parameter_in_different_positions.rs b/tests/target/issue-5125/long_parameter_in_different_positions.rs new file mode 100644 index 000000000000..cab20381ce8f --- /dev/null +++ b/tests/target/issue-5125/long_parameter_in_different_positions.rs @@ -0,0 +1,24 @@ +fn middle( + a: usize, + b: >::ForeignType, + c: bool, +) { +} + +fn last( + a: usize, + b: >::ForeignType, +) { +} + +fn first( + a: >::ForeignType, + b: usize, +) { +} diff --git a/tests/target/issue-5125/minimum_example.rs b/tests/target/issue-5125/minimum_example.rs new file mode 100644 index 000000000000..8003e66968c7 --- /dev/null +++ b/tests/target/issue-5125/minimum_example.rs @@ -0,0 +1,6 @@ +fn foo( + a: >::ForeignType, +) { +} diff --git a/tests/target/issue-5125/with_leading_and_inline_comments.rs b/tests/target/issue-5125/with_leading_and_inline_comments.rs new file mode 100644 index 000000000000..2340b2f3472e --- /dev/null +++ b/tests/target/issue-5125/with_leading_and_inline_comments.rs @@ -0,0 +1,7 @@ +fn foo( + // Pre Comment + a: >::ForeignType, // Inline comment +) { +} From 368a9b7cef25c22c3e836453e73d8584b251b578 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Campinas?= Date: Wed, 2 Feb 2022 02:06:14 +0100 Subject: [PATCH 135/401] Handle non-ascii character at boundary (#5089) * Handle non-ascii character at boundary * Replace substraction underflow check with early termination --- src/string.rs | 5 ++++- tests/source/issue-5023.rs | 22 ++++++++++++++++++++++ tests/target/issue-5023.rs | 23 +++++++++++++++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 tests/source/issue-5023.rs create mode 100644 tests/target/issue-5023.rs diff --git a/src/string.rs b/src/string.rs index 64ae15672df8..b65aa5b33b24 100644 --- a/src/string.rs +++ b/src/string.rs @@ -278,6 +278,9 @@ fn break_string(max_width: usize, trim_end: bool, line_end: &str, input: &[&str] } cur_index }; + if max_width_index_in_input == 0 { + return SnippetState::EndOfInput(input.concat()); + } // Find the position in input for breaking the string if line_end.is_empty() @@ -301,7 +304,7 @@ fn break_string(max_width: usize, trim_end: bool, line_end: &str, input: &[&str] return if trim_end { SnippetState::LineEnd(input[..=url_index_end].concat(), index_plus_ws + 1) } else { - return SnippetState::LineEnd(input[..=index_plus_ws].concat(), index_plus_ws + 1); + SnippetState::LineEnd(input[..=index_plus_ws].concat(), index_plus_ws + 1) }; } diff --git a/tests/source/issue-5023.rs b/tests/source/issue-5023.rs new file mode 100644 index 000000000000..ae1c723eff76 --- /dev/null +++ b/tests/source/issue-5023.rs @@ -0,0 +1,22 @@ +// rustfmt-wrap_comments: true + +/// A comment to test special unicode characters on boundaries +/// 是,是,是,是,是,是,是,是,是,是,是,是 it should break right here this goes to the next line +fn main() { + if xxx { + let xxx = xxx + .into_iter() + .filter(|(xxx, xxx)| { + if let Some(x) = Some(1) { + // xxxxxxxxxxxxxxxxxx, xxxxxxxxxxxx, xxxxxxxxxxxxxxxxxxxx xxx xxxxxxx, xxxxx xxx + // xxxxxxxxxx. xxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxx xxx xxxxxxx + // 是sdfadsdfxxxxxxxxx,sdfaxxxxxx_xxxxx_masdfaonxxx, + if false { + return true; + } + } + false + }) + .collect(); + } +} diff --git a/tests/target/issue-5023.rs b/tests/target/issue-5023.rs new file mode 100644 index 000000000000..4e84c7d98427 --- /dev/null +++ b/tests/target/issue-5023.rs @@ -0,0 +1,23 @@ +// rustfmt-wrap_comments: true + +/// A comment to test special unicode characters on boundaries +/// 是,是,是,是,是,是,是,是,是,是,是,是 it should break right here +/// this goes to the next line +fn main() { + if xxx { + let xxx = xxx + .into_iter() + .filter(|(xxx, xxx)| { + if let Some(x) = Some(1) { + // xxxxxxxxxxxxxxxxxx, xxxxxxxxxxxx, xxxxxxxxxxxxxxxxxxxx xxx xxxxxxx, xxxxx xxx + // xxxxxxxxxx. xxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxx xxx xxxxxxx + // 是sdfadsdfxxxxxxxxx,sdfaxxxxxx_xxxxx_masdfaonxxx, + if false { + return true; + } + } + false + }) + .collect(); + } +} From 606894eb0b1dcc3a4616aff4b54dbd19a2a46ba5 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Sun, 19 Dec 2021 16:47:13 -0500 Subject: [PATCH 136/401] Retain trailing separator when extracting the last inline post comment Fixes 5042 Previously, trailing commas were removed from the last inline comment. This lead to rustfmt refusing to format code snippets because the original comment did not match the rewritten comment. Now, when rustfmt extracts the last inline comment it leaves trailing separators alone. Rustfmt does not need to remove these separators because they are commented out. --- src/lists.rs | 27 ++++++++++++++----- .../multi-line_comment_with_trailing_comma.rs | 24 +++++++++++++++++ ...lti-line_comment_without_trailing_comma.rs | 24 +++++++++++++++++ ...single-line_comment_with_trailing_comma.rs | 9 +++++++ ...gle-line_comment_without_trailing_comma.rs | 10 +++++++ .../multi-line_comment_with_trailing_comma.rs | 24 +++++++++++++++++ ...lti-line_comment_without_trailing_comma.rs | 24 +++++++++++++++++ ...single-line_comment_with_trailing_comma.rs | 7 +++++ ...gle-line_comment_without_trailing_comma.rs | 7 +++++ 9 files changed, 149 insertions(+), 7 deletions(-) create mode 100644 tests/source/issue-5042/multi-line_comment_with_trailing_comma.rs create mode 100644 tests/source/issue-5042/multi-line_comment_without_trailing_comma.rs create mode 100644 tests/source/issue-5042/single-line_comment_with_trailing_comma.rs create mode 100644 tests/source/issue-5042/single-line_comment_without_trailing_comma.rs create mode 100644 tests/target/issue-5042/multi-line_comment_with_trailing_comma.rs create mode 100644 tests/target/issue-5042/multi-line_comment_without_trailing_comma.rs create mode 100644 tests/target/issue-5042/single-line_comment_with_trailing_comma.rs create mode 100644 tests/target/issue-5042/single-line_comment_without_trailing_comma.rs diff --git a/src/lists.rs b/src/lists.rs index 7aa0315f18c2..97eea19f9321 100644 --- a/src/lists.rs +++ b/src/lists.rs @@ -611,15 +611,30 @@ pub(crate) fn extract_post_comment( post_snippet: &str, comment_end: usize, separator: &str, + is_last: bool, ) -> Option { let white_space: &[_] = &[' ', '\t']; // Cleanup post-comment: strip separators and whitespace. let post_snippet = post_snippet[..comment_end].trim(); + + let last_inline_comment_ends_with_separator = if is_last { + if let Some(line) = post_snippet.lines().last() { + line.ends_with(separator) && line.trim().starts_with("//") + } else { + false + } + } else { + false + }; + let post_snippet_trimmed = if post_snippet.starts_with(|c| c == ',' || c == ':') { post_snippet[1..].trim_matches(white_space) } else if let Some(stripped) = post_snippet.strip_prefix(separator) { stripped.trim_matches(white_space) + } else if last_inline_comment_ends_with_separator { + // since we're on the last item it's fine to keep any trailing separators in comments + post_snippet.trim_matches(white_space) } // not comment or over two lines else if post_snippet.ends_with(',') @@ -748,14 +763,12 @@ where .snippet_provider .span_to_snippet(mk_sp((self.get_hi)(&item), next_start)) .unwrap_or(""); - let comment_end = get_comment_end( - post_snippet, - self.separator, - self.terminator, - self.inner.peek().is_none(), - ); + let is_last = self.inner.peek().is_none(); + let comment_end = + get_comment_end(post_snippet, self.separator, self.terminator, is_last); let new_lines = has_extra_newline(post_snippet, comment_end); - let post_comment = extract_post_comment(post_snippet, comment_end, self.separator); + let post_comment = + extract_post_comment(post_snippet, comment_end, self.separator, is_last); self.prev_span_end = (self.get_hi)(&item) + BytePos(comment_end as u32); diff --git a/tests/source/issue-5042/multi-line_comment_with_trailing_comma.rs b/tests/source/issue-5042/multi-line_comment_with_trailing_comma.rs new file mode 100644 index 000000000000..5d171f32a1ae --- /dev/null +++ b/tests/source/issue-5042/multi-line_comment_with_trailing_comma.rs @@ -0,0 +1,24 @@ +fn main() { + // 5042 deals with trailing commas, not the indentation issue of these comments + // When a future PR fixes the inentation issues these test can be updated + let _ = std::ops::Add::add(10, 20 + // ... + // ..., + ); + + let _ = std::ops::Add::add(10, 20 + /* ... */ + // ..., + ); + + let _ = std::ops::Add::add(10, 20 + // ..., + // ..., + ); + + let _ = std::ops::Add::add(10, 20 + // ..., + /* ... + */, + ); +} diff --git a/tests/source/issue-5042/multi-line_comment_without_trailing_comma.rs b/tests/source/issue-5042/multi-line_comment_without_trailing_comma.rs new file mode 100644 index 000000000000..b8a824b34b79 --- /dev/null +++ b/tests/source/issue-5042/multi-line_comment_without_trailing_comma.rs @@ -0,0 +1,24 @@ +fn main() { + // 5042 deals with trailing commas, not the indentation issue of these comments + // When a future PR fixes the inentation issues these test can be updated + let _ = std::ops::Add::add(10, 20 + // ... + // ... + ); + + let _ = std::ops::Add::add(10, 20 + /* ... */ + // ... + ); + + let _ = std::ops::Add::add(10, 20 + // ... + // ... + ); + + let _ = std::ops::Add::add(10, 20 + // ... + /* ... + */ + ); +} diff --git a/tests/source/issue-5042/single-line_comment_with_trailing_comma.rs b/tests/source/issue-5042/single-line_comment_with_trailing_comma.rs new file mode 100644 index 000000000000..bd765b7b41f4 --- /dev/null +++ b/tests/source/issue-5042/single-line_comment_with_trailing_comma.rs @@ -0,0 +1,9 @@ +fn main() { + let _ = std::ops::Add::add(10, 20 + // ..., + ); + + let _ = std::ops::Add::add(10, 20 + /* ... */, + ); +} diff --git a/tests/source/issue-5042/single-line_comment_without_trailing_comma.rs b/tests/source/issue-5042/single-line_comment_without_trailing_comma.rs new file mode 100644 index 000000000000..2ed8de875add --- /dev/null +++ b/tests/source/issue-5042/single-line_comment_without_trailing_comma.rs @@ -0,0 +1,10 @@ +fn main() { + let _ = std::ops::Add::add(10, 20 + // ... + ); + + let _ = std::ops::Add::add(10, 20 + /* ... */ + ); +} + diff --git a/tests/target/issue-5042/multi-line_comment_with_trailing_comma.rs b/tests/target/issue-5042/multi-line_comment_with_trailing_comma.rs new file mode 100644 index 000000000000..1ae1212b488d --- /dev/null +++ b/tests/target/issue-5042/multi-line_comment_with_trailing_comma.rs @@ -0,0 +1,24 @@ +fn main() { + // 5042 deals with trailing commas, not the indentation issue of these comments + // When a future PR fixes the inentation issues these test can be updated + let _ = std::ops::Add::add( + 10, 20, // ... + // ..., + ); + + let _ = std::ops::Add::add( + 10, 20, /* ... */ + // ..., + ); + + let _ = std::ops::Add::add( + 10, 20, // ..., + // ..., + ); + + let _ = std::ops::Add::add( + 10, 20, // ..., + /* ... + */ + ); +} diff --git a/tests/target/issue-5042/multi-line_comment_without_trailing_comma.rs b/tests/target/issue-5042/multi-line_comment_without_trailing_comma.rs new file mode 100644 index 000000000000..30d174664c9c --- /dev/null +++ b/tests/target/issue-5042/multi-line_comment_without_trailing_comma.rs @@ -0,0 +1,24 @@ +fn main() { + // 5042 deals with trailing commas, not the indentation issue of these comments + // When a future PR fixes the inentation issues these test can be updated + let _ = std::ops::Add::add( + 10, 20, // ... + // ... + ); + + let _ = std::ops::Add::add( + 10, 20, /* ... */ + // ... + ); + + let _ = std::ops::Add::add( + 10, 20, // ... + // ... + ); + + let _ = std::ops::Add::add( + 10, 20, // ... + /* ... + */ + ); +} diff --git a/tests/target/issue-5042/single-line_comment_with_trailing_comma.rs b/tests/target/issue-5042/single-line_comment_with_trailing_comma.rs new file mode 100644 index 000000000000..87b651dd285e --- /dev/null +++ b/tests/target/issue-5042/single-line_comment_with_trailing_comma.rs @@ -0,0 +1,7 @@ +fn main() { + let _ = std::ops::Add::add( + 10, 20, // ..., + ); + + let _ = std::ops::Add::add(10, 20 /* ... */); +} diff --git a/tests/target/issue-5042/single-line_comment_without_trailing_comma.rs b/tests/target/issue-5042/single-line_comment_without_trailing_comma.rs new file mode 100644 index 000000000000..116df86a4b55 --- /dev/null +++ b/tests/target/issue-5042/single-line_comment_without_trailing_comma.rs @@ -0,0 +1,7 @@ +fn main() { + let _ = std::ops::Add::add( + 10, 20, // ... + ); + + let _ = std::ops::Add::add(10, 20 /* ... */); +} From b2c7a52ea865f877bc0b57075fe1a88f83120546 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Campinas?= Date: Mon, 31 Jan 2022 23:45:30 +0100 Subject: [PATCH 137/401] Fix import_granularity option when the use tree has an alias --- src/imports.rs | 6 ++++-- tests/source/5131.rs | 33 +++++++++++++++++++++++++++++++++ tests/target/5131.rs | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 tests/source/5131.rs create mode 100644 tests/target/5131.rs diff --git a/src/imports.rs b/src/imports.rs index 40e0d06f99df..c60bec6d4a20 100644 --- a/src/imports.rs +++ b/src/imports.rs @@ -238,7 +238,8 @@ impl fmt::Display for UseSegment { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { UseSegment::Glob => write!(f, "*"), - UseSegment::Ident(ref s, _) => write!(f, "{}", s), + UseSegment::Ident(ref s, Some(ref alias)) => write!(f, "{} as {}", s, alias), + UseSegment::Ident(ref s, None) => write!(f, "{}", s), UseSegment::Slf(..) => write!(f, "self"), UseSegment::Super(..) => write!(f, "super"), UseSegment::Crate(..) => write!(f, "crate"), @@ -622,7 +623,8 @@ impl UseTree { fn merge(&mut self, other: &UseTree, merge_by: SharedPrefix) { let mut prefix = 0; for (a, b) in self.path.iter().zip(other.path.iter()) { - if a.equal_except_alias(b) { + // only discard the alias at the root of the tree + if (prefix == 0 && a.equal_except_alias(b)) || a == b { prefix += 1; } else { break; diff --git a/tests/source/5131.rs b/tests/source/5131.rs new file mode 100644 index 000000000000..3e9139177c56 --- /dev/null +++ b/tests/source/5131.rs @@ -0,0 +1,33 @@ +// rustfmt-imports_granularity: Module + +#![allow(dead_code)] + +mod a { + pub mod b { + pub struct Data { + pub a: i32, + } + } + + use crate::a::b::Data; + use crate::a::b::Data as Data2; + + pub fn data(a: i32) -> Data { + Data { a } + } + + pub fn data2(a: i32) -> Data2 { + Data2 { a } + } + + #[cfg(test)] + mod tests { + use super::*; + + #[test] + pub fn test() { + data(1); + data2(1); + } + } +} diff --git a/tests/target/5131.rs b/tests/target/5131.rs new file mode 100644 index 000000000000..763024d6fa49 --- /dev/null +++ b/tests/target/5131.rs @@ -0,0 +1,32 @@ +// rustfmt-imports_granularity: Module + +#![allow(dead_code)] + +mod a { + pub mod b { + pub struct Data { + pub a: i32, + } + } + + use crate::a::b::{Data, Data as Data2}; + + pub fn data(a: i32) -> Data { + Data { a } + } + + pub fn data2(a: i32) -> Data2 { + Data2 { a } + } + + #[cfg(test)] + mod tests { + use super::*; + + #[test] + pub fn test() { + data(1); + data2(1); + } + } +} From fd6e11cc57da5728a9a52c0a515fd80e811a43d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Campinas?= Date: Wed, 2 Feb 2022 15:43:31 +0100 Subject: [PATCH 138/401] Add tests for the One and Crate variants --- tests/source/5131_crate.rs | 14 ++++++++++++++ tests/source/{5131.rs => 5131_module.rs} | 0 tests/source/5131_one.rs | 15 +++++++++++++++ tests/target/5131_crate.rs | 9 +++++++++ tests/target/{5131.rs => 5131_module.rs} | 0 tests/target/5131_one.rs | 12 ++++++++++++ 6 files changed, 50 insertions(+) create mode 100644 tests/source/5131_crate.rs rename tests/source/{5131.rs => 5131_module.rs} (100%) create mode 100644 tests/source/5131_one.rs create mode 100644 tests/target/5131_crate.rs rename tests/target/{5131.rs => 5131_module.rs} (100%) create mode 100644 tests/target/5131_one.rs diff --git a/tests/source/5131_crate.rs b/tests/source/5131_crate.rs new file mode 100644 index 000000000000..96a31659022a --- /dev/null +++ b/tests/source/5131_crate.rs @@ -0,0 +1,14 @@ +// rustfmt-imports_granularity: Crate + +use foo::a; +use foo::a; +use foo::b; +use foo::b as b2; +use foo::b::f; +use foo::b::g; +use foo::b::g as g2; +use foo::c; +use foo::d::e; +use qux::h; +use qux::h as h2; +use qux::i; diff --git a/tests/source/5131.rs b/tests/source/5131_module.rs similarity index 100% rename from tests/source/5131.rs rename to tests/source/5131_module.rs diff --git a/tests/source/5131_one.rs b/tests/source/5131_one.rs new file mode 100644 index 000000000000..61ddf13410d4 --- /dev/null +++ b/tests/source/5131_one.rs @@ -0,0 +1,15 @@ +// rustfmt-imports_granularity: One + +pub use foo::x; +pub use foo::x as x2; +pub use foo::y; +use bar::a; +use bar::b; +use bar::b::f; +use bar::b::f as f2; +use bar::b::g; +use bar::c; +use bar::d::e; +use bar::d::e as e2; +use qux::h; +use qux::i; diff --git a/tests/target/5131_crate.rs b/tests/target/5131_crate.rs new file mode 100644 index 000000000000..557d66703554 --- /dev/null +++ b/tests/target/5131_crate.rs @@ -0,0 +1,9 @@ +// rustfmt-imports_granularity: Crate + +use foo::{ + a, b, b as b2, + b::{f, g, g as g2}, + c, + d::e, +}; +use qux::{h, h as h2, i}; diff --git a/tests/target/5131.rs b/tests/target/5131_module.rs similarity index 100% rename from tests/target/5131.rs rename to tests/target/5131_module.rs diff --git a/tests/target/5131_one.rs b/tests/target/5131_one.rs new file mode 100644 index 000000000000..a086dae5a422 --- /dev/null +++ b/tests/target/5131_one.rs @@ -0,0 +1,12 @@ +// rustfmt-imports_granularity: One + +pub use foo::{x, x as x2, y}; +use { + bar::{ + a, + b::{self, f, g}, + c, + d::{e, e as e2}, + }, + qux::{h, i}, +}; From 5df8c8f7e554e036fcb14a2d93d145c92d56bc2e Mon Sep 17 00:00:00 2001 From: Frank King Date: Mon, 7 Feb 2022 10:57:39 +0800 Subject: [PATCH 139/401] Fix doc of generic items formmating error (#5124) * Fix doc of generic items formmating error * Remove tracked `attrs_end_with_doc_comment` flag in `RewriteContext` * Fix duplicated doc comments of const generic params * Fix `::span()` * Remove duplicated source file of `doc-of-generic-item.rs` --- src/spanned.rs | 15 ++++----------- src/types.rs | 11 ++++++++++- tests/target/doc-of-generic-item.rs | 14 ++++++++++++++ 3 files changed, 28 insertions(+), 12 deletions(-) create mode 100644 tests/target/doc-of-generic-item.rs diff --git a/src/spanned.rs b/src/spanned.rs index 8e6c75a3744a..2136cfeae1af 100644 --- a/src/spanned.rs +++ b/src/spanned.rs @@ -113,17 +113,10 @@ impl Spanned for ast::Param { impl Spanned for ast::GenericParam { fn span(&self) -> Span { - let lo = if let ast::GenericParamKind::Const { - ty: _, - kw_span, - default: _, - } = self.kind - { - kw_span.lo() - } else if self.attrs.is_empty() { - self.ident.span.lo() - } else { - self.attrs[0].span.lo() + let lo = match self.kind { + _ if !self.attrs.is_empty() => self.attrs[0].span.lo(), + ast::GenericParamKind::Const { kw_span, .. } => kw_span.lo(), + _ => self.ident.span.lo(), }; let hi = if self.bounds.is_empty() { self.ident.span.hi() diff --git a/src/types.rs b/src/types.rs index 5de30129266a..b13e75a93b35 100644 --- a/src/types.rs +++ b/src/types.rs @@ -575,7 +575,16 @@ impl Rewrite for ast::GenericParam { let mut result = String::with_capacity(128); // FIXME: If there are more than one attributes, this will force multiline. match self.attrs.rewrite(context, shape) { - Some(ref rw) if !rw.is_empty() => result.push_str(&format!("{} ", rw)), + Some(ref rw) if !rw.is_empty() => { + result.push_str(rw); + // When rewriting generic params, an extra newline should be put + // if the attributes end with a doc comment + if let Some(true) = self.attrs.last().map(|a| a.is_doc_comment()) { + result.push_str(&shape.indent.to_string_with_newline(context.config)); + } else { + result.push(' '); + } + } _ => (), } diff --git a/tests/target/doc-of-generic-item.rs b/tests/target/doc-of-generic-item.rs new file mode 100644 index 000000000000..2efc5e09a3d3 --- /dev/null +++ b/tests/target/doc-of-generic-item.rs @@ -0,0 +1,14 @@ +// Non-doc pre-comment of Foo +/// doc of Foo +// Non-doc post-comment of Foo +struct Foo< + // Non-doc pre-comment of 'a + /// doc of 'a + 'a, + // Non-doc pre-comment of T + /// doc of T + T, + // Non-doc pre-comment of N + /// doc of N + const N: item, +>; From ace7241087bedd2ea26d7b1dfcdd6133cbf77ce7 Mon Sep 17 00:00:00 2001 From: Gabriel Smith Date: Sat, 29 Jan 2022 16:44:49 -0500 Subject: [PATCH 140/401] Fix incorrect string indentation in macro defs with `format_strings` --- CHANGELOG.md | 4 ++++ src/utils.rs | 19 ++++++++++++++++--- tests/source/issue-4036/one.rs | 11 +++++++++++ tests/source/issue-4036/three.rs | 12 ++++++++++++ tests/source/issue-4036/two.rs | 11 +++++++++++ tests/target/issue-4036/one.rs | 12 ++++++++++++ tests/target/issue-4036/three.rs | 17 +++++++++++++++++ tests/target/issue-4036/two.rs | 16 ++++++++++++++++ 8 files changed, 99 insertions(+), 3 deletions(-) create mode 100644 tests/source/issue-4036/one.rs create mode 100644 tests/source/issue-4036/three.rs create mode 100644 tests/source/issue-4036/two.rs create mode 100644 tests/target/issue-4036/one.rs create mode 100644 tests/target/issue-4036/three.rs create mode 100644 tests/target/issue-4036/two.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index b59438dc4fe7..1b9208d31e00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Fixed + +- Fixes issue where wrapped strings would be incorrectly indented in macro defs when `format_strings` was enabled [#4036](https://github.com/rust-lang/rustfmt/issues/4036) + ## [1.4.38] 2021-10-20 ### Changed diff --git a/src/utils.rs b/src/utils.rs index 2428d8cb0fd8..35512e78fa6e 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -646,9 +646,22 @@ pub(crate) fn trim_left_preserve_layout( } /// Based on the given line, determine if the next line can be indented or not. -/// This allows to preserve the indentation of multi-line literals. -pub(crate) fn indent_next_line(kind: FullCodeCharKind, _line: &str, config: &Config) -> bool { - !(kind.is_string() || (config.version() == Version::Two && kind.is_commented_string())) +/// This allows to preserve the indentation of multi-line literals when +/// re-inserted a code block that has been formatted separately from the rest +/// of the code, such as code in macro defs or code blocks doc comments. +pub(crate) fn indent_next_line(kind: FullCodeCharKind, line: &str, config: &Config) -> bool { + if kind.is_string() { + // If the string ends with '\', the string has been wrapped over + // multiple lines. If `format_strings = true`, then the indentation of + // strings wrapped over multiple lines will have been adjusted while + // formatting the code block, therefore the string's indentation needs + // to be adjusted for the code surrounding the code block. + config.format_strings() && line.ends_with('\\') + } else if config.version() == Version::Two { + !kind.is_commented_string() + } else { + true + } } pub(crate) fn is_empty_line(s: &str) -> bool { diff --git a/tests/source/issue-4036/one.rs b/tests/source/issue-4036/one.rs new file mode 100644 index 000000000000..9f9675f51631 --- /dev/null +++ b/tests/source/issue-4036/one.rs @@ -0,0 +1,11 @@ +// rustfmt-format_strings: true + +macro_rules! test { + () => { + fn from() { + None.expect( + "We asserted that `buffer.len()` is exactly `$n` so we can expect `ApInt::from_iter` to be successful.", + ) + } + }; +} diff --git a/tests/source/issue-4036/three.rs b/tests/source/issue-4036/three.rs new file mode 100644 index 000000000000..e1865dd0868b --- /dev/null +++ b/tests/source/issue-4036/three.rs @@ -0,0 +1,12 @@ +// rustfmt-format_strings: true +// rustfmt-hard_tabs: true + +macro_rules! test { + () => { + fn from() { + None.expect( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", + ) + } + }; +} diff --git a/tests/source/issue-4036/two.rs b/tests/source/issue-4036/two.rs new file mode 100644 index 000000000000..fa54d2e3e09c --- /dev/null +++ b/tests/source/issue-4036/two.rs @@ -0,0 +1,11 @@ +// rustfmt-format_strings: true + +macro_rules! test { + () => { + fn from() { + None.expect( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", + ) + } + }; +} diff --git a/tests/target/issue-4036/one.rs b/tests/target/issue-4036/one.rs new file mode 100644 index 000000000000..54e490b7fbea --- /dev/null +++ b/tests/target/issue-4036/one.rs @@ -0,0 +1,12 @@ +// rustfmt-format_strings: true + +macro_rules! test { + () => { + fn from() { + None.expect( + "We asserted that `buffer.len()` is exactly `$n` so we can expect \ + `ApInt::from_iter` to be successful.", + ) + } + }; +} diff --git a/tests/target/issue-4036/three.rs b/tests/target/issue-4036/three.rs new file mode 100644 index 000000000000..394dc8633f53 --- /dev/null +++ b/tests/target/issue-4036/three.rs @@ -0,0 +1,17 @@ +// rustfmt-format_strings: true +// rustfmt-hard_tabs: true + +macro_rules! test { + () => { + fn from() { + None.expect( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor \ + incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis \ + nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. \ + Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu \ + fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in \ + culpa qui officia deserunt mollit anim id est laborum.", + ) + } + }; +} diff --git a/tests/target/issue-4036/two.rs b/tests/target/issue-4036/two.rs new file mode 100644 index 000000000000..01cafa76b684 --- /dev/null +++ b/tests/target/issue-4036/two.rs @@ -0,0 +1,16 @@ +// rustfmt-format_strings: true + +macro_rules! test { + () => { + fn from() { + None.expect( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor \ + incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis \ + nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. \ + Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu \ + fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in \ + culpa qui officia deserunt mollit anim id est laborum.", + ) + } + }; +} From 813d127c824eb22e3fe1fc7a34bf12ad4c0fa2f6 Mon Sep 17 00:00:00 2001 From: Stepan Koltsov Date: Mon, 7 Feb 2022 04:39:53 +0000 Subject: [PATCH 141/401] Clarify generated marker requires a config option --- CHANGELOG.md | 1 + Configurations.md | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b9208d31e00..5b2336085835 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,6 +61,7 @@ Note this hit the rustup distributions prior to the v1.4.38 release as part of a - New `One` variant added to `imports_granularity` configuration option which can be used to reformat all imports into a single use statement [#4669](https://github.com/rust-lang/rustfmt/issues/4669) - rustfmt will now skip files that are annotated with `@generated` at the top of the file [#3958](https://github.com/rust-lang/rustfmt/issues/3958) + if `format_generated_files` option is set to `false` (by default `@generated` files are formatted) - New configuration option `hex_literal_case` that allows user to control the casing utilized for hex literals [PR #4903](https://github.com/rust-lang/rustfmt/pull/4903) See the section on the configuration site for more information diff --git a/Configurations.md b/Configurations.md index 4476f2a449b1..2e2b0f7cfbec 100644 --- a/Configurations.md +++ b/Configurations.md @@ -930,6 +930,8 @@ fn add_one(x: i32) -> i32 { Format generated files. A file is considered generated if any of the first five lines contain a `@generated` comment marker. +By default, generated files are reformatted, i. e. `@generated` marker is ignored. +This option is currently ignored for stdin (`@generated` in stdin is ignored.) - **Default value**: `true` - **Possible values**: `true`, `false` From b05b3138001c97cbabeb3e20a0960b9f75341ee0 Mon Sep 17 00:00:00 2001 From: Tharun Rajendran Date: Fri, 11 Feb 2022 10:05:45 +0530 Subject: [PATCH 142/401] chore(rustfmt): remove executable path from usage string (#5216) * chore(rustfmt): remove executable path from usage string * add unit test for usage string * rename test and check usage text in a single assert --- src/bin/main.rs | 5 ++--- tests/rustfmt/main.rs | 9 +++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/bin/main.rs b/src/bin/main.rs index 6f5b09fc86ad..196de6056b5c 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -394,9 +394,8 @@ fn print_usage_to_stdout(opts: &Options, reason: &str) { format!("{}\n\n", reason) }; let msg = format!( - "{}Format Rust code\n\nusage: {} [options] ...", - sep, - env::args_os().next().unwrap().to_string_lossy() + "{}Format Rust code\n\nusage: rustfmt [options] ...", + sep ); println!("{}", opts.usage(&msg)); } diff --git a/tests/rustfmt/main.rs b/tests/rustfmt/main.rs index 8effb1c6fcab..6976cc4d346c 100644 --- a/tests/rustfmt/main.rs +++ b/tests/rustfmt/main.rs @@ -106,3 +106,12 @@ fn inline_config() { && contains("format_strings = true") ); } + +#[test] +fn rustfmt_usage_text() { + let args = [ + "--help", + ]; + let (stdout, _) = rustfmt(&args); + assert!(stdout.contains(&format!("Format Rust code\n\nusage: rustfmt [options] ..."))); +} From 1e78a2b258470fcab13e8c933212667e64b21d3e Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Wed, 29 Dec 2021 19:59:54 -0500 Subject: [PATCH 143/401] Leverage itemized blocks to support formatting markdown block quotes Fixes 5157 Doc comments support markdown, but rustfmt didn't previously assign any semantic value to leading '> ' in comments. This lead to poor formatting when using ``wrap_comments=true``. Now, rustfmt treats block quotes as itemized blocks, which greatly improves how block quotes are formatted when ``wrap_comments=true``. --- src/comment.rs | 46 ++++++++++++++++--- .../indented_itemized_markdown_blockquote.rs | 4 ++ .../nested_itemized_markdown_blockquote.rs | 10 ++++ .../support_itemized_markdown_blockquote.rs | 4 ++ .../indented_itemized_markdown_blockquote.rs | 6 +++ .../nested_itemized_markdown_blockquote.rs | 18 ++++++++ .../support_itemized_markdown_blockquote.rs | 6 +++ 7 files changed, 87 insertions(+), 7 deletions(-) create mode 100644 tests/source/issue-5157/indented_itemized_markdown_blockquote.rs create mode 100644 tests/source/issue-5157/nested_itemized_markdown_blockquote.rs create mode 100644 tests/source/issue-5157/support_itemized_markdown_blockquote.rs create mode 100644 tests/target/issue-5157/indented_itemized_markdown_blockquote.rs create mode 100644 tests/target/issue-5157/nested_itemized_markdown_blockquote.rs create mode 100644 tests/target/issue-5157/support_itemized_markdown_blockquote.rs diff --git a/src/comment.rs b/src/comment.rs index 0f850b9b2f2f..96778c4ef1dd 100644 --- a/src/comment.rs +++ b/src/comment.rs @@ -432,16 +432,16 @@ impl CodeBlockAttribute { /// Block that is formatted as an item. /// -/// An item starts with either a star `*` or a dash `-`. Different level of indentation are -/// handled by shrinking the shape accordingly. +/// An item starts with either a star `*` a dash `-` or a greater-than `>`. +/// Different level of indentation are handled by shrinking the shape accordingly. struct ItemizedBlock { /// the lines that are identified as part of an itemized block lines: Vec, - /// the number of whitespaces up to the item sigil + /// the number of characters (typically whitespaces) up to the item sigil indent: usize, /// the string that marks the start of an item opener: String, - /// sequence of whitespaces to prefix new lines that are part of the item + /// sequence of characters (typically whitespaces) to prefix new lines that are part of the item line_start: String, } @@ -449,19 +449,32 @@ impl ItemizedBlock { /// Returns `true` if the line is formatted as an item fn is_itemized_line(line: &str) -> bool { let trimmed = line.trim_start(); - trimmed.starts_with("* ") || trimmed.starts_with("- ") + trimmed.starts_with("* ") || trimmed.starts_with("- ") || trimmed.starts_with("> ") } /// Creates a new ItemizedBlock described with the given line. /// The `is_itemized_line` needs to be called first. fn new(line: &str) -> ItemizedBlock { let space_to_sigil = line.chars().take_while(|c| c.is_whitespace()).count(); - let indent = space_to_sigil + 2; + // +2 = '* ', which will add the appropriate amount of whitespace to keep itemized + // content formatted correctly. + let mut indent = space_to_sigil + 2; + let mut line_start = " ".repeat(indent); + + // Markdown blockquote start with a "> " + if line.trim_start().starts_with(">") { + // remove the original +2 indent because there might be multiple nested block quotes + // and it's easier to reason about the final indent by just taking the length + // of th new line_start. We update the indent because it effects the max width + // of each formatted line. + line_start = itemized_block_quote_start(line, line_start, 2); + indent = line_start.len(); + } ItemizedBlock { lines: vec![line[indent..].to_string()], indent, opener: line[..indent].to_string(), - line_start: " ".repeat(indent), + line_start, } } @@ -504,6 +517,25 @@ impl ItemizedBlock { } } +/// Determine the line_start when formatting markdown block quotes. +/// The original line_start likely contains indentation (whitespaces), which we'd like to +/// replace with '> ' characters. +fn itemized_block_quote_start(line: &str, mut line_start: String, remove_indent: usize) -> String { + let quote_level = line + .chars() + .take_while(|c| !c.is_alphanumeric()) + .fold(0, |acc, c| if c == '>' { acc + 1 } else { acc }); + + for _ in 0..remove_indent { + line_start.pop(); + } + + for _ in 0..quote_level { + line_start.push_str("> ") + } + line_start +} + struct CommentRewrite<'a> { result: String, code_block_buffer: String, diff --git a/tests/source/issue-5157/indented_itemized_markdown_blockquote.rs b/tests/source/issue-5157/indented_itemized_markdown_blockquote.rs new file mode 100644 index 000000000000..5c1d79a74309 --- /dev/null +++ b/tests/source/issue-5157/indented_itemized_markdown_blockquote.rs @@ -0,0 +1,4 @@ +// rustfmt-wrap_comments: true + +/// > For each sample received, the middleware internally maintains a sample_state relative to each DataReader. The sample_state can either be READ or NOT_READ. +fn block_quote() {} diff --git a/tests/source/issue-5157/nested_itemized_markdown_blockquote.rs b/tests/source/issue-5157/nested_itemized_markdown_blockquote.rs new file mode 100644 index 000000000000..cf200d04e08e --- /dev/null +++ b/tests/source/issue-5157/nested_itemized_markdown_blockquote.rs @@ -0,0 +1,10 @@ +// rustfmt-wrap_comments: true + +/// > For each sample received, the middleware internally maintains a sample_state relative to each DataReader. The sample_state can either be READ or NOT_READ. +/// +/// > > For each sample received, the middleware internally maintains a sample_state relative to each DataReader. The sample_state can either be READ or NOT_READ. +/// +/// > > > For each sample received, the middleware internally maintains a sample_state relative to each DataReader. The sample_state can either be READ or NOT_READ. +/// +/// > > > > > > > > For each sample received, the middleware internally maintains a sample_state relative to each DataReader. The sample_state can either be READ or NOT_READ. +fn block_quote() {} diff --git a/tests/source/issue-5157/support_itemized_markdown_blockquote.rs b/tests/source/issue-5157/support_itemized_markdown_blockquote.rs new file mode 100644 index 000000000000..eb436402e4e0 --- /dev/null +++ b/tests/source/issue-5157/support_itemized_markdown_blockquote.rs @@ -0,0 +1,4 @@ +// rustfmt-wrap_comments: true + +/// > For each sample received, the middleware internally maintains a sample_state relative to each DataReader. The sample_state can either be READ or NOT_READ. +fn block_quote() {} diff --git a/tests/target/issue-5157/indented_itemized_markdown_blockquote.rs b/tests/target/issue-5157/indented_itemized_markdown_blockquote.rs new file mode 100644 index 000000000000..e47677f20390 --- /dev/null +++ b/tests/target/issue-5157/indented_itemized_markdown_blockquote.rs @@ -0,0 +1,6 @@ +// rustfmt-wrap_comments: true + +/// > For each sample received, the middleware internally maintains a +/// > sample_state relative to each DataReader. The sample_state can +/// > either be READ or NOT_READ. +fn block_quote() {} diff --git a/tests/target/issue-5157/nested_itemized_markdown_blockquote.rs b/tests/target/issue-5157/nested_itemized_markdown_blockquote.rs new file mode 100644 index 000000000000..079510442b79 --- /dev/null +++ b/tests/target/issue-5157/nested_itemized_markdown_blockquote.rs @@ -0,0 +1,18 @@ +// rustfmt-wrap_comments: true + +/// > For each sample received, the middleware internally maintains a +/// > sample_state relative to each DataReader. The sample_state can either be +/// > READ or NOT_READ. +/// +/// > > For each sample received, the middleware internally maintains a +/// > > sample_state relative to each DataReader. The sample_state can either be +/// > > READ or NOT_READ. +/// +/// > > > For each sample received, the middleware internally maintains a +/// > > > sample_state relative to each DataReader. The sample_state can either +/// > > > be READ or NOT_READ. +/// +/// > > > > > > > > For each sample received, the middleware internally +/// > > > > > > > > maintains a sample_state relative to each DataReader. The +/// > > > > > > > > sample_state can either be READ or NOT_READ. +fn block_quote() {} diff --git a/tests/target/issue-5157/support_itemized_markdown_blockquote.rs b/tests/target/issue-5157/support_itemized_markdown_blockquote.rs new file mode 100644 index 000000000000..029ee37d22a8 --- /dev/null +++ b/tests/target/issue-5157/support_itemized_markdown_blockquote.rs @@ -0,0 +1,6 @@ +// rustfmt-wrap_comments: true + +/// > For each sample received, the middleware internally maintains a +/// > sample_state relative to each DataReader. The sample_state can either be +/// > READ or NOT_READ. +fn block_quote() {} From 6c476127aedc37b43769469f87e0fbc382382739 Mon Sep 17 00:00:00 2001 From: Travis Finkenauer Date: Tue, 15 Feb 2022 15:25:44 -0800 Subject: [PATCH 144/401] Add context to get_toml_path() error (#5207) * rustfmt: print full error chain * Add context to get_toml_path() error Instead of an error like: ``` Permission denied (os error 13) ``` Gives error like: ``` Failed to get metadata for config file "/root/.rustfmt.toml": Permission denied (os error 13) ``` --- src/bin/main.rs | 2 +- src/config/mod.rs | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/bin/main.rs b/src/bin/main.rs index 196de6056b5c..ad10b9ede608 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -26,7 +26,7 @@ fn main() { let exit_code = match execute(&opts) { Ok(code) => code, Err(e) => { - eprintln!("{}", e); + eprintln!("{:#}", e); 1 } }; diff --git a/src/config/mod.rs b/src/config/mod.rs index cd90e0904b6c..5041e1e36dd6 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -364,7 +364,9 @@ fn get_toml_path(dir: &Path) -> Result, Error> { // find the project file yet, and continue searching. Err(e) => { if e.kind() != ErrorKind::NotFound { - return Err(e); + let ctx = format!("Failed to get metadata for config file {:?}", &config_file); + let err = anyhow::Error::new(e).context(ctx); + return Err(Error::new(ErrorKind::Other, err)); } } _ => {} From d5aabccfebc645d9fce62f91512b03ca0fa1fb6d Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 1 Feb 2022 13:38:54 -0600 Subject: [PATCH 145/401] Format code --- config_proc_macro/src/utils.rs | 4 ++-- tests/cargo-fmt/main.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config_proc_macro/src/utils.rs b/config_proc_macro/src/utils.rs index 5b68d2748490..f5cba87b07b6 100644 --- a/config_proc_macro/src/utils.rs +++ b/config_proc_macro/src/utils.rs @@ -22,10 +22,10 @@ pub fn is_unit(v: &syn::Variant) -> bool { #[cfg(feature = "debug-with-rustfmt")] /// Pretty-print the output of proc macro using rustfmt. pub fn debug_with_rustfmt(input: &TokenStream) { - use std::io::Write; - use std::process::{Command, Stdio}; use std::env; use std::ffi::OsStr; + use std::io::Write; + use std::process::{Command, Stdio}; let rustfmt_var = env::var_os("RUSTFMT"); let rustfmt = match &rustfmt_var { diff --git a/tests/cargo-fmt/main.rs b/tests/cargo-fmt/main.rs index bf81f253f691..3713552c66af 100644 --- a/tests/cargo-fmt/main.rs +++ b/tests/cargo-fmt/main.rs @@ -1,8 +1,8 @@ // Integration tests for cargo-fmt. use std::env; -use std::process::Command; use std::path::Path; +use std::process::Command; /// Run the cargo-fmt executable and return its output. fn cargo_fmt(args: &[&str]) -> (String, String) { From c63d42e80473a0c18714b55058f27506fd24955c Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 1 Feb 2022 13:39:01 -0600 Subject: [PATCH 146/401] Use cargo-fmt in self_tests --- src/test/mod.rs | 46 ++++++++++++---------------------------------- 1 file changed, 12 insertions(+), 34 deletions(-) diff --git a/src/test/mod.rs b/src/test/mod.rs index ab966d4a3607..4191e3e96b0a 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -375,43 +375,21 @@ fn idempotence_tests() { }); } -// Run rustfmt on itself. This operation must be idempotent. We also check that -// no warnings are emitted. -// Issue-3443: these tests require nightly #[nightly_only_test] #[test] fn self_tests() { - init_log(); - let mut files = get_test_files(Path::new("tests"), false); - let bin_directories = vec!["cargo-fmt", "git-rustfmt", "bin", "format-diff"]; - for dir in bin_directories { - let mut path = PathBuf::from("src"); - path.push(dir); - path.push("main.rs"); - files.push(path); - } - files.push(PathBuf::from("src/lib.rs")); - - let (reports, count, fails) = check_files(files, &Some(PathBuf::from("rustfmt.toml"))); - let mut warnings = 0; - - // Display results. - println!("Ran {} self tests.", count); - assert_eq!(fails, 0, "{} self tests failed", fails); - - for format_report in reports { - println!( - "{}", - FormatReportFormatterBuilder::new(&format_report).build() - ); - warnings += format_report.warning_count(); - } - - assert_eq!( - warnings, 0, - "Rustfmt's code generated {} warnings", - warnings - ); + let get_exe_path = |name| { + let mut path = env::current_exe().unwrap(); + path.pop(); + path.set_file_name(format!("{name}{}", env::consts::EXE_SUFFIX)); + path + }; + let status = Command::new(get_exe_path("cargo-fmt")) + .args(["--check", "--all"]) + .env("RUSTFMT", get_exe_path("rustfmt")) + .status() + .unwrap(); + assert!(status.success()); } #[test] From 281bf03e6492bf2627c24ea502ce5fd567d7a08e Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Tue, 15 Feb 2022 20:49:36 -0600 Subject: [PATCH 147/401] fix: formatting in new test --- tests/rustfmt/main.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/rustfmt/main.rs b/tests/rustfmt/main.rs index 6976cc4d346c..2262ae3aaacd 100644 --- a/tests/rustfmt/main.rs +++ b/tests/rustfmt/main.rs @@ -109,9 +109,7 @@ fn inline_config() { #[test] fn rustfmt_usage_text() { - let args = [ - "--help", - ]; + let args = ["--help"]; let (stdout, _) = rustfmt(&args); - assert!(stdout.contains(&format!("Format Rust code\n\nusage: rustfmt [options] ..."))); + assert!(stdout.contains("Format Rust code\n\nusage: rustfmt [options] ...")); } From 89ca3f3a100456d652b156b1a62b8e244e526c4e Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Wed, 23 Feb 2022 21:37:42 -0600 Subject: [PATCH 148/401] fix: unused test imports on non-nightly, prevent regression --- .github/workflows/linux.yml | 4 ++++ src/ignore_path.rs | 9 ++++----- src/lib.rs | 1 - 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index db4979416421..45f63b83c056 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -40,6 +40,10 @@ jobs: rustc -Vv cargo -V cargo build + env: + RUSTFLAGS: '-D warnings' - name: test run: cargo test + env: + RUSTFLAGS: '-D warnings' diff --git a/src/ignore_path.rs b/src/ignore_path.rs index 7738eee0a760..d955949496a6 100644 --- a/src/ignore_path.rs +++ b/src/ignore_path.rs @@ -32,16 +32,15 @@ impl IgnorePathSet { #[cfg(test)] mod test { - use std::path::{Path, PathBuf}; - - use crate::config::{Config, FileName}; - use crate::ignore_path::IgnorePathSet; - use rustfmt_config_proc_macro::nightly_only_test; #[nightly_only_test] #[test] fn test_ignore_path_set() { + use crate::config::{Config, FileName}; + use crate::ignore_path::IgnorePathSet; + use std::path::{Path, PathBuf}; + let config = Config::from_toml(r#"ignore = ["foo.rs", "bar_dir/*"]"#, Path::new("")).unwrap(); let ignore_path_set = IgnorePathSet::from_ignore_list(&config.ignore()).unwrap(); diff --git a/src/lib.rs b/src/lib.rs index fae8080c02e4..ad23b16e02ec 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,6 @@ #![warn(unreachable_pub)] #![recursion_limit = "256"] #![allow(clippy::match_like_matches_macro)] -#![allow(unreachable_pub)] #[macro_use] extern crate derive_new; From 12048e444f8a68b3c13e98b15c11a69de9f0b485 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Sun, 30 Jan 2022 13:46:53 -0500 Subject: [PATCH 149/401] fallback to dir_path when relative external mod resolution fails We only want to fall back if two conditions are met: 1) Initial module resolution is performed relative to some nested directory. 2) Module resolution fails because of a ModError::FileNotFound error. When these conditions are met we can try to fallback to searching for the module's file relative to the dir_path instead of the nested relative directory. Fixes 5198 As demonstrated by 5198, it's possible that a directory name conflicts with a rust file name. For example, src/lib/ and src/lib.rs. If src/lib.rs references an external module like ``mod foo;``, then module resolution will try to resolve ``foo`` to src/lib/foo.rs or src/lib/foo/mod.rs. Module resolution would fail with a file not found error if the ``foo`` module were defined at src/foo.rs. When encountering these kinds of module resolution issues we now fall back to the current directory and attempt to resolve the module again. Given the current example, this means that if we can't find the module ``foo`` at src/lib/foo.rs or src/lib/foo/mod.rs, we'll attempt to resolve the module to src/foo.rs. --- src/parse/session.rs | 22 +++++++++++++++++-- src/test/mod_resolver.rs | 16 ++++++++++++++ tests/mod-resolver/issue-5198/a.rs | 1 + tests/mod-resolver/issue-5198/lib.rs | 3 +++ tests/mod-resolver/issue-5198/lib/b.rs | 1 + tests/mod-resolver/issue-5198/lib/c/d.rs | 3 +++ .../issue-5198/lib/c/d/explanation.txt | 16 ++++++++++++++ tests/mod-resolver/issue-5198/lib/c/d/f.rs | 1 + .../mod-resolver/issue-5198/lib/c/d/g/mod.rs | 1 + tests/mod-resolver/issue-5198/lib/c/e.rs | 1 + tests/mod-resolver/issue-5198/lib/c/mod.rs | 3 +++ .../issue-5198/lib/explanation.txt | 16 ++++++++++++++ 12 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 tests/mod-resolver/issue-5198/a.rs create mode 100644 tests/mod-resolver/issue-5198/lib.rs create mode 100644 tests/mod-resolver/issue-5198/lib/b.rs create mode 100644 tests/mod-resolver/issue-5198/lib/c/d.rs create mode 100644 tests/mod-resolver/issue-5198/lib/c/d/explanation.txt create mode 100644 tests/mod-resolver/issue-5198/lib/c/d/f.rs create mode 100644 tests/mod-resolver/issue-5198/lib/c/d/g/mod.rs create mode 100644 tests/mod-resolver/issue-5198/lib/c/e.rs create mode 100644 tests/mod-resolver/issue-5198/lib/c/mod.rs create mode 100644 tests/mod-resolver/issue-5198/lib/explanation.txt diff --git a/src/parse/session.rs b/src/parse/session.rs index 624fed0d2de2..fb9182152d1d 100644 --- a/src/parse/session.rs +++ b/src/parse/session.rs @@ -12,6 +12,7 @@ use rustc_span::{ use crate::config::file_lines::LineRange; use crate::ignore_path::IgnorePathSet; +use crate::parse::parser::{ModError, ModulePathSuccess}; use crate::source_map::LineRangeUtils; use crate::utils::starts_with_newline; use crate::visitor::SnippetProvider; @@ -145,13 +146,30 @@ impl ParseSess { }) } + /// Determine the submodule path for the given module identifier. + /// + /// * `id` - The name of the module + /// * `relative` - If Some(symbol), the symbol name is a directory relative to the dir_path. + /// If relative is Some, resolve the submodle at {dir_path}/{symbol}/{id}.rs + /// or {dir_path}/{symbol}/{id}/mod.rs. if None, resolve the module at {dir_path}/{id}.rs. + /// * `dir_path` - Module resolution will occur relative to this direcotry. pub(crate) fn default_submod_path( &self, id: symbol::Ident, relative: Option, dir_path: &Path, - ) -> Result> { - rustc_expand::module::default_submod_path(&self.parse_sess, id, relative, dir_path) + ) -> Result> { + rustc_expand::module::default_submod_path(&self.parse_sess, id, relative, dir_path).or_else( + |e| { + // If resloving a module relative to {dir_path}/{symbol} fails because a file + // could not be found, then try to resolve the module relative to {dir_path}. + if matches!(e, ModError::FileNotFound(..)) && relative.is_some() { + rustc_expand::module::default_submod_path(&self.parse_sess, id, None, dir_path) + } else { + Err(e) + } + }, + ) } pub(crate) fn is_file_parsed(&self, path: &Path) -> bool { diff --git a/src/test/mod_resolver.rs b/src/test/mod_resolver.rs index fcff6d14e6fa..aacb2acc6849 100644 --- a/src/test/mod_resolver.rs +++ b/src/test/mod_resolver.rs @@ -64,3 +64,19 @@ fn fmt_out_of_line_test_modules() { ], ) } + +#[test] +fn fallback_and_try_to_resolve_external_submod_relative_to_current_dir_path() { + // See also https://github.com/rust-lang/rustfmt/issues/5198 + verify_mod_resolution( + "tests/mod-resolver/issue-5198/lib.rs", + &[ + "tests/mod-resolver/issue-5198/a.rs", + "tests/mod-resolver/issue-5198/lib/b.rs", + "tests/mod-resolver/issue-5198/lib/c/mod.rs", + "tests/mod-resolver/issue-5198/lib/c/e.rs", + "tests/mod-resolver/issue-5198/lib/c/d/f.rs", + "tests/mod-resolver/issue-5198/lib/c/d/g/mod.rs", + ], + ) +} diff --git a/tests/mod-resolver/issue-5198/a.rs b/tests/mod-resolver/issue-5198/a.rs new file mode 100644 index 000000000000..cd686f561169 --- /dev/null +++ b/tests/mod-resolver/issue-5198/a.rs @@ -0,0 +1 @@ +fn main( ) { println!("Hello World!") } diff --git a/tests/mod-resolver/issue-5198/lib.rs b/tests/mod-resolver/issue-5198/lib.rs new file mode 100644 index 000000000000..696832913c87 --- /dev/null +++ b/tests/mod-resolver/issue-5198/lib.rs @@ -0,0 +1,3 @@ +mod a; +mod b; +mod c; diff --git a/tests/mod-resolver/issue-5198/lib/b.rs b/tests/mod-resolver/issue-5198/lib/b.rs new file mode 100644 index 000000000000..cd686f561169 --- /dev/null +++ b/tests/mod-resolver/issue-5198/lib/b.rs @@ -0,0 +1 @@ +fn main( ) { println!("Hello World!") } diff --git a/tests/mod-resolver/issue-5198/lib/c/d.rs b/tests/mod-resolver/issue-5198/lib/c/d.rs new file mode 100644 index 000000000000..d1604aa23a3c --- /dev/null +++ b/tests/mod-resolver/issue-5198/lib/c/d.rs @@ -0,0 +1,3 @@ +mod e; +mod f; +mod g; diff --git a/tests/mod-resolver/issue-5198/lib/c/d/explanation.txt b/tests/mod-resolver/issue-5198/lib/c/d/explanation.txt new file mode 100644 index 000000000000..92c9e3021431 --- /dev/null +++ b/tests/mod-resolver/issue-5198/lib/c/d/explanation.txt @@ -0,0 +1,16 @@ +This file is contained in the './lib/c/d/' directory. + +The directory name './lib/c/d/' conflicts with the './lib/c/d.rs' file name. + +'./lib/c/d.rs' defines 3 external modules: + + * mod e; + * mod f; + * mod g; + +Module resolution will fail if we look for './lib/c/d/e.rs' or './lib/c/d/e/mod.rs', +so we should fall back to looking for './lib/c/e.rs', which correctly finds the modlue, that +rustfmt should format. + +'./lib/c/d/f.rs' and './lib/c/d/g/mod.rs' exist at the default submodule paths so we should be able +to resolve these modules with no problems. \ No newline at end of file diff --git a/tests/mod-resolver/issue-5198/lib/c/d/f.rs b/tests/mod-resolver/issue-5198/lib/c/d/f.rs new file mode 100644 index 000000000000..cd686f561169 --- /dev/null +++ b/tests/mod-resolver/issue-5198/lib/c/d/f.rs @@ -0,0 +1 @@ +fn main( ) { println!("Hello World!") } diff --git a/tests/mod-resolver/issue-5198/lib/c/d/g/mod.rs b/tests/mod-resolver/issue-5198/lib/c/d/g/mod.rs new file mode 100644 index 000000000000..cd686f561169 --- /dev/null +++ b/tests/mod-resolver/issue-5198/lib/c/d/g/mod.rs @@ -0,0 +1 @@ +fn main( ) { println!("Hello World!") } diff --git a/tests/mod-resolver/issue-5198/lib/c/e.rs b/tests/mod-resolver/issue-5198/lib/c/e.rs new file mode 100644 index 000000000000..cd686f561169 --- /dev/null +++ b/tests/mod-resolver/issue-5198/lib/c/e.rs @@ -0,0 +1 @@ +fn main( ) { println!("Hello World!") } diff --git a/tests/mod-resolver/issue-5198/lib/c/mod.rs b/tests/mod-resolver/issue-5198/lib/c/mod.rs new file mode 100644 index 000000000000..81904619650f --- /dev/null +++ b/tests/mod-resolver/issue-5198/lib/c/mod.rs @@ -0,0 +1,3 @@ +mod d; + +fn main( ) { println!("Hello World!") } diff --git a/tests/mod-resolver/issue-5198/lib/explanation.txt b/tests/mod-resolver/issue-5198/lib/explanation.txt new file mode 100644 index 000000000000..d436a8076cd7 --- /dev/null +++ b/tests/mod-resolver/issue-5198/lib/explanation.txt @@ -0,0 +1,16 @@ +This file is contained in the './lib' directory. + +The directory name './lib' conflicts with the './lib.rs' file name. + +'lib.rs' defines 3 external modules: + + * mod a; + * mod b; + * mod c; + +Module resolution will fail if we look for './lib/a.rs' or './lib/a/mod.rs', +so we should fall back to looking for './a.rs', which correctly finds the modlue that +rustfmt should format. + +'./lib/b.rs' and './lib/c/mod.rs' exist at the default submodule paths so we should be able +to resolve these modules with no problems. From 272fb42f06479afb62a9503f181540101f67982a Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Fri, 25 Feb 2022 11:31:09 -0500 Subject: [PATCH 150/401] Prevent wrapping markdown headers in doc comments Fixes 5238 A markdown header is defined by a string that starts with `#`. Previously, rustfmt would wrap long markdown headers when `wrap_comments=true`. This lead to issues when rendering these headers in HTML using rustdoc. Now, rustfmt leaves markdown headers alone when wrapping comments. --- src/comment.rs | 18 ++++++++++++++---- .../markdown_header_wrap_comments_false.rs | 11 +++++++++++ .../markdown_header_wrap_comments_true.rs | 11 +++++++++++ .../markdown_header_wrap_comments_false.rs | 11 +++++++++++ .../markdown_header_wrap_comments_true.rs | 14 ++++++++++++++ 5 files changed, 61 insertions(+), 4 deletions(-) create mode 100644 tests/source/issue-5238/markdown_header_wrap_comments_false.rs create mode 100644 tests/source/issue-5238/markdown_header_wrap_comments_true.rs create mode 100644 tests/target/issue-5238/markdown_header_wrap_comments_false.rs create mode 100644 tests/target/issue-5238/markdown_header_wrap_comments_true.rs diff --git a/src/comment.rs b/src/comment.rs index 96778c4ef1dd..f9d8a0fa70c0 100644 --- a/src/comment.rs +++ b/src/comment.rs @@ -683,6 +683,7 @@ impl<'a> CommentRewrite<'a> { i: usize, line: &'a str, has_leading_whitespace: bool, + is_doc_comment: bool, ) -> bool { let num_newlines = count_newlines(orig); let is_last = i == num_newlines; @@ -789,10 +790,19 @@ impl<'a> CommentRewrite<'a> { } } - if self.fmt.config.wrap_comments() + let is_markdown_header_doc_comment = is_doc_comment && line.starts_with("#"); + + // We only want to wrap the comment if: + // 1) wrap_comments = true is configured + // 2) The comment is not the start of a markdown header doc comment + // 3) The comment width exceeds the shape's width + // 4) No URLS were found in the commnet + let should_wrap_comment = self.fmt.config.wrap_comments() + && !is_markdown_header_doc_comment && unicode_str_width(line) > self.fmt.shape.width - && !has_url(line) - { + && !has_url(line); + + if should_wrap_comment { match rewrite_string(line, &self.fmt, self.max_width) { Some(ref s) => { self.is_prev_line_multi_line = s.contains('\n'); @@ -882,7 +892,7 @@ fn rewrite_comment_inner( }); for (i, (line, has_leading_whitespace)) in lines.enumerate() { - if rewriter.handle_line(orig, i, line, has_leading_whitespace) { + if rewriter.handle_line(orig, i, line, has_leading_whitespace, is_doc_comment) { break; } } diff --git a/tests/source/issue-5238/markdown_header_wrap_comments_false.rs b/tests/source/issue-5238/markdown_header_wrap_comments_false.rs new file mode 100644 index 000000000000..229c6e5753d2 --- /dev/null +++ b/tests/source/issue-5238/markdown_header_wrap_comments_false.rs @@ -0,0 +1,11 @@ +// rustfmt-wrap_comments: false + +/// no markdown header so rustfmt should wrap this comment when `format_code_in_doc_comments = true` and `wrap_comments = true` +fn not_documented_with_markdown_header() { + // This is just a normal inline comment so rustfmt should wrap this comment when `wrap_comments = true` +} + +/// # We're using a markdown header here so rustfmt should refuse to wrap this comment in all circumstances +fn documented_with_markdown_header() { + // # We're using a markdown header in an inline comment. rustfmt should be able to wrap this comment when `wrap_comments = true` +} diff --git a/tests/source/issue-5238/markdown_header_wrap_comments_true.rs b/tests/source/issue-5238/markdown_header_wrap_comments_true.rs new file mode 100644 index 000000000000..c547ff35c691 --- /dev/null +++ b/tests/source/issue-5238/markdown_header_wrap_comments_true.rs @@ -0,0 +1,11 @@ +// rustfmt-wrap_comments: true + +/// no markdown header so rustfmt should wrap this comment when `format_code_in_doc_comments = true` and `wrap_comments = true` +fn not_documented_with_markdown_header() { + // This is just a normal inline comment so rustfmt should wrap this comment when `wrap_comments = true` +} + +/// # We're using a markdown header here so rustfmt should refuse to wrap this comment in all circumstances +fn documented_with_markdown_header() { + // # We're using a markdown header in an inline comment. rustfmt should be able to wrap this comment when `wrap_comments = true` +} diff --git a/tests/target/issue-5238/markdown_header_wrap_comments_false.rs b/tests/target/issue-5238/markdown_header_wrap_comments_false.rs new file mode 100644 index 000000000000..229c6e5753d2 --- /dev/null +++ b/tests/target/issue-5238/markdown_header_wrap_comments_false.rs @@ -0,0 +1,11 @@ +// rustfmt-wrap_comments: false + +/// no markdown header so rustfmt should wrap this comment when `format_code_in_doc_comments = true` and `wrap_comments = true` +fn not_documented_with_markdown_header() { + // This is just a normal inline comment so rustfmt should wrap this comment when `wrap_comments = true` +} + +/// # We're using a markdown header here so rustfmt should refuse to wrap this comment in all circumstances +fn documented_with_markdown_header() { + // # We're using a markdown header in an inline comment. rustfmt should be able to wrap this comment when `wrap_comments = true` +} diff --git a/tests/target/issue-5238/markdown_header_wrap_comments_true.rs b/tests/target/issue-5238/markdown_header_wrap_comments_true.rs new file mode 100644 index 000000000000..87dae58eccd7 --- /dev/null +++ b/tests/target/issue-5238/markdown_header_wrap_comments_true.rs @@ -0,0 +1,14 @@ +// rustfmt-wrap_comments: true + +/// no markdown header so rustfmt should wrap this comment when +/// `format_code_in_doc_comments = true` and `wrap_comments = true` +fn not_documented_with_markdown_header() { + // This is just a normal inline comment so rustfmt should wrap this comment + // when `wrap_comments = true` +} + +/// # We're using a markdown header here so rustfmt should refuse to wrap this comment in all circumstances +fn documented_with_markdown_header() { + // # We're using a markdown header in an inline comment. rustfmt should be + // able to wrap this comment when `wrap_comments = true` +} From ee130515e3788d886cdf18442871936a99c134b6 Mon Sep 17 00:00:00 2001 From: cassaundra Date: Wed, 29 Dec 2021 15:41:26 -0800 Subject: [PATCH 151/401] Fix missing struct field separators under certain conditions When struct_field_align_threshold is non-zero and trailing_comma is set to "Never," struct field separators are omitted between field groups. This issue is resolved by forcing separators between groups. Fixes #4791. A test is included with a minimal reproducible example. --- src/vertical.rs | 23 ++++++++++++++++++++--- tests/source/issue_4791.rs | 14 ++++++++++++++ tests/target/issue_4791.rs | 14 ++++++++++++++ 3 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 tests/source/issue_4791.rs create mode 100644 tests/target/issue_4791.rs diff --git a/src/vertical.rs b/src/vertical.rs index c4208848c6c2..a06bc995aa55 100644 --- a/src/vertical.rs +++ b/src/vertical.rs @@ -160,8 +160,18 @@ pub(crate) fn rewrite_with_alignment( }; let init_span = mk_sp(span.lo(), init_last_pos); let one_line_width = if rest.is_empty() { one_line_width } else { 0 }; - let result = - rewrite_aligned_items_inner(context, init, init_span, shape.indent, one_line_width)?; + + // if another group follows, we must force a separator + let force_separator = !rest.is_empty(); + + let result = rewrite_aligned_items_inner( + context, + init, + init_span, + shape.indent, + one_line_width, + force_separator, + )?; if rest.is_empty() { Some(result + spaces) } else { @@ -201,6 +211,7 @@ fn rewrite_aligned_items_inner( span: Span, offset: Indent, one_line_width: usize, + force_trailing_separator: bool, ) -> Option { // 1 = "," let item_shape = Shape::indented(offset, context.config).sub_width(1)?; @@ -246,9 +257,15 @@ fn rewrite_aligned_items_inner( }); } + let separator_tactic = if force_trailing_separator { + SeparatorTactic::Always + } else { + context.config.trailing_comma() + }; + let fmt = ListFormatting::new(item_shape, context.config) .tactic(tactic) - .trailing_separator(context.config.trailing_comma()) + .trailing_separator(separator_tactic) .preserve_newline(true); write_list(&items, &fmt) } diff --git a/tests/source/issue_4791.rs b/tests/source/issue_4791.rs new file mode 100644 index 000000000000..4760022eeaf0 --- /dev/null +++ b/tests/source/issue_4791.rs @@ -0,0 +1,14 @@ +// rustfmt-struct_field_align_threshold: 30 +// rustfmt-trailing_comma: Never + +struct Foo { + group_a: u8, + + group_b: u8, +} + +struct Bar { + group_a: u8, + + group_b: u8 +} diff --git a/tests/target/issue_4791.rs b/tests/target/issue_4791.rs new file mode 100644 index 000000000000..fff58be99a50 --- /dev/null +++ b/tests/target/issue_4791.rs @@ -0,0 +1,14 @@ +// rustfmt-struct_field_align_threshold: 30 +// rustfmt-trailing_comma: Never + +struct Foo { + group_a: u8, + + group_b: u8 +} + +struct Bar { + group_a: u8, + + group_b: u8 +} From ab9f2a8ac78a66ae1fb0f92ae5a32ce5c0db0d8d Mon Sep 17 00:00:00 2001 From: cassaundra Date: Fri, 14 Jan 2022 17:25:46 -0800 Subject: [PATCH 152/401] Add more tests for struct_field_align_threshold and trailing_comma --- .../source/{issue_4791.rs => issue-4791/buggy.rs} | 0 tests/source/issue-4791/trailing_comma.rs | 14 ++++++++++++++ .../target/{issue_4791.rs => issue-4791/buggy.rs} | 0 tests/target/issue-4791/no_trailing_comma.rs | 8 ++++++++ tests/target/issue-4791/trailing_comma.rs | 14 ++++++++++++++ 5 files changed, 36 insertions(+) rename tests/source/{issue_4791.rs => issue-4791/buggy.rs} (100%) create mode 100644 tests/source/issue-4791/trailing_comma.rs rename tests/target/{issue_4791.rs => issue-4791/buggy.rs} (100%) create mode 100644 tests/target/issue-4791/no_trailing_comma.rs create mode 100644 tests/target/issue-4791/trailing_comma.rs diff --git a/tests/source/issue_4791.rs b/tests/source/issue-4791/buggy.rs similarity index 100% rename from tests/source/issue_4791.rs rename to tests/source/issue-4791/buggy.rs diff --git a/tests/source/issue-4791/trailing_comma.rs b/tests/source/issue-4791/trailing_comma.rs new file mode 100644 index 000000000000..c56c70faeae4 --- /dev/null +++ b/tests/source/issue-4791/trailing_comma.rs @@ -0,0 +1,14 @@ +// rustfmt-struct_field_align_threshold: 30 +// rustfmt-trailing_comma: Always + +struct Foo { + group_a: u8, + + group_b: u8 +} + +struct Bar { + group_a: u8, + + group_b: u8, +} diff --git a/tests/target/issue_4791.rs b/tests/target/issue-4791/buggy.rs similarity index 100% rename from tests/target/issue_4791.rs rename to tests/target/issue-4791/buggy.rs diff --git a/tests/target/issue-4791/no_trailing_comma.rs b/tests/target/issue-4791/no_trailing_comma.rs new file mode 100644 index 000000000000..4a37163969ae --- /dev/null +++ b/tests/target/issue-4791/no_trailing_comma.rs @@ -0,0 +1,8 @@ +// rustfmt-struct_field_align_threshold: 0 +// rustfmt-trailing_comma: Never + +pub struct Baz { + group_a: u8, + + group_b: u8 +} diff --git a/tests/target/issue-4791/trailing_comma.rs b/tests/target/issue-4791/trailing_comma.rs new file mode 100644 index 000000000000..29a224b3f6d9 --- /dev/null +++ b/tests/target/issue-4791/trailing_comma.rs @@ -0,0 +1,14 @@ +// rustfmt-struct_field_align_threshold: 30 +// rustfmt-trailing_comma: Always + +struct Foo { + group_a: u8, + + group_b: u8, +} + +struct Bar { + group_a: u8, + + group_b: u8, +} From 58de4142c525cc50824dab1cf199dc967c88356f Mon Sep 17 00:00:00 2001 From: cassaundra Date: Sat, 5 Mar 2022 14:17:15 -0800 Subject: [PATCH 153/401] Add test for issue #4791 --- tests/target/issue-4791/issue_4928.rs | 70 +++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 tests/target/issue-4791/issue_4928.rs diff --git a/tests/target/issue-4791/issue_4928.rs b/tests/target/issue-4791/issue_4928.rs new file mode 100644 index 000000000000..588656b535fa --- /dev/null +++ b/tests/target/issue-4791/issue_4928.rs @@ -0,0 +1,70 @@ +// rustfmt-brace_style: SameLineWhere +// rustfmt-comment_width: 100 +// rustfmt-edition: 2018 +// rustfmt-fn_args_layout: Compressed +// rustfmt-hard_tabs: false +// rustfmt-match_block_trailing_comma: true +// rustfmt-max_width: 100 +// rustfmt-merge_derives: false +// rustfmt-newline_style: Unix +// rustfmt-normalize_doc_attributes: true +// rustfmt-overflow_delimited_expr: true +// rustfmt-reorder_imports: false +// rustfmt-reorder_modules: true +// rustfmt-struct_field_align_threshold: 20 +// rustfmt-tab_spaces: 4 +// rustfmt-trailing_comma: Never +// rustfmt-use_small_heuristics: Max +// rustfmt-use_try_shorthand: true +// rustfmt-wrap_comments: true + +/// Lorem ipsum dolor sit amet. +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)] +pub struct BufferAttr { + /* NOTE: Blah blah blah blah blah. */ + /// Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt + /// ut labore et dolore magna aliqua. Morbi quis commodo odio aenean sed adipiscing. Nunc + /// congue nisi vitae suscipit tellus mauris a. Consectetur adipiscing elit pellentesque + /// habitant morbi tristique senectus. + pub foo: u32, + + /// Elit eget gravida cum sociis natoque penatibus et magnis dis. Consequat semper viverra nam + /// libero. Accumsan in nisl nisi scelerisque eu. Pellentesque id nibh tortor id aliquet. Sed + /// velit dignissim sodales ut. Facilisis sed odio morbi quis commodo odio aenean sed. Et + /// ultrices neque ornare aenean euismod elementum. Condimentum lacinia quis vel eros donec ac + /// odio tempor. + /// + /// Lacinia at quis risus sed vulputate odio ut enim. Etiam erat velit scelerisque in dictum. + /// Nibh tellus molestie nunc non blandit massa enim nec. Nascetur ridiculus mus mauris vitae. + pub bar: u32, + + /// Mi proin sed libero enim sed faucibus turpis. Amet consectetur adipiscing elit duis + /// tristique sollicitudin nibh sit amet. Congue quisque egestas diam in arcu cursus euismod + /// quis viverra. Cum sociis natoque penatibus et magnis dis parturient montes. Enim sit amet + /// venenatis urna cursus eget nunc scelerisque viverra. Cras semper auctor neque vitae tempus + /// quam pellentesque. Tortor posuere ac ut consequat semper viverra nam libero justo. Vitae + /// auctor eu augue ut lectus arcu bibendum at. Faucibus vitae aliquet nec ullamcorper sit amet + /// risus nullam. Maecenas accumsan lacus vel facilisis volutpat. Arcu non odio euismod + /// lacinia. + /// + /// [`FooBar::beep()`]: crate::foobar::FooBar::beep + /// [`FooBar::boop()`]: crate::foobar::FooBar::boop + /// [`foobar::BazBaq::BEEP_BOOP`]: crate::foobar::BazBaq::BEEP_BOOP + pub baz: u32, + + /// Eu consequat ac felis donec et odio pellentesque diam. Ut eu sem integer vitae justo eget. + /// Consequat ac felis donec et odio pellentesque diam volutpat. + pub baq: u32, + + /// Amet consectetur adipiscing elit pellentesque habitant. Ut morbi tincidunt augue interdum + /// velit euismod in pellentesque. Imperdiet sed euismod nisi porta lorem. Nec tincidunt + /// praesent semper feugiat. Facilisis leo vel fringilla est. Egestas diam in arcu cursus + /// euismod quis viverra. Sagittis eu volutpat odio facilisis mauris sit amet. Posuere morbi + /// leo urna molestie at. + /// + /// Pretium aenean pharetra magna ac. Nisl condimentum id venenatis a condimentum vitae. Semper + /// quis lectus nulla at volutpat diam ut venenatis tellus. Egestas tellus rutrum tellus + /// pellentesque eu tincidunt tortor aliquam. + pub foobar: u32 +} From 18c0369688d5854ea67dbe29cfac3cc8380cb20e Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Mon, 10 Jan 2022 20:45:27 -0500 Subject: [PATCH 154/401] Improve mod resolution error for mods with multiple candidate files Fixes 5167 When ``a.rs`` and ``a/mod.rs`` are both present we would emit an error message telling the user that the module couldn't be found. However, this behavior is misleading because we're dealing with an ambiguous module path, not a "file not found" error. Is the file ``a.rs`` or is it ``a/mod.rs``? Rustfmt can't decide, and the user needs to resolve this ambiguity themselves. Now, the error message displayed to the user is in line with what they would see if they went to compile their code with these conflicting module names. --- src/modules.rs | 38 +++++++++++++--- src/parse/session.rs | 3 ++ tests/mod-resolver/issue-5167/src/a.rs | 0 tests/mod-resolver/issue-5167/src/a/mod.rs | 0 tests/mod-resolver/issue-5167/src/lib.rs | 1 + .../bad_path_attribute/lib.rs | 3 ++ .../module-not-found/relative_module/a.rs | 2 + .../module-not-found/relative_module/lib.rs | 1 + .../module-not-found/sibling_module/lib.rs | 2 + tests/rustfmt/main.rs | 44 +++++++++++++++++++ 10 files changed, 88 insertions(+), 6 deletions(-) create mode 100644 tests/mod-resolver/issue-5167/src/a.rs create mode 100644 tests/mod-resolver/issue-5167/src/a/mod.rs create mode 100644 tests/mod-resolver/issue-5167/src/lib.rs create mode 100644 tests/mod-resolver/module-not-found/bad_path_attribute/lib.rs create mode 100644 tests/mod-resolver/module-not-found/relative_module/a.rs create mode 100644 tests/mod-resolver/module-not-found/relative_module/lib.rs create mode 100644 tests/mod-resolver/module-not-found/sibling_module/lib.rs diff --git a/src/modules.rs b/src/modules.rs index 70b937b02836..49c99403974e 100644 --- a/src/modules.rs +++ b/src/modules.rs @@ -81,6 +81,7 @@ pub struct ModuleResolutionError { pub(crate) kind: ModuleResolutionErrorKind, } +/// Defines variants similar to those of [rustc_expand::module::ModError] #[derive(Debug, Error)] pub(crate) enum ModuleResolutionErrorKind { /// Find a file that cannot be parsed. @@ -89,6 +90,12 @@ pub(crate) enum ModuleResolutionErrorKind { /// File cannot be found. #[error("{file} does not exist")] NotFound { file: PathBuf }, + /// File a.rs and a/mod.rs both exist + #[error("file for module found at both {default_path:?} and {secondary_path:?}")] + MultipleCandidates { + default_path: PathBuf, + secondary_path: PathBuf, + }, } #[derive(Clone)] @@ -444,12 +451,31 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> { } Ok(Some(SubModKind::MultiExternal(mods_outside_ast))) } - Err(_) => Err(ModuleResolutionError { - module: mod_name.to_string(), - kind: ModuleResolutionErrorKind::NotFound { - file: self.directory.path.clone(), - }, - }), + Err(e) => match e { + ModError::FileNotFound(_, default_path, _secondary_path) => { + Err(ModuleResolutionError { + module: mod_name.to_string(), + kind: ModuleResolutionErrorKind::NotFound { file: default_path }, + }) + } + ModError::MultipleCandidates(_, default_path, secondary_path) => { + Err(ModuleResolutionError { + module: mod_name.to_string(), + kind: ModuleResolutionErrorKind::MultipleCandidates { + default_path, + secondary_path, + }, + }) + } + ModError::ParserError(_) + | ModError::CircularInclusion(_) + | ModError::ModInBlock(_) => Err(ModuleResolutionError { + module: mod_name.to_string(), + kind: ModuleResolutionErrorKind::ParseError { + file: self.directory.path.clone(), + }, + }), + }, } } diff --git a/src/parse/session.rs b/src/parse/session.rs index fb9182152d1d..87ab8fbf20ab 100644 --- a/src/parse/session.rs +++ b/src/parse/session.rs @@ -163,8 +163,11 @@ impl ParseSess { |e| { // If resloving a module relative to {dir_path}/{symbol} fails because a file // could not be found, then try to resolve the module relative to {dir_path}. + // If we still can't find the module after searching for it in {dir_path}, + // surface the original error. if matches!(e, ModError::FileNotFound(..)) && relative.is_some() { rustc_expand::module::default_submod_path(&self.parse_sess, id, None, dir_path) + .map_err(|_| e) } else { Err(e) } diff --git a/tests/mod-resolver/issue-5167/src/a.rs b/tests/mod-resolver/issue-5167/src/a.rs new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/mod-resolver/issue-5167/src/a/mod.rs b/tests/mod-resolver/issue-5167/src/a/mod.rs new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/mod-resolver/issue-5167/src/lib.rs b/tests/mod-resolver/issue-5167/src/lib.rs new file mode 100644 index 000000000000..f21af614da05 --- /dev/null +++ b/tests/mod-resolver/issue-5167/src/lib.rs @@ -0,0 +1 @@ +mod a; diff --git a/tests/mod-resolver/module-not-found/bad_path_attribute/lib.rs b/tests/mod-resolver/module-not-found/bad_path_attribute/lib.rs new file mode 100644 index 000000000000..2a63c961be8f --- /dev/null +++ b/tests/mod-resolver/module-not-found/bad_path_attribute/lib.rs @@ -0,0 +1,3 @@ +// module resolution fails because the path does not exist. +#[path = "path/to/does_not_exist.rs"] +mod a; diff --git a/tests/mod-resolver/module-not-found/relative_module/a.rs b/tests/mod-resolver/module-not-found/relative_module/a.rs new file mode 100644 index 000000000000..4a1eac8965de --- /dev/null +++ b/tests/mod-resolver/module-not-found/relative_module/a.rs @@ -0,0 +1,2 @@ +// module resolution fails because `./a/b.rs` does not exist +mod b; diff --git a/tests/mod-resolver/module-not-found/relative_module/lib.rs b/tests/mod-resolver/module-not-found/relative_module/lib.rs new file mode 100644 index 000000000000..f21af614da05 --- /dev/null +++ b/tests/mod-resolver/module-not-found/relative_module/lib.rs @@ -0,0 +1 @@ +mod a; diff --git a/tests/mod-resolver/module-not-found/sibling_module/lib.rs b/tests/mod-resolver/module-not-found/sibling_module/lib.rs new file mode 100644 index 000000000000..d9d9e1e3c908 --- /dev/null +++ b/tests/mod-resolver/module-not-found/sibling_module/lib.rs @@ -0,0 +1,2 @@ +// module resolution fails because `./a.rs` does not exist +mod a; diff --git a/tests/rustfmt/main.rs b/tests/rustfmt/main.rs index 2262ae3aaacd..450051d2fec6 100644 --- a/tests/rustfmt/main.rs +++ b/tests/rustfmt/main.rs @@ -113,3 +113,47 @@ fn rustfmt_usage_text() { let (stdout, _) = rustfmt(&args); assert!(stdout.contains("Format Rust code\n\nusage: rustfmt [options] ...")); } + +#[test] +fn mod_resolution_error_multiple_candidate_files() { + // See also https://github.com/rust-lang/rustfmt/issues/5167 + let default_path = Path::new("tests/mod-resolver/issue-5167/src/a.rs"); + let secondary_path = Path::new("tests/mod-resolver/issue-5167/src/a/mod.rs"); + let error_message = format!( + "file for module found at both {:?} and {:?}", + default_path.canonicalize().unwrap(), + secondary_path.canonicalize().unwrap(), + ); + + let args = ["tests/mod-resolver/issue-5167/src/lib.rs"]; + let (_stdout, stderr) = rustfmt(&args); + assert!(stderr.contains(&error_message)) +} + +#[test] +fn mod_resolution_error_sibling_module_not_found() { + let args = ["tests/mod-resolver/module-not-found/sibling_module/lib.rs"]; + let (_stdout, stderr) = rustfmt(&args); + // Module resolution fails because we're unable to find `a.rs` in the same directory as lib.rs + assert!(stderr.contains("a.rs does not exist")) +} + +#[test] +fn mod_resolution_error_relative_module_not_found() { + let args = ["tests/mod-resolver/module-not-found/relative_module/lib.rs"]; + let (_stdout, stderr) = rustfmt(&args); + // The file `./a.rs` and directory `./a` both exist. + // Module resolution fails becuase we're unable to find `./a/b.rs` + #[cfg(not(windows))] + assert!(stderr.contains("a/b.rs does not exist")); + #[cfg(windows)] + assert!(stderr.contains("a\\b.rs does not exist")); +} + +#[test] +fn mod_resolution_error_path_attribute_does_not_exist() { + let args = ["tests/mod-resolver/module-not-found/bad_path_attribute/lib.rs"]; + let (_stdout, stderr) = rustfmt(&args); + // The path attribute points to a file that does not exist + assert!(stderr.contains("does_not_exist.rs does not exist")); +} From b4de150dbc65a6bb95c86517430726c55251d0b0 Mon Sep 17 00:00:00 2001 From: Tom Milligan Date: Fri, 4 Mar 2022 18:13:08 +0000 Subject: [PATCH 155/401] fix: imports_granularity module with path containing self --- src/imports.rs | 50 ++++++++++++++++------ tests/source/imports_granularity_module.rs | 1 + tests/target/imports_granularity_module.rs | 2 + 3 files changed, 41 insertions(+), 12 deletions(-) diff --git a/src/imports.rs b/src/imports.rs index c60bec6d4a20..023198094868 100644 --- a/src/imports.rs +++ b/src/imports.rs @@ -190,13 +190,17 @@ pub(crate) fn merge_use_trees(use_trees: Vec, merge_by: SharedPrefix) - continue; } - for flattened in use_tree.flatten() { + for mut flattened in use_tree.flatten() { if let Some(tree) = result .iter_mut() .find(|tree| tree.share_prefix(&flattened, merge_by)) { tree.merge(&flattened, merge_by); } else { + // If this is the first tree with this prefix, handle potential trailing ::self + if merge_by == SharedPrefix::Module { + flattened = flattened.nest_trailing_self(); + } result.push(flattened); } } @@ -208,17 +212,7 @@ pub(crate) fn flatten_use_trees(use_trees: Vec) -> Vec { use_trees .into_iter() .flat_map(UseTree::flatten) - .map(|mut tree| { - // If a path ends in `::self`, rewrite it to `::{self}`. - if let Some(UseSegment::Slf(..)) = tree.path.last() { - let self_segment = tree.path.pop().unwrap(); - tree.path.push(UseSegment::List(vec![UseTree::from_path( - vec![self_segment], - DUMMY_SP, - )])); - } - tree - }) + .map(UseTree::nest_trailing_self) .collect() } @@ -635,6 +629,18 @@ impl UseTree { self.span = self.span.to(other.span); } } + + /// If this tree ends in `::self`, rewrite it to `::{self}`. + fn nest_trailing_self(mut self) -> UseTree { + if let Some(UseSegment::Slf(..)) = self.path.last() { + let self_segment = self.path.pop().unwrap(); + self.path.push(UseSegment::List(vec![UseTree::from_path( + vec![self_segment], + DUMMY_SP, + )])); + } + self + } } fn merge_rest( @@ -1311,4 +1317,24 @@ mod test { < parse_use_tree("std::cmp::{b, e, g, f}").normalize() ); } + + #[test] + fn test_use_tree_nest_trailing_self() { + assert_eq!( + parse_use_tree("a::b::self").nest_trailing_self(), + parse_use_tree("a::b::{self}") + ); + assert_eq!( + parse_use_tree("a::b::c").nest_trailing_self(), + parse_use_tree("a::b::c") + ); + assert_eq!( + parse_use_tree("a::b::{c, d}").nest_trailing_self(), + parse_use_tree("a::b::{c, d}") + ); + assert_eq!( + parse_use_tree("a::b::{self, c}").nest_trailing_self(), + parse_use_tree("a::b::{self, c}") + ); + } } diff --git a/tests/source/imports_granularity_module.rs b/tests/source/imports_granularity_module.rs index 5a4fad5872bd..2d7bb299aaac 100644 --- a/tests/source/imports_granularity_module.rs +++ b/tests/source/imports_granularity_module.rs @@ -4,6 +4,7 @@ use a::{b::c, d::e}; use a::{f, g::{h, i}}; use a::{j::{self, k::{self, l}, m}, n::{o::p, q}}; pub use a::{r::s, t}; +use b::{c::d, self}; #[cfg(test)] use foo::{a::b, c::d}; diff --git a/tests/target/imports_granularity_module.rs b/tests/target/imports_granularity_module.rs index 9c1387c466af..e4e1a299e586 100644 --- a/tests/target/imports_granularity_module.rs +++ b/tests/target/imports_granularity_module.rs @@ -10,6 +10,8 @@ use a::n::o::p; use a::n::q; pub use a::r::s; pub use a::t; +use b::c::d; +use b::{self}; use foo::e; #[cfg(test)] From 9c65db61bee3f4f47a8d889ea781902283c5b5bd Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Sat, 12 Mar 2022 01:14:38 -0500 Subject: [PATCH 156/401] Correct tracking issue link for `skip_children` Update the issue link to point to issue 3389 --- Configurations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Configurations.md b/Configurations.md index 2e2b0f7cfbec..ecec97dca522 100644 --- a/Configurations.md +++ b/Configurations.md @@ -2206,7 +2206,7 @@ Don't reformat out of line modules - **Default value**: `false` - **Possible values**: `true`, `false` -- **Stable**: No (tracking issue: [#3389](https://github.com/rust-lang/rustfmt/issues/3386)) +- **Stable**: No (tracking issue: [#3389](https://github.com/rust-lang/rustfmt/issues/3389)) ## `single_line_if_else_max_width` From 5696e3859707d2abf12465e7bfbdcf2d9f42c8a2 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Sat, 12 Mar 2022 01:16:08 -0500 Subject: [PATCH 157/401] Add test to verify tracking issue links Now, tracking issue links are checked against the reference number listed in the link text to ensure they match. --- src/test/configuration_snippet.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/test/configuration_snippet.rs b/src/test/configuration_snippet.rs index 92949ab576a6..c8fda7c8556d 100644 --- a/src/test/configuration_snippet.rs +++ b/src/test/configuration_snippet.rs @@ -290,3 +290,33 @@ fn get_code_blocks() -> Vec { code_blocks } + +#[test] +fn check_unstable_option_tracking_issue_numbers() { + // Ensure that tracking issue links point to the correct issue number + let tracking_issue = + regex::Regex::new(r"\(tracking issue: \[#(?P\d+)\]\((?P\S+)\)\)") + .expect("failed creating configuration pattern"); + + let lines = BufReader::new( + fs::File::open(Path::new(CONFIGURATIONS_FILE_NAME)) + .unwrap_or_else(|_| panic!("couldn't read file {}", CONFIGURATIONS_FILE_NAME)), + ) + .lines() + .map(Result::unwrap) + .enumerate(); + + for (idx, line) in lines { + if let Some(capture) = tracking_issue.captures(&line) { + let number = capture.name("number").unwrap().as_str(); + let link = capture.name("link").unwrap().as_str(); + assert!( + link.ends_with(number), + "{} on line {} does not point to issue #{}", + link, + idx + 1, + number, + ); + } + } +} From 1bb85bdf6b6dc0e205009b9f531a6220fe2031f9 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Wed, 9 Mar 2022 20:53:51 -0600 Subject: [PATCH 158/401] chore: add utility function for relative span positions --- src/parse/session.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/parse/session.rs b/src/parse/session.rs index 87ab8fbf20ab..a95324bbb0e4 100644 --- a/src/parse/session.rs +++ b/src/parse/session.rs @@ -218,6 +218,15 @@ impl ParseSess { self.parse_sess.source_map().lookup_char_pos(pos).line } + // TODO(calebcartwright): Preemptive, currently unused addition + // that will be used to support formatting scenarios that take original + // positions into account + /// Determines whether two byte positions are in the same source line. + #[allow(dead_code)] + pub(crate) fn byte_pos_same_line(&self, a: BytePos, b: BytePos) -> bool { + self.line_of_byte_pos(a) == self.line_of_byte_pos(b) + } + pub(crate) fn span_to_debug_info(&self, span: Span) -> String { self.parse_sess.source_map().span_to_diagnostic_string(span) } From 432b8dea64b84cec3efc3205bdc9e6687d59812d Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Wed, 16 Mar 2022 22:26:15 -0500 Subject: [PATCH 159/401] chore: bump toolchain --- Cargo.lock | 4 ++-- rust-toolchain | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2ef83ddd1ae6..b932e15ef746 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -275,9 +275,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.9.0" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" +checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" dependencies = [ "either", ] diff --git a/rust-toolchain b/rust-toolchain index d8bf02aec85e..0d407f11994b 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-01-23" +channel = "nightly-2022-03-17" components = ["rustc-dev"] From e41329ce87409df929ecfa191297f944472cc999 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Sun, 20 Mar 2022 13:21:44 -0400 Subject: [PATCH 160/401] Search for struct body span after any generic arguments Fixes 5273 Previously, rustfmt searched for the start of a struct body after the opening `{`. In most cases this works just fine, but const values can also be defined between `{ }`, which lead to issues when rewriting the struct body. Now, rustfmt will search for the `{` after the generic argument list to guarantee that the `{` it finds is the start of the struct body. --- src/items.rs | 8 +++++++- tests/target/issue_5273.rs | 3 +++ 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 tests/target/issue_5273.rs diff --git a/src/items.rs b/src/items.rs index 9b35d28f1195..92f423bbb627 100644 --- a/src/items.rs +++ b/src/items.rs @@ -1273,7 +1273,13 @@ pub(crate) fn format_struct_struct( result.push_str(&header_str); let header_hi = struct_parts.ident.span.hi(); - let body_lo = context.snippet_provider.span_after(span, "{"); + let body_lo = if let Some(generics) = struct_parts.generics { + // Adjust the span to start at the end of the generic arguments before searching for the '{' + let span = span.with_lo(generics.span.hi()); + context.snippet_provider.span_after(span, "{") + } else { + context.snippet_provider.span_after(span, "{") + }; let generics_str = match struct_parts.generics { Some(g) => format_generics( diff --git a/tests/target/issue_5273.rs b/tests/target/issue_5273.rs new file mode 100644 index 000000000000..3bb9048a5fd3 --- /dev/null +++ b/tests/target/issue_5273.rs @@ -0,0 +1,3 @@ +struct Example { + // +} From 0dba01aee15a45b8417c06df69e38af6956e03a8 Mon Sep 17 00:00:00 2001 From: 123vivekr <123vivekr@gmail.com> Date: Sun, 13 Feb 2022 17:54:09 +0530 Subject: [PATCH 161/401] Add `short_item_threshold` config option Allow custom short item threshold values via config --- Configurations.md | 34 +++++++++++++++++++ src/config/mod.rs | 3 ++ src/overflow.rs | 13 ++++--- .../short_array_element_width_threshold/10.rs | 11 ++++++ .../short_array_element_width_threshold/20.rs | 11 ++++++ .../greater_than_max_width.rs | 12 +++++++ .../short_array_element_width_threshold/10.rs | 11 ++++++ .../short_array_element_width_threshold/20.rs | 8 +++++ .../greater_than_max_width.rs | 12 +++++++ 9 files changed, 110 insertions(+), 5 deletions(-) create mode 100644 tests/source/configs/short_array_element_width_threshold/10.rs create mode 100644 tests/source/configs/short_array_element_width_threshold/20.rs create mode 100644 tests/source/configs/short_array_element_width_threshold/greater_than_max_width.rs create mode 100644 tests/target/configs/short_array_element_width_threshold/10.rs create mode 100644 tests/target/configs/short_array_element_width_threshold/20.rs create mode 100644 tests/target/configs/short_array_element_width_threshold/greater_than_max_width.rs diff --git a/Configurations.md b/Configurations.md index ecec97dca522..a47439b9ba96 100644 --- a/Configurations.md +++ b/Configurations.md @@ -2200,6 +2200,40 @@ specific version of rustfmt is used in your CI, use this option. - **Possible values**: any published version (e.g. `"0.3.8"`) - **Stable**: No (tracking issue: [#3386](https://github.com/rust-lang/rustfmt/issues/3386)) +## `short_array_element_width_threshold` + +The width threshold for an array element to be considered "short". + +The layout of an array is dependent on the length of each of its elements. +If the length of every element in an array is below this threshold (all elements are "short") then the array can be formatted in the mixed/compressed style, but if any one element has a length that exceeds this threshold then the array elements will have to be formatted vertically. + +- **Default value**: `10` +- **Possible values**: any positive integer that is less than or equal to the value specified for [`max_width`](#max_width) +- **Stable**: Yes + +#### `10` (default): +```rust +fn main() { + pub const FORMAT_TEST: [u64; 5] = [ + 0x0000000000000000, + 0xaaaaaaaaaaaaaaaa, + 0xbbbbbbbbbbbbbbbb, + 0xcccccccccccccccc, + 0xdddddddddddddddd, + ]; +} +``` +#### `20`: +```rust +fn main() { + pub const FORMAT_TEST: [u64; 5] = [ + 0x0000000000000000, 0xaaaaaaaaaaaaaaaa, 0xbbbbbbbbbbbbbbbb, 0xcccccccccccccccc, + 0xdddddddddddddddd, + ]; +} +``` +See also [`max_width`](#max_width). + ## `skip_children` Don't reformat out of line modules diff --git a/src/config/mod.rs b/src/config/mod.rs index 5041e1e36dd6..18e1854612bf 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -106,6 +106,8 @@ create_config! { // Misc. remove_nested_parens: bool, true, true, "Remove nested parens"; combine_control_expr: bool, true, false, "Combine control expressions with function calls"; + short_array_element_width_threshold: usize, 10, true, + "Width threshold for an array element to be considered short"; overflow_delimited_expr: bool, false, false, "Allow trailing bracket/brace delimited expressions to overflow"; struct_field_align_threshold: usize, 0, false, @@ -591,6 +593,7 @@ spaces_around_ranges = false binop_separator = "Front" remove_nested_parens = true combine_control_expr = true +short_array_element_width_threshold = 10 overflow_delimited_expr = false struct_field_align_threshold = 0 enum_discrim_align_threshold = 0 diff --git a/src/overflow.rs b/src/overflow.rs index 3475f5c378cd..80aed998d737 100644 --- a/src/overflow.rs +++ b/src/overflow.rs @@ -26,8 +26,6 @@ use crate::spanned::Spanned; use crate::types::{can_be_overflowed_type, SegmentParam}; use crate::utils::{count_newlines, extra_offset, first_line_width, last_line_width, mk_sp}; -const SHORT_ITEM_THRESHOLD: usize = 10; - /// A list of `format!`-like macros, that take a long format string and a list of arguments to /// format. /// @@ -572,7 +570,12 @@ impl<'a> Context<'a> { if one_line { tactic = DefinitiveListTactic::SpecialMacro(num_args_before); }; - } else if is_every_expr_simple(&self.items) && no_long_items(list_items) { + } else if is_every_expr_simple(&self.items) + && no_long_items( + list_items, + self.context.config.short_array_element_width_threshold(), + ) + { tactic = DefinitiveListTactic::Mixed; } } @@ -755,9 +758,9 @@ fn shape_from_indent_style( } } -fn no_long_items(list: &[ListItem]) -> bool { +fn no_long_items(list: &[ListItem], short_array_element_width_threshold: usize) -> bool { list.iter() - .all(|item| item.inner_as_ref().len() <= SHORT_ITEM_THRESHOLD) + .all(|item| item.inner_as_ref().len() <= short_array_element_width_threshold) } /// In case special-case style is required, returns an offset from which we start horizontal layout. diff --git a/tests/source/configs/short_array_element_width_threshold/10.rs b/tests/source/configs/short_array_element_width_threshold/10.rs new file mode 100644 index 000000000000..7d0d70919a60 --- /dev/null +++ b/tests/source/configs/short_array_element_width_threshold/10.rs @@ -0,0 +1,11 @@ +// rustfmt-short_array_element_width_threshold: 10 + +fn main() { + pub const FORMAT_TEST: [u64; 5] = [ + 0x0000000000000000, + 0xaaaaaaaaaaaaaaaa, + 0xbbbbbbbbbbbbbbbb, + 0xcccccccccccccccc, + 0xdddddddddddddddd, + ]; +} \ No newline at end of file diff --git a/tests/source/configs/short_array_element_width_threshold/20.rs b/tests/source/configs/short_array_element_width_threshold/20.rs new file mode 100644 index 000000000000..8a93a51d6a28 --- /dev/null +++ b/tests/source/configs/short_array_element_width_threshold/20.rs @@ -0,0 +1,11 @@ +// rustfmt-short_array_element_width_threshold: 20 + +fn main() { + pub const FORMAT_TEST: [u64; 5] = [ + 0x0000000000000000, + 0xaaaaaaaaaaaaaaaa, + 0xbbbbbbbbbbbbbbbb, + 0xcccccccccccccccc, + 0xdddddddddddddddd, + ]; +} \ No newline at end of file diff --git a/tests/source/configs/short_array_element_width_threshold/greater_than_max_width.rs b/tests/source/configs/short_array_element_width_threshold/greater_than_max_width.rs new file mode 100644 index 000000000000..710b6fe7c4ba --- /dev/null +++ b/tests/source/configs/short_array_element_width_threshold/greater_than_max_width.rs @@ -0,0 +1,12 @@ +// rustfmt-max_width: 20 +// rustfmt-short_array_element_width_threshold: 30 + +fn main() { + pub const FORMAT_TEST: [u64; 5] = [ + 0x0000000000000000, + 0xaaaaaaaaaaaaaaaa, + 0xbbbbbbbbbbbbbbbb, + 0xcccccccccccccccc, + 0xdddddddddddddddd, + ]; +} diff --git a/tests/target/configs/short_array_element_width_threshold/10.rs b/tests/target/configs/short_array_element_width_threshold/10.rs new file mode 100644 index 000000000000..78c4adba1c1f --- /dev/null +++ b/tests/target/configs/short_array_element_width_threshold/10.rs @@ -0,0 +1,11 @@ +// rustfmt-short_array_element_width_threshold: 10 + +fn main() { + pub const FORMAT_TEST: [u64; 5] = [ + 0x0000000000000000, + 0xaaaaaaaaaaaaaaaa, + 0xbbbbbbbbbbbbbbbb, + 0xcccccccccccccccc, + 0xdddddddddddddddd, + ]; +} diff --git a/tests/target/configs/short_array_element_width_threshold/20.rs b/tests/target/configs/short_array_element_width_threshold/20.rs new file mode 100644 index 000000000000..6084690652f0 --- /dev/null +++ b/tests/target/configs/short_array_element_width_threshold/20.rs @@ -0,0 +1,8 @@ +// rustfmt-short_array_element_width_threshold: 20 + +fn main() { + pub const FORMAT_TEST: [u64; 5] = [ + 0x0000000000000000, 0xaaaaaaaaaaaaaaaa, 0xbbbbbbbbbbbbbbbb, 0xcccccccccccccccc, + 0xdddddddddddddddd, + ]; +} diff --git a/tests/target/configs/short_array_element_width_threshold/greater_than_max_width.rs b/tests/target/configs/short_array_element_width_threshold/greater_than_max_width.rs new file mode 100644 index 000000000000..710b6fe7c4ba --- /dev/null +++ b/tests/target/configs/short_array_element_width_threshold/greater_than_max_width.rs @@ -0,0 +1,12 @@ +// rustfmt-max_width: 20 +// rustfmt-short_array_element_width_threshold: 30 + +fn main() { + pub const FORMAT_TEST: [u64; 5] = [ + 0x0000000000000000, + 0xaaaaaaaaaaaaaaaa, + 0xbbbbbbbbbbbbbbbb, + 0xcccccccccccccccc, + 0xdddddddddddddddd, + ]; +} From 8984438a6faf11e0cb8e876e80f177a42a43904d Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Fri, 18 Mar 2022 09:48:45 -0400 Subject: [PATCH 162/401] Honor `#[rustfmt::skip::attributes(derive)]` attribute Fixes 5270 Previously, rustfmt only checked the `merge_derives` configuration value to determine if it should merge_derives. This lead to derives being merged even when annotated with the `rustfmt::skip` attribute. Now, rustfmt also checks if derives are explicitly being skipped in the current context via the `rustfmt::skip` attribute. --- src/attr.rs | 6 +- tests/source/issue-5270/merge_derives_true.rs | 62 +++++++++++++++++++ .../target/issue-5270/merge_derives_false.rs | 62 +++++++++++++++++++ tests/target/issue-5270/merge_derives_true.rs | 60 ++++++++++++++++++ 4 files changed, 189 insertions(+), 1 deletion(-) create mode 100644 tests/source/issue-5270/merge_derives_true.rs create mode 100644 tests/target/issue-5270/merge_derives_false.rs create mode 100644 tests/target/issue-5270/merge_derives_true.rs diff --git a/src/attr.rs b/src/attr.rs index 3887a8051f20..befe12ae2c4c 100644 --- a/src/attr.rs +++ b/src/attr.rs @@ -389,6 +389,10 @@ impl Rewrite for [ast::Attribute] { let mut attrs = self; let mut result = String::new(); + // Determine if the source text is annotated with `#[rustfmt::skip::attributes(derive)]` + // or `#![rustfmt::skip::attributes(derive)]` + let skip_derives = context.skip_context.skip_attribute("derive"); + // This is not just a simple map because we need to handle doc comments // (where we take as many doc comment attributes as possible) and possibly // merging derives into a single attribute. @@ -431,7 +435,7 @@ impl Rewrite for [ast::Attribute] { } // Handle derives if we will merge them. - if context.config.merge_derives() && is_derive(&attrs[0]) { + if !skip_derives && context.config.merge_derives() && is_derive(&attrs[0]) { let derives = take_while_with_pred(context, attrs, is_derive); let derive_str = format_derive(derives, shape, context)?; result.push_str(&derive_str); diff --git a/tests/source/issue-5270/merge_derives_true.rs b/tests/source/issue-5270/merge_derives_true.rs new file mode 100644 index 000000000000..b31bbf095e73 --- /dev/null +++ b/tests/source/issue-5270/merge_derives_true.rs @@ -0,0 +1,62 @@ +// rustfmt-merge_derives:true + +#[rustfmt::skip::attributes(derive)] +#[allow(dead_code)] +#[derive(StructField)] +#[derive(Clone)] +struct DoNotMergeDerives { + field: String, +} + +#[allow(dead_code)] +#[derive(StructField)] +#[rustfmt::skip::attributes(derive)] +#[derive(Clone)] +struct DoNotMergeDerivesSkipInMiddle { + field: String, +} + +#[allow(dead_code)] +#[derive(StructField)] +#[derive(Clone)] +#[rustfmt::skip::attributes(derive)] +struct DoNotMergeDerivesSkipAtEnd { + field: String, +} + +#[allow(dead_code)] +#[derive(StructField)] +#[derive(Clone)] +struct MergeDerives { + field: String, +} + +mod inner_attribute_derive_skip { + #![rustfmt::skip::attributes(derive)] + + #[allow(dead_code)] + #[derive(StructField)] + #[derive(Clone)] + struct DoNotMergeDerives { + field: String, + } +} + +#[rustfmt::skip::attributes(derive)] +mod outer_attribute_derive_skip { + #[allow(dead_code)] + #[derive(StructField)] + #[derive(Clone)] + struct DoNotMergeDerives { + field: String, + } +} + +mod no_derive_skip { + #[allow(dead_code)] + #[derive(StructField)] + #[derive(Clone)] + struct MergeDerives { + field: String, + } +} diff --git a/tests/target/issue-5270/merge_derives_false.rs b/tests/target/issue-5270/merge_derives_false.rs new file mode 100644 index 000000000000..3b6f7e66993c --- /dev/null +++ b/tests/target/issue-5270/merge_derives_false.rs @@ -0,0 +1,62 @@ +// rustfmt-merge_derives:false + +#[rustfmt::skip::attributes(derive)] +#[allow(dead_code)] +#[derive(StructField)] +#[derive(Clone)] +struct DoNotMergeDerives { + field: String, +} + +#[allow(dead_code)] +#[derive(StructField)] +#[rustfmt::skip::attributes(derive)] +#[derive(Clone)] +struct DoNotMergeDerivesSkipInMiddle { + field: String, +} + +#[allow(dead_code)] +#[derive(StructField)] +#[derive(Clone)] +#[rustfmt::skip::attributes(derive)] +struct DoNotMergeDerivesSkipAtEnd { + field: String, +} + +#[allow(dead_code)] +#[derive(StructField)] +#[derive(Clone)] +struct MergeDerives { + field: String, +} + +mod inner_attribute_derive_skip { + #![rustfmt::skip::attributes(derive)] + + #[allow(dead_code)] + #[derive(StructField)] + #[derive(Clone)] + struct DoNotMergeDerives { + field: String, + } +} + +#[rustfmt::skip::attributes(derive)] +mod outer_attribute_derive_skip { + #[allow(dead_code)] + #[derive(StructField)] + #[derive(Clone)] + struct DoNotMergeDerives { + field: String, + } +} + +mod no_derive_skip { + #[allow(dead_code)] + #[derive(StructField)] + #[derive(Clone)] + struct MergeDerives { + field: String, + } +} diff --git a/tests/target/issue-5270/merge_derives_true.rs b/tests/target/issue-5270/merge_derives_true.rs new file mode 100644 index 000000000000..5f488b4542d0 --- /dev/null +++ b/tests/target/issue-5270/merge_derives_true.rs @@ -0,0 +1,60 @@ +// rustfmt-merge_derives:true + +#[rustfmt::skip::attributes(derive)] +#[allow(dead_code)] +#[derive(StructField)] +#[derive(Clone)] +struct DoNotMergeDerives { + field: String, +} + +#[allow(dead_code)] +#[derive(StructField)] +#[rustfmt::skip::attributes(derive)] +#[derive(Clone)] +struct DoNotMergeDerivesSkipInMiddle { + field: String, +} + +#[allow(dead_code)] +#[derive(StructField)] +#[derive(Clone)] +#[rustfmt::skip::attributes(derive)] +struct DoNotMergeDerivesSkipAtEnd { + field: String, +} + +#[allow(dead_code)] +#[derive(StructField, Clone)] +struct MergeDerives { + field: String, +} + +mod inner_attribute_derive_skip { + #![rustfmt::skip::attributes(derive)] + + #[allow(dead_code)] + #[derive(StructField)] + #[derive(Clone)] + struct DoNotMergeDerives { + field: String, + } +} + +#[rustfmt::skip::attributes(derive)] +mod outer_attribute_derive_skip { + #[allow(dead_code)] + #[derive(StructField)] + #[derive(Clone)] + struct DoNotMergeDerives { + field: String, + } +} + +mod no_derive_skip { + #[allow(dead_code)] + #[derive(StructField, Clone)] + struct MergeDerives { + field: String, + } +} From c2039d95c6fd5c27a90e768e79c630af374fcaf0 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Sun, 27 Mar 2022 20:46:25 -0500 Subject: [PATCH 163/401] chore: bump toolchain --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index 0d407f11994b..94b57d506c20 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-03-17" +channel = "nightly-2022-03-27" components = ["rustc-dev"] From 4fecede7fdaeeb859b066394bf27401fd13743a7 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Mon, 28 Mar 2022 20:27:42 -0500 Subject: [PATCH 164/401] Revert "Use cargo-fmt in self_tests" This reverts commit c63d42e80473a0c18714b55058f27506fd24955c. --- src/test/mod.rs | 46 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/src/test/mod.rs b/src/test/mod.rs index 4191e3e96b0a..ab966d4a3607 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -375,21 +375,43 @@ fn idempotence_tests() { }); } +// Run rustfmt on itself. This operation must be idempotent. We also check that +// no warnings are emitted. +// Issue-3443: these tests require nightly #[nightly_only_test] #[test] fn self_tests() { - let get_exe_path = |name| { - let mut path = env::current_exe().unwrap(); - path.pop(); - path.set_file_name(format!("{name}{}", env::consts::EXE_SUFFIX)); - path - }; - let status = Command::new(get_exe_path("cargo-fmt")) - .args(["--check", "--all"]) - .env("RUSTFMT", get_exe_path("rustfmt")) - .status() - .unwrap(); - assert!(status.success()); + init_log(); + let mut files = get_test_files(Path::new("tests"), false); + let bin_directories = vec!["cargo-fmt", "git-rustfmt", "bin", "format-diff"]; + for dir in bin_directories { + let mut path = PathBuf::from("src"); + path.push(dir); + path.push("main.rs"); + files.push(path); + } + files.push(PathBuf::from("src/lib.rs")); + + let (reports, count, fails) = check_files(files, &Some(PathBuf::from("rustfmt.toml"))); + let mut warnings = 0; + + // Display results. + println!("Ran {} self tests.", count); + assert_eq!(fails, 0, "{} self tests failed", fails); + + for format_report in reports { + println!( + "{}", + FormatReportFormatterBuilder::new(&format_report).build() + ); + warnings += format_report.warning_count(); + } + + assert_eq!( + warnings, 0, + "Rustfmt's code generated {} warnings", + warnings + ); } #[test] From e0c7b7d5d30d5b47f9d6a9fd3c449fc610a330b9 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Mon, 28 Mar 2022 20:29:05 -0500 Subject: [PATCH 165/401] tests: ignore cargo fmt test for rust-lang/rust runs --- tests/cargo-fmt/main.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/cargo-fmt/main.rs b/tests/cargo-fmt/main.rs index 3713552c66af..348876cd264f 100644 --- a/tests/cargo-fmt/main.rs +++ b/tests/cargo-fmt/main.rs @@ -73,6 +73,7 @@ fn rustfmt_help() { assert_that!(&["--", "--help=config"], contains("Configuration Options:")); } +#[ignore] #[test] fn cargo_fmt_out_of_line_test_modules() { // See also https://github.com/rust-lang/rustfmt/issues/5119 From 8e94761a94162f43e6c3e9bfbbc5005da3dbeea5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 29 Mar 2022 13:09:28 -0700 Subject: [PATCH 166/401] Add test of macro calls inside extern block --- tests/source/extern.rs | 13 +++++++++++++ tests/target/extern.rs | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/tests/source/extern.rs b/tests/source/extern.rs index d0a033b12432..5b981385d2b1 100644 --- a/tests/source/extern.rs +++ b/tests/source/extern.rs @@ -77,3 +77,16 @@ libc::c_long; extern { } + +macro_rules! x { + ($tt:tt) => {}; +} + +extern "macros" { + x!(ident); + // x!(#); FIXME + x![ident]; + // x![#]; FIXME + x! {ident} + x! {#} +} diff --git a/tests/target/extern.rs b/tests/target/extern.rs index 44ed6d4b4756..570d21c17dfc 100644 --- a/tests/target/extern.rs +++ b/tests/target/extern.rs @@ -82,3 +82,16 @@ extern "C" { } extern "C" {} + +macro_rules! x { + ($tt:tt) => {}; +} + +extern "macros" { + x!(ident); + // x!(#); FIXME + x![ident]; + // x![#]; FIXME + x! {ident} + x! {#} +} From 5ff7b632a95bac6955611d85040859128902c580 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 29 Mar 2022 12:59:07 -0700 Subject: [PATCH 167/401] Preserve semicolon after macro call inside foreign mod --- src/macros.rs | 14 ++++++++++++-- tests/source/extern.rs | 4 ++-- tests/target/extern.rs | 4 ++-- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index fdbe3374615c..664f152e8be1 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -112,6 +112,7 @@ fn rewrite_macro_name( fn return_macro_parse_failure_fallback( context: &RewriteContext<'_>, indent: Indent, + position: MacroPosition, span: Span, ) -> Option { // Mark this as a failure however we format it @@ -140,7 +141,11 @@ fn return_macro_parse_failure_fallback( )); // Return the snippet unmodified if the macro is not block-like - Some(context.snippet(span).to_owned()) + let mut snippet = context.snippet(span).to_owned(); + if position == MacroPosition::Item { + snippet.push(';'); + } + Some(snippet) } pub(crate) fn rewrite_macro( @@ -233,7 +238,12 @@ fn rewrite_macro_inner( } = match parse_macro_args(context, ts, style, is_forced_bracket) { Some(args) => args, None => { - return return_macro_parse_failure_fallback(context, shape.indent, mac.span()); + return return_macro_parse_failure_fallback( + context, + shape.indent, + position, + mac.span(), + ); } }; diff --git a/tests/source/extern.rs b/tests/source/extern.rs index 5b981385d2b1..f51ba6e98c9f 100644 --- a/tests/source/extern.rs +++ b/tests/source/extern.rs @@ -84,9 +84,9 @@ macro_rules! x { extern "macros" { x!(ident); - // x!(#); FIXME + x!(#); x![ident]; - // x![#]; FIXME + x![#]; x! {ident} x! {#} } diff --git a/tests/target/extern.rs b/tests/target/extern.rs index 570d21c17dfc..d1741360cfd6 100644 --- a/tests/target/extern.rs +++ b/tests/target/extern.rs @@ -89,9 +89,9 @@ macro_rules! x { extern "macros" { x!(ident); - // x!(#); FIXME + x!(#); x![ident]; - // x![#]; FIXME + x![#]; x! {ident} x! {#} } From 1dcdfb276d8a10470ce1c6a6fd20a6104c313d82 Mon Sep 17 00:00:00 2001 From: Tharun Rajendran Date: Sat, 2 Apr 2022 05:30:53 +0530 Subject: [PATCH 168/401] fix(rustfmt): fix struct field formatting with doc comments present (#5217) * fix(rustfmt): fix struct field formatting with doc comments present Fixes #5215 * fix review feedbacks * add unit test without doc comment * move tests to a seperate file * add additional test cases * reintroduce a newline at the of test/souce/structs.rs --- src/items.rs | 2 +- tests/source/struct_field_doc_comment.rs | 72 ++++++++++++++++++++++++ tests/target/struct_field_doc_comment.rs | 69 +++++++++++++++++++++++ 3 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 tests/source/struct_field_doc_comment.rs create mode 100644 tests/target/struct_field_doc_comment.rs diff --git a/src/items.rs b/src/items.rs index 92f423bbb627..79f6ff69aa92 100644 --- a/src/items.rs +++ b/src/items.rs @@ -1771,7 +1771,7 @@ pub(crate) fn rewrite_struct_field( .offset_left(overhead + spacing.len()) .and_then(|ty_shape| field.ty.rewrite(context, ty_shape)); if let Some(ref ty) = orig_ty { - if !ty.contains('\n') { + if !ty.contains('\n') && !contains_comment(context.snippet(missing_span)) { return Some(attr_prefix + &spacing + ty); } } diff --git a/tests/source/struct_field_doc_comment.rs b/tests/source/struct_field_doc_comment.rs new file mode 100644 index 000000000000..191a62100459 --- /dev/null +++ b/tests/source/struct_field_doc_comment.rs @@ -0,0 +1,72 @@ +// #5215 +struct MyTuple( + /// Doc Comments + /* TODO note to add more to Doc Comments */ u32, + /// Doc Comments + // TODO note + u64, +); + +struct MyTuple( + #[cfg(unix)] // some comment + u64, + #[cfg(not(unix))] /*block comment */ + u32, +); + +struct MyTuple( + #[cfg(unix)] + // some comment + u64, + #[cfg(not(unix))] + /*block comment */ + u32, +); + +struct MyTuple( + #[cfg(unix)] // some comment + pub u64, + #[cfg(not(unix))] /*block comment */ + pub(crate) u32, +); + +struct MyTuple( + /// Doc Comments + /* TODO note to add more to Doc Comments */ + pub u32, + /// Doc Comments + // TODO note + pub(crate) u64, +); + +struct MyStruct { + #[cfg(unix)] // some comment + a: u64, + #[cfg(not(unix))] /*block comment */ + b: u32, +} + +struct MyStruct { + #[cfg(unix)] // some comment + pub a: u64, + #[cfg(not(unix))] /*block comment */ + pub(crate) b: u32, +} + +struct MyStruct { + /// Doc Comments + /* TODO note to add more to Doc Comments */ + a: u32, + /// Doc Comments + // TODO note + b: u64, +} + +struct MyStruct { + /// Doc Comments + /* TODO note to add more to Doc Comments */ + pub a: u32, + /// Doc Comments + // TODO note + pub(crate) b: u64, +} diff --git a/tests/target/struct_field_doc_comment.rs b/tests/target/struct_field_doc_comment.rs new file mode 100644 index 000000000000..ebb01a668f4c --- /dev/null +++ b/tests/target/struct_field_doc_comment.rs @@ -0,0 +1,69 @@ +// #5215 +struct MyTuple( + /// Doc Comments + /* TODO note to add more to Doc Comments */ + u32, + /// Doc Comments + // TODO note + u64, +); + +struct MyTuple( + #[cfg(unix)] // some comment + u64, + #[cfg(not(unix))] /*block comment */ u32, +); + +struct MyTuple( + #[cfg(unix)] + // some comment + u64, + #[cfg(not(unix))] + /*block comment */ + u32, +); + +struct MyTuple( + #[cfg(unix)] // some comment + pub u64, + #[cfg(not(unix))] /*block comment */ pub(crate) u32, +); + +struct MyTuple( + /// Doc Comments + /* TODO note to add more to Doc Comments */ + pub u32, + /// Doc Comments + // TODO note + pub(crate) u64, +); + +struct MyStruct { + #[cfg(unix)] // some comment + a: u64, + #[cfg(not(unix))] /*block comment */ b: u32, +} + +struct MyStruct { + #[cfg(unix)] // some comment + pub a: u64, + #[cfg(not(unix))] /*block comment */ pub(crate) b: u32, +} + +struct MyStruct { + /// Doc Comments + /* TODO note to add more to Doc Comments */ + a: u32, + /// Doc Comments + // TODO note + b: u64, +} + +struct MyStruct { + /// Doc Comments + /* TODO note to add more to Doc Comments */ + pub a: u32, + /// Doc Comments + // TODO note + pub(crate) b: u64, +} From 7d6ca7c35c275ab7a6a50d290babe874cb8e3fc7 Mon Sep 17 00:00:00 2001 From: Light Ning Date: Sat, 2 Apr 2022 23:45:38 +0800 Subject: [PATCH 169/401] Bump deps (#5237) * bump deps * sort the deps --- Cargo.lock | 338 +++++++++++++++++++++++++++-------------------------- Cargo.toml | 41 +++---- 2 files changed, 194 insertions(+), 185 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b932e15ef746..1b444136585d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,64 +4,60 @@ version = 3 [[package]] name = "aho-corasick" -version = "0.7.6" +version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" dependencies = [ "memchr", ] [[package]] name = "annotate-snippets" -version = "0.8.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d78ea013094e5ea606b1c05fe35f1dd7ea1eb1ea259908d040b25bd5ec677ee5" +checksum = "c3b9d411ecbaf79885c6df4d75fff75858d5995ff25385657a28af47e82f9c36" dependencies = [ + "unicode-width", "yansi-term", ] [[package]] name = "ansi_term" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" dependencies = [ "winapi", ] [[package]] name = "anyhow" -version = "1.0.25" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9267dff192e68f3399525901e709a48c1d3982c9c072fa32f2127a0cb0babf14" +checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27" [[package]] name = "atty" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ + "hermit-abi", "libc", "winapi", ] -[[package]] -name = "autocfg" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" - [[package]] name = "bitflags" -version = "1.2.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bstr" -version = "0.2.8" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d6c2c5b58ab920a4f5aeaaca34b4488074e8cc7596af94e6f8c6ff247c60245" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" dependencies = [ "memchr", ] @@ -77,27 +73,27 @@ dependencies = [ [[package]] name = "camino" -version = "1.0.5" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52d74260d9bf6944e2208aa46841b4b8f0d7ffc0849a06837b2f510337f86b2b" +checksum = "6f3132262930b0522068049f5870a856ab8affc80c70d08b6ecb785771a6fc23" dependencies = [ "serde", ] [[package]] name = "cargo-platform" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0226944a63d1bf35a3b5f948dd7c59e263db83695c9e8bffc4037de02e30f1d7" +checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27" dependencies = [ "serde", ] [[package]] name = "cargo_metadata" -version = "0.14.0" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c297bd3135f558552f99a0daa180876984ea2c4ffa7470314540dff8c654109a" +checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" dependencies = [ "camino", "cargo-platform", @@ -106,12 +102,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - [[package]] name = "cfg-if" version = "1.0.0" @@ -120,9 +110,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "2.33.0" +version = "2.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ "ansi_term", "atty", @@ -135,20 +125,19 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.3" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49" +checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" dependencies = [ - "autocfg", - "cfg-if 1.0.0", + "cfg-if", "lazy_static", ] [[package]] name = "derive-new" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71f31892cd5c62e414316f2963c5689242c43d8e7bbcaaeca97e5e28c95d91d9" +checksum = "3418329ca0ad70234b9735dc4ceed10af4df60eff9c8e7b06cb5e520d92c3535" dependencies = [ "proc-macro2", "quote", @@ -157,25 +146,45 @@ dependencies = [ [[package]] name = "diff" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c2b69f912779fbb121ceb775d74d51e915af17aaebc38d28a592843a2dd0a3a" +checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499" [[package]] name = "dirs" -version = "2.0.2" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" dependencies = [ - "cfg-if 0.1.10", "dirs-sys", ] [[package]] -name = "dirs-sys" -version = "0.3.6" +name = "dirs-next" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" dependencies = [ "libc", "redox_users", @@ -184,15 +193,15 @@ dependencies = [ [[package]] name = "either" -version = "1.5.3" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" [[package]] name = "env_logger" -version = "0.8.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54532e3223c5af90a6a757c90b5c5521564b07e5e7a958681bcd2afad421cdcd" +checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" dependencies = [ "atty", "humantime", @@ -203,9 +212,9 @@ dependencies = [ [[package]] name = "fnv" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "getopts" @@ -218,20 +227,20 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.3" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "wasi", ] [[package]] name = "globset" -version = "0.4.6" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c152169ef1e421390738366d2f796655fec62621dabbd0fd476f905934061e4a" +checksum = "10463d9ff00a2a068db14231982f5132edebad0d7660cd956a1c30292dbcbfbd" dependencies = [ "aho-corasick", "bstr", @@ -242,13 +251,22 @@ dependencies = [ [[package]] name = "heck" -version = "0.3.1" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" dependencies = [ "unicode-segmentation", ] +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + [[package]] name = "humantime" version = "2.1.0" @@ -257,9 +275,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "ignore" -version = "0.4.17" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b287fb45c60bb826a0dc68ff08742b9d88a2fea13d6e0c286b3172065aaf878c" +checksum = "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d" dependencies = [ "crossbeam-utils", "globset", @@ -284,9 +302,9 @@ dependencies = [ [[package]] name = "itoa" -version = "0.4.4" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" [[package]] name = "lazy_static" @@ -296,9 +314,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.77" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235" +checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f" [[package]] name = "libm" @@ -308,34 +326,40 @@ checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" [[package]] name = "log" -version = "0.4.14" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] name = "memchr" -version = "2.2.1" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "once_cell" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" [[package]] name = "packed_simd_2" -version = "0.3.4" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3278e0492f961fd4ae70909f56b2723a7e8d01a228427294e19cdfdebda89a17" +checksum = "defdcfef86dcc44ad208f71d9ff4ce28df6537a4e0d6b0e8e845cb8ca10059a6" dependencies = [ - "cfg-if 0.1.10", + "cfg-if", "libm", ] [[package]] name = "proc-macro-error" -version = "0.4.11" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7959c6467d962050d639361f7703b2051c43036d03493c36f01d440fdd3138a" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", "proc-macro2", @@ -346,71 +370,69 @@ dependencies = [ [[package]] name = "proc-macro-error-attr" -version = "0.4.11" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4002d9f55991d5e019fb940a90e1a95eb80c24e77cb2462dd4dc869604d543a" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ "proc-macro2", "quote", - "syn", - "syn-mid", "version_check", ] [[package]] name = "proc-macro2" -version = "1.0.26" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" +checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" dependencies = [ "unicode-xid", ] [[package]] name = "quote" -version = "1.0.6" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea" +checksum = "632d02bff7f874a36f33ea8bb416cd484b90cc66c1194b1a1110d067a7013f58" dependencies = [ "proc-macro2", ] [[package]] name = "redox_syscall" -version = "0.2.4" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ec8ca9416c5ea37062b502703cd7fcb207736bc294f6e0cf367ac6fc234570" +checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" dependencies = [ "bitflags", ] [[package]] name = "redox_users" -version = "0.4.0" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ "getrandom", "redox_syscall", + "thiserror", ] [[package]] name = "regex" -version = "1.4.3" +version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a" +checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" dependencies = [ "aho-corasick", "memchr", "regex-syntax", - "thread_local", ] [[package]] name = "regex-syntax" -version = "0.6.22" +version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" [[package]] name = "rustc-workspace-hack" @@ -459,43 +481,49 @@ dependencies = [ ] [[package]] -name = "ryu" -version = "1.0.2" +name = "rustversion" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" +checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f" + +[[package]] +name = "ryu" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" [[package]] name = "same-file" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585e8ddcedc187886a30fa705c47985c3fa88d06624095856b36ca0b82ff4421" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" dependencies = [ "winapi-util", ] [[package]] name = "semver" -version = "1.0.4" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012" +checksum = "d65bd28f48be7196d222d95b9243287f48d27aca604e08497513019ff0502cc4" dependencies = [ "serde", ] [[package]] name = "serde" -version = "1.0.126" +version = "1.0.136" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03" +checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.126" +version = "1.0.136" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43" +checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" dependencies = [ "proc-macro2", "quote", @@ -504,9 +532,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.59" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcac07dbffa1c65e7f816ab9eba78eb142c6d44410f4eeba1e26e4f5dfa56b95" +checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" dependencies = [ "itoa", "ryu", @@ -521,9 +549,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "structopt" -version = "0.3.11" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fe43617218c0805c6eb37160119dc3c548110a67786da7218d1c6555212f073" +checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" dependencies = [ "clap", "lazy_static", @@ -532,9 +560,9 @@ dependencies = [ [[package]] name = "structopt-derive" -version = "0.4.4" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6e79c80e0f4efd86ca960218d4e056249be189ff1c42824dcd9a7f51a56f0bd" +checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" dependencies = [ "heck", "proc-macro-error", @@ -545,43 +573,33 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.65" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a1d708c221c5a612956ef9f75b37e454e88d1f7b899fbd3a18d4252012d663" +checksum = "704df27628939572cd88d33f171cd6f896f4eaca85252c6e0a72d8d8287ee86f" dependencies = [ "proc-macro2", "quote", "unicode-xid", ] -[[package]] -name = "syn-mid" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "term" -version = "0.6.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0863a3345e70f61d613eab32ee046ccd1bcc5f9105fe402c61fcd0c13eeb8b5" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" dependencies = [ - "dirs", + "dirs-next", + "rustversion", "winapi", ] [[package]] name = "termcolor" -version = "1.0.5" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d6098003bde162e4277c70665bd87c326f5a0c3f3fbfb285787fa482d54e6e" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" dependencies = [ - "wincolor", + "winapi-util", ] [[package]] @@ -595,18 +613,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.6" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc6b305ec0e323c7b6cfff6098a22516e0063d0bb7c3d88660a890217dca099a" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.6" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45ba8d810d9c48fc456b7ad54574e8bfb7c7918a57ad7a6e6a0985d7959e8597" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" dependencies = [ "proc-macro2", "quote", @@ -615,39 +633,39 @@ dependencies = [ [[package]] name = "thread_local" -version = "1.0.1" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" +checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" dependencies = [ - "lazy_static", + "once_cell", ] [[package]] name = "toml" -version = "0.5.3" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7aabe75941d914b72bf3e5d3932ed92ce0664d49d8432305a8b547c37227724" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" dependencies = [ "serde", ] [[package]] name = "unicode-segmentation" -version = "1.3.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1967f4cdfc355b37fd76d2a954fb2ed3871034eb4f26d60537d88795cfc332a9" +checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" [[package]] name = "unicode-width" -version = "0.1.6" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7007dbd421b92cc6e28410fe7362e2e0a2503394908f417b68ec8d1c364c4e20" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" [[package]] name = "unicode-xid" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" [[package]] name = "unicode_categories" @@ -657,21 +675,21 @@ checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" [[package]] name = "vec_map" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "version_check" -version = "0.9.1" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "walkdir" -version = "2.2.9" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9658c94fa8b940eab2250bd5a457f9c48b748420d71293b165c8cdbe2f55f71e" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" dependencies = [ "same-file", "winapi", @@ -686,9 +704,9 @@ checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" [[package]] name = "winapi" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", @@ -702,9 +720,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.2" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ "winapi", ] @@ -715,16 +733,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "wincolor" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96f5016b18804d24db43cebf3c77269e7569b8954a8464501c216cc5e070eaa9" -dependencies = [ - "winapi", - "winapi-util", -] - [[package]] name = "yansi-term" version = "0.1.2" diff --git a/Cargo.toml b/Cargo.toml index 764714638a97..4325f46db9fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,30 +33,31 @@ rustfmt-format-diff = [] generic-simd = ["bytecount/generic-simd"] [dependencies] -itertools = "0.10.1" -toml = "0.5" +annotate-snippets = { version = "0.9", features = ["color"] } +anyhow = "1.0" +bytecount = "0.6" +cargo_metadata = "0.14" +derive-new = "0.5" +diff = "0.1" +dirs = "4.0" +env_logger = "0.9" +getopts = "0.2" +ignore = "0.4" +itertools = "0.10" +lazy_static = "1.4" +log = "0.4" +regex = "1.5" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -unicode-segmentation = "1.0.0" -regex = "1.0" -term = "0.6" -diff = "0.1" -log = "0.4.14" -env_logger = "0.8" -getopts = "0.2" -derive-new = "0.5" -cargo_metadata = "0.14" -bytecount = "0.6" -unicode-width = "0.1.5" -unicode_categories = "0.1.1" -dirs = "2.0.1" -ignore = "0.4.17" -annotate-snippets = { version = "0.8", features = ["color"] } structopt = "0.3" -rustfmt-config_proc_macro = { version = "0.2", path = "config_proc_macro" } -lazy_static = "1.0.0" -anyhow = "1.0" +term = "0.7" thiserror = "1.0" +toml = "0.5" +unicode-segmentation = "1.9" +unicode-width = "0.1" +unicode_categories = "0.1" + +rustfmt-config_proc_macro = { version = "0.2", path = "config_proc_macro" } # A noop dependency that changes in the Rust repository, it's a bit of a hack. # See the `src/tools/rustc-workspace-hack/README.md` file in `rust-lang/rust` From 91995b6142faf1a5e56e1b7b1cb922504726197c Mon Sep 17 00:00:00 2001 From: Expyron <5100376+Expyron@users.noreply.github.com> Date: Thu, 7 Apr 2022 09:57:58 +0200 Subject: [PATCH 170/401] Replace `structopt` dependency by `clap` --- Cargo.lock | 129 +++++++++++++++++++------------------- Cargo.toml | 2 +- src/cargo-fmt/main.rs | 33 +++++----- src/cargo-fmt/test/mod.rs | 42 +++++++------ src/format-diff/main.rs | 45 ++++++------- 5 files changed, 131 insertions(+), 120 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1b444136585d..4eaaee74cf96 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,15 +21,6 @@ dependencies = [ "yansi-term", ] -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - [[package]] name = "anyhow" version = "1.0.56" @@ -47,6 +38,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + [[package]] name = "bitflags" version = "1.3.2" @@ -110,17 +107,32 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "2.34.0" +version = "3.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +checksum = "71c47df61d9e16dc010b55dba1952a57d8c215dbb533fd13cdd13369aac73b1c" dependencies = [ - "ansi_term", "atty", "bitflags", + "clap_derive", + "indexmap", + "lazy_static", + "os_str_bytes", "strsim", + "termcolor", "textwrap", - "unicode-width", - "vec_map", +] + +[[package]] +name = "clap_derive" +version = "3.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3aab4734e083b809aaf5794e14e756d1c798d2c69c7f7de7a09a2f5214993c1" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -250,13 +262,16 @@ dependencies = [ ] [[package]] -name = "heck" -version = "0.3.3" +name = "hashbrown" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", -] +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" [[package]] name = "hermit-abi" @@ -291,6 +306,16 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "indexmap" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" +dependencies = [ + "autocfg", + "hashbrown", +] + [[package]] name = "itertools" version = "0.10.3" @@ -314,9 +339,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.121" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f" +checksum = "ec647867e2bf0772e28c8bcde4f0d19a9216916e890543b5a03ed8ef27b8f259" [[package]] name = "libm" @@ -345,6 +370,15 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" +[[package]] +name = "os_str_bytes" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64" +dependencies = [ + "memchr", +] + [[package]] name = "packed_simd_2" version = "0.3.7" @@ -381,9 +415,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1" dependencies = [ "unicode-xid", ] @@ -457,6 +491,7 @@ dependencies = [ "anyhow", "bytecount", "cargo_metadata", + "clap", "derive-new", "diff", "dirs", @@ -471,7 +506,6 @@ dependencies = [ "rustfmt-config_proc_macro", "serde", "serde_json", - "structopt", "term", "thiserror", "toml", @@ -543,39 +577,15 @@ dependencies = [ [[package]] name = "strsim" -version = "0.8.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" - -[[package]] -name = "structopt" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" -dependencies = [ - "clap", - "lazy_static", - "structopt-derive", -] - -[[package]] -name = "structopt-derive" -version = "0.4.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" -dependencies = [ - "heck", - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.90" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "704df27628939572cd88d33f171cd6f896f4eaca85252c6e0a72d8d8287ee86f" +checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d" dependencies = [ "proc-macro2", "quote", @@ -604,12 +614,9 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.11.0" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] +checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" [[package]] name = "thiserror" @@ -673,12 +680,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - [[package]] name = "version_check" version = "0.9.4" diff --git a/Cargo.toml b/Cargo.toml index 4325f46db9fe..0be9723bc4dc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,6 +37,7 @@ annotate-snippets = { version = "0.9", features = ["color"] } anyhow = "1.0" bytecount = "0.6" cargo_metadata = "0.14" +clap = { version = "3.1", features = ["derive"] } derive-new = "0.5" diff = "0.1" dirs = "4.0" @@ -49,7 +50,6 @@ log = "0.4" regex = "1.5" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -structopt = "0.3" term = "0.7" thiserror = "1.0" toml = "0.5" diff --git a/src/cargo-fmt/main.rs b/src/cargo-fmt/main.rs index 8cb7b4585ecb..3542536f29b6 100644 --- a/src/cargo-fmt/main.rs +++ b/src/cargo-fmt/main.rs @@ -15,54 +15,59 @@ use std::path::{Path, PathBuf}; use std::process::Command; use std::str; -use structopt::StructOpt; +use clap::{CommandFactory, Parser}; #[path = "test/mod.rs"] #[cfg(test)] mod cargo_fmt_tests; -#[derive(StructOpt, Debug)] -#[structopt( +#[derive(Parser)] +#[clap( bin_name = "cargo fmt", about = "This utility formats all bin and lib files of \ the current crate using rustfmt." )] pub struct Opts { /// No output printed to stdout - #[structopt(short = "q", long = "quiet")] + #[clap(short = 'q', long = "quiet")] quiet: bool, /// Use verbose output - #[structopt(short = "v", long = "verbose")] + #[clap(short = 'v', long = "verbose")] verbose: bool, /// Print rustfmt version and exit - #[structopt(long = "version")] + #[clap(long = "version")] version: bool, /// Specify package to format - #[structopt(short = "p", long = "package", value_name = "package")] + #[clap( + short = 'p', + long = "package", + value_name = "package", + multiple_values = true + )] packages: Vec, /// Specify path to Cargo.toml - #[structopt(long = "manifest-path", value_name = "manifest-path")] + #[clap(long = "manifest-path", value_name = "manifest-path")] manifest_path: Option, /// Specify message-format: short|json|human - #[structopt(long = "message-format", value_name = "message-format")] + #[clap(long = "message-format", value_name = "message-format")] message_format: Option, /// Options passed to rustfmt // 'raw = true' to make `--` explicit. - #[structopt(name = "rustfmt_options", raw(true))] + #[clap(name = "rustfmt_options", raw(true))] rustfmt_options: Vec, /// Format all packages, and also their local path-based dependencies - #[structopt(long = "all")] + #[clap(long = "all")] format_all: bool, /// Run rustfmt in check mode - #[structopt(long = "check")] + #[clap(long = "check")] check: bool, } @@ -87,7 +92,7 @@ fn execute() -> i32 { } }); - let opts = Opts::from_iter(args); + let opts = Opts::parse_from(args); let verbosity = match (opts.verbose, opts.quiet) { (false, false) => Verbosity::Normal, @@ -204,7 +209,7 @@ fn convert_message_format_to_rustfmt_args( fn print_usage_to_stderr(reason: &str) { eprintln!("{}", reason); - let app = Opts::clap(); + let app = Opts::command(); app.after_help("") .write_help(&mut io::stderr()) .expect("failed to write to stderr"); diff --git a/src/cargo-fmt/test/mod.rs b/src/cargo-fmt/test/mod.rs index 360503632c7e..56e52fbabb68 100644 --- a/src/cargo-fmt/test/mod.rs +++ b/src/cargo-fmt/test/mod.rs @@ -6,7 +6,7 @@ mod targets; #[test] fn default_options() { let empty: Vec = vec![]; - let o = Opts::from_iter(&empty); + let o = Opts::parse_from(&empty); assert_eq!(false, o.quiet); assert_eq!(false, o.verbose); assert_eq!(false, o.version); @@ -20,7 +20,7 @@ fn default_options() { #[test] fn good_options() { - let o = Opts::from_iter(&[ + let o = Opts::parse_from(&[ "test", "-q", "-p", @@ -47,8 +47,8 @@ fn good_options() { #[test] fn unexpected_option() { assert!( - Opts::clap() - .get_matches_from_safe(&["test", "unexpected"]) + Opts::command() + .try_get_matches_from(&["test", "unexpected"]) .is_err() ); } @@ -56,8 +56,8 @@ fn unexpected_option() { #[test] fn unexpected_flag() { assert!( - Opts::clap() - .get_matches_from_safe(&["test", "--flag"]) + Opts::command() + .try_get_matches_from(&["test", "--flag"]) .is_err() ); } @@ -65,20 +65,20 @@ fn unexpected_flag() { #[test] fn mandatory_separator() { assert!( - Opts::clap() - .get_matches_from_safe(&["test", "--emit"]) + Opts::command() + .try_get_matches_from(&["test", "--emit"]) .is_err() ); assert!( - !Opts::clap() - .get_matches_from_safe(&["test", "--", "--emit"]) + !Opts::command() + .try_get_matches_from(&["test", "--", "--emit"]) .is_err() ); } #[test] fn multiple_packages_one_by_one() { - let o = Opts::from_iter(&[ + let o = Opts::parse_from(&[ "test", "-p", "package1", @@ -92,7 +92,7 @@ fn multiple_packages_one_by_one() { #[test] fn multiple_packages_grouped() { - let o = Opts::from_iter(&[ + let o = Opts::parse_from(&[ "test", "--package", "package1", @@ -106,14 +106,18 @@ fn multiple_packages_grouped() { #[test] fn empty_packages_1() { - assert!(Opts::clap().get_matches_from_safe(&["test", "-p"]).is_err()); + assert!( + Opts::command() + .try_get_matches_from(&["test", "-p"]) + .is_err() + ); } #[test] fn empty_packages_2() { assert!( - Opts::clap() - .get_matches_from_safe(&["test", "-p", "--", "--check"]) + Opts::command() + .try_get_matches_from(&["test", "-p", "--", "--check"]) .is_err() ); } @@ -121,8 +125,8 @@ fn empty_packages_2() { #[test] fn empty_packages_3() { assert!( - Opts::clap() - .get_matches_from_safe(&["test", "-p", "--verbose"]) + Opts::command() + .try_get_matches_from(&["test", "-p", "--verbose"]) .is_err() ); } @@ -130,8 +134,8 @@ fn empty_packages_3() { #[test] fn empty_packages_4() { assert!( - Opts::clap() - .get_matches_from_safe(&["test", "-p", "--check"]) + Opts::command() + .try_get_matches_from(&["test", "-p", "--check"]) .is_err() ); } diff --git a/src/format-diff/main.rs b/src/format-diff/main.rs index 655aeda42bf2..f6b739e1c2a3 100644 --- a/src/format-diff/main.rs +++ b/src/format-diff/main.rs @@ -19,8 +19,7 @@ use std::process; use regex::Regex; -use structopt::clap::AppSettings; -use structopt::StructOpt; +use clap::{CommandFactory, Parser}; /// The default pattern of files to format. /// @@ -37,16 +36,16 @@ enum FormatDiffError { IoError(#[from] io::Error), } -#[derive(StructOpt, Debug)] -#[structopt( +#[derive(Parser, Debug)] +#[clap( name = "rustfmt-format-diff", - setting = AppSettings::DisableVersion, - setting = AppSettings::NextLineHelp + disable_version_flag = true, + next_line_help = true )] pub struct Opts { /// Skip the smallest prefix containing NUMBER slashes - #[structopt( - short = "p", + #[clap( + short = 'p', long = "skip-prefix", value_name = "NUMBER", default_value = "0" @@ -54,8 +53,8 @@ pub struct Opts { skip_prefix: u32, /// Custom pattern selecting file paths to reformat - #[structopt( - short = "f", + #[clap( + short = 'f', long = "filter", value_name = "PATTERN", default_value = DEFAULT_PATTERN @@ -65,10 +64,12 @@ pub struct Opts { fn main() { env_logger::Builder::from_env("RUSTFMT_LOG").init(); - let opts = Opts::from_args(); + let opts = Opts::parse(); if let Err(e) = run(opts) { println!("{}", e); - Opts::clap().print_help().expect("cannot write to stdout"); + Opts::command() + .print_help() + .expect("cannot write to stdout"); process::exit(1); } } @@ -230,14 +231,14 @@ mod cmd_line_tests { #[test] fn default_options() { let empty: Vec = vec![]; - let o = Opts::from_iter(&empty); + let o = Opts::parse_from(&empty); assert_eq!(DEFAULT_PATTERN, o.filter); assert_eq!(0, o.skip_prefix); } #[test] fn good_options() { - let o = Opts::from_iter(&["test", "-p", "10", "-f", r".*\.hs"]); + let o = Opts::parse_from(&["test", "-p", "10", "-f", r".*\.hs"]); assert_eq!(r".*\.hs", o.filter); assert_eq!(10, o.skip_prefix); } @@ -245,8 +246,8 @@ mod cmd_line_tests { #[test] fn unexpected_option() { assert!( - Opts::clap() - .get_matches_from_safe(&["test", "unexpected"]) + Opts::command() + .try_get_matches_from(&["test", "unexpected"]) .is_err() ); } @@ -254,8 +255,8 @@ mod cmd_line_tests { #[test] fn unexpected_flag() { assert!( - Opts::clap() - .get_matches_from_safe(&["test", "--flag"]) + Opts::command() + .try_get_matches_from(&["test", "--flag"]) .is_err() ); } @@ -263,8 +264,8 @@ mod cmd_line_tests { #[test] fn overridden_option() { assert!( - Opts::clap() - .get_matches_from_safe(&["test", "-p", "10", "-p", "20"]) + Opts::command() + .try_get_matches_from(&["test", "-p", "10", "-p", "20"]) .is_err() ); } @@ -272,8 +273,8 @@ mod cmd_line_tests { #[test] fn negative_filter() { assert!( - Opts::clap() - .get_matches_from_safe(&["test", "-p", "-1"]) + Opts::command() + .try_get_matches_from(&["test", "-p", "-1"]) .is_err() ); } From 2d9bc460108df4e3587be82e36a58f2fb3f4813f Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Tue, 14 Jul 2020 00:03:13 -0500 Subject: [PATCH 171/401] Backport 4326 refactor: rename some private whitelist names --- src/overflow.rs | 12 ++++++------ src/test/mod.rs | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/overflow.rs b/src/overflow.rs index 80aed998d737..c296961d1f0c 100644 --- a/src/overflow.rs +++ b/src/overflow.rs @@ -32,7 +32,7 @@ use crate::utils::{count_newlines, extra_offset, first_line_width, last_line_wid /// Organized as a list of `(&str, usize)` tuples, giving the name of the macro and the number of /// arguments before the format string (none for `format!("format", ...)`, one for `assert!(result, /// "format", ...)`, two for `assert_eq!(left, right, "format", ...)`). -const SPECIAL_MACRO_WHITELIST: &[(&str, usize)] = &[ +const SPECIAL_CASE_MACROS: &[(&str, usize)] = &[ // format! like macros // From the Rust Standard Library. ("eprint!", 0), @@ -60,7 +60,7 @@ const SPECIAL_MACRO_WHITELIST: &[(&str, usize)] = &[ ("debug_assert_ne!", 2), ]; -const SPECIAL_ATTR_WHITELIST: &[(&str, usize)] = &[ +const SPECIAL_CASE_ATTR: &[(&str, usize)] = &[ // From the `failure` crate. ("fail", 0), ]; @@ -182,10 +182,10 @@ impl<'a> OverflowableItem<'a> { } } - fn whitelist(&self) -> &'static [(&'static str, usize)] { + fn special_cases(&self) -> &'static [(&'static str, usize)] { match self { - OverflowableItem::MacroArg(..) => SPECIAL_MACRO_WHITELIST, - OverflowableItem::NestedMetaItem(..) => SPECIAL_ATTR_WHITELIST, + OverflowableItem::MacroArg(..) => SPECIAL_CASE_MACROS, + OverflowableItem::NestedMetaItem(..) => SPECIAL_CASE_ATTR, _ => &[], } } @@ -770,7 +770,7 @@ pub(crate) fn maybe_get_args_offset( ) -> Option<(bool, usize)> { if let Some(&(_, num_args_before)) = args .get(0)? - .whitelist() + .special_cases() .iter() .find(|&&(s, _)| s == callee_str) { diff --git a/src/test/mod.rs b/src/test/mod.rs index ab966d4a3607..4bad8e71481e 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -24,7 +24,7 @@ mod parser; const DIFF_CONTEXT_SIZE: usize = 3; // A list of files on which we want to skip testing. -const SKIP_FILE_WHITE_LIST: &[&str] = &[ +const FILE_SKIP_LIST: &[&str] = &[ // We want to make sure that the `skip_children` is correctly working, // so we do not want to test this file directly. "configs/skip_children/foo/mod.rs", @@ -90,7 +90,7 @@ where } fn is_file_skip(path: &Path) -> bool { - SKIP_FILE_WHITE_LIST + FILE_SKIP_LIST .iter() .any(|file_path| is_subpath(path, file_path)) } From 4f3f87fb9a629db8bf0c6e04604320cd028f79e8 Mon Sep 17 00:00:00 2001 From: Tom Milligan Date: Sat, 5 Mar 2022 23:01:43 +0000 Subject: [PATCH 172/401] group_imports: test and document non-consecutive imports --- Configurations.md | 6 ++++- .../StdExternalCrate-non_consecutive.rs | 27 +++++++++++++++++++ .../StdExternalCrate-non_consecutive.rs | 18 +++++++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 tests/source/configs/group_imports/StdExternalCrate-non_consecutive.rs create mode 100644 tests/target/configs/group_imports/StdExternalCrate-non_consecutive.rs diff --git a/Configurations.md b/Configurations.md index a47439b9ba96..dc8d38267a83 100644 --- a/Configurations.md +++ b/Configurations.md @@ -2061,12 +2061,16 @@ use sit; ## `group_imports` -Controls the strategy for how imports are grouped together. +Controls the strategy for how consecutive imports are grouped together. + +Controls the strategy for grouping sets of consecutive imports. Imports may contain newlines between imports and still be grouped together as a single set, but other statements between imports will result in different grouping sets. - **Default value**: `Preserve` - **Possible values**: `Preserve`, `StdExternalCrate`, `One` - **Stable**: No (tracking issue: [#5083](https://github.com/rust-lang/rustfmt/issues/5083)) +Each set of imports (one or more `use` statements, optionally separated by newlines) will be formatted independently. Other statements such as `mod ...` or `extern crate ...` will cause imports to not be grouped together. + #### `Preserve` (default): Preserve the source file's import groups. diff --git a/tests/source/configs/group_imports/StdExternalCrate-non_consecutive.rs b/tests/source/configs/group_imports/StdExternalCrate-non_consecutive.rs new file mode 100644 index 000000000000..f239a0efa085 --- /dev/null +++ b/tests/source/configs/group_imports/StdExternalCrate-non_consecutive.rs @@ -0,0 +1,27 @@ +// rustfmt-group_imports: StdExternalCrate +use chrono::Utc; +use super::update::convert_publish_payload; + + + + + +use juniper::{FieldError, FieldResult}; + +use uuid::Uuid; +use alloc::alloc::Layout; + +extern crate uuid; + + + + + +use std::sync::Arc; + + +use broker::database::PooledConnection; + +use super::schema::{Context, Payload}; +use core::f32; +use crate::models::Event; diff --git a/tests/target/configs/group_imports/StdExternalCrate-non_consecutive.rs b/tests/target/configs/group_imports/StdExternalCrate-non_consecutive.rs new file mode 100644 index 000000000000..ecc8ede02cc6 --- /dev/null +++ b/tests/target/configs/group_imports/StdExternalCrate-non_consecutive.rs @@ -0,0 +1,18 @@ +// rustfmt-group_imports: StdExternalCrate +use alloc::alloc::Layout; + +use chrono::Utc; +use juniper::{FieldError, FieldResult}; +use uuid::Uuid; + +use super::update::convert_publish_payload; + +extern crate uuid; + +use core::f32; +use std::sync::Arc; + +use broker::database::PooledConnection; + +use super::schema::{Context, Payload}; +use crate::models::Event; From ba0351a1460e1e415293aa5e2b347b83e933703f Mon Sep 17 00:00:00 2001 From: Paul Gey Date: Sun, 17 Oct 2021 16:21:53 +0200 Subject: [PATCH 173/401] Preserve attributes for `imports_granularity=Item` Fixes #5030 --- src/imports.rs | 52 ++++++++++++++++++++++++++++---------- src/reorder.rs | 15 +++-------- tests/source/issue-5030.rs | 7 +++++ tests/target/issue-5030.rs | 6 +++++ 4 files changed, 56 insertions(+), 24 deletions(-) create mode 100644 tests/source/issue-5030.rs create mode 100644 tests/target/issue-5030.rs diff --git a/src/imports.rs b/src/imports.rs index 023198094868..485a72b85da4 100644 --- a/src/imports.rs +++ b/src/imports.rs @@ -10,6 +10,7 @@ use rustc_span::{ use crate::comment::combine_strs_with_missing_comments; use crate::config::lists::*; +use crate::config::ImportGranularity; use crate::config::{Edition, IndentStyle}; use crate::lists::{ definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator, @@ -182,7 +183,18 @@ impl UseSegment { } } -pub(crate) fn merge_use_trees(use_trees: Vec, merge_by: SharedPrefix) -> Vec { +pub(crate) fn regroup_use_trees( + use_trees: Vec, + import_granularity: ImportGranularity, +) -> Vec { + let merge_by = match import_granularity { + ImportGranularity::Item => return flatten_use_trees(use_trees, ImportGranularity::Item), + ImportGranularity::Preserve => return use_trees, + ImportGranularity::Crate => SharedPrefix::Crate, + ImportGranularity::Module => SharedPrefix::Module, + ImportGranularity::One => SharedPrefix::One, + }; + let mut result = Vec::with_capacity(use_trees.len()); for use_tree in use_trees { if use_tree.has_comment() || use_tree.attrs.is_some() { @@ -190,7 +202,7 @@ pub(crate) fn merge_use_trees(use_trees: Vec, merge_by: SharedPrefix) - continue; } - for mut flattened in use_tree.flatten() { + for mut flattened in use_tree.flatten(import_granularity) { if let Some(tree) = result .iter_mut() .find(|tree| tree.share_prefix(&flattened, merge_by)) @@ -208,10 +220,13 @@ pub(crate) fn merge_use_trees(use_trees: Vec, merge_by: SharedPrefix) - result } -pub(crate) fn flatten_use_trees(use_trees: Vec) -> Vec { +fn flatten_use_trees( + use_trees: Vec, + import_granularity: ImportGranularity, +) -> Vec { use_trees .into_iter() - .flat_map(UseTree::flatten) + .flat_map(|tree| tree.flatten(import_granularity)) .map(UseTree::nest_trailing_self) .collect() } @@ -581,7 +596,7 @@ impl UseTree { } } - fn flatten(self) -> Vec { + fn flatten(self, import_granularity: ImportGranularity) -> Vec { if self.path.is_empty() { return vec![self]; } @@ -595,7 +610,7 @@ impl UseTree { let prefix = &self.path[..self.path.len() - 1]; let mut result = vec![]; for nested_use_tree in list { - for flattend in &mut nested_use_tree.clone().flatten() { + for flattend in &mut nested_use_tree.clone().flatten(import_granularity) { let mut new_path = prefix.to_vec(); new_path.append(&mut flattend.path); result.push(UseTree { @@ -603,7 +618,11 @@ impl UseTree { span: self.span, list_item: None, visibility: self.visibility.clone(), - attrs: None, + // only retain attributes for `ImportGranularity::Item` + attrs: match import_granularity { + ImportGranularity::Item => self.attrs.clone(), + _ => None, + }, }); } } @@ -951,7 +970,7 @@ impl Rewrite for UseTree { } #[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub(crate) enum SharedPrefix { +enum SharedPrefix { Crate, Module, One, @@ -1106,7 +1125,7 @@ mod test { macro_rules! test_merge { ($by:ident, [$($input:expr),* $(,)*], [$($output:expr),* $(,)*]) => { assert_eq!( - merge_use_trees(parse_use_trees!($($input,)*), SharedPrefix::$by), + regroup_use_trees(parse_use_trees!($($input,)*), ImportGranularity::$by), parse_use_trees!($($output,)*), ); } @@ -1215,12 +1234,18 @@ mod test { #[test] fn test_flatten_use_trees() { assert_eq!( - flatten_use_trees(parse_use_trees!["foo::{a::{b, c}, d::e}"]), + flatten_use_trees( + parse_use_trees!["foo::{a::{b, c}, d::e}"], + ImportGranularity::Item + ), parse_use_trees!["foo::a::b", "foo::a::c", "foo::d::e"] ); assert_eq!( - flatten_use_trees(parse_use_trees!["foo::{self, a, b::{c, d}, e::*}"]), + flatten_use_trees( + parse_use_trees!["foo::{self, a, b::{c, d}, e::*}"], + ImportGranularity::Item + ), parse_use_trees![ "foo::{self}", "foo::a", @@ -1234,12 +1259,13 @@ mod test { #[test] fn test_use_tree_flatten() { assert_eq!( - parse_use_tree("a::b::{c, d, e, f}").flatten(), + parse_use_tree("a::b::{c, d, e, f}").flatten(ImportGranularity::Item), parse_use_trees!("a::b::c", "a::b::d", "a::b::e", "a::b::f",) ); assert_eq!( - parse_use_tree("a::b::{c::{d, e, f}, g, h::{i, j, k}}").flatten(), + parse_use_tree("a::b::{c::{d, e, f}, g, h::{i, j, k}}") + .flatten(ImportGranularity::Item), parse_use_trees![ "a::b::c::d", "a::b::c::e", diff --git a/src/reorder.rs b/src/reorder.rs index 13bfc92507d0..f565612ded18 100644 --- a/src/reorder.rs +++ b/src/reorder.rs @@ -11,8 +11,8 @@ use std::cmp::{Ord, Ordering}; use rustc_ast::ast; use rustc_span::{symbol::sym, Span}; -use crate::config::{Config, GroupImportsTactic, ImportGranularity}; -use crate::imports::{flatten_use_trees, merge_use_trees, SharedPrefix, UseSegment, UseTree}; +use crate::config::{Config, GroupImportsTactic}; +use crate::imports::{regroup_use_trees, UseSegment, UseTree}; use crate::items::{is_mod_decl, rewrite_extern_crate, rewrite_mod}; use crate::lists::{itemize_list, write_list, ListFormatting, ListItem}; use crate::rewrite::RewriteContext; @@ -107,15 +107,8 @@ fn rewrite_reorderable_or_regroupable_items( for (item, list_item) in normalized_items.iter_mut().zip(list_items) { item.list_item = Some(list_item.clone()); } - normalized_items = match context.config.imports_granularity() { - ImportGranularity::Crate => merge_use_trees(normalized_items, SharedPrefix::Crate), - ImportGranularity::Module => { - merge_use_trees(normalized_items, SharedPrefix::Module) - } - ImportGranularity::Item => flatten_use_trees(normalized_items), - ImportGranularity::One => merge_use_trees(normalized_items, SharedPrefix::One), - ImportGranularity::Preserve => normalized_items, - }; + normalized_items = + regroup_use_trees(normalized_items, context.config.imports_granularity()); let mut regrouped_items = match context.config.group_imports() { GroupImportsTactic::Preserve | GroupImportsTactic::One => { diff --git a/tests/source/issue-5030.rs b/tests/source/issue-5030.rs new file mode 100644 index 000000000000..f367e79f01f6 --- /dev/null +++ b/tests/source/issue-5030.rs @@ -0,0 +1,7 @@ +// rustfmt-imports_granularity: Item + +#[cfg(feature = "foo")] +use std::collections::{ + HashMap, + HashSet, +}; diff --git a/tests/target/issue-5030.rs b/tests/target/issue-5030.rs new file mode 100644 index 000000000000..b371331ed009 --- /dev/null +++ b/tests/target/issue-5030.rs @@ -0,0 +1,6 @@ +// rustfmt-imports_granularity: Item + +#[cfg(feature = "foo")] +use std::collections::HashMap; +#[cfg(feature = "foo")] +use std::collections::HashSet; From 9b1b3d69559750cf459638076dc32202110ec40e Mon Sep 17 00:00:00 2001 From: Tom Milligan Date: Sat, 16 Apr 2022 11:50:24 +0100 Subject: [PATCH 174/401] [review] rename internal function --- src/imports.rs | 7 +++++-- src/reorder.rs | 8 +++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/imports.rs b/src/imports.rs index 485a72b85da4..efe4e9498c90 100644 --- a/src/imports.rs +++ b/src/imports.rs @@ -183,7 +183,7 @@ impl UseSegment { } } -pub(crate) fn regroup_use_trees( +pub(crate) fn normalize_use_trees_with_granularity( use_trees: Vec, import_granularity: ImportGranularity, ) -> Vec { @@ -1125,7 +1125,10 @@ mod test { macro_rules! test_merge { ($by:ident, [$($input:expr),* $(,)*], [$($output:expr),* $(,)*]) => { assert_eq!( - regroup_use_trees(parse_use_trees!($($input,)*), ImportGranularity::$by), + normalize_use_trees_with_granularity( + parse_use_trees!($($input,)*), + ImportGranularity::$by, + ), parse_use_trees!($($output,)*), ); } diff --git a/src/reorder.rs b/src/reorder.rs index f565612ded18..8ae297de25bc 100644 --- a/src/reorder.rs +++ b/src/reorder.rs @@ -12,7 +12,7 @@ use rustc_ast::ast; use rustc_span::{symbol::sym, Span}; use crate::config::{Config, GroupImportsTactic}; -use crate::imports::{regroup_use_trees, UseSegment, UseTree}; +use crate::imports::{normalize_use_trees_with_granularity, UseSegment, UseTree}; use crate::items::{is_mod_decl, rewrite_extern_crate, rewrite_mod}; use crate::lists::{itemize_list, write_list, ListFormatting, ListItem}; use crate::rewrite::RewriteContext; @@ -107,8 +107,10 @@ fn rewrite_reorderable_or_regroupable_items( for (item, list_item) in normalized_items.iter_mut().zip(list_items) { item.list_item = Some(list_item.clone()); } - normalized_items = - regroup_use_trees(normalized_items, context.config.imports_granularity()); + normalized_items = normalize_use_trees_with_granularity( + normalized_items, + context.config.imports_granularity(), + ); let mut regrouped_items = match context.config.group_imports() { GroupImportsTactic::Preserve | GroupImportsTactic::One => { From acdab00ecc4c5064b6a42f87a523961304678c06 Mon Sep 17 00:00:00 2001 From: Tom Milligan Date: Sat, 16 Apr 2022 12:12:27 +0100 Subject: [PATCH 175/401] [review] check interaction with import grouping --- tests/source/issue-5030.rs | 15 +++++++++++++++ tests/target/issue-5030.rs | 15 +++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/tests/source/issue-5030.rs b/tests/source/issue-5030.rs index f367e79f01f6..08ffaac7d1dc 100644 --- a/tests/source/issue-5030.rs +++ b/tests/source/issue-5030.rs @@ -1,7 +1,22 @@ // rustfmt-imports_granularity: Item +// rustfmt-group_imports: One +// Confirm that attributes are duplicated to all items in the use statement #[cfg(feature = "foo")] use std::collections::{ HashMap, HashSet, }; + +// Separate the imports below from the ones above +const A: usize = 0; + +// Copying attrs works with import grouping as well +#[cfg(feature = "foo")] +use std::collections::{ + HashMap, + HashSet, +}; + +#[cfg(feature = "spam")] +use qux::{bar, baz}; diff --git a/tests/target/issue-5030.rs b/tests/target/issue-5030.rs index b371331ed009..8ac3888bdbee 100644 --- a/tests/target/issue-5030.rs +++ b/tests/target/issue-5030.rs @@ -1,5 +1,20 @@ // rustfmt-imports_granularity: Item +// rustfmt-group_imports: One +// Confirm that attributes are duplicated to all items in the use statement +#[cfg(feature = "foo")] +use std::collections::HashMap; +#[cfg(feature = "foo")] +use std::collections::HashSet; + +// Separate the imports below from the ones above +const A: usize = 0; + +// Copying attrs works with import grouping as well +#[cfg(feature = "spam")] +use qux::bar; +#[cfg(feature = "spam")] +use qux::baz; #[cfg(feature = "foo")] use std::collections::HashMap; #[cfg(feature = "foo")] From a37d3ab0e1c7c05f1a6410fb7ddf5539f0d030f8 Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Wed, 15 Dec 2021 19:38:31 +0330 Subject: [PATCH 176/401] Memoize format_expr --- src/expr.rs | 51 +- src/formatting.rs | 2 + src/rewrite.rs | 13 + src/shape.rs | 4 +- src/visitor.rs | 7 +- tests/source/performance/issue-4476.rs | 638 +++ tests/source/performance/issue-5128.rs | 5127 ++++++++++++++++++++++++ tests/target/performance/issue-4476.rs | 705 ++++ tests/target/performance/issue-4867.rs | 13 + tests/target/performance/issue-5128.rs | 4898 ++++++++++++++++++++++ 10 files changed, 11454 insertions(+), 4 deletions(-) create mode 100644 tests/source/performance/issue-4476.rs create mode 100644 tests/source/performance/issue-5128.rs create mode 100644 tests/target/performance/issue-4476.rs create mode 100644 tests/target/performance/issue-4867.rs create mode 100644 tests/target/performance/issue-5128.rs diff --git a/src/expr.rs b/src/expr.rs index 4f333cd27cef..cfecc9b9d89a 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -1,5 +1,6 @@ use std::borrow::Cow; use std::cmp::min; +use std::collections::HashMap; use itertools::Itertools; use rustc_ast::token::{DelimToken, LitKind}; @@ -22,7 +23,7 @@ use crate::macros::{rewrite_macro, MacroPosition}; use crate::matches::rewrite_match; use crate::overflow::{self, IntoOverflowableItem, OverflowableItem}; use crate::pairs::{rewrite_all_pairs, rewrite_pair, PairParts}; -use crate::rewrite::{Rewrite, RewriteContext}; +use crate::rewrite::{QueryId, Rewrite, RewriteContext}; use crate::shape::{Indent, Shape}; use crate::source_map::{LineRangeUtils, SpanUtils}; use crate::spanned::Spanned; @@ -53,6 +54,54 @@ pub(crate) fn format_expr( expr_type: ExprType, context: &RewriteContext<'_>, shape: Shape, +) -> Option { + // when max_width is tight, we should check all possible formattings, in order to find + // if we can fit expression in the limit. Doing it recursively takes exponential time + // relative to input size, and people hit it with rustfmt takes minutes in #4476 #4867 #5128 + // By memoization of format_expr function, we format each pair of expression and shape + // only once, so worst case execution time becomes O(n*max_width^3). + if context.inside_macro() || context.is_macro_def { + // span ids are not unique in macros, so we don't memoize result of them. + return format_expr_inner(expr, expr_type, context, shape); + } + let clean; + let query_id = QueryId { + shape, + span: expr.span, + }; + if let Some(map) = context.memoize.take() { + if let Some(r) = map.get(&query_id) { + let r = r.clone(); + context.memoize.set(Some(map)); // restore map in the memoize cell for other users + return r; + } + context.memoize.set(Some(map)); + clean = false; + } else { + context.memoize.set(Some(HashMap::default())); + clean = true; // We got None, so we are the top level called function. When + // this function finishes, no one is interested in what is in the map, because + // all of them are sub expressions of this top level expression, and this is + // done. So we should clean up memoize map to save some memory. + } + + let r = format_expr_inner(expr, expr_type, context, shape); + if clean { + context.memoize.set(None); + } else { + if let Some(mut map) = context.memoize.take() { + map.insert(query_id, r.clone()); // insert the result in the memoize map + context.memoize.set(Some(map)); // so it won't be computed again + } + } + r +} + +fn format_expr_inner( + expr: &ast::Expr, + expr_type: ExprType, + context: &RewriteContext<'_>, + shape: Shape, ) -> Option { skip_out_of_file_lines_range!(context, expr.span); diff --git a/src/formatting.rs b/src/formatting.rs index ca93955a549d..281d3e4e808a 100644 --- a/src/formatting.rs +++ b/src/formatting.rs @@ -2,6 +2,7 @@ use std::collections::HashMap; use std::io::{self, Write}; +use std::rc::Rc; use std::time::{Duration, Instant}; use rustc_ast::ast; @@ -190,6 +191,7 @@ impl<'a, T: FormatHandler + 'a> FormatContext<'a, T> { self.config, &snippet_provider, self.report.clone(), + Rc::default(), ); visitor.skip_context.update_with_attrs(&self.krate.attrs); visitor.is_macro_def = is_macro_def; diff --git a/src/rewrite.rs b/src/rewrite.rs index 4a3bd129d16f..f97df70cc6a7 100644 --- a/src/rewrite.rs +++ b/src/rewrite.rs @@ -12,6 +12,7 @@ use crate::shape::Shape; use crate::skip::SkipContext; use crate::visitor::SnippetProvider; use crate::FormatReport; +use rustc_data_structures::stable_map::FxHashMap; pub(crate) trait Rewrite { /// Rewrite self into shape. @@ -24,10 +25,22 @@ impl Rewrite for ptr::P { } } +#[derive(Clone, PartialEq, Eq, Hash)] +pub(crate) struct QueryId { + pub(crate) shape: Shape, + pub(crate) span: Span, +} + +// We use Option instead of HashMap, because in case of `None` +// the function clean the memoize map, but it doesn't clean when +// there is `Some(empty)`, so they are different. +pub(crate) type Memoize = Rc>>>>; + #[derive(Clone)] pub(crate) struct RewriteContext<'a> { pub(crate) parse_sess: &'a ParseSess, pub(crate) config: &'a Config, + pub(crate) memoize: Memoize, pub(crate) inside_macro: Rc>, // Force block indent style even if we are using visual indent style. pub(crate) use_block: Cell, diff --git a/src/shape.rs b/src/shape.rs index 4376fd12b526..b3f785a9470e 100644 --- a/src/shape.rs +++ b/src/shape.rs @@ -4,7 +4,7 @@ use std::ops::{Add, Sub}; use crate::Config; -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub(crate) struct Indent { // Width of the block indent, in characters. Must be a multiple of // Config::tab_spaces. @@ -139,7 +139,7 @@ impl Sub for Indent { // 8096 is close enough to infinite for rustfmt. const INFINITE_SHAPE_WIDTH: usize = 8096; -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub(crate) struct Shape { pub(crate) width: usize, // The current indentation of code. diff --git a/src/visitor.rs b/src/visitor.rs index 3ebfa551d1cb..06736e3079a5 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -17,7 +17,7 @@ use crate::items::{ use crate::macros::{macro_style, rewrite_macro, rewrite_macro_def, MacroPosition}; use crate::modules::Module; use crate::parse::session::ParseSess; -use crate::rewrite::{Rewrite, RewriteContext}; +use crate::rewrite::{Memoize, Rewrite, RewriteContext}; use crate::shape::{Indent, Shape}; use crate::skip::{is_skip_attr, SkipContext}; use crate::source_map::{LineRangeUtils, SpanUtils}; @@ -71,6 +71,7 @@ impl SnippetProvider { pub(crate) struct FmtVisitor<'a> { parent_context: Option<&'a RewriteContext<'a>>, + pub(crate) memoize: Memoize, pub(crate) parse_sess: &'a ParseSess, pub(crate) buffer: String, pub(crate) last_pos: BytePos, @@ -754,6 +755,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { ctx.config, ctx.snippet_provider, ctx.report.clone(), + ctx.memoize.clone(), ); visitor.skip_context.update(ctx.skip_context.clone()); visitor.set_parent_context(ctx); @@ -765,10 +767,12 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { config: &'a Config, snippet_provider: &'a SnippetProvider, report: FormatReport, + memoize: Memoize, ) -> FmtVisitor<'a> { FmtVisitor { parent_context: None, parse_sess: parse_session, + memoize, buffer: String::with_capacity(snippet_provider.big_snippet.len() * 2), last_pos: BytePos(0), block_indent: Indent::empty(), @@ -991,6 +995,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { RewriteContext { parse_sess: self.parse_sess, config: self.config, + memoize: self.memoize.clone(), inside_macro: Rc::new(Cell::new(false)), use_block: Cell::new(false), is_if_else_block: Cell::new(false), diff --git a/tests/source/performance/issue-4476.rs b/tests/source/performance/issue-4476.rs new file mode 100644 index 000000000000..8da3f19b62d6 --- /dev/null +++ b/tests/source/performance/issue-4476.rs @@ -0,0 +1,638 @@ +use super::SemverParser; + +#[allow(dead_code, non_camel_case_types)] +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub enum Rule { + EOI, + range_set, + logical_or, + range, + empty, + hyphen, + simple, + primitive, + primitive_op, + partial, + xr, + xr_op, + nr, + tilde, + caret, + qualifier, + parts, + part, + space, +} +#[allow(clippy::all)] +impl ::pest::Parser for SemverParser { + fn parse<'i>( + rule: Rule, + input: &'i str, + ) -> ::std::result::Result<::pest::iterators::Pairs<'i, Rule>, ::pest::error::Error> { + mod rules { + pub mod hidden { + use super::super::Rule; + #[inline] + #[allow(dead_code, non_snake_case, unused_variables)] + pub fn skip( + state: Box<::pest::ParserState>, + ) -> ::pest::ParseResult>> { + Ok(state) + } + } + pub mod visible { + use super::super::Rule; + #[inline] + #[allow(non_snake_case, unused_variables)] + pub fn range_set( + state: Box<::pest::ParserState>, + ) -> ::pest::ParseResult>> { + state.rule(Rule::range_set, |state| { + state.sequence(|state| { + self::SOI(state) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| { + state.sequence(|state| { + state.optional(|state| { + self::space(state).and_then(|state| { + state.repeat(|state| { + state.sequence(|state| { + super::hidden::skip(state).and_then(|state| self::space(state)) + }) + }) + }) + }) + }) + }) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| self::range(state)) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| { + state.sequence(|state| { + state.optional(|state| { + state + .sequence(|state| { + self::logical_or(state) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| self::range(state)) + }) + .and_then(|state| { + state.repeat(|state| { + state.sequence(|state| { + super::hidden::skip(state).and_then(|state| { + state.sequence(|state| { + self::logical_or(state) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| self::range(state)) + }) + }) + }) + }) + }) + }) + }) + }) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| { + state.sequence(|state| { + state.optional(|state| { + self::space(state).and_then(|state| { + state.repeat(|state| { + state.sequence(|state| { + super::hidden::skip(state).and_then(|state| self::space(state)) + }) + }) + }) + }) + }) + }) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| self::EOI(state)) + }) + }) + } + #[inline] + #[allow(non_snake_case, unused_variables)] + pub fn logical_or( + state: Box<::pest::ParserState>, + ) -> ::pest::ParseResult>> { + state.rule(Rule::logical_or, |state| { + state.sequence(|state| { + state + .sequence(|state| { + state.optional(|state| { + self::space(state).and_then(|state| { + state.repeat(|state| { + state.sequence(|state| { + super::hidden::skip(state).and_then(|state| self::space(state)) + }) + }) + }) + }) + }) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| state.match_string("||")) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| { + state.sequence(|state| { + state.optional(|state| { + self::space(state).and_then(|state| { + state.repeat(|state| { + state.sequence(|state| { + super::hidden::skip(state).and_then(|state| self::space(state)) + }) + }) + }) + }) + }) + }) + }) + }) + } + #[inline] + #[allow(non_snake_case, unused_variables)] + pub fn range( + state: Box<::pest::ParserState>, + ) -> ::pest::ParseResult>> { + state.rule(Rule::range, |state| { + self::hyphen(state) + .or_else(|state| { + state.sequence(|state| { + self::simple(state) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| { + state.sequence(|state| { + state.optional(|state| { + state + .sequence(|state| { + state + .optional(|state| state.match_string(",")) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| { + state.sequence(|state| { + self::space(state) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| { + state.sequence(|state| { + state.optional(|state| { + self::space(state).and_then(|state| { + state.repeat(|state| { + state.sequence(|state| { + super::hidden::skip(state) + .and_then(|state| self::space(state)) + }) + }) + }) + }) + }) + }) + }) + }) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| self::simple(state)) + }) + .and_then(|state| { + state.repeat(|state| { + state.sequence(|state| { + super::hidden::skip(state).and_then(|state| { + state.sequence(|state| { + state + .optional(|state| state.match_string(",")) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| { + state.sequence(|state| { + self::space(state) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| { + state.sequence(|state| { + state.optional(|state| { + self::space(state).and_then(|state| { + state.repeat(|state| { + state.sequence(|state| { + super::hidden::skip(state) + .and_then(|state| self::space(state)) + }) + }) + }) + }) + }) + }) + }) + }) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| self::simple(state)) + }) + }) + }) + }) + }) + }) + }) + }) + }) + }) + .or_else(|state| self::empty(state)) + }) + } + #[inline] + #[allow(non_snake_case, unused_variables)] + pub fn empty( + state: Box<::pest::ParserState>, + ) -> ::pest::ParseResult>> { + state.rule(Rule::empty, |state| state.match_string("")) + } + #[inline] + #[allow(non_snake_case, unused_variables)] + pub fn hyphen( + state: Box<::pest::ParserState>, + ) -> ::pest::ParseResult>> { + state.rule(Rule::hyphen, |state| { + state.sequence(|state| { + self::partial(state) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| { + state.sequence(|state| { + self::space(state) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| { + state.sequence(|state| { + state.optional(|state| { + self::space(state).and_then(|state| { + state.repeat(|state| { + state.sequence(|state| { + super::hidden::skip(state).and_then(|state| self::space(state)) + }) + }) + }) + }) + }) + }) + }) + }) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| state.match_string("-")) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| { + state.sequence(|state| { + self::space(state) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| { + state.sequence(|state| { + state.optional(|state| { + self::space(state).and_then(|state| { + state.repeat(|state| { + state.sequence(|state| { + super::hidden::skip(state).and_then(|state| self::space(state)) + }) + }) + }) + }) + }) + }) + }) + }) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| self::partial(state)) + }) + }) + } + #[inline] + #[allow(non_snake_case, unused_variables)] + pub fn simple( + state: Box<::pest::ParserState>, + ) -> ::pest::ParseResult>> { + state.rule(Rule::simple, |state| { + self::primitive(state) + .or_else(|state| self::partial(state)) + .or_else(|state| self::tilde(state)) + .or_else(|state| self::caret(state)) + }) + } + #[inline] + #[allow(non_snake_case, unused_variables)] + pub fn primitive( + state: Box<::pest::ParserState>, + ) -> ::pest::ParseResult>> { + state.rule(Rule::primitive, |state| { + state.sequence(|state| { + self::primitive_op(state) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| { + state.sequence(|state| { + state.optional(|state| { + self::space(state).and_then(|state| { + state.repeat(|state| { + state.sequence(|state| { + super::hidden::skip(state).and_then(|state| self::space(state)) + }) + }) + }) + }) + }) + }) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| self::partial(state)) + }) + }) + } + #[inline] + #[allow(non_snake_case, unused_variables)] + pub fn primitive_op( + state: Box<::pest::ParserState>, + ) -> ::pest::ParseResult>> { + state.rule(Rule::primitive_op, |state| { + state + .match_string("<=") + .or_else(|state| state.match_string(">=")) + .or_else(|state| state.match_string(">")) + .or_else(|state| state.match_string("<")) + .or_else(|state| state.match_string("=")) + }) + } + #[inline] + #[allow(non_snake_case, unused_variables)] + pub fn partial( + state: Box<::pest::ParserState>, + ) -> ::pest::ParseResult>> { + state.rule(Rule::partial, |state| { + state.sequence(|state| { + self::xr(state) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| { + state.optional(|state| { + state.sequence(|state| { + state + .match_string(".") + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| self::xr(state)) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| { + state.optional(|state| { + state.sequence(|state| { + state + .match_string(".") + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| self::xr(state)) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| state.optional(|state| self::qualifier(state))) + }) + }) + }) + }) + }) + }) + }) + }) + } + #[inline] + #[allow(non_snake_case, unused_variables)] + pub fn xr( + state: Box<::pest::ParserState>, + ) -> ::pest::ParseResult>> { + state.rule(Rule::xr, |state| { + self::xr_op(state).or_else(|state| self::nr(state)) + }) + } + #[inline] + #[allow(non_snake_case, unused_variables)] + pub fn xr_op( + state: Box<::pest::ParserState>, + ) -> ::pest::ParseResult>> { + state.rule(Rule::xr_op, |state| { + state + .match_string("x") + .or_else(|state| state.match_string("X")) + .or_else(|state| state.match_string("*")) + }) + } + #[inline] + #[allow(non_snake_case, unused_variables)] + pub fn nr( + state: Box<::pest::ParserState>, + ) -> ::pest::ParseResult>> { + state.rule(Rule::nr, |state| { + state.match_string("0").or_else(|state| { + state.sequence(|state| { + state + .match_range('1'..'9') + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| { + state.sequence(|state| { + state.optional(|state| { + state.match_range('0'..'9').and_then(|state| { + state.repeat(|state| { + state.sequence(|state| { + super::hidden::skip(state) + .and_then(|state| state.match_range('0'..'9')) + }) + }) + }) + }) + }) + }) + }) + }) + }) + } + #[inline] + #[allow(non_snake_case, unused_variables)] + pub fn tilde( + state: Box<::pest::ParserState>, + ) -> ::pest::ParseResult>> { + state.rule(Rule::tilde, |state| { + state.sequence(|state| { + state + .match_string("~>") + .or_else(|state| state.match_string("~")) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| { + state.sequence(|state| { + state.optional(|state| { + self::space(state).and_then(|state| { + state.repeat(|state| { + state.sequence(|state| { + super::hidden::skip(state).and_then(|state| self::space(state)) + }) + }) + }) + }) + }) + }) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| self::partial(state)) + }) + }) + } + #[inline] + #[allow(non_snake_case, unused_variables)] + pub fn caret( + state: Box<::pest::ParserState>, + ) -> ::pest::ParseResult>> { + state.rule(Rule::caret, |state| { + state.sequence(|state| { + state + .match_string("^") + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| { + state.sequence(|state| { + state.optional(|state| { + self::space(state).and_then(|state| { + state.repeat(|state| { + state.sequence(|state| { + super::hidden::skip(state).and_then(|state| self::space(state)) + }) + }) + }) + }) + }) + }) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| self::partial(state)) + }) + }) + } + #[inline] + #[allow(non_snake_case, unused_variables)] + pub fn qualifier( + state: Box<::pest::ParserState>, + ) -> ::pest::ParseResult>> { + state.rule(Rule::qualifier, |state| { + state.sequence(|state| { + state + .match_string("-") + .or_else(|state| state.match_string("+")) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| self::parts(state)) + }) + }) + } + #[inline] + #[allow(non_snake_case, unused_variables)] + pub fn parts( + state: Box<::pest::ParserState>, + ) -> ::pest::ParseResult>> { + state.rule(Rule::parts, |state| { + state.sequence(|state| { + self::part(state) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| { + state.sequence(|state| { + state.optional(|state| { + state + .sequence(|state| { + state + .match_string(".") + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| self::part(state)) + }) + .and_then(|state| { + state.repeat(|state| { + state.sequence(|state| { + super::hidden::skip(state).and_then(|state| { + state.sequence(|state| { + state + .match_string(".") + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| self::part(state)) + }) + }) + }) + }) + }) + }) + }) + }) + }) + }) + } + #[inline] + #[allow(non_snake_case, unused_variables)] + pub fn part( + state: Box<::pest::ParserState>, + ) -> ::pest::ParseResult>> { + state.rule(Rule::part, |state| { + self::nr(state).or_else(|state| { + state.sequence(|state| { + state + .match_string("-") + .or_else(|state| state.match_range('0'..'9')) + .or_else(|state| state.match_range('A'..'Z')) + .or_else(|state| state.match_range('a'..'z')) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| { + state.sequence(|state| { + state.optional(|state| { + state + .match_string("-") + .or_else(|state| state.match_range('0'..'9')) + .or_else(|state| state.match_range('A'..'Z')) + .or_else(|state| state.match_range('a'..'z')) + .and_then(|state| { + state.repeat(|state| { + state.sequence(|state| { + super::hidden::skip(state).and_then(|state| { + state + .match_string("-") + .or_else(|state| state.match_range('0'..'9')) + .or_else(|state| state.match_range('A'..'Z')) + .or_else(|state| state.match_range('a'..'z')) + }) + }) + }) + }) + }) + }) + }) + }) + }) + }) + } + #[inline] + #[allow(non_snake_case, unused_variables)] + pub fn space( + state: Box<::pest::ParserState>, + ) -> ::pest::ParseResult>> { + state + .match_string(" ") + .or_else(|state| state.match_string("\t")) + } + #[inline] + #[allow(dead_code, non_snake_case, unused_variables)] + pub fn EOI( + state: Box<::pest::ParserState>, + ) -> ::pest::ParseResult>> { + state.rule(Rule::EOI, |state| state.end_of_input()) + } + #[inline] + #[allow(dead_code, non_snake_case, unused_variables)] + pub fn SOI( + state: Box<::pest::ParserState>, + ) -> ::pest::ParseResult>> { + state.start_of_input() + } + } + pub use self::visible::*; + } + ::pest::state(input, |state| match rule { + Rule::range_set => rules::range_set(state), + Rule::logical_or => rules::logical_or(state), + Rule::range => rules::range(state), + Rule::empty => rules::empty(state), + Rule::hyphen => rules::hyphen(state), + Rule::simple => rules::simple(state), + Rule::primitive => rules::primitive(state), + Rule::primitive_op => rules::primitive_op(state), + Rule::partial => rules::partial(state), + Rule::xr => rules::xr(state), + Rule::xr_op => rules::xr_op(state), + Rule::nr => rules::nr(state), + Rule::tilde => rules::tilde(state), + Rule::caret => rules::caret(state), + Rule::qualifier => rules::qualifier(state), + Rule::parts => rules::parts(state), + Rule::part => rules::part(state), + Rule::space => rules::space(state), + Rule::EOI => rules::EOI(state), + }) + } +} \ No newline at end of file diff --git a/tests/source/performance/issue-5128.rs b/tests/source/performance/issue-5128.rs new file mode 100644 index 000000000000..3adce49601c0 --- /dev/null +++ b/tests/source/performance/issue-5128.rs @@ -0,0 +1,5127 @@ + +fn takes_a_long_time_to_rustfmt() { + let inner_cte = vec![Node { + node: Some(node::Node::CommonTableExpr(Box::new(CommonTableExpr { + ctename: String::from("ranked_by_age_within_key"), + aliascolnames: vec![], + ctematerialized: CteMaterialize::Default as i32, + ctequery: Some(Box::new(Node { + node: Some(node::Node::SelectStmt(Box::new(SelectStmt { + distinct_clause: vec![], + into_clause: None, + target_list: vec![ + Node { + node: Some(node::Node::ResTarget(Box::new(ResTarget { + name: String::from(""), + indirection: vec![], + val: Some(Box::new(Node { + node: Some(node::Node::ColumnRef(ColumnRef { + fields: vec![Node { + node: Some(node::Node::AStar(AStar{})) + }], + location: 80 + })) + })), + location: 80 + }))) + }, + Node { + node: Some(node::Node::ResTarget(Box::new(ResTarget { + name: String::from("rank_in_key"), + indirection: vec![], + val: Some(Box::new(Node { + node: Some(node::Node::FuncCall(Box::new(FuncCall { + funcname: vec![Node { + node: Some(node::Node::String(String2 { + str: String::from("row_number") + })) + }], + args: vec![], + agg_order: vec![], + agg_filter: None, + agg_within_group: false, + agg_star: false, + agg_distinct: false, + func_variadic: false, + over: Some(Box::new(WindowDef { + name: String::from(""), + refname: String::from(""), + partition_clause: vec![ + Node { + node: Some(node::Node::ColumnRef(ColumnRef { + fields: vec![Node { + node: Some(node::Node::String(String2 { + str: String::from("synthetic_key") + })) + }], location: 123 + })) + }], order_clause: vec![Node { + node: Some(node::Node::SortBy(Box::new(SortBy { + node: Some(Box::new(Node { + node: Some(node::Node::ColumnRef(ColumnRef { + fields: vec![Node { + node: Some(node::Node::String(String2 { + str: String::from("logical_timestamp") + })) + }], location: 156 + })) + })), + sortby_dir: SortByDir::SortbyDesc as i32, + sortby_nulls: SortByNulls::SortbyNullsDefault as i32, + use_op: vec![], + location: -1 + }))) + }], frame_options: 1058, start_offset: None, end_offset: None, location: 109 + })), + location: 91 + }))) + })), + location: 91 + }))) + }], + from_clause: vec![Node { + node: Some(node::Node::RangeVar(RangeVar { + catalogname: String::from(""), schemaname: String::from("_supertables"), relname: String::from("9999-9999-9999"), inh: true, relpersistence: String::from("p"), alias: None, location: 206 + })) + }], + where_clause: Some(Box::new(Node { + node: Some(node::Node::AExpr(Box::new(AExpr { + kind: AExprKind::AexprOp as i32, + name: vec![Node { + node: Some(node::Node::String(String2 { + str: String::from("<=") + })) + }], + lexpr: Some(Box::new(Node { + node: Some(node::Node::ColumnRef(ColumnRef { + fields: vec![Node { + node: Some(node::Node::String(String2 { + str: String::from("logical_timestamp") + })) + }], + location: 250 + })) + })), + rexpr: Some(Box::new(Node { + node: Some(node::Node::AConst(Box::new(AConst { + val: Some(Box::new(Node { + node: Some(node::Node::Integer(Integer { + ival: 9000 + })) + })), + location: 271 + }))) + })), + location: 268 + }))) + })), + group_clause: vec![], + having_clause: None, + window_clause: vec![], + values_lists: vec![], + sort_clause: vec![], + limit_offset: None, + limit_count: None, + limit_option: LimitOption::Default as i32, + locking_clause: vec![], + with_clause: None, + op: SetOperation::SetopNone as i32, + all: false, + larg: None, + rarg: None + }))), + })), + location: 29, + cterecursive: false, + cterefcount: 0, + ctecolnames: vec![], + ctecoltypes: vec![], + ctecoltypmods: vec![], + ctecolcollations: vec![], + }))), + }]; + let outer_cte = vec![Node { + node: Some(node::Node::CommonTableExpr(Box::new(CommonTableExpr { + ctename: String::from("table_name"), + aliascolnames: vec![], + ctematerialized: CteMaterialize::Default as i32, + ctequery: Some(Box::new(Node { + node: Some(node::Node::SelectStmt(Box::new(SelectStmt { + distinct_clause: vec![], + into_clause: None, + target_list: vec![ + Node { + node: Some(node::Node::ResTarget(Box::new(ResTarget { + name: String::from("column1"), + indirection: vec![], + val: Some(Box::new(Node { + node: Some(node::Node::ColumnRef(ColumnRef { + fields: vec![Node { + node: Some(node::Node::String(String2 { + str: String::from("c1"), + })), + }], + location: 301, + })), + })), + location: 301, + }))), + }, + Node { + node: Some(node::Node::ResTarget(Box::new(ResTarget { + name: String::from("column2"), + indirection: vec![], + val: Some(Box::new(Node { + node: Some(node::Node::ColumnRef(ColumnRef { + fields: vec![Node { + node: Some(node::Node::String(String2 { + str: String::from("c2"), + })), + }], + location: 324, + })), + })), + location: 324, + }))), + }, + ], + from_clause: vec![Node { + node: Some(node::Node::RangeVar(RangeVar { + catalogname: String::from(""), + schemaname: String::from(""), + relname: String::from("ranked_by_age_within_key"), + inh: true, + relpersistence: String::from("p"), + alias: None, + location: 347, + })), + }], + where_clause: Some(Box::new(Node { + node: Some(node::Node::BoolExpr(Box::new(BoolExpr { + xpr: None, + boolop: BoolExprType::AndExpr as i32, + args: vec![ + Node { + node: Some(node::Node::AExpr(Box::new(AExpr { + kind: AExprKind::AexprOp as i32, + name: vec![Node { + node: Some(node::Node::String(String2 { + str: String::from("="), + })), + }], + lexpr: Some(Box::new(Node { + node: Some(node::Node::ColumnRef(ColumnRef { + fields: vec![Node { + node: Some(node::Node::String( + String2 { + str: String::from("rank_in_key"), + }, + )), + }], + location: 382, + })), + })), + rexpr: Some(Box::new(Node { + node: Some(node::Node::AConst(Box::new(AConst { + val: Some(Box::new(Node { + node: Some(node::Node::Integer( + Integer { ival: 1 }, + )), + })), + location: 396, + }))), + })), + location: 394, + }))), + }, + Node { + node: Some(node::Node::AExpr(Box::new(AExpr { + kind: AExprKind::AexprOp as i32, + name: vec![Node { + node: Some(node::Node::String(String2 { + str: String::from("="), + })), + }], + lexpr: Some(Box::new(Node { + node: Some(node::Node::ColumnRef(ColumnRef { + fields: vec![Node { + node: Some(node::Node::String( + String2 { + str: String::from("is_deleted"), + }, + )), + }], + location: 402, + })), + })), + rexpr: Some(Box::new(Node { + node: Some(node::Node::TypeCast(Box::new( + TypeCast { + arg: Some(Box::new(Node { + node: Some(node::Node::AConst( + Box::new(AConst { + val: Some(Box::new(Node { + node: Some( + node::Node::String( + String2 { + str: + String::from( + "f", + ), + }, + ), + ), + })), + location: 415, + }), + )), + })), + type_name: Some(TypeName { + names: vec![ + Node { + node: Some(node::Node::String( + String2 { + str: String::from( + "pg_catalog", + ), + }, + )), + }, + Node { + node: Some(node::Node::String( + String2 { + str: String::from( + "bool", + ), + }, + )), + }, + ], + type_oid: 0, + setof: false, + pct_type: false, + typmods: vec![], + typemod: -1, + array_bounds: vec![], + location: -1, + }), + location: -1, + }, + ))), + })), + location: 413, + }))), + }, + ], + location: 398, + }))), + })), + group_clause: vec![], + having_clause: None, + window_clause: vec![], + values_lists: vec![], + sort_clause: vec![], + limit_offset: None, + limit_count: None, + limit_option: LimitOption::Default as i32, + locking_clause: vec![], + with_clause: Some(WithClause { + ctes: inner_cte, + recursive: false, + location: 24, + }), + op: SetOperation::SetopNone as i32, + all: false, + larg: None, + rarg: None, + }))), + })), + location: 5, + cterecursive: false, + cterefcount: 0, + ctecolnames: vec![], + ctecoltypes: vec![], + ctecoltypmods: vec![], + ctecolcollations: vec![], + }))), + }]; + let expected_result = ParseResult { + version: 130003, + stmts: vec![RawStmt { + stmt: Some(Box::new(Node { + node: Some(node::Node::SelectStmt(Box::new(SelectStmt { + distinct_clause: vec![], + into_clause: None, + + target_list: vec![Node { + node: Some(node::Node::ResTarget(Box::new(ResTarget { + name: String::from(""), + indirection: vec![], + val: Some(Box::new(Node { + node: Some(node::Node::ColumnRef(ColumnRef { + fields: vec![Node { + node: Some(node::Node::String(String2 { + str: String::from("column1"), + })), + }], + location: 430, + })), + })), + location: 430, + }))), + }], + from_clause: vec![Node { + node: Some(node::Node::RangeVar(RangeVar { + catalogname: String::from(""), + schemaname: String::from(""), + relname: String::from("table_name"), + inh: true, + relpersistence: String::from("p"), + alias: None, + location: 443, + })), + }], + where_clause: Some(Box::new(Node { + node: Some(node::Node::AExpr(Box::new(AExpr { + kind: AExprKind::AexprOp as i32, + name: vec![Node { + node: Some(node::Node::String(String2 { + str: String::from(">"), + })), + }], + lexpr: Some(Box::new(Node { + node: Some(node::Node::ColumnRef(ColumnRef { + fields: vec![Node { + node: Some(node::Node::String(String2 { + str: String::from("column2"), + })), + }], + location: 460, + })), + })), + rexpr: Some(Box::new(Node { + node: Some(node::Node::AConst(Box::new(AConst { + val: Some(Box::new(Node { + node: Some(node::Node::Integer(Integer { + ival: 9000, + })), + })), + location: 470, + }))), + })), + location: 468, + }))), + })), + group_clause: vec![], + having_clause: None, + window_clause: vec![], + values_lists: vec![], + sort_clause: vec![], + limit_offset: None, + limit_count: None, + limit_option: LimitOption::Default as i32, + locking_clause: vec![], + with_clause: Some(WithClause { + ctes: outer_cte, + recursive: false, + location: 0, + }), + op: SetOperation::SetopNone as i32, + all: false, + larg: None, + rarg: None, + }))), + })), + stmt_location: 0, + stmt_len: 0, + }], + }; + +} +#[derive(Clone, PartialEq)] +pub struct ParseResult { + + pub version: i32, + + pub stmts: Vec, +} +#[derive(Clone, PartialEq)] +pub struct ScanResult { + + pub version: i32, + + pub tokens: Vec, +} +#[derive(Clone, PartialEq)] +pub struct Node { + pub node: ::core::option::Option, +} +/// Nested message and enum types in `Node`. +pub mod node { + #[derive(Clone, PartialEq)] + pub enum Node { + + Alias(super::Alias), + + RangeVar(super::RangeVar), + + TableFunc(Box), + + Expr(super::Expr), + + Var(Box), + + Param(Box), + + Aggref(Box), + + GroupingFunc(Box), + + WindowFunc(Box), + + SubscriptingRef(Box), + + FuncExpr(Box), + + NamedArgExpr(Box), + + OpExpr(Box), + + DistinctExpr(Box), + + NullIfExpr(Box), + + ScalarArrayOpExpr(Box), + + BoolExpr(Box), + + SubLink(Box), + + SubPlan(Box), + + AlternativeSubPlan(Box), + + FieldSelect(Box), + + FieldStore(Box), + + RelabelType(Box), + + CoerceViaIo(Box), + + ArrayCoerceExpr(Box), + + ConvertRowtypeExpr(Box), + + CollateExpr(Box), + + CaseExpr(Box), + + CaseWhen(Box), + + CaseTestExpr(Box), + + ArrayExpr(Box), + + RowExpr(Box), + + RowCompareExpr(Box), + + CoalesceExpr(Box), + + MinMaxExpr(Box), + + SqlvalueFunction(Box), + + XmlExpr(Box), + + NullTest(Box), + + BooleanTest(Box), + + CoerceToDomain(Box), + + CoerceToDomainValue(Box), + + SetToDefault(Box), + + CurrentOfExpr(Box), + + NextValueExpr(Box), + + InferenceElem(Box), + + TargetEntry(Box), + + RangeTblRef(super::RangeTblRef), + + JoinExpr(Box), + + FromExpr(Box), + + OnConflictExpr(Box), + + IntoClause(Box), + + RawStmt(Box), + + Query(Box), + + InsertStmt(Box), + + DeleteStmt(Box), + + UpdateStmt(Box), + + SelectStmt(Box), + + AlterTableStmt(super::AlterTableStmt), + + AlterTableCmd(Box), + + AlterDomainStmt(Box), + + SetOperationStmt(Box), + + GrantStmt(super::GrantStmt), + + GrantRoleStmt(super::GrantRoleStmt), + + AlterDefaultPrivilegesStmt(super::AlterDefaultPrivilegesStmt), + + ClosePortalStmt(super::ClosePortalStmt), + + ClusterStmt(super::ClusterStmt), + + CopyStmt(Box), + + CreateStmt(super::CreateStmt), + + DefineStmt(super::DefineStmt), + + DropStmt(super::DropStmt), + + TruncateStmt(super::TruncateStmt), + + CommentStmt(Box), + + FetchStmt(super::FetchStmt), + + IndexStmt(Box), + + CreateFunctionStmt(super::CreateFunctionStmt), + + AlterFunctionStmt(super::AlterFunctionStmt), + + DoStmt(super::DoStmt), + + RenameStmt(Box), + + RuleStmt(Box), + + NotifyStmt(super::NotifyStmt), + + ListenStmt(super::ListenStmt), + + UnlistenStmt(super::UnlistenStmt), + + TransactionStmt(super::TransactionStmt), + + ViewStmt(Box), + + LoadStmt(super::LoadStmt), + + CreateDomainStmt(Box), + + CreatedbStmt(super::CreatedbStmt), + + DropdbStmt(super::DropdbStmt), + + VacuumStmt(super::VacuumStmt), + + ExplainStmt(Box), + + CreateTableAsStmt(Box), + + CreateSeqStmt(super::CreateSeqStmt), + + AlterSeqStmt(super::AlterSeqStmt), + + VariableSetStmt(super::VariableSetStmt), + + VariableShowStmt(super::VariableShowStmt), + + DiscardStmt(super::DiscardStmt), + + CreateTrigStmt(Box), + + CreatePlangStmt(super::CreatePLangStmt), + + CreateRoleStmt(super::CreateRoleStmt), + + AlterRoleStmt(super::AlterRoleStmt), + + DropRoleStmt(super::DropRoleStmt), + + LockStmt(super::LockStmt), + + ConstraintsSetStmt(super::ConstraintsSetStmt), + + ReindexStmt(super::ReindexStmt), + + CheckPointStmt(super::CheckPointStmt), + + CreateSchemaStmt(super::CreateSchemaStmt), + + AlterDatabaseStmt(super::AlterDatabaseStmt), + + AlterDatabaseSetStmt(super::AlterDatabaseSetStmt), + + AlterRoleSetStmt(super::AlterRoleSetStmt), + + CreateConversionStmt(super::CreateConversionStmt), + + CreateCastStmt(super::CreateCastStmt), + + CreateOpClassStmt(super::CreateOpClassStmt), + + CreateOpFamilyStmt(super::CreateOpFamilyStmt), + + AlterOpFamilyStmt(super::AlterOpFamilyStmt), + + PrepareStmt(Box), + + ExecuteStmt(super::ExecuteStmt), + + DeallocateStmt(super::DeallocateStmt), + + DeclareCursorStmt(Box), + + CreateTableSpaceStmt(super::CreateTableSpaceStmt), + + DropTableSpaceStmt(super::DropTableSpaceStmt), + + AlterObjectDependsStmt(Box), + + AlterObjectSchemaStmt(Box), + + AlterOwnerStmt(Box), + + AlterOperatorStmt(super::AlterOperatorStmt), + + AlterTypeStmt(super::AlterTypeStmt), + + DropOwnedStmt(super::DropOwnedStmt), + + ReassignOwnedStmt(super::ReassignOwnedStmt), + + CompositeTypeStmt(super::CompositeTypeStmt), + + CreateEnumStmt(super::CreateEnumStmt), + + CreateRangeStmt(super::CreateRangeStmt), + + AlterEnumStmt(super::AlterEnumStmt), + + AlterTsdictionaryStmt(super::AlterTsDictionaryStmt), + + AlterTsconfigurationStmt(super::AlterTsConfigurationStmt), + + CreateFdwStmt(super::CreateFdwStmt), + + AlterFdwStmt(super::AlterFdwStmt), + + CreateForeignServerStmt(super::CreateForeignServerStmt), + + AlterForeignServerStmt(super::AlterForeignServerStmt), + + CreateUserMappingStmt(super::CreateUserMappingStmt), + + AlterUserMappingStmt(super::AlterUserMappingStmt), + + DropUserMappingStmt(super::DropUserMappingStmt), + + AlterTableSpaceOptionsStmt(super::AlterTableSpaceOptionsStmt), + + AlterTableMoveAllStmt(super::AlterTableMoveAllStmt), + + SecLabelStmt(Box), + + CreateForeignTableStmt(super::CreateForeignTableStmt), + + ImportForeignSchemaStmt(super::ImportForeignSchemaStmt), + + CreateExtensionStmt(super::CreateExtensionStmt), + + AlterExtensionStmt(super::AlterExtensionStmt), + + AlterExtensionContentsStmt(Box), + + CreateEventTrigStmt(super::CreateEventTrigStmt), + + AlterEventTrigStmt(super::AlterEventTrigStmt), + + RefreshMatViewStmt(super::RefreshMatViewStmt), + + ReplicaIdentityStmt(super::ReplicaIdentityStmt), + + AlterSystemStmt(super::AlterSystemStmt), + + CreatePolicyStmt(Box), + + AlterPolicyStmt(Box), + + CreateTransformStmt(super::CreateTransformStmt), + + CreateAmStmt(super::CreateAmStmt), + + CreatePublicationStmt(super::CreatePublicationStmt), + + AlterPublicationStmt(super::AlterPublicationStmt), + + CreateSubscriptionStmt(super::CreateSubscriptionStmt), + + AlterSubscriptionStmt(super::AlterSubscriptionStmt), + + DropSubscriptionStmt(super::DropSubscriptionStmt), + + CreateStatsStmt(super::CreateStatsStmt), + + AlterCollationStmt(super::AlterCollationStmt), + + CallStmt(Box), + + AlterStatsStmt(super::AlterStatsStmt), + + AExpr(Box), + + ColumnRef(super::ColumnRef), + + ParamRef(super::ParamRef), + + AConst(Box), + + FuncCall(Box), + + AStar(super::AStar), + + AIndices(Box), + + AIndirection(Box), + + AArrayExpr(super::AArrayExpr), + + ResTarget(Box), + + MultiAssignRef(Box), + + TypeCast(Box), + + CollateClause(Box), + + SortBy(Box), + + WindowDef(Box), + + RangeSubselect(Box), + + RangeFunction(super::RangeFunction), + + RangeTableSample(Box), + + RangeTableFunc(Box), + + RangeTableFuncCol(Box), + + TypeName(super::TypeName), + + ColumnDef(Box), + + IndexElem(Box), + + Constraint(Box), + + DefElem(Box), + + RangeTblEntry(Box), + + RangeTblFunction(Box), + + TableSampleClause(Box), + + WithCheckOption(Box), + + SortGroupClause(super::SortGroupClause), + + GroupingSet(super::GroupingSet), + + WindowClause(Box), + + ObjectWithArgs(super::ObjectWithArgs), + + AccessPriv(super::AccessPriv), + + CreateOpClassItem(super::CreateOpClassItem), + + TableLikeClause(super::TableLikeClause), + + FunctionParameter(Box), + + LockingClause(super::LockingClause), + + RowMarkClause(super::RowMarkClause), + + XmlSerialize(Box), + + WithClause(super::WithClause), + + InferClause(Box), + + OnConflictClause(Box), + + CommonTableExpr(Box), + + RoleSpec(super::RoleSpec), + + TriggerTransition(super::TriggerTransition), + + PartitionElem(Box), + + PartitionSpec(super::PartitionSpec), + + PartitionBoundSpec(super::PartitionBoundSpec), + + PartitionRangeDatum(Box), + + PartitionCmd(super::PartitionCmd), + + VacuumRelation(super::VacuumRelation), + + InlineCodeBlock(super::InlineCodeBlock), + + CallContext(super::CallContext), + + Integer(super::Integer), + + Float(super::Float), + + String(super::String2), + + BitString(super::BitString), + + Null(super::Null), + + List(super::List), + + IntList(super::IntList), + + OidList(super::OidList), + } +} +#[derive(Clone, PartialEq)] +pub struct Integer { + /// machine integer + + pub ival: i32, +} +#[derive(Clone, PartialEq)] +pub struct Float { + /// string + + pub str: String, +} +#[derive(Clone, PartialEq)] +pub struct String2 { + /// string + + pub str: String, +} +#[derive(Clone, PartialEq)] +pub struct BitString { + /// string + + pub str: String, +} +/// intentionally empty +#[derive(Clone, PartialEq)] +pub struct Null {} +#[derive(Clone, PartialEq)] +pub struct List { + + pub items: Vec, +} +#[derive(Clone, PartialEq)] +pub struct OidList { + + pub items: Vec, +} +#[derive(Clone, PartialEq)] +pub struct IntList { + + pub items: Vec, +} +#[derive(Clone, PartialEq)] +pub struct Alias { + + pub aliasname: String, + + pub colnames: Vec, +} +#[derive(Clone, PartialEq)] +pub struct RangeVar { + + pub catalogname: String, + + pub schemaname: String, + + pub relname: String, + + pub inh: bool, + + pub relpersistence: String, + + pub alias: ::core::option::Option, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct TableFunc { + + pub ns_uris: Vec, + + pub ns_names: Vec, + + pub docexpr: ::core::option::Option>, + + pub rowexpr: ::core::option::Option>, + + pub colnames: Vec, + + pub coltypes: Vec, + + pub coltypmods: Vec, + + pub colcollations: Vec, + + pub colexprs: Vec, + + pub coldefexprs: Vec, + + pub notnulls: Vec, + + pub ordinalitycol: i32, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct Expr {} +#[derive(Clone, PartialEq)] +pub struct Var { + + pub xpr: ::core::option::Option>, + + pub varno: u32, + + pub varattno: i32, + + pub vartype: u32, + + pub vartypmod: i32, + + pub varcollid: u32, + + pub varlevelsup: u32, + + pub varnosyn: u32, + + pub varattnosyn: i32, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct Param { + + pub xpr: ::core::option::Option>, + + pub paramkind: i32, + + pub paramid: i32, + + pub paramtype: u32, + + pub paramtypmod: i32, + + pub paramcollid: u32, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct Aggref { + + pub xpr: ::core::option::Option>, + + pub aggfnoid: u32, + + pub aggtype: u32, + + pub aggcollid: u32, + + pub inputcollid: u32, + + pub aggtranstype: u32, + + pub aggargtypes: Vec, + + pub aggdirectargs: Vec, + + pub args: Vec, + + pub aggorder: Vec, + + pub aggdistinct: Vec, + + pub aggfilter: ::core::option::Option>, + + pub aggstar: bool, + + pub aggvariadic: bool, + + pub aggkind: String, + + pub agglevelsup: u32, + + pub aggsplit: i32, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct GroupingFunc { + + pub xpr: ::core::option::Option>, + + pub args: Vec, + + pub refs: Vec, + + pub cols: Vec, + + pub agglevelsup: u32, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct WindowFunc { + + pub xpr: ::core::option::Option>, + + pub winfnoid: u32, + + pub wintype: u32, + + pub wincollid: u32, + + pub inputcollid: u32, + + pub args: Vec, + + pub aggfilter: ::core::option::Option>, + + pub winref: u32, + + pub winstar: bool, + + pub winagg: bool, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct SubscriptingRef { + + pub xpr: ::core::option::Option>, + + pub refcontainertype: u32, + + pub refelemtype: u32, + + pub reftypmod: i32, + + pub refcollid: u32, + + pub refupperindexpr: Vec, + + pub reflowerindexpr: Vec, + + pub refexpr: ::core::option::Option>, + + pub refassgnexpr: ::core::option::Option>, +} +#[derive(Clone, PartialEq)] +pub struct FuncExpr { + + pub xpr: ::core::option::Option>, + + pub funcid: u32, + + pub funcresulttype: u32, + + pub funcretset: bool, + + pub funcvariadic: bool, + + pub funcformat: i32, + + pub funccollid: u32, + + pub inputcollid: u32, + + pub args: Vec, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct NamedArgExpr { + + pub xpr: ::core::option::Option>, + + pub arg: ::core::option::Option>, + + pub name: String, + + pub argnumber: i32, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct OpExpr { + + pub xpr: ::core::option::Option>, + + pub opno: u32, + + pub opfuncid: u32, + + pub opresulttype: u32, + + pub opretset: bool, + + pub opcollid: u32, + + pub inputcollid: u32, + + pub args: Vec, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct DistinctExpr { + + pub xpr: ::core::option::Option>, + + pub opno: u32, + + pub opfuncid: u32, + + pub opresulttype: u32, + + pub opretset: bool, + + pub opcollid: u32, + + pub inputcollid: u32, + + pub args: Vec, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct NullIfExpr { + + pub xpr: ::core::option::Option>, + + pub opno: u32, + + pub opfuncid: u32, + + pub opresulttype: u32, + + pub opretset: bool, + + pub opcollid: u32, + + pub inputcollid: u32, + + pub args: Vec, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct ScalarArrayOpExpr { + + pub xpr: ::core::option::Option>, + + pub opno: u32, + + pub opfuncid: u32, + + pub use_or: bool, + + pub inputcollid: u32, + + pub args: Vec, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct BoolExpr { + + pub xpr: ::core::option::Option>, + + pub boolop: i32, + + pub args: Vec, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct SubLink { + + pub xpr: ::core::option::Option>, + + pub sub_link_type: i32, + + pub sub_link_id: i32, + + pub testexpr: ::core::option::Option>, + + pub oper_name: Vec, + + pub subselect: ::core::option::Option>, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct SubPlan { + + pub xpr: ::core::option::Option>, + + pub sub_link_type: i32, + + pub testexpr: ::core::option::Option>, + + pub param_ids: Vec, + + pub plan_id: i32, + + pub plan_name: String, + + pub first_col_type: u32, + + pub first_col_typmod: i32, + + pub first_col_collation: u32, + + pub use_hash_table: bool, + + pub unknown_eq_false: bool, + + pub parallel_safe: bool, + + pub set_param: Vec, + + pub par_param: Vec, + + pub args: Vec, + + pub startup_cost: f64, + + pub per_call_cost: f64, +} +#[derive(Clone, PartialEq)] +pub struct AlternativeSubPlan { + + pub xpr: ::core::option::Option>, + + pub subplans: Vec, +} +#[derive(Clone, PartialEq)] +pub struct FieldSelect { + + pub xpr: ::core::option::Option>, + + pub arg: ::core::option::Option>, + + pub fieldnum: i32, + + pub resulttype: u32, + + pub resulttypmod: i32, + + pub resultcollid: u32, +} +#[derive(Clone, PartialEq)] +pub struct FieldStore { + + pub xpr: ::core::option::Option>, + + pub arg: ::core::option::Option>, + + pub newvals: Vec, + + pub fieldnums: Vec, + + pub resulttype: u32, +} +#[derive(Clone, PartialEq)] +pub struct RelabelType { + + pub xpr: ::core::option::Option>, + + pub arg: ::core::option::Option>, + + pub resulttype: u32, + + pub resulttypmod: i32, + + pub resultcollid: u32, + + pub relabelformat: i32, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct CoerceViaIo { + + pub xpr: ::core::option::Option>, + + pub arg: ::core::option::Option>, + + pub resulttype: u32, + + pub resultcollid: u32, + + pub coerceformat: i32, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct ArrayCoerceExpr { + + pub xpr: ::core::option::Option>, + + pub arg: ::core::option::Option>, + + pub elemexpr: ::core::option::Option>, + + pub resulttype: u32, + + pub resulttypmod: i32, + + pub resultcollid: u32, + + pub coerceformat: i32, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct ConvertRowtypeExpr { + + pub xpr: ::core::option::Option>, + + pub arg: ::core::option::Option>, + + pub resulttype: u32, + + pub convertformat: i32, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct CollateExpr { + + pub xpr: ::core::option::Option>, + + pub arg: ::core::option::Option>, + + pub coll_oid: u32, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct CaseExpr { + + pub xpr: ::core::option::Option>, + + pub casetype: u32, + + pub casecollid: u32, + + pub arg: ::core::option::Option>, + + pub args: Vec, + + pub defresult: ::core::option::Option>, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct CaseWhen { + + pub xpr: ::core::option::Option>, + + pub expr: ::core::option::Option>, + + pub result: ::core::option::Option>, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct CaseTestExpr { + + pub xpr: ::core::option::Option>, + + pub type_id: u32, + + pub type_mod: i32, + + pub collation: u32, +} +#[derive(Clone, PartialEq)] +pub struct ArrayExpr { + + pub xpr: ::core::option::Option>, + + pub array_typeid: u32, + + pub array_collid: u32, + + pub element_typeid: u32, + + pub elements: Vec, + + pub multidims: bool, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct RowExpr { + + pub xpr: ::core::option::Option>, + + pub args: Vec, + + pub row_typeid: u32, + + pub row_format: i32, + + pub colnames: Vec, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct RowCompareExpr { + + pub xpr: ::core::option::Option>, + + pub rctype: i32, + + pub opnos: Vec, + + pub opfamilies: Vec, + + pub inputcollids: Vec, + + pub largs: Vec, + + pub rargs: Vec, +} +#[derive(Clone, PartialEq)] +pub struct CoalesceExpr { + + pub xpr: ::core::option::Option>, + + pub coalescetype: u32, + + pub coalescecollid: u32, + + pub args: Vec, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct MinMaxExpr { + + pub xpr: ::core::option::Option>, + + pub minmaxtype: u32, + + pub minmaxcollid: u32, + + pub inputcollid: u32, + + pub op: i32, + + pub args: Vec, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct SqlValueFunction { + + pub xpr: ::core::option::Option>, + + pub op: i32, + + pub r#type: u32, + + pub typmod: i32, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct XmlExpr { + + pub xpr: ::core::option::Option>, + + pub op: i32, + + pub name: String, + + pub named_args: Vec, + + pub arg_names: Vec, + + pub args: Vec, + + pub xmloption: i32, + + pub r#type: u32, + + pub typmod: i32, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct NullTest { + + pub xpr: ::core::option::Option>, + + pub arg: ::core::option::Option>, + + pub nulltesttype: i32, + + pub argisrow: bool, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct BooleanTest { + + pub xpr: ::core::option::Option>, + + pub arg: ::core::option::Option>, + + pub booltesttype: i32, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct CoerceToDomain { + + pub xpr: ::core::option::Option>, + + pub arg: ::core::option::Option>, + + pub resulttype: u32, + + pub resulttypmod: i32, + + pub resultcollid: u32, + + pub coercionformat: i32, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct CoerceToDomainValue { + + pub xpr: ::core::option::Option>, + + pub type_id: u32, + + pub type_mod: i32, + + pub collation: u32, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct SetToDefault { + + pub xpr: ::core::option::Option>, + + pub type_id: u32, + + pub type_mod: i32, + + pub collation: u32, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct CurrentOfExpr { + + pub xpr: ::core::option::Option>, + + pub cvarno: u32, + + pub cursor_name: String, + + pub cursor_param: i32, +} +#[derive(Clone, PartialEq)] +pub struct NextValueExpr { + + pub xpr: ::core::option::Option>, + + pub seqid: u32, + + pub type_id: u32, +} +#[derive(Clone, PartialEq)] +pub struct InferenceElem { + + pub xpr: ::core::option::Option>, + + pub expr: ::core::option::Option>, + + pub infercollid: u32, + + pub inferopclass: u32, +} +#[derive(Clone, PartialEq)] +pub struct TargetEntry { + + pub xpr: ::core::option::Option>, + + pub expr: ::core::option::Option>, + + pub resno: i32, + + pub resname: String, + + pub ressortgroupref: u32, + + pub resorigtbl: u32, + + pub resorigcol: i32, + + pub resjunk: bool, +} +#[derive(Clone, PartialEq)] +pub struct RangeTblRef { + + pub rtindex: i32, +} +#[derive(Clone, PartialEq)] +pub struct JoinExpr { + + pub jointype: i32, + + pub is_natural: bool, + + pub larg: ::core::option::Option>, + + pub rarg: ::core::option::Option>, + + pub using_clause: Vec, + + pub quals: ::core::option::Option>, + + pub alias: ::core::option::Option, + + pub rtindex: i32, +} +#[derive(Clone, PartialEq)] +pub struct FromExpr { + + pub fromlist: Vec, + + pub quals: ::core::option::Option>, +} +#[derive(Clone, PartialEq)] +pub struct OnConflictExpr { + + pub action: i32, + + pub arbiter_elems: Vec, + + pub arbiter_where: ::core::option::Option>, + + pub constraint: u32, + + pub on_conflict_set: Vec, + + pub on_conflict_where: ::core::option::Option>, + + pub excl_rel_index: i32, + + pub excl_rel_tlist: Vec, +} +#[derive(Clone, PartialEq)] +pub struct IntoClause { + + pub rel: ::core::option::Option, + + pub col_names: Vec, + + pub access_method: String, + + pub options: Vec, + + pub on_commit: i32, + + pub table_space_name: String, + + pub view_query: ::core::option::Option>, + + pub skip_data: bool, +} +#[derive(Clone, PartialEq)] +pub struct RawStmt { + + pub stmt: ::core::option::Option>, + + pub stmt_location: i32, + + pub stmt_len: i32, +} +#[derive(Clone, PartialEq)] +pub struct Query { + + pub command_type: i32, + + pub query_source: i32, + + pub can_set_tag: bool, + + pub utility_stmt: ::core::option::Option>, + + pub result_relation: i32, + + pub has_aggs: bool, + + pub has_window_funcs: bool, + + pub has_target_srfs: bool, + + pub has_sub_links: bool, + + pub has_distinct_on: bool, + + pub has_recursive: bool, + + pub has_modifying_cte: bool, + + pub has_for_update: bool, + + pub has_row_security: bool, + + pub cte_list: Vec, + + pub rtable: Vec, + + pub jointree: ::core::option::Option>, + + pub target_list: Vec, + + pub r#override: i32, + + pub on_conflict: ::core::option::Option>, + + pub returning_list: Vec, + + pub group_clause: Vec, + + pub grouping_sets: Vec, + + pub having_qual: ::core::option::Option>, + + pub window_clause: Vec, + + pub distinct_clause: Vec, + + pub sort_clause: Vec, + + pub limit_offset: ::core::option::Option>, + + pub limit_count: ::core::option::Option>, + + pub limit_option: i32, + + pub row_marks: Vec, + + pub set_operations: ::core::option::Option>, + + pub constraint_deps: Vec, + + pub with_check_options: Vec, + + pub stmt_location: i32, + + pub stmt_len: i32, +} +#[derive(Clone, PartialEq)] +pub struct InsertStmt { + + pub relation: ::core::option::Option, + + pub cols: Vec, + + pub select_stmt: ::core::option::Option>, + + pub on_conflict_clause: ::core::option::Option>, + + pub returning_list: Vec, + + pub with_clause: ::core::option::Option, + + pub r#override: i32, +} +#[derive(Clone, PartialEq)] +pub struct DeleteStmt { + + pub relation: ::core::option::Option, + + pub using_clause: Vec, + + pub where_clause: ::core::option::Option>, + + pub returning_list: Vec, + + pub with_clause: ::core::option::Option, +} +#[derive(Clone, PartialEq)] +pub struct UpdateStmt { + + pub relation: ::core::option::Option, + + pub target_list: Vec, + + pub where_clause: ::core::option::Option>, + + pub from_clause: Vec, + + pub returning_list: Vec, + + pub with_clause: ::core::option::Option, +} +#[derive(Clone, PartialEq)] +pub struct SelectStmt { + + pub distinct_clause: Vec, + + pub into_clause: ::core::option::Option>, + + pub target_list: Vec, + + pub from_clause: Vec, + + pub where_clause: ::core::option::Option>, + + pub group_clause: Vec, + + pub having_clause: ::core::option::Option>, + + pub window_clause: Vec, + + pub values_lists: Vec, + + pub sort_clause: Vec, + + pub limit_offset: ::core::option::Option>, + + pub limit_count: ::core::option::Option>, + + pub limit_option: i32, + + pub locking_clause: Vec, + + pub with_clause: ::core::option::Option, + + pub op: i32, + + pub all: bool, + + pub larg: ::core::option::Option>, + + pub rarg: ::core::option::Option>, +} +#[derive(Clone, PartialEq)] +pub struct AlterTableStmt { + + pub relation: ::core::option::Option, + + pub cmds: Vec, + + pub relkind: i32, + + pub missing_ok: bool, +} +#[derive(Clone, PartialEq)] +pub struct AlterTableCmd { + + pub subtype: i32, + + pub name: String, + + pub num: i32, + + pub newowner: ::core::option::Option, + + pub def: ::core::option::Option>, + + pub behavior: i32, + + pub missing_ok: bool, +} +#[derive(Clone, PartialEq)] +pub struct AlterDomainStmt { + + pub subtype: String, + + pub type_name: Vec, + + pub name: String, + + pub def: ::core::option::Option>, + + pub behavior: i32, + + pub missing_ok: bool, +} +#[derive(Clone, PartialEq)] +pub struct SetOperationStmt { + + pub op: i32, + + pub all: bool, + + pub larg: ::core::option::Option>, + + pub rarg: ::core::option::Option>, + + pub col_types: Vec, + + pub col_typmods: Vec, + + pub col_collations: Vec, + + pub group_clauses: Vec, +} +#[derive(Clone, PartialEq)] +pub struct GrantStmt { + + pub is_grant: bool, + + pub targtype: i32, + + pub objtype: i32, + + pub objects: Vec, + + pub privileges: Vec, + + pub grantees: Vec, + + pub grant_option: bool, + + pub behavior: i32, +} +#[derive(Clone, PartialEq)] +pub struct GrantRoleStmt { + + pub granted_roles: Vec, + + pub grantee_roles: Vec, + + pub is_grant: bool, + + pub admin_opt: bool, + + pub grantor: ::core::option::Option, + + pub behavior: i32, +} +#[derive(Clone, PartialEq)] +pub struct AlterDefaultPrivilegesStmt { + + pub options: Vec, + + pub action: ::core::option::Option, +} +#[derive(Clone, PartialEq)] +pub struct ClosePortalStmt { + + pub portalname: String, +} +#[derive(Clone, PartialEq)] +pub struct ClusterStmt { + + pub relation: ::core::option::Option, + + pub indexname: String, + + pub options: i32, +} +#[derive(Clone, PartialEq)] +pub struct CopyStmt { + + pub relation: ::core::option::Option, + + pub query: ::core::option::Option>, + + pub attlist: Vec, + + pub is_from: bool, + + pub is_program: bool, + + pub filename: String, + + pub options: Vec, + + pub where_clause: ::core::option::Option>, +} +#[derive(Clone, PartialEq)] +pub struct CreateStmt { + + pub relation: ::core::option::Option, + + pub table_elts: Vec, + + pub inh_relations: Vec, + + pub partbound: ::core::option::Option, + + pub partspec: ::core::option::Option, + + pub of_typename: ::core::option::Option, + + pub constraints: Vec, + + pub options: Vec, + + pub oncommit: i32, + + pub tablespacename: String, + + pub access_method: String, + + pub if_not_exists: bool, +} +#[derive(Clone, PartialEq)] +pub struct DefineStmt { + + pub kind: i32, + + pub oldstyle: bool, + + pub defnames: Vec, + + pub args: Vec, + + pub definition: Vec, + + pub if_not_exists: bool, + + pub replace: bool, +} +#[derive(Clone, PartialEq)] +pub struct DropStmt { + + pub objects: Vec, + + pub remove_type: i32, + + pub behavior: i32, + + pub missing_ok: bool, + + pub concurrent: bool, +} +#[derive(Clone, PartialEq)] +pub struct TruncateStmt { + + pub relations: Vec, + + pub restart_seqs: bool, + + pub behavior: i32, +} +#[derive(Clone, PartialEq)] +pub struct CommentStmt { + + pub objtype: i32, + + pub object: ::core::option::Option>, + + pub comment: String, +} +#[derive(Clone, PartialEq)] +pub struct FetchStmt { + + pub direction: i32, + + pub how_many: i64, + + pub portalname: String, + + pub ismove: bool, +} +#[derive(Clone, PartialEq)] +pub struct IndexStmt { + + pub idxname: String, + + pub relation: ::core::option::Option, + + pub access_method: String, + + pub table_space: String, + + pub index_params: Vec, + + pub index_including_params: Vec, + + pub options: Vec, + + pub where_clause: ::core::option::Option>, + + pub exclude_op_names: Vec, + + pub idxcomment: String, + + pub index_oid: u32, + + pub old_node: u32, + + pub old_create_subid: u32, + + pub old_first_relfilenode_subid: u32, + + pub unique: bool, + + pub primary: bool, + + pub isconstraint: bool, + + pub deferrable: bool, + + pub initdeferred: bool, + + pub transformed: bool, + + pub concurrent: bool, + + pub if_not_exists: bool, + + pub reset_default_tblspc: bool, +} +#[derive(Clone, PartialEq)] +pub struct CreateFunctionStmt { + + pub is_procedure: bool, + + pub replace: bool, + + pub funcname: Vec, + + pub parameters: Vec, + + pub return_type: ::core::option::Option, + + pub options: Vec, +} +#[derive(Clone, PartialEq)] +pub struct AlterFunctionStmt { + + pub objtype: i32, + + pub func: ::core::option::Option, + + pub actions: Vec, +} +#[derive(Clone, PartialEq)] +pub struct DoStmt { + + pub args: Vec, +} +#[derive(Clone, PartialEq)] +pub struct RenameStmt { + + pub rename_type: i32, + + pub relation_type: i32, + + pub relation: ::core::option::Option, + + pub object: ::core::option::Option>, + + pub subname: String, + + pub newname: String, + + pub behavior: i32, + + pub missing_ok: bool, +} +#[derive(Clone, PartialEq)] +pub struct RuleStmt { + + pub relation: ::core::option::Option, + + pub rulename: String, + + pub where_clause: ::core::option::Option>, + + pub event: i32, + + pub instead: bool, + + pub actions: Vec, + + pub replace: bool, +} +#[derive(Clone, PartialEq)] +pub struct NotifyStmt { + + pub conditionname: String, + + pub payload: String, +} +#[derive(Clone, PartialEq)] +pub struct ListenStmt { + + pub conditionname: String, +} +#[derive(Clone, PartialEq)] +pub struct UnlistenStmt { + + pub conditionname: String, +} +#[derive(Clone, PartialEq)] +pub struct TransactionStmt { + + pub kind: i32, + + pub options: Vec, + + pub savepoint_name: String, + + pub gid: String, + + pub chain: bool, +} +#[derive(Clone, PartialEq)] +pub struct ViewStmt { + + pub view: ::core::option::Option, + + pub aliases: Vec, + + pub query: ::core::option::Option>, + + pub replace: bool, + + pub options: Vec, + + pub with_check_option: i32, +} +#[derive(Clone, PartialEq)] +pub struct LoadStmt { + + pub filename: String, +} +#[derive(Clone, PartialEq)] +pub struct CreateDomainStmt { + + pub domainname: Vec, + + pub type_name: ::core::option::Option, + + pub coll_clause: ::core::option::Option>, + + pub constraints: Vec, +} +#[derive(Clone, PartialEq)] +pub struct CreatedbStmt { + + pub dbname: String, + + pub options: Vec, +} +#[derive(Clone, PartialEq)] +pub struct DropdbStmt { + + pub dbname: String, + + pub missing_ok: bool, + + pub options: Vec, +} +#[derive(Clone, PartialEq)] +pub struct VacuumStmt { + + pub options: Vec, + + pub rels: Vec, + + pub is_vacuumcmd: bool, +} +#[derive(Clone, PartialEq)] +pub struct ExplainStmt { + + pub query: ::core::option::Option>, + + pub options: Vec, +} +#[derive(Clone, PartialEq)] +pub struct CreateTableAsStmt { + + pub query: ::core::option::Option>, + + pub into: ::core::option::Option>, + + pub relkind: i32, + + pub is_select_into: bool, + + pub if_not_exists: bool, +} +#[derive(Clone, PartialEq)] +pub struct CreateSeqStmt { + + pub sequence: ::core::option::Option, + + pub options: Vec, + + pub owner_id: u32, + + pub for_identity: bool, + + pub if_not_exists: bool, +} +#[derive(Clone, PartialEq)] +pub struct AlterSeqStmt { + + pub sequence: ::core::option::Option, + + pub options: Vec, + + pub for_identity: bool, + + pub missing_ok: bool, +} +#[derive(Clone, PartialEq)] +pub struct VariableSetStmt { + + pub kind: i32, + + pub name: String, + + pub args: Vec, + + pub is_local: bool, +} +#[derive(Clone, PartialEq)] +pub struct VariableShowStmt { + + pub name: String, +} +#[derive(Clone, PartialEq)] +pub struct DiscardStmt { + + pub target: i32, +} +#[derive(Clone, PartialEq)] +pub struct CreateTrigStmt { + + pub trigname: String, + + pub relation: ::core::option::Option, + + pub funcname: Vec, + + pub args: Vec, + + pub row: bool, + + pub timing: i32, + + pub events: i32, + + pub columns: Vec, + + pub when_clause: ::core::option::Option>, + + pub isconstraint: bool, + + pub transition_rels: Vec, + + pub deferrable: bool, + + pub initdeferred: bool, + + pub constrrel: ::core::option::Option, +} +#[derive(Clone, PartialEq)] +pub struct CreatePLangStmt { + + pub replace: bool, + + pub plname: String, + + pub plhandler: Vec, + + pub plinline: Vec, + + pub plvalidator: Vec, + + pub pltrusted: bool, +} +#[derive(Clone, PartialEq)] +pub struct CreateRoleStmt { + + pub stmt_type: i32, + + pub role: String, + + pub options: Vec, +} +#[derive(Clone, PartialEq)] +pub struct AlterRoleStmt { + + pub role: ::core::option::Option, + + pub options: Vec, + + pub action: i32, +} +#[derive(Clone, PartialEq)] +pub struct DropRoleStmt { + + pub roles: Vec, + + pub missing_ok: bool, +} +#[derive(Clone, PartialEq)] +pub struct LockStmt { + + pub relations: Vec, + + pub mode: i32, + + pub nowait: bool, +} +#[derive(Clone, PartialEq)] +pub struct ConstraintsSetStmt { + + pub constraints: Vec, + + pub deferred: bool, +} +#[derive(Clone, PartialEq)] +pub struct ReindexStmt { + + pub kind: i32, + + pub relation: ::core::option::Option, + + pub name: String, + + pub options: i32, + + pub concurrent: bool, +} +#[derive(Clone, PartialEq)] +pub struct CheckPointStmt {} +#[derive(Clone, PartialEq)] +pub struct CreateSchemaStmt { + + pub schemaname: String, + + pub authrole: ::core::option::Option, + + pub schema_elts: Vec, + + pub if_not_exists: bool, +} +#[derive(Clone, PartialEq)] +pub struct AlterDatabaseStmt { + + pub dbname: String, + + pub options: Vec, +} +#[derive(Clone, PartialEq)] +pub struct AlterDatabaseSetStmt { + + pub dbname: String, + + pub setstmt: ::core::option::Option, +} +#[derive(Clone, PartialEq)] +pub struct AlterRoleSetStmt { + + pub role: ::core::option::Option, + + pub database: String, + + pub setstmt: ::core::option::Option, +} +#[derive(Clone, PartialEq)] +pub struct CreateConversionStmt { + + pub conversion_name: Vec, + + pub for_encoding_name: String, + + pub to_encoding_name: String, + + pub func_name: Vec, + + pub def: bool, +} +#[derive(Clone, PartialEq)] +pub struct CreateCastStmt { + + pub sourcetype: ::core::option::Option, + + pub targettype: ::core::option::Option, + + pub func: ::core::option::Option, + + pub context: i32, + + pub inout: bool, +} +#[derive(Clone, PartialEq)] +pub struct CreateOpClassStmt { + + pub opclassname: Vec, + + pub opfamilyname: Vec, + + pub amname: String, + + pub datatype: ::core::option::Option, + + pub items: Vec, + + pub is_default: bool, +} +#[derive(Clone, PartialEq)] +pub struct CreateOpFamilyStmt { + + pub opfamilyname: Vec, + + pub amname: String, +} +#[derive(Clone, PartialEq)] +pub struct AlterOpFamilyStmt { + + pub opfamilyname: Vec, + + pub amname: String, + + pub is_drop: bool, + + pub items: Vec, +} +#[derive(Clone, PartialEq)] +pub struct PrepareStmt { + + pub name: String, + + pub argtypes: Vec, + + pub query: ::core::option::Option>, +} +#[derive(Clone, PartialEq)] +pub struct ExecuteStmt { + + pub name: String, + + pub params: Vec, +} +#[derive(Clone, PartialEq)] +pub struct DeallocateStmt { + + pub name: String, +} +#[derive(Clone, PartialEq)] +pub struct DeclareCursorStmt { + + pub portalname: String, + + pub options: i32, + + pub query: ::core::option::Option>, +} +#[derive(Clone, PartialEq)] +pub struct CreateTableSpaceStmt { + + pub tablespacename: String, + + pub owner: ::core::option::Option, + + pub location: String, + + pub options: Vec, +} +#[derive(Clone, PartialEq)] +pub struct DropTableSpaceStmt { + + pub tablespacename: String, + + pub missing_ok: bool, +} +#[derive(Clone, PartialEq)] +pub struct AlterObjectDependsStmt { + + pub object_type: i32, + + pub relation: ::core::option::Option, + + pub object: ::core::option::Option>, + + pub extname: ::core::option::Option>, + + pub remove: bool, +} +#[derive(Clone, PartialEq)] +pub struct AlterObjectSchemaStmt { + + pub object_type: i32, + + pub relation: ::core::option::Option, + + pub object: ::core::option::Option>, + + pub newschema: String, + + pub missing_ok: bool, +} +#[derive(Clone, PartialEq)] +pub struct AlterOwnerStmt { + + pub object_type: i32, + + pub relation: ::core::option::Option, + + pub object: ::core::option::Option>, + + pub newowner: ::core::option::Option, +} +#[derive(Clone, PartialEq)] +pub struct AlterOperatorStmt { + + pub opername: ::core::option::Option, + + pub options: Vec, +} +#[derive(Clone, PartialEq)] +pub struct AlterTypeStmt { + + pub type_name: Vec, + + pub options: Vec, +} +#[derive(Clone, PartialEq)] +pub struct DropOwnedStmt { + + pub roles: Vec, + + pub behavior: i32, +} +#[derive(Clone, PartialEq)] +pub struct ReassignOwnedStmt { + + pub roles: Vec, + + pub newrole: ::core::option::Option, +} +#[derive(Clone, PartialEq)] +pub struct CompositeTypeStmt { + + pub typevar: ::core::option::Option, + + pub coldeflist: Vec, +} +#[derive(Clone, PartialEq)] +pub struct CreateEnumStmt { + + pub type_name: Vec, + + pub vals: Vec, +} +#[derive(Clone, PartialEq)] +pub struct CreateRangeStmt { + + pub type_name: Vec, + + pub params: Vec, +} +#[derive(Clone, PartialEq)] +pub struct AlterEnumStmt { + + pub type_name: Vec, + + pub old_val: String, + + pub new_val: String, + + pub new_val_neighbor: String, + + pub new_val_is_after: bool, + + pub skip_if_new_val_exists: bool, +} +#[derive(Clone, PartialEq)] +pub struct AlterTsDictionaryStmt { + + pub dictname: Vec, + + pub options: Vec, +} +#[derive(Clone, PartialEq)] +pub struct AlterTsConfigurationStmt { + + pub kind: i32, + + pub cfgname: Vec, + + pub tokentype: Vec, + + pub dicts: Vec, + + pub r#override: bool, + + pub replace: bool, + + pub missing_ok: bool, +} +#[derive(Clone, PartialEq)] +pub struct CreateFdwStmt { + + pub fdwname: String, + + pub func_options: Vec, + + pub options: Vec, +} +#[derive(Clone, PartialEq)] +pub struct AlterFdwStmt { + + pub fdwname: String, + + pub func_options: Vec, + + pub options: Vec, +} +#[derive(Clone, PartialEq)] +pub struct CreateForeignServerStmt { + + pub servername: String, + + pub servertype: String, + + pub version: String, + + pub fdwname: String, + + pub if_not_exists: bool, + + pub options: Vec, +} +#[derive(Clone, PartialEq)] +pub struct AlterForeignServerStmt { + + pub servername: String, + + pub version: String, + + pub options: Vec, + + pub has_version: bool, +} +#[derive(Clone, PartialEq)] +pub struct CreateUserMappingStmt { + + pub user: ::core::option::Option, + + pub servername: String, + + pub if_not_exists: bool, + + pub options: Vec, +} +#[derive(Clone, PartialEq)] +pub struct AlterUserMappingStmt { + + pub user: ::core::option::Option, + + pub servername: String, + + pub options: Vec, +} +#[derive(Clone, PartialEq)] +pub struct DropUserMappingStmt { + + pub user: ::core::option::Option, + + pub servername: String, + + pub missing_ok: bool, +} +#[derive(Clone, PartialEq)] +pub struct AlterTableSpaceOptionsStmt { + + pub tablespacename: String, + + pub options: Vec, + + pub is_reset: bool, +} +#[derive(Clone, PartialEq)] +pub struct AlterTableMoveAllStmt { + + pub orig_tablespacename: String, + + pub objtype: i32, + + pub roles: Vec, + + pub new_tablespacename: String, + + pub nowait: bool, +} +#[derive(Clone, PartialEq)] +pub struct SecLabelStmt { + + pub objtype: i32, + + pub object: ::core::option::Option>, + + pub provider: String, + + pub label: String, +} +#[derive(Clone, PartialEq)] +pub struct CreateForeignTableStmt { + + pub base_stmt: ::core::option::Option, + + pub servername: String, + + pub options: Vec, +} +#[derive(Clone, PartialEq)] +pub struct ImportForeignSchemaStmt { + + pub server_name: String, + + pub remote_schema: String, + + pub local_schema: String, + + pub list_type: i32, + + pub table_list: Vec, + + pub options: Vec, +} +#[derive(Clone, PartialEq)] +pub struct CreateExtensionStmt { + + pub extname: String, + + pub if_not_exists: bool, + + pub options: Vec, +} +#[derive(Clone, PartialEq)] +pub struct AlterExtensionStmt { + + pub extname: String, + + pub options: Vec, +} +#[derive(Clone, PartialEq)] +pub struct AlterExtensionContentsStmt { + + pub extname: String, + + pub action: i32, + + pub objtype: i32, + + pub object: ::core::option::Option>, +} +#[derive(Clone, PartialEq)] +pub struct CreateEventTrigStmt { + + pub trigname: String, + + pub eventname: String, + + pub whenclause: Vec, + + pub funcname: Vec, +} +#[derive(Clone, PartialEq)] +pub struct AlterEventTrigStmt { + + pub trigname: String, + + pub tgenabled: String, +} +#[derive(Clone, PartialEq)] +pub struct RefreshMatViewStmt { + + pub concurrent: bool, + + pub skip_data: bool, + + pub relation: ::core::option::Option, +} +#[derive(Clone, PartialEq)] +pub struct ReplicaIdentityStmt { + + pub identity_type: String, + + pub name: String, +} +#[derive(Clone, PartialEq)] +pub struct AlterSystemStmt { + + pub setstmt: ::core::option::Option, +} +#[derive(Clone, PartialEq)] +pub struct CreatePolicyStmt { + + pub policy_name: String, + + pub table: ::core::option::Option, + + pub cmd_name: String, + + pub permissive: bool, + + pub roles: Vec, + + pub qual: ::core::option::Option>, + + pub with_check: ::core::option::Option>, +} +#[derive(Clone, PartialEq)] +pub struct AlterPolicyStmt { + + pub policy_name: String, + + pub table: ::core::option::Option, + + pub roles: Vec, + + pub qual: ::core::option::Option>, + + pub with_check: ::core::option::Option>, +} +#[derive(Clone, PartialEq)] +pub struct CreateTransformStmt { + + pub replace: bool, + + pub type_name: ::core::option::Option, + + pub lang: String, + + pub fromsql: ::core::option::Option, + + pub tosql: ::core::option::Option, +} +#[derive(Clone, PartialEq)] +pub struct CreateAmStmt { + + pub amname: String, + + pub handler_name: Vec, + + pub amtype: String, +} +#[derive(Clone, PartialEq)] +pub struct CreatePublicationStmt { + + pub pubname: String, + + pub options: Vec, + + pub tables: Vec, + + pub for_all_tables: bool, +} +#[derive(Clone, PartialEq)] +pub struct AlterPublicationStmt { + + pub pubname: String, + + pub options: Vec, + + pub tables: Vec, + + pub for_all_tables: bool, + + pub table_action: i32, +} +#[derive(Clone, PartialEq)] +pub struct CreateSubscriptionStmt { + + pub subname: String, + + pub conninfo: String, + + pub publication: Vec, + + pub options: Vec, +} +#[derive(Clone, PartialEq)] +pub struct AlterSubscriptionStmt { + + pub kind: i32, + + pub subname: String, + + pub conninfo: String, + + pub publication: Vec, + + pub options: Vec, +} +#[derive(Clone, PartialEq)] +pub struct DropSubscriptionStmt { + + pub subname: String, + + pub missing_ok: bool, + + pub behavior: i32, +} +#[derive(Clone, PartialEq)] +pub struct CreateStatsStmt { + + pub defnames: Vec, + + pub stat_types: Vec, + + pub exprs: Vec, + + pub relations: Vec, + + pub stxcomment: String, + + pub if_not_exists: bool, +} +#[derive(Clone, PartialEq)] +pub struct AlterCollationStmt { + + pub collname: Vec, +} +#[derive(Clone, PartialEq)] +pub struct CallStmt { + + pub funccall: ::core::option::Option>, + + pub funcexpr: ::core::option::Option>, +} +#[derive(Clone, PartialEq)] +pub struct AlterStatsStmt { + + pub defnames: Vec, + + pub stxstattarget: i32, + + pub missing_ok: bool, +} +#[derive(Clone, PartialEq)] +pub struct AExpr { + + pub kind: i32, + + pub name: Vec, + + pub lexpr: ::core::option::Option>, + + pub rexpr: ::core::option::Option>, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct ColumnRef { + + pub fields: Vec, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct ParamRef { + + pub number: i32, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct AConst { + + pub val: ::core::option::Option>, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct FuncCall { + + pub funcname: Vec, + + pub args: Vec, + + pub agg_order: Vec, + + pub agg_filter: ::core::option::Option>, + + pub agg_within_group: bool, + + pub agg_star: bool, + + pub agg_distinct: bool, + + pub func_variadic: bool, + + pub over: ::core::option::Option>, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct AStar {} +#[derive(Clone, PartialEq)] +pub struct AIndices { + + pub is_slice: bool, + + pub lidx: ::core::option::Option>, + + pub uidx: ::core::option::Option>, +} +#[derive(Clone, PartialEq)] +pub struct AIndirection { + + pub arg: ::core::option::Option>, + + pub indirection: Vec, +} +#[derive(Clone, PartialEq)] +pub struct AArrayExpr { + + pub elements: Vec, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct ResTarget { + + pub name: String, + + pub indirection: Vec, + + pub val: ::core::option::Option>, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct MultiAssignRef { + + pub source: ::core::option::Option>, + + pub colno: i32, + + pub ncolumns: i32, +} +#[derive(Clone, PartialEq)] +pub struct TypeCast { + + pub arg: ::core::option::Option>, + + pub type_name: ::core::option::Option, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct CollateClause { + + pub arg: ::core::option::Option>, + + pub collname: Vec, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct SortBy { + + pub node: ::core::option::Option>, + + pub sortby_dir: i32, + + pub sortby_nulls: i32, + + pub use_op: Vec, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct WindowDef { + + pub name: String, + + pub refname: String, + + pub partition_clause: Vec, + + pub order_clause: Vec, + + pub frame_options: i32, + + pub start_offset: ::core::option::Option>, + + pub end_offset: ::core::option::Option>, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct RangeSubselect { + + pub lateral: bool, + + pub subquery: ::core::option::Option>, + + pub alias: ::core::option::Option, +} +#[derive(Clone, PartialEq)] +pub struct RangeFunction { + + pub lateral: bool, + + pub ordinality: bool, + + pub is_rowsfrom: bool, + + pub functions: Vec, + + pub alias: ::core::option::Option, + + pub coldeflist: Vec, +} +#[derive(Clone, PartialEq)] +pub struct RangeTableSample { + + pub relation: ::core::option::Option>, + + pub method: Vec, + + pub args: Vec, + + pub repeatable: ::core::option::Option>, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct RangeTableFunc { + + pub lateral: bool, + + pub docexpr: ::core::option::Option>, + + pub rowexpr: ::core::option::Option>, + + pub namespaces: Vec, + + pub columns: Vec, + + pub alias: ::core::option::Option, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct RangeTableFuncCol { + + pub colname: String, + + pub type_name: ::core::option::Option, + + pub for_ordinality: bool, + + pub is_not_null: bool, + + pub colexpr: ::core::option::Option>, + + pub coldefexpr: ::core::option::Option>, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct TypeName { + + pub names: Vec, + + pub type_oid: u32, + + pub setof: bool, + + pub pct_type: bool, + + pub typmods: Vec, + + pub typemod: i32, + + pub array_bounds: Vec, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct ColumnDef { + + pub colname: String, + + pub type_name: ::core::option::Option, + + pub inhcount: i32, + + pub is_local: bool, + + pub is_not_null: bool, + + pub is_from_type: bool, + + pub storage: String, + + pub raw_default: ::core::option::Option>, + + pub cooked_default: ::core::option::Option>, + + pub identity: String, + + pub identity_sequence: ::core::option::Option, + + pub generated: String, + + pub coll_clause: ::core::option::Option>, + + pub coll_oid: u32, + + pub constraints: Vec, + + pub fdwoptions: Vec, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct IndexElem { + + pub name: String, + + pub expr: ::core::option::Option>, + + pub indexcolname: String, + + pub collation: Vec, + + pub opclass: Vec, + + pub opclassopts: Vec, + + pub ordering: i32, + + pub nulls_ordering: i32, +} +#[derive(Clone, PartialEq)] +pub struct Constraint { + + pub contype: i32, + + pub conname: String, + + pub deferrable: bool, + + pub initdeferred: bool, + + pub location: i32, + + pub is_no_inherit: bool, + + pub raw_expr: ::core::option::Option>, + + pub cooked_expr: String, + + pub generated_when: String, + + pub keys: Vec, + + pub including: Vec, + + pub exclusions: Vec, + + pub options: Vec, + + pub indexname: String, + + pub indexspace: String, + + pub reset_default_tblspc: bool, + + pub access_method: String, + + pub where_clause: ::core::option::Option>, + + pub pktable: ::core::option::Option, + + pub fk_attrs: Vec, + + pub pk_attrs: Vec, + + pub fk_matchtype: String, + + pub fk_upd_action: String, + + pub fk_del_action: String, + + pub old_conpfeqop: Vec, + + pub old_pktable_oid: u32, + + pub skip_validation: bool, + + pub initially_valid: bool, +} +#[derive(Clone, PartialEq)] +pub struct DefElem { + + pub defnamespace: String, + + pub defname: String, + + pub arg: ::core::option::Option>, + + pub defaction: i32, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct RangeTblEntry { + + pub rtekind: i32, + + pub relid: u32, + + pub relkind: String, + + pub rellockmode: i32, + + pub tablesample: ::core::option::Option>, + + pub subquery: ::core::option::Option>, + + pub security_barrier: bool, + + pub jointype: i32, + + pub joinmergedcols: i32, + + pub joinaliasvars: Vec, + + pub joinleftcols: Vec, + + pub joinrightcols: Vec, + + pub functions: Vec, + + pub funcordinality: bool, + + pub tablefunc: ::core::option::Option>, + + pub values_lists: Vec, + + pub ctename: String, + + pub ctelevelsup: u32, + + pub self_reference: bool, + + pub coltypes: Vec, + + pub coltypmods: Vec, + + pub colcollations: Vec, + + pub enrname: String, + + pub enrtuples: f64, + + pub alias: ::core::option::Option, + + pub eref: ::core::option::Option, + + pub lateral: bool, + + pub inh: bool, + + pub in_from_cl: bool, + + pub required_perms: u32, + + pub check_as_user: u32, + + pub selected_cols: Vec, + + pub inserted_cols: Vec, + + pub updated_cols: Vec, + + pub extra_updated_cols: Vec, + + pub security_quals: Vec, +} +#[derive(Clone, PartialEq)] +pub struct RangeTblFunction { + + pub funcexpr: ::core::option::Option>, + + pub funccolcount: i32, + + pub funccolnames: Vec, + + pub funccoltypes: Vec, + + pub funccoltypmods: Vec, + + pub funccolcollations: Vec, + + pub funcparams: Vec, +} +#[derive(Clone, PartialEq)] +pub struct TableSampleClause { + + pub tsmhandler: u32, + + pub args: Vec, + + pub repeatable: ::core::option::Option>, +} +#[derive(Clone, PartialEq)] +pub struct WithCheckOption { + + pub kind: i32, + + pub relname: String, + + pub polname: String, + + pub qual: ::core::option::Option>, + + pub cascaded: bool, +} +#[derive(Clone, PartialEq)] +pub struct SortGroupClause { + + pub tle_sort_group_ref: u32, + + pub eqop: u32, + + pub sortop: u32, + + pub nulls_first: bool, + + pub hashable: bool, +} +#[derive(Clone, PartialEq)] +pub struct GroupingSet { + + pub kind: i32, + + pub content: Vec, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct WindowClause { + + pub name: String, + + pub refname: String, + + pub partition_clause: Vec, + + pub order_clause: Vec, + + pub frame_options: i32, + + pub start_offset: ::core::option::Option>, + + pub end_offset: ::core::option::Option>, + + pub start_in_range_func: u32, + + pub end_in_range_func: u32, + + pub in_range_coll: u32, + + pub in_range_asc: bool, + + pub in_range_nulls_first: bool, + + pub winref: u32, + + pub copied_order: bool, +} +#[derive(Clone, PartialEq)] +pub struct ObjectWithArgs { + + pub objname: Vec, + + pub objargs: Vec, + + pub args_unspecified: bool, +} +#[derive(Clone, PartialEq)] +pub struct AccessPriv { + + pub priv_name: String, + + pub cols: Vec, +} +#[derive(Clone, PartialEq)] +pub struct CreateOpClassItem { + + pub itemtype: i32, + + pub name: ::core::option::Option, + + pub number: i32, + + pub order_family: Vec, + + pub class_args: Vec, + + pub storedtype: ::core::option::Option, +} +#[derive(Clone, PartialEq)] +pub struct TableLikeClause { + + pub relation: ::core::option::Option, + + pub options: u32, + + pub relation_oid: u32, +} +#[derive(Clone, PartialEq)] +pub struct FunctionParameter { + + pub name: String, + + pub arg_type: ::core::option::Option, + + pub mode: i32, + + pub defexpr: ::core::option::Option>, +} +#[derive(Clone, PartialEq)] +pub struct LockingClause { + + pub locked_rels: Vec, + + pub strength: i32, + + pub wait_policy: i32, +} +#[derive(Clone, PartialEq)] +pub struct RowMarkClause { + + pub rti: u32, + + pub strength: i32, + + pub wait_policy: i32, + + pub pushed_down: bool, +} +#[derive(Clone, PartialEq)] +pub struct XmlSerialize { + + pub xmloption: i32, + + pub expr: ::core::option::Option>, + + pub type_name: ::core::option::Option, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct WithClause { + + pub ctes: Vec, + + pub recursive: bool, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct InferClause { + + pub index_elems: Vec, + + pub where_clause: ::core::option::Option>, + + pub conname: String, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct OnConflictClause { + + pub action: i32, + + pub infer: ::core::option::Option>, + + pub target_list: Vec, + + pub where_clause: ::core::option::Option>, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct CommonTableExpr { + + pub ctename: String, + + pub aliascolnames: Vec, + + pub ctematerialized: i32, + + pub ctequery: ::core::option::Option>, + + pub location: i32, + + pub cterecursive: bool, + + pub cterefcount: i32, + + pub ctecolnames: Vec, + + pub ctecoltypes: Vec, + + pub ctecoltypmods: Vec, + + pub ctecolcollations: Vec, +} +#[derive(Clone, PartialEq)] +pub struct RoleSpec { + + pub roletype: i32, + + pub rolename: String, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct TriggerTransition { + + pub name: String, + + pub is_new: bool, + + pub is_table: bool, +} +#[derive(Clone, PartialEq)] +pub struct PartitionElem { + + pub name: String, + + pub expr: ::core::option::Option>, + + pub collation: Vec, + + pub opclass: Vec, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct PartitionSpec { + + pub strategy: String, + + pub part_params: Vec, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct PartitionBoundSpec { + + pub strategy: String, + + pub is_default: bool, + + pub modulus: i32, + + pub remainder: i32, + + pub listdatums: Vec, + + pub lowerdatums: Vec, + + pub upperdatums: Vec, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct PartitionRangeDatum { + + pub kind: i32, + + pub value: ::core::option::Option>, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct PartitionCmd { + + pub name: ::core::option::Option, + + pub bound: ::core::option::Option, +} +#[derive(Clone, PartialEq)] +pub struct VacuumRelation { + + pub relation: ::core::option::Option, + + pub oid: u32, + + pub va_cols: Vec, +} +#[derive(Clone, PartialEq)] +pub struct InlineCodeBlock { + + pub source_text: String, + + pub lang_oid: u32, + + pub lang_is_trusted: bool, + + pub atomic: bool, +} +#[derive(Clone, PartialEq)] +pub struct CallContext { + + pub atomic: bool, +} +#[derive(Clone, PartialEq)] +pub struct ScanToken { + + pub start: i32, + + pub end: i32, + + pub token: i32, + + pub keyword_kind: i32, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum OverridingKind { + Undefined = 0, + OverridingNotSet = 1, + OverridingUserValue = 2, + OverridingSystemValue = 3, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum QuerySource { + Undefined = 0, + QsrcOriginal = 1, + QsrcParser = 2, + QsrcInsteadRule = 3, + QsrcQualInsteadRule = 4, + QsrcNonInsteadRule = 5, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum SortByDir { + Undefined = 0, + SortbyDefault = 1, + SortbyAsc = 2, + SortbyDesc = 3, + SortbyUsing = 4, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum SortByNulls { + Undefined = 0, + SortbyNullsDefault = 1, + SortbyNullsFirst = 2, + SortbyNullsLast = 3, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum AExprKind { + Undefined = 0, + AexprOp = 1, + AexprOpAny = 2, + AexprOpAll = 3, + AexprDistinct = 4, + AexprNotDistinct = 5, + AexprNullif = 6, + AexprOf = 7, + AexprIn = 8, + AexprLike = 9, + AexprIlike = 10, + AexprSimilar = 11, + AexprBetween = 12, + AexprNotBetween = 13, + AexprBetweenSym = 14, + AexprNotBetweenSym = 15, + AexprParen = 16, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum RoleSpecType { + Undefined = 0, + RolespecCstring = 1, + RolespecCurrentUser = 2, + RolespecSessionUser = 3, + RolespecPublic = 4, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum TableLikeOption { + Undefined = 0, + CreateTableLikeComments = 1, + CreateTableLikeConstraints = 2, + CreateTableLikeDefaults = 3, + CreateTableLikeGenerated = 4, + CreateTableLikeIdentity = 5, + CreateTableLikeIndexes = 6, + CreateTableLikeStatistics = 7, + CreateTableLikeStorage = 8, + CreateTableLikeAll = 9, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum DefElemAction { + Undefined = 0, + DefelemUnspec = 1, + DefelemSet = 2, + DefelemAdd = 3, + DefelemDrop = 4, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum PartitionRangeDatumKind { + Undefined = 0, + PartitionRangeDatumMinvalue = 1, + PartitionRangeDatumValue = 2, + PartitionRangeDatumMaxvalue = 3, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum RteKind { + RtekindUndefined = 0, + RteRelation = 1, + RteSubquery = 2, + RteJoin = 3, + RteFunction = 4, + RteTablefunc = 5, + RteValues = 6, + RteCte = 7, + RteNamedtuplestore = 8, + RteResult = 9, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum WcoKind { + WcokindUndefined = 0, + WcoViewCheck = 1, + WcoRlsInsertCheck = 2, + WcoRlsUpdateCheck = 3, + WcoRlsConflictCheck = 4, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum GroupingSetKind { + Undefined = 0, + GroupingSetEmpty = 1, + GroupingSetSimple = 2, + GroupingSetRollup = 3, + GroupingSetCube = 4, + GroupingSetSets = 5, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum CteMaterialize { + CtematerializeUndefined = 0, + Default = 1, + Always = 2, + Never = 3, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum SetOperation { + Undefined = 0, + SetopNone = 1, + SetopUnion = 2, + SetopIntersect = 3, + SetopExcept = 4, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum ObjectType { + Undefined = 0, + ObjectAccessMethod = 1, + ObjectAggregate = 2, + ObjectAmop = 3, + ObjectAmproc = 4, + ObjectAttribute = 5, + ObjectCast = 6, + ObjectColumn = 7, + ObjectCollation = 8, + ObjectConversion = 9, + ObjectDatabase = 10, + ObjectDefault = 11, + ObjectDefacl = 12, + ObjectDomain = 13, + ObjectDomconstraint = 14, + ObjectEventTrigger = 15, + ObjectExtension = 16, + ObjectFdw = 17, + ObjectForeignServer = 18, + ObjectForeignTable = 19, + ObjectFunction = 20, + ObjectIndex = 21, + ObjectLanguage = 22, + ObjectLargeobject = 23, + ObjectMatview = 24, + ObjectOpclass = 25, + ObjectOperator = 26, + ObjectOpfamily = 27, + ObjectPolicy = 28, + ObjectProcedure = 29, + ObjectPublication = 30, + ObjectPublicationRel = 31, + ObjectRole = 32, + ObjectRoutine = 33, + ObjectRule = 34, + ObjectSchema = 35, + ObjectSequence = 36, + ObjectSubscription = 37, + ObjectStatisticExt = 38, + ObjectTabconstraint = 39, + ObjectTable = 40, + ObjectTablespace = 41, + ObjectTransform = 42, + ObjectTrigger = 43, + ObjectTsconfiguration = 44, + ObjectTsdictionary = 45, + ObjectTsparser = 46, + ObjectTstemplate = 47, + ObjectType = 48, + ObjectUserMapping = 49, + ObjectView = 50, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum DropBehavior { + Undefined = 0, + DropRestrict = 1, + DropCascade = 2, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum AlterTableType { + Undefined = 0, + AtAddColumn = 1, + AtAddColumnRecurse = 2, + AtAddColumnToView = 3, + AtColumnDefault = 4, + AtCookedColumnDefault = 5, + AtDropNotNull = 6, + AtSetNotNull = 7, + AtDropExpression = 8, + AtCheckNotNull = 9, + AtSetStatistics = 10, + AtSetOptions = 11, + AtResetOptions = 12, + AtSetStorage = 13, + AtDropColumn = 14, + AtDropColumnRecurse = 15, + AtAddIndex = 16, + AtReAddIndex = 17, + AtAddConstraint = 18, + AtAddConstraintRecurse = 19, + AtReAddConstraint = 20, + AtReAddDomainConstraint = 21, + AtAlterConstraint = 22, + AtValidateConstraint = 23, + AtValidateConstraintRecurse = 24, + AtAddIndexConstraint = 25, + AtDropConstraint = 26, + AtDropConstraintRecurse = 27, + AtReAddComment = 28, + AtAlterColumnType = 29, + AtAlterColumnGenericOptions = 30, + AtChangeOwner = 31, + AtClusterOn = 32, + AtDropCluster = 33, + AtSetLogged = 34, + AtSetUnLogged = 35, + AtDropOids = 36, + AtSetTableSpace = 37, + AtSetRelOptions = 38, + AtResetRelOptions = 39, + AtReplaceRelOptions = 40, + AtEnableTrig = 41, + AtEnableAlwaysTrig = 42, + AtEnableReplicaTrig = 43, + AtDisableTrig = 44, + AtEnableTrigAll = 45, + AtDisableTrigAll = 46, + AtEnableTrigUser = 47, + AtDisableTrigUser = 48, + AtEnableRule = 49, + AtEnableAlwaysRule = 50, + AtEnableReplicaRule = 51, + AtDisableRule = 52, + AtAddInherit = 53, + AtDropInherit = 54, + AtAddOf = 55, + AtDropOf = 56, + AtReplicaIdentity = 57, + AtEnableRowSecurity = 58, + AtDisableRowSecurity = 59, + AtForceRowSecurity = 60, + AtNoForceRowSecurity = 61, + AtGenericOptions = 62, + AtAttachPartition = 63, + AtDetachPartition = 64, + AtAddIdentity = 65, + AtSetIdentity = 66, + AtDropIdentity = 67, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum GrantTargetType { + Undefined = 0, + AclTargetObject = 1, + AclTargetAllInSchema = 2, + AclTargetDefaults = 3, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum VariableSetKind { + Undefined = 0, + VarSetValue = 1, + VarSetDefault = 2, + VarSetCurrent = 3, + VarSetMulti = 4, + VarReset = 5, + VarResetAll = 6, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum ConstrType { + Undefined = 0, + ConstrNull = 1, + ConstrNotnull = 2, + ConstrDefault = 3, + ConstrIdentity = 4, + ConstrGenerated = 5, + ConstrCheck = 6, + ConstrPrimary = 7, + ConstrUnique = 8, + ConstrExclusion = 9, + ConstrForeign = 10, + ConstrAttrDeferrable = 11, + ConstrAttrNotDeferrable = 12, + ConstrAttrDeferred = 13, + ConstrAttrImmediate = 14, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum ImportForeignSchemaType { + Undefined = 0, + FdwImportSchemaAll = 1, + FdwImportSchemaLimitTo = 2, + FdwImportSchemaExcept = 3, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum RoleStmtType { + Undefined = 0, + RolestmtRole = 1, + RolestmtUser = 2, + RolestmtGroup = 3, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum FetchDirection { + Undefined = 0, + FetchForward = 1, + FetchBackward = 2, + FetchAbsolute = 3, + FetchRelative = 4, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum FunctionParameterMode { + Undefined = 0, + FuncParamIn = 1, + FuncParamOut = 2, + FuncParamInout = 3, + FuncParamVariadic = 4, + FuncParamTable = 5, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum TransactionStmtKind { + Undefined = 0, + TransStmtBegin = 1, + TransStmtStart = 2, + TransStmtCommit = 3, + TransStmtRollback = 4, + TransStmtSavepoint = 5, + TransStmtRelease = 6, + TransStmtRollbackTo = 7, + TransStmtPrepare = 8, + TransStmtCommitPrepared = 9, + TransStmtRollbackPrepared = 10, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum ViewCheckOption { + Undefined = 0, + NoCheckOption = 1, + LocalCheckOption = 2, + CascadedCheckOption = 3, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum ClusterOption { + Undefined = 0, + CluoptRecheck = 1, + CluoptVerbose = 2, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum DiscardMode { + Undefined = 0, + DiscardAll = 1, + DiscardPlans = 2, + DiscardSequences = 3, + DiscardTemp = 4, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum ReindexObjectType { + Undefined = 0, + ReindexObjectIndex = 1, + ReindexObjectTable = 2, + ReindexObjectSchema = 3, + ReindexObjectSystem = 4, + ReindexObjectDatabase = 5, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum AlterTsConfigType { + AlterTsconfigTypeUndefined = 0, + AlterTsconfigAddMapping = 1, + AlterTsconfigAlterMappingForToken = 2, + AlterTsconfigReplaceDict = 3, + AlterTsconfigReplaceDictForToken = 4, + AlterTsconfigDropMapping = 5, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum AlterSubscriptionType { + Undefined = 0, + AlterSubscriptionOptions = 1, + AlterSubscriptionConnection = 2, + AlterSubscriptionPublication = 3, + AlterSubscriptionRefresh = 4, + AlterSubscriptionEnabled = 5, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum OnCommitAction { + Undefined = 0, + OncommitNoop = 1, + OncommitPreserveRows = 2, + OncommitDeleteRows = 3, + OncommitDrop = 4, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum ParamKind { + Undefined = 0, + ParamExtern = 1, + ParamExec = 2, + ParamSublink = 3, + ParamMultiexpr = 4, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum CoercionContext { + Undefined = 0, + CoercionImplicit = 1, + CoercionAssignment = 2, + CoercionExplicit = 3, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum CoercionForm { + Undefined = 0, + CoerceExplicitCall = 1, + CoerceExplicitCast = 2, + CoerceImplicitCast = 3, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum BoolExprType { + Undefined = 0, + AndExpr = 1, + OrExpr = 2, + NotExpr = 3, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum SubLinkType { + Undefined = 0, + ExistsSublink = 1, + AllSublink = 2, + AnySublink = 3, + RowcompareSublink = 4, + ExprSublink = 5, + MultiexprSublink = 6, + ArraySublink = 7, + CteSublink = 8, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum RowCompareType { + Undefined = 0, + RowcompareLt = 1, + RowcompareLe = 2, + RowcompareEq = 3, + RowcompareGe = 4, + RowcompareGt = 5, + RowcompareNe = 6, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum MinMaxOp { + Undefined = 0, + IsGreatest = 1, + IsLeast = 2, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum SqlValueFunctionOp { + SqlvalueFunctionOpUndefined = 0, + SvfopCurrentDate = 1, + SvfopCurrentTime = 2, + SvfopCurrentTimeN = 3, + SvfopCurrentTimestamp = 4, + SvfopCurrentTimestampN = 5, + SvfopLocaltime = 6, + SvfopLocaltimeN = 7, + SvfopLocaltimestamp = 8, + SvfopLocaltimestampN = 9, + SvfopCurrentRole = 10, + SvfopCurrentUser = 11, + SvfopUser = 12, + SvfopSessionUser = 13, + SvfopCurrentCatalog = 14, + SvfopCurrentSchema = 15, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum XmlExprOp { + Undefined = 0, + IsXmlconcat = 1, + IsXmlelement = 2, + IsXmlforest = 3, + IsXmlparse = 4, + IsXmlpi = 5, + IsXmlroot = 6, + IsXmlserialize = 7, + IsDocument = 8, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum XmlOptionType { + Undefined = 0, + XmloptionDocument = 1, + XmloptionContent = 2, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum NullTestType { + Undefined = 0, + IsNull = 1, + IsNotNull = 2, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum BoolTestType { + Undefined = 0, + IsTrue = 1, + IsNotTrue = 2, + IsFalse = 3, + IsNotFalse = 4, + IsUnknown = 5, + IsNotUnknown = 6, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum CmdType { + Undefined = 0, + CmdUnknown = 1, + CmdSelect = 2, + CmdUpdate = 3, + CmdInsert = 4, + CmdDelete = 5, + CmdUtility = 6, + CmdNothing = 7, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum JoinType { + Undefined = 0, + JoinInner = 1, + JoinLeft = 2, + JoinFull = 3, + JoinRight = 4, + JoinSemi = 5, + JoinAnti = 6, + JoinUniqueOuter = 7, + JoinUniqueInner = 8, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum AggStrategy { + Undefined = 0, + AggPlain = 1, + AggSorted = 2, + AggHashed = 3, + AggMixed = 4, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum AggSplit { + Undefined = 0, + AggsplitSimple = 1, + AggsplitInitialSerial = 2, + AggsplitFinalDeserial = 3, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum SetOpCmd { + Undefined = 0, + SetopcmdIntersect = 1, + SetopcmdIntersectAll = 2, + SetopcmdExcept = 3, + SetopcmdExceptAll = 4, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum SetOpStrategy { + Undefined = 0, + SetopSorted = 1, + SetopHashed = 2, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum OnConflictAction { + Undefined = 0, + OnconflictNone = 1, + OnconflictNothing = 2, + OnconflictUpdate = 3, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum LimitOption { + Undefined = 0, + Default = 1, + Count = 2, + WithTies = 3, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum LockClauseStrength { + Undefined = 0, + LcsNone = 1, + LcsForkeyshare = 2, + LcsForshare = 3, + LcsFornokeyupdate = 4, + LcsForupdate = 5, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum LockWaitPolicy { + Undefined = 0, + LockWaitBlock = 1, + LockWaitSkip = 2, + LockWaitError = 3, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum LockTupleMode { + Undefined = 0, + LockTupleKeyShare = 1, + LockTupleShare = 2, + LockTupleNoKeyExclusive = 3, + LockTupleExclusive = 4, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum KeywordKind { + NoKeyword = 0, + UnreservedKeyword = 1, + ColNameKeyword = 2, + TypeFuncNameKeyword = 3, + ReservedKeyword = 4, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum Token { + Nul = 0, + /// Single-character tokens that are returned 1:1 (identical with "self" list in scan.l) + /// Either supporting syntax, or single-character operators (some can be both) + /// Also see + /// + /// "%" + Ascii37 = 37, + /// "(" + Ascii40 = 40, + /// ")" + Ascii41 = 41, + /// "*" + Ascii42 = 42, + /// "+" + Ascii43 = 43, + /// "," + Ascii44 = 44, + /// "-" + Ascii45 = 45, + /// "." + Ascii46 = 46, + /// "/" + Ascii47 = 47, + /// ":" + Ascii58 = 58, + /// ";" + Ascii59 = 59, + /// "<" + Ascii60 = 60, + /// "=" + Ascii61 = 61, + /// ">" + Ascii62 = 62, + /// "?" + Ascii63 = 63, + /// "[" + Ascii91 = 91, + /// "\" + Ascii92 = 92, + /// "]" + Ascii93 = 93, + /// "^" + Ascii94 = 94, + /// Named tokens in scan.l + Ident = 258, + Uident = 259, + Fconst = 260, + Sconst = 261, + Usconst = 262, + Bconst = 263, + Xconst = 264, + Op = 265, + Iconst = 266, + Param = 267, + Typecast = 268, + DotDot = 269, + ColonEquals = 270, + EqualsGreater = 271, + LessEquals = 272, + GreaterEquals = 273, + NotEquals = 274, + SqlComment = 275, + CComment = 276, + AbortP = 277, + AbsoluteP = 278, + Access = 279, + Action = 280, + AddP = 281, + Admin = 282, + After = 283, + Aggregate = 284, + All = 285, + Also = 286, + Alter = 287, + Always = 288, + Analyse = 289, + Analyze = 290, + And = 291, + Any = 292, + Array = 293, + As = 294, + Asc = 295, + Assertion = 296, + Assignment = 297, + Asymmetric = 298, + At = 299, + Attach = 300, + Attribute = 301, + Authorization = 302, + Backward = 303, + Before = 304, + BeginP = 305, + Between = 306, + Bigint = 307, + Binary = 308, + Bit = 309, + BooleanP = 310, + Both = 311, + By = 312, + Cache = 313, + Call = 314, + Called = 315, + Cascade = 316, + Cascaded = 317, + Case = 318, + Cast = 319, + CatalogP = 320, + Chain = 321, + CharP = 322, + Character = 323, + Characteristics = 324, + Check = 325, + Checkpoint = 326, + Class = 327, + Close = 328, + Cluster = 329, + Coalesce = 330, + Collate = 331, + Collation = 332, + Column = 333, + Columns = 334, + Comment = 335, + Comments = 336, + Commit = 337, + Committed = 338, + Concurrently = 339, + Configuration = 340, + Conflict = 341, + Connection = 342, + Constraint = 343, + Constraints = 344, + ContentP = 345, + ContinueP = 346, + ConversionP = 347, + Copy = 348, + Cost = 349, + Create = 350, + Cross = 351, + Csv = 352, + Cube = 353, + CurrentP = 354, + CurrentCatalog = 355, + CurrentDate = 356, + CurrentRole = 357, + CurrentSchema = 358, + CurrentTime = 359, + CurrentTimestamp = 360, + CurrentUser = 361, + Cursor = 362, + Cycle = 363, + DataP = 364, + Database = 365, + DayP = 366, + Deallocate = 367, + Dec = 368, + DecimalP = 369, + Declare = 370, + Default = 371, + Defaults = 372, + Deferrable = 373, + Deferred = 374, + Definer = 375, + DeleteP = 376, + Delimiter = 377, + Delimiters = 378, + Depends = 379, + Desc = 380, + Detach = 381, + Dictionary = 382, + DisableP = 383, + Discard = 384, + Distinct = 385, + Do = 386, + DocumentP = 387, + DomainP = 388, + DoubleP = 389, + Drop = 390, + Each = 391, + Else = 392, + EnableP = 393, + Encoding = 394, + Encrypted = 395, + EndP = 396, + EnumP = 397, + Escape = 398, + Event = 399, + Except = 400, + Exclude = 401, + Excluding = 402, + Exclusive = 403, + Execute = 404, + Exists = 405, + Explain = 406, + Expression = 407, + Extension = 408, + External = 409, + Extract = 410, + FalseP = 411, + Family = 412, + Fetch = 413, + Filter = 414, + FirstP = 415, + FloatP = 416, + Following = 417, + For = 418, + Force = 419, + Foreign = 420, + Forward = 421, + Freeze = 422, + From = 423, + Full = 424, + Function = 425, + Functions = 426, + Generated = 427, + Global = 428, + Grant = 429, + Granted = 430, + Greatest = 431, + GroupP = 432, + Grouping = 433, + Groups = 434, + Handler = 435, + Having = 436, + HeaderP = 437, + Hold = 438, + HourP = 439, + IdentityP = 440, + IfP = 441, + Ilike = 442, + Immediate = 443, + Immutable = 444, + ImplicitP = 445, + ImportP = 446, + InP = 447, + Include = 448, + Including = 449, + Increment = 450, + Index = 451, + Indexes = 452, + Inherit = 453, + Inherits = 454, + Initially = 455, + InlineP = 456, + InnerP = 457, + Inout = 458, + InputP = 459, + Insensitive = 460, + Insert = 461, + Instead = 462, + IntP = 463, + Integer = 464, + Intersect = 465, + Interval = 466, + Into = 467, + Invoker = 468, + Is = 469, + Isnull = 470, + Isolation = 471, + Join = 472, + Key = 473, + Label = 474, + Language = 475, + LargeP = 476, + LastP = 477, + LateralP = 478, + Leading = 479, + Leakproof = 480, + Least = 481, + Left = 482, + Level = 483, + Like = 484, + Limit = 485, + Listen = 486, + Load = 487, + Local = 488, + Localtime = 489, + Localtimestamp = 490, + Location = 491, + LockP = 492, + Locked = 493, + Logged = 494, + Mapping = 495, + Match = 496, + Materialized = 497, + Maxvalue = 498, + Method = 499, + MinuteP = 500, + Minvalue = 501, + Mode = 502, + MonthP = 503, + Move = 504, + NameP = 505, + Names = 506, + National = 507, + Natural = 508, + Nchar = 509, + New = 510, + Next = 511, + Nfc = 512, + Nfd = 513, + Nfkc = 514, + Nfkd = 515, + No = 516, + None = 517, + Normalize = 518, + Normalized = 519, + Not = 520, + Nothing = 521, + Notify = 522, + Notnull = 523, + Nowait = 524, + NullP = 525, + Nullif = 526, + NullsP = 527, + Numeric = 528, + ObjectP = 529, + Of = 530, + Off = 531, + Offset = 532, + Oids = 533, + Old = 534, + On = 535, + Only = 536, + Operator = 537, + Option = 538, + Options = 539, + Or = 540, + Order = 541, + Ordinality = 542, + Others = 543, + OutP = 544, + OuterP = 545, + Over = 546, + Overlaps = 547, + Overlay = 548, + Overriding = 549, + Owned = 550, + Owner = 551, + Parallel = 552, + Parser = 553, + Partial = 554, + Partition = 555, + Passing = 556, + Password = 557, + Placing = 558, + Plans = 559, + Policy = 560, + Position = 561, + Preceding = 562, + Precision = 563, + Preserve = 564, + Prepare = 565, + Prepared = 566, + Primary = 567, + Prior = 568, + Privileges = 569, + Procedural = 570, + Procedure = 571, + Procedures = 572, + Program = 573, + Publication = 574, + Quote = 575, + Range = 576, + Read = 577, + Real = 578, + Reassign = 579, + Recheck = 580, + Recursive = 581, + Ref = 582, + References = 583, + Referencing = 584, + Refresh = 585, + Reindex = 586, + RelativeP = 587, + Release = 588, + Rename = 589, + Repeatable = 590, + Replace = 591, + Replica = 592, + Reset = 593, + Restart = 594, + Restrict = 595, + Returning = 596, + Returns = 597, + Revoke = 598, + Right = 599, + Role = 600, + Rollback = 601, + Rollup = 602, + Routine = 603, + Routines = 604, + Row = 605, + Rows = 606, + Rule = 607, + Savepoint = 608, + Schema = 609, + Schemas = 610, + Scroll = 611, + Search = 612, + SecondP = 613, + Security = 614, + Select = 615, + Sequence = 616, + Sequences = 617, + Serializable = 618, + Server = 619, + Session = 620, + SessionUser = 621, + Set = 622, + Sets = 623, + Setof = 624, + Share = 625, + Show = 626, + Similar = 627, + Simple = 628, + Skip = 629, + Smallint = 630, + Snapshot = 631, + Some = 632, + SqlP = 633, + Stable = 634, + StandaloneP = 635, + Start = 636, + Statement = 637, + Statistics = 638, + Stdin = 639, + Stdout = 640, + Storage = 641, + Stored = 642, + StrictP = 643, + StripP = 644, + Subscription = 645, + Substring = 646, + Support = 647, + Symmetric = 648, + Sysid = 649, + SystemP = 650, + Table = 651, + Tables = 652, + Tablesample = 653, + Tablespace = 654, + Temp = 655, + Template = 656, + Temporary = 657, + TextP = 658, + Then = 659, + Ties = 660, + Time = 661, + Timestamp = 662, + To = 663, + Trailing = 664, + Transaction = 665, + Transform = 666, + Treat = 667, + Trigger = 668, + Trim = 669, + TrueP = 670, + Truncate = 671, + Trusted = 672, + TypeP = 673, + TypesP = 674, + Uescape = 675, + Unbounded = 676, + Uncommitted = 677, + Unencrypted = 678, + Union = 679, + Unique = 680, + Unknown = 681, + Unlisten = 682, + Unlogged = 683, + Until = 684, + Update = 685, + User = 686, + Using = 687, + Vacuum = 688, + Valid = 689, + Validate = 690, + Validator = 691, + ValueP = 692, + Values = 693, + Varchar = 694, + Variadic = 695, + Varying = 696, + Verbose = 697, + VersionP = 698, + View = 699, + Views = 700, + Volatile = 701, + When = 702, + Where = 703, + WhitespaceP = 704, + Window = 705, + With = 706, + Within = 707, + Without = 708, + Work = 709, + Wrapper = 710, + Write = 711, + XmlP = 712, + Xmlattributes = 713, + Xmlconcat = 714, + Xmlelement = 715, + Xmlexists = 716, + Xmlforest = 717, + Xmlnamespaces = 718, + Xmlparse = 719, + Xmlpi = 720, + Xmlroot = 721, + Xmlserialize = 722, + Xmltable = 723, + YearP = 724, + YesP = 725, + Zone = 726, + NotLa = 727, + NullsLa = 728, + WithLa = 729, + Postfixop = 730, + Uminus = 731, +} diff --git a/tests/target/performance/issue-4476.rs b/tests/target/performance/issue-4476.rs new file mode 100644 index 000000000000..30567f2644b7 --- /dev/null +++ b/tests/target/performance/issue-4476.rs @@ -0,0 +1,705 @@ +use super::SemverParser; + +#[allow(dead_code, non_camel_case_types)] +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub enum Rule { + EOI, + range_set, + logical_or, + range, + empty, + hyphen, + simple, + primitive, + primitive_op, + partial, + xr, + xr_op, + nr, + tilde, + caret, + qualifier, + parts, + part, + space, +} +#[allow(clippy::all)] +impl ::pest::Parser for SemverParser { + fn parse<'i>( + rule: Rule, + input: &'i str, + ) -> ::std::result::Result<::pest::iterators::Pairs<'i, Rule>, ::pest::error::Error> { + mod rules { + pub mod hidden { + use super::super::Rule; + #[inline] + #[allow(dead_code, non_snake_case, unused_variables)] + pub fn skip( + state: Box<::pest::ParserState>, + ) -> ::pest::ParseResult>> { + Ok(state) + } + } + pub mod visible { + use super::super::Rule; + #[inline] + #[allow(non_snake_case, unused_variables)] + pub fn range_set( + state: Box<::pest::ParserState>, + ) -> ::pest::ParseResult>> { + state.rule(Rule::range_set, |state| { + state.sequence(|state| { + self::SOI(state) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| { + state.sequence(|state| { + state.optional(|state| { + self::space(state).and_then(|state| { + state.repeat(|state| { + state.sequence(|state| { + super::hidden::skip(state) + .and_then(|state| self::space(state)) + }) + }) + }) + }) + }) + }) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| self::range(state)) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| { + state.sequence(|state| { + state.optional(|state| { + state + .sequence(|state| { + self::logical_or(state) + .and_then(|state| { + super::hidden::skip(state) + }) + .and_then(|state| self::range(state)) + }) + .and_then(|state| { + state.repeat(|state| { + state.sequence(|state| { + super::hidden::skip(state).and_then( + |state| { + state.sequence(|state| { + self::logical_or(state) + .and_then(|state| { + super::hidden::skip( + state, + ) + }) + .and_then(|state| { + self::range(state) + }) + }) + }, + ) + }) + }) + }) + }) + }) + }) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| { + state.sequence(|state| { + state.optional(|state| { + self::space(state).and_then(|state| { + state.repeat(|state| { + state.sequence(|state| { + super::hidden::skip(state) + .and_then(|state| self::space(state)) + }) + }) + }) + }) + }) + }) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| self::EOI(state)) + }) + }) + } + #[inline] + #[allow(non_snake_case, unused_variables)] + pub fn logical_or( + state: Box<::pest::ParserState>, + ) -> ::pest::ParseResult>> { + state.rule(Rule::logical_or, |state| { + state.sequence(|state| { + state + .sequence(|state| { + state.optional(|state| { + self::space(state).and_then(|state| { + state.repeat(|state| { + state.sequence(|state| { + super::hidden::skip(state) + .and_then(|state| self::space(state)) + }) + }) + }) + }) + }) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| state.match_string("||")) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| { + state.sequence(|state| { + state.optional(|state| { + self::space(state).and_then(|state| { + state.repeat(|state| { + state.sequence(|state| { + super::hidden::skip(state) + .and_then(|state| self::space(state)) + }) + }) + }) + }) + }) + }) + }) + }) + } + #[inline] + #[allow(non_snake_case, unused_variables)] + pub fn range( + state: Box<::pest::ParserState>, + ) -> ::pest::ParseResult>> { + state.rule(Rule::range, |state| { + self::hyphen(state) + .or_else(|state| { + state.sequence(|state| { + self::simple(state) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| { + state.sequence(|state| { + state.optional(|state| { + state + .sequence(|state| { + state + .optional(|state| { + state.match_string(",") + }) + .and_then(|state| { + super::hidden::skip(state) + }) + .and_then(|state| { + state.sequence(|state| { + self::space(state) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| { + state.sequence(|state| { + state.optional(|state| { + self::space(state).and_then(|state| { + state.repeat(|state| { + state.sequence(|state| { + super::hidden::skip(state) + .and_then(|state| self::space(state)) + }) + }) + }) + }) + }) + }) + }) + }) + .and_then(|state| { + super::hidden::skip(state) + }) + .and_then(|state| { + self::simple(state) + }) + }) + .and_then(|state| { + state.repeat(|state| { + state.sequence(|state| { + super::hidden::skip(state) + .and_then(|state| { + state.sequence( + |state| { + state + .optional(|state| state.match_string(",")) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| { + state.sequence(|state| { + self::space(state) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| { + state.sequence(|state| { + state.optional(|state| { + self::space(state).and_then(|state| { + state.repeat(|state| { + state.sequence(|state| { + super::hidden::skip(state) + .and_then(|state| self::space(state)) + }) + }) + }) + }) + }) + }) + }) + }) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| self::simple(state)) + }, + ) + }) + }) + }) + }) + }) + }) + }) + }) + }) + .or_else(|state| self::empty(state)) + }) + } + #[inline] + #[allow(non_snake_case, unused_variables)] + pub fn empty( + state: Box<::pest::ParserState>, + ) -> ::pest::ParseResult>> { + state.rule(Rule::empty, |state| state.match_string("")) + } + #[inline] + #[allow(non_snake_case, unused_variables)] + pub fn hyphen( + state: Box<::pest::ParserState>, + ) -> ::pest::ParseResult>> { + state.rule(Rule::hyphen, |state| { + state.sequence(|state| { + self::partial(state) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| { + state.sequence(|state| { + self::space(state) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| { + state.sequence(|state| { + state.optional(|state| { + self::space(state).and_then(|state| { + state.repeat(|state| { + state.sequence(|state| { + super::hidden::skip(state) + .and_then(|state| { + self::space(state) + }) + }) + }) + }) + }) + }) + }) + }) + }) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| state.match_string("-")) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| { + state.sequence(|state| { + self::space(state) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| { + state.sequence(|state| { + state.optional(|state| { + self::space(state).and_then(|state| { + state.repeat(|state| { + state.sequence(|state| { + super::hidden::skip(state) + .and_then(|state| { + self::space(state) + }) + }) + }) + }) + }) + }) + }) + }) + }) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| self::partial(state)) + }) + }) + } + #[inline] + #[allow(non_snake_case, unused_variables)] + pub fn simple( + state: Box<::pest::ParserState>, + ) -> ::pest::ParseResult>> { + state.rule(Rule::simple, |state| { + self::primitive(state) + .or_else(|state| self::partial(state)) + .or_else(|state| self::tilde(state)) + .or_else(|state| self::caret(state)) + }) + } + #[inline] + #[allow(non_snake_case, unused_variables)] + pub fn primitive( + state: Box<::pest::ParserState>, + ) -> ::pest::ParseResult>> { + state.rule(Rule::primitive, |state| { + state.sequence(|state| { + self::primitive_op(state) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| { + state.sequence(|state| { + state.optional(|state| { + self::space(state).and_then(|state| { + state.repeat(|state| { + state.sequence(|state| { + super::hidden::skip(state) + .and_then(|state| self::space(state)) + }) + }) + }) + }) + }) + }) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| self::partial(state)) + }) + }) + } + #[inline] + #[allow(non_snake_case, unused_variables)] + pub fn primitive_op( + state: Box<::pest::ParserState>, + ) -> ::pest::ParseResult>> { + state.rule(Rule::primitive_op, |state| { + state + .match_string("<=") + .or_else(|state| state.match_string(">=")) + .or_else(|state| state.match_string(">")) + .or_else(|state| state.match_string("<")) + .or_else(|state| state.match_string("=")) + }) + } + #[inline] + #[allow(non_snake_case, unused_variables)] + pub fn partial( + state: Box<::pest::ParserState>, + ) -> ::pest::ParseResult>> { + state.rule(Rule::partial, |state| { + state.sequence(|state| { + self::xr(state) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| { + state.optional(|state| { + state.sequence(|state| { + state + .match_string(".") + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| self::xr(state)) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| { + state.optional(|state| { + state.sequence(|state| { + state + .match_string(".") + .and_then(|state| { + super::hidden::skip(state) + }) + .and_then(|state| self::xr(state)) + .and_then(|state| { + super::hidden::skip(state) + }) + .and_then(|state| { + state.optional(|state| { + self::qualifier(state) + }) + }) + }) + }) + }) + }) + }) + }) + }) + }) + } + #[inline] + #[allow(non_snake_case, unused_variables)] + pub fn xr( + state: Box<::pest::ParserState>, + ) -> ::pest::ParseResult>> { + state.rule(Rule::xr, |state| { + self::xr_op(state).or_else(|state| self::nr(state)) + }) + } + #[inline] + #[allow(non_snake_case, unused_variables)] + pub fn xr_op( + state: Box<::pest::ParserState>, + ) -> ::pest::ParseResult>> { + state.rule(Rule::xr_op, |state| { + state + .match_string("x") + .or_else(|state| state.match_string("X")) + .or_else(|state| state.match_string("*")) + }) + } + #[inline] + #[allow(non_snake_case, unused_variables)] + pub fn nr( + state: Box<::pest::ParserState>, + ) -> ::pest::ParseResult>> { + state.rule(Rule::nr, |state| { + state.match_string("0").or_else(|state| { + state.sequence(|state| { + state + .match_range('1'..'9') + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| { + state.sequence(|state| { + state.optional(|state| { + state.match_range('0'..'9').and_then(|state| { + state.repeat(|state| { + state.sequence(|state| { + super::hidden::skip(state).and_then( + |state| state.match_range('0'..'9'), + ) + }) + }) + }) + }) + }) + }) + }) + }) + }) + } + #[inline] + #[allow(non_snake_case, unused_variables)] + pub fn tilde( + state: Box<::pest::ParserState>, + ) -> ::pest::ParseResult>> { + state.rule(Rule::tilde, |state| { + state.sequence(|state| { + state + .match_string("~>") + .or_else(|state| state.match_string("~")) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| { + state.sequence(|state| { + state.optional(|state| { + self::space(state).and_then(|state| { + state.repeat(|state| { + state.sequence(|state| { + super::hidden::skip(state) + .and_then(|state| self::space(state)) + }) + }) + }) + }) + }) + }) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| self::partial(state)) + }) + }) + } + #[inline] + #[allow(non_snake_case, unused_variables)] + pub fn caret( + state: Box<::pest::ParserState>, + ) -> ::pest::ParseResult>> { + state.rule(Rule::caret, |state| { + state.sequence(|state| { + state + .match_string("^") + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| { + state.sequence(|state| { + state.optional(|state| { + self::space(state).and_then(|state| { + state.repeat(|state| { + state.sequence(|state| { + super::hidden::skip(state) + .and_then(|state| self::space(state)) + }) + }) + }) + }) + }) + }) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| self::partial(state)) + }) + }) + } + #[inline] + #[allow(non_snake_case, unused_variables)] + pub fn qualifier( + state: Box<::pest::ParserState>, + ) -> ::pest::ParseResult>> { + state.rule(Rule::qualifier, |state| { + state.sequence(|state| { + state + .match_string("-") + .or_else(|state| state.match_string("+")) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| self::parts(state)) + }) + }) + } + #[inline] + #[allow(non_snake_case, unused_variables)] + pub fn parts( + state: Box<::pest::ParserState>, + ) -> ::pest::ParseResult>> { + state.rule(Rule::parts, |state| { + state.sequence(|state| { + self::part(state) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| { + state.sequence(|state| { + state.optional(|state| { + state + .sequence(|state| { + state + .match_string(".") + .and_then(|state| { + super::hidden::skip(state) + }) + .and_then(|state| self::part(state)) + }) + .and_then(|state| { + state.repeat(|state| { + state.sequence(|state| { + super::hidden::skip(state).and_then( + |state| { + state.sequence(|state| { + state + .match_string(".") + .and_then(|state| { + super::hidden::skip( + state, + ) + }) + .and_then(|state| { + self::part(state) + }) + }) + }, + ) + }) + }) + }) + }) + }) + }) + }) + }) + } + #[inline] + #[allow(non_snake_case, unused_variables)] + pub fn part( + state: Box<::pest::ParserState>, + ) -> ::pest::ParseResult>> { + state.rule(Rule::part, |state| { + self::nr(state).or_else(|state| { + state.sequence(|state| { + state + .match_string("-") + .or_else(|state| state.match_range('0'..'9')) + .or_else(|state| state.match_range('A'..'Z')) + .or_else(|state| state.match_range('a'..'z')) + .and_then(|state| super::hidden::skip(state)) + .and_then(|state| { + state.sequence(|state| { + state.optional(|state| { + state + .match_string("-") + .or_else(|state| state.match_range('0'..'9')) + .or_else(|state| state.match_range('A'..'Z')) + .or_else(|state| state.match_range('a'..'z')) + .and_then(|state| { + state.repeat(|state| { + state.sequence(|state| { + super::hidden::skip(state).and_then( + |state| { + state + .match_string("-") + .or_else(|state| { + state.match_range( + '0'..'9', + ) + }) + .or_else(|state| { + state.match_range( + 'A'..'Z', + ) + }) + .or_else(|state| { + state.match_range( + 'a'..'z', + ) + }) + }, + ) + }) + }) + }) + }) + }) + }) + }) + }) + }) + } + #[inline] + #[allow(non_snake_case, unused_variables)] + pub fn space( + state: Box<::pest::ParserState>, + ) -> ::pest::ParseResult>> { + state + .match_string(" ") + .or_else(|state| state.match_string("\t")) + } + #[inline] + #[allow(dead_code, non_snake_case, unused_variables)] + pub fn EOI( + state: Box<::pest::ParserState>, + ) -> ::pest::ParseResult>> { + state.rule(Rule::EOI, |state| state.end_of_input()) + } + #[inline] + #[allow(dead_code, non_snake_case, unused_variables)] + pub fn SOI( + state: Box<::pest::ParserState>, + ) -> ::pest::ParseResult>> { + state.start_of_input() + } + } + pub use self::visible::*; + } + ::pest::state(input, |state| match rule { + Rule::range_set => rules::range_set(state), + Rule::logical_or => rules::logical_or(state), + Rule::range => rules::range(state), + Rule::empty => rules::empty(state), + Rule::hyphen => rules::hyphen(state), + Rule::simple => rules::simple(state), + Rule::primitive => rules::primitive(state), + Rule::primitive_op => rules::primitive_op(state), + Rule::partial => rules::partial(state), + Rule::xr => rules::xr(state), + Rule::xr_op => rules::xr_op(state), + Rule::nr => rules::nr(state), + Rule::tilde => rules::tilde(state), + Rule::caret => rules::caret(state), + Rule::qualifier => rules::qualifier(state), + Rule::parts => rules::parts(state), + Rule::part => rules::part(state), + Rule::space => rules::space(state), + Rule::EOI => rules::EOI(state), + }) + } +} diff --git a/tests/target/performance/issue-4867.rs b/tests/target/performance/issue-4867.rs new file mode 100644 index 000000000000..336dae1b64ab --- /dev/null +++ b/tests/target/performance/issue-4867.rs @@ -0,0 +1,13 @@ +mod modA { + mod modB { + mod modC { + mod modD { + mod modE { + fn func() { + state . rule (Rule :: myrule , | state | { state . sequence (| state | { state . sequence (| state | { state . match_string ("abc") . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { state . match_string ("def") }) }) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { state . sequence (| state | { state . optional (| state | { state . sequence (| state | { state . match_string ("abc") . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { state . match_string ("def") }) }) . and_then (| state | { state . repeat (| state | { state . sequence (| state | { super :: hidden :: skip (state) . and_then (| state | { state . sequence (| state | { state . match_string ("abc") . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { state . match_string ("def") }) }) }) }) }) }) }) }) }) }) }); + } + } + } + } + } +} diff --git a/tests/target/performance/issue-5128.rs b/tests/target/performance/issue-5128.rs new file mode 100644 index 000000000000..ba9ebfc6243f --- /dev/null +++ b/tests/target/performance/issue-5128.rs @@ -0,0 +1,4898 @@ +fn takes_a_long_time_to_rustfmt() { + let inner_cte = vec![Node { + node: Some(node::Node::CommonTableExpr(Box::new(CommonTableExpr { + ctename: String::from("ranked_by_age_within_key"), + aliascolnames: vec![], + ctematerialized: CteMaterialize::Default as i32, + ctequery: Some(Box::new(Node { + node: Some(node::Node::SelectStmt(Box::new(SelectStmt { + distinct_clause: vec![], + into_clause: None, + target_list: vec![ + Node { + node: Some(node::Node::ResTarget(Box::new(ResTarget { + name: String::from(""), + indirection: vec![], + val: Some(Box::new(Node { + node: Some(node::Node::ColumnRef(ColumnRef { + fields: vec![Node { + node: Some(node::Node::AStar(AStar {})), + }], + location: 80, + })), + })), + location: 80, + }))), + }, + Node { + node: Some(node::Node::ResTarget(Box::new(ResTarget { + name: String::from("rank_in_key"), + indirection: vec![], + val: Some(Box::new(Node { + node: Some(node::Node::FuncCall(Box::new(FuncCall { + funcname: vec![Node { + node: Some(node::Node::String(String2 { + str: String::from("row_number"), + })), + }], + args: vec![], + agg_order: vec![], + agg_filter: None, + agg_within_group: false, + agg_star: false, + agg_distinct: false, + func_variadic: false, + over: Some(Box::new(WindowDef { + name: String::from(""), + refname: String::from(""), + partition_clause: vec![Node { + node: Some(node::Node::ColumnRef(ColumnRef { + fields: vec![Node { + node: Some(node::Node::String(String2 { + str: String::from("synthetic_key"), + })), + }], + location: 123, + })), + }], + order_clause: vec![Node { + node: Some(node::Node::SortBy(Box::new(SortBy { + node: Some(Box::new(Node { + node: Some(node::Node::ColumnRef( + ColumnRef { + fields: vec![Node { + node: Some(node::Node::String( + String2 { + str: String::from( + "logical_timestamp", + ), + }, + )), + }], + location: 156, + }, + )), + })), + sortby_dir: SortByDir::SortbyDesc as i32, + sortby_nulls: SortByNulls::SortbyNullsDefault + as i32, + use_op: vec![], + location: -1, + }))), + }], + frame_options: 1058, + start_offset: None, + end_offset: None, + location: 109, + })), + location: 91, + }))), + })), + location: 91, + }))), + }, + ], + from_clause: vec![Node { + node: Some(node::Node::RangeVar(RangeVar { + catalogname: String::from(""), + schemaname: String::from("_supertables"), + relname: String::from("9999-9999-9999"), + inh: true, + relpersistence: String::from("p"), + alias: None, + location: 206, + })), + }], + where_clause: Some(Box::new(Node { + node: Some(node::Node::AExpr(Box::new(AExpr { + kind: AExprKind::AexprOp as i32, + name: vec![Node { + node: Some(node::Node::String(String2 { + str: String::from("<="), + })), + }], + lexpr: Some(Box::new(Node { + node: Some(node::Node::ColumnRef(ColumnRef { + fields: vec![Node { + node: Some(node::Node::String(String2 { + str: String::from("logical_timestamp"), + })), + }], + location: 250, + })), + })), + rexpr: Some(Box::new(Node { + node: Some(node::Node::AConst(Box::new(AConst { + val: Some(Box::new(Node { + node: Some(node::Node::Integer(Integer { ival: 9000 })), + })), + location: 271, + }))), + })), + location: 268, + }))), + })), + group_clause: vec![], + having_clause: None, + window_clause: vec![], + values_lists: vec![], + sort_clause: vec![], + limit_offset: None, + limit_count: None, + limit_option: LimitOption::Default as i32, + locking_clause: vec![], + with_clause: None, + op: SetOperation::SetopNone as i32, + all: false, + larg: None, + rarg: None, + }))), + })), + location: 29, + cterecursive: false, + cterefcount: 0, + ctecolnames: vec![], + ctecoltypes: vec![], + ctecoltypmods: vec![], + ctecolcollations: vec![], + }))), + }]; + let outer_cte = vec![Node { + node: Some(node::Node::CommonTableExpr(Box::new(CommonTableExpr { + ctename: String::from("table_name"), + aliascolnames: vec![], + ctematerialized: CteMaterialize::Default as i32, + ctequery: Some(Box::new(Node { + node: Some(node::Node::SelectStmt(Box::new(SelectStmt { + distinct_clause: vec![], + into_clause: None, + target_list: vec![ + Node { + node: Some(node::Node::ResTarget(Box::new(ResTarget { + name: String::from("column1"), + indirection: vec![], + val: Some(Box::new(Node { + node: Some(node::Node::ColumnRef(ColumnRef { + fields: vec![Node { + node: Some(node::Node::String(String2 { + str: String::from("c1"), + })), + }], + location: 301, + })), + })), + location: 301, + }))), + }, + Node { + node: Some(node::Node::ResTarget(Box::new(ResTarget { + name: String::from("column2"), + indirection: vec![], + val: Some(Box::new(Node { + node: Some(node::Node::ColumnRef(ColumnRef { + fields: vec![Node { + node: Some(node::Node::String(String2 { + str: String::from("c2"), + })), + }], + location: 324, + })), + })), + location: 324, + }))), + }, + ], + from_clause: vec![Node { + node: Some(node::Node::RangeVar(RangeVar { + catalogname: String::from(""), + schemaname: String::from(""), + relname: String::from("ranked_by_age_within_key"), + inh: true, + relpersistence: String::from("p"), + alias: None, + location: 347, + })), + }], + where_clause: Some(Box::new(Node { + node: Some(node::Node::BoolExpr(Box::new(BoolExpr { + xpr: None, + boolop: BoolExprType::AndExpr as i32, + args: vec![ + Node { + node: Some(node::Node::AExpr(Box::new(AExpr { + kind: AExprKind::AexprOp as i32, + name: vec![Node { + node: Some(node::Node::String(String2 { + str: String::from("="), + })), + }], + lexpr: Some(Box::new(Node { + node: Some(node::Node::ColumnRef(ColumnRef { + fields: vec![Node { + node: Some(node::Node::String(String2 { + str: String::from("rank_in_key"), + })), + }], + location: 382, + })), + })), + rexpr: Some(Box::new(Node { + node: Some(node::Node::AConst(Box::new(AConst { + val: Some(Box::new(Node { + node: Some(node::Node::Integer(Integer { + ival: 1, + })), + })), + location: 396, + }))), + })), + location: 394, + }))), + }, + Node { + node: Some(node::Node::AExpr(Box::new(AExpr { + kind: AExprKind::AexprOp as i32, + name: vec![Node { + node: Some(node::Node::String(String2 { + str: String::from("="), + })), + }], + lexpr: Some(Box::new(Node { + node: Some(node::Node::ColumnRef(ColumnRef { + fields: vec![Node { + node: Some(node::Node::String(String2 { + str: String::from("is_deleted"), + })), + }], + location: 402, + })), + })), + rexpr: Some(Box::new(Node { + node: Some(node::Node::TypeCast(Box::new(TypeCast { + arg: Some(Box::new(Node { + node: Some(node::Node::AConst(Box::new( + AConst { + val: Some(Box::new(Node { + node: Some(node::Node::String( + String2 { + str: String::from("f"), + }, + )), + })), + location: 415, + }, + ))), + })), + type_name: Some(TypeName { + names: vec![ + Node { + node: Some(node::Node::String( + String2 { + str: String::from("pg_catalog"), + }, + )), + }, + Node { + node: Some(node::Node::String( + String2 { + str: String::from("bool"), + }, + )), + }, + ], + type_oid: 0, + setof: false, + pct_type: false, + typmods: vec![], + typemod: -1, + array_bounds: vec![], + location: -1, + }), + location: -1, + }))), + })), + location: 413, + }))), + }, + ], + location: 398, + }))), + })), + group_clause: vec![], + having_clause: None, + window_clause: vec![], + values_lists: vec![], + sort_clause: vec![], + limit_offset: None, + limit_count: None, + limit_option: LimitOption::Default as i32, + locking_clause: vec![], + with_clause: Some(WithClause { + ctes: inner_cte, + recursive: false, + location: 24, + }), + op: SetOperation::SetopNone as i32, + all: false, + larg: None, + rarg: None, + }))), + })), + location: 5, + cterecursive: false, + cterefcount: 0, + ctecolnames: vec![], + ctecoltypes: vec![], + ctecoltypmods: vec![], + ctecolcollations: vec![], + }))), + }]; + let expected_result = ParseResult { + version: 130003, + stmts: vec![RawStmt { + stmt: Some(Box::new(Node { + node: Some(node::Node::SelectStmt(Box::new(SelectStmt { + distinct_clause: vec![], + into_clause: None, + + target_list: vec![Node { + node: Some(node::Node::ResTarget(Box::new(ResTarget { + name: String::from(""), + indirection: vec![], + val: Some(Box::new(Node { + node: Some(node::Node::ColumnRef(ColumnRef { + fields: vec![Node { + node: Some(node::Node::String(String2 { + str: String::from("column1"), + })), + }], + location: 430, + })), + })), + location: 430, + }))), + }], + from_clause: vec![Node { + node: Some(node::Node::RangeVar(RangeVar { + catalogname: String::from(""), + schemaname: String::from(""), + relname: String::from("table_name"), + inh: true, + relpersistence: String::from("p"), + alias: None, + location: 443, + })), + }], + where_clause: Some(Box::new(Node { + node: Some(node::Node::AExpr(Box::new(AExpr { + kind: AExprKind::AexprOp as i32, + name: vec![Node { + node: Some(node::Node::String(String2 { + str: String::from(">"), + })), + }], + lexpr: Some(Box::new(Node { + node: Some(node::Node::ColumnRef(ColumnRef { + fields: vec![Node { + node: Some(node::Node::String(String2 { + str: String::from("column2"), + })), + }], + location: 460, + })), + })), + rexpr: Some(Box::new(Node { + node: Some(node::Node::AConst(Box::new(AConst { + val: Some(Box::new(Node { + node: Some(node::Node::Integer(Integer { ival: 9000 })), + })), + location: 470, + }))), + })), + location: 468, + }))), + })), + group_clause: vec![], + having_clause: None, + window_clause: vec![], + values_lists: vec![], + sort_clause: vec![], + limit_offset: None, + limit_count: None, + limit_option: LimitOption::Default as i32, + locking_clause: vec![], + with_clause: Some(WithClause { + ctes: outer_cte, + recursive: false, + location: 0, + }), + op: SetOperation::SetopNone as i32, + all: false, + larg: None, + rarg: None, + }))), + })), + stmt_location: 0, + stmt_len: 0, + }], + }; +} +#[derive(Clone, PartialEq)] +pub struct ParseResult { + pub version: i32, + + pub stmts: Vec, +} +#[derive(Clone, PartialEq)] +pub struct ScanResult { + pub version: i32, + + pub tokens: Vec, +} +#[derive(Clone, PartialEq)] +pub struct Node { + pub node: ::core::option::Option, +} +/// Nested message and enum types in `Node`. +pub mod node { + #[derive(Clone, PartialEq)] + pub enum Node { + Alias(super::Alias), + + RangeVar(super::RangeVar), + + TableFunc(Box), + + Expr(super::Expr), + + Var(Box), + + Param(Box), + + Aggref(Box), + + GroupingFunc(Box), + + WindowFunc(Box), + + SubscriptingRef(Box), + + FuncExpr(Box), + + NamedArgExpr(Box), + + OpExpr(Box), + + DistinctExpr(Box), + + NullIfExpr(Box), + + ScalarArrayOpExpr(Box), + + BoolExpr(Box), + + SubLink(Box), + + SubPlan(Box), + + AlternativeSubPlan(Box), + + FieldSelect(Box), + + FieldStore(Box), + + RelabelType(Box), + + CoerceViaIo(Box), + + ArrayCoerceExpr(Box), + + ConvertRowtypeExpr(Box), + + CollateExpr(Box), + + CaseExpr(Box), + + CaseWhen(Box), + + CaseTestExpr(Box), + + ArrayExpr(Box), + + RowExpr(Box), + + RowCompareExpr(Box), + + CoalesceExpr(Box), + + MinMaxExpr(Box), + + SqlvalueFunction(Box), + + XmlExpr(Box), + + NullTest(Box), + + BooleanTest(Box), + + CoerceToDomain(Box), + + CoerceToDomainValue(Box), + + SetToDefault(Box), + + CurrentOfExpr(Box), + + NextValueExpr(Box), + + InferenceElem(Box), + + TargetEntry(Box), + + RangeTblRef(super::RangeTblRef), + + JoinExpr(Box), + + FromExpr(Box), + + OnConflictExpr(Box), + + IntoClause(Box), + + RawStmt(Box), + + Query(Box), + + InsertStmt(Box), + + DeleteStmt(Box), + + UpdateStmt(Box), + + SelectStmt(Box), + + AlterTableStmt(super::AlterTableStmt), + + AlterTableCmd(Box), + + AlterDomainStmt(Box), + + SetOperationStmt(Box), + + GrantStmt(super::GrantStmt), + + GrantRoleStmt(super::GrantRoleStmt), + + AlterDefaultPrivilegesStmt(super::AlterDefaultPrivilegesStmt), + + ClosePortalStmt(super::ClosePortalStmt), + + ClusterStmt(super::ClusterStmt), + + CopyStmt(Box), + + CreateStmt(super::CreateStmt), + + DefineStmt(super::DefineStmt), + + DropStmt(super::DropStmt), + + TruncateStmt(super::TruncateStmt), + + CommentStmt(Box), + + FetchStmt(super::FetchStmt), + + IndexStmt(Box), + + CreateFunctionStmt(super::CreateFunctionStmt), + + AlterFunctionStmt(super::AlterFunctionStmt), + + DoStmt(super::DoStmt), + + RenameStmt(Box), + + RuleStmt(Box), + + NotifyStmt(super::NotifyStmt), + + ListenStmt(super::ListenStmt), + + UnlistenStmt(super::UnlistenStmt), + + TransactionStmt(super::TransactionStmt), + + ViewStmt(Box), + + LoadStmt(super::LoadStmt), + + CreateDomainStmt(Box), + + CreatedbStmt(super::CreatedbStmt), + + DropdbStmt(super::DropdbStmt), + + VacuumStmt(super::VacuumStmt), + + ExplainStmt(Box), + + CreateTableAsStmt(Box), + + CreateSeqStmt(super::CreateSeqStmt), + + AlterSeqStmt(super::AlterSeqStmt), + + VariableSetStmt(super::VariableSetStmt), + + VariableShowStmt(super::VariableShowStmt), + + DiscardStmt(super::DiscardStmt), + + CreateTrigStmt(Box), + + CreatePlangStmt(super::CreatePLangStmt), + + CreateRoleStmt(super::CreateRoleStmt), + + AlterRoleStmt(super::AlterRoleStmt), + + DropRoleStmt(super::DropRoleStmt), + + LockStmt(super::LockStmt), + + ConstraintsSetStmt(super::ConstraintsSetStmt), + + ReindexStmt(super::ReindexStmt), + + CheckPointStmt(super::CheckPointStmt), + + CreateSchemaStmt(super::CreateSchemaStmt), + + AlterDatabaseStmt(super::AlterDatabaseStmt), + + AlterDatabaseSetStmt(super::AlterDatabaseSetStmt), + + AlterRoleSetStmt(super::AlterRoleSetStmt), + + CreateConversionStmt(super::CreateConversionStmt), + + CreateCastStmt(super::CreateCastStmt), + + CreateOpClassStmt(super::CreateOpClassStmt), + + CreateOpFamilyStmt(super::CreateOpFamilyStmt), + + AlterOpFamilyStmt(super::AlterOpFamilyStmt), + + PrepareStmt(Box), + + ExecuteStmt(super::ExecuteStmt), + + DeallocateStmt(super::DeallocateStmt), + + DeclareCursorStmt(Box), + + CreateTableSpaceStmt(super::CreateTableSpaceStmt), + + DropTableSpaceStmt(super::DropTableSpaceStmt), + + AlterObjectDependsStmt(Box), + + AlterObjectSchemaStmt(Box), + + AlterOwnerStmt(Box), + + AlterOperatorStmt(super::AlterOperatorStmt), + + AlterTypeStmt(super::AlterTypeStmt), + + DropOwnedStmt(super::DropOwnedStmt), + + ReassignOwnedStmt(super::ReassignOwnedStmt), + + CompositeTypeStmt(super::CompositeTypeStmt), + + CreateEnumStmt(super::CreateEnumStmt), + + CreateRangeStmt(super::CreateRangeStmt), + + AlterEnumStmt(super::AlterEnumStmt), + + AlterTsdictionaryStmt(super::AlterTsDictionaryStmt), + + AlterTsconfigurationStmt(super::AlterTsConfigurationStmt), + + CreateFdwStmt(super::CreateFdwStmt), + + AlterFdwStmt(super::AlterFdwStmt), + + CreateForeignServerStmt(super::CreateForeignServerStmt), + + AlterForeignServerStmt(super::AlterForeignServerStmt), + + CreateUserMappingStmt(super::CreateUserMappingStmt), + + AlterUserMappingStmt(super::AlterUserMappingStmt), + + DropUserMappingStmt(super::DropUserMappingStmt), + + AlterTableSpaceOptionsStmt(super::AlterTableSpaceOptionsStmt), + + AlterTableMoveAllStmt(super::AlterTableMoveAllStmt), + + SecLabelStmt(Box), + + CreateForeignTableStmt(super::CreateForeignTableStmt), + + ImportForeignSchemaStmt(super::ImportForeignSchemaStmt), + + CreateExtensionStmt(super::CreateExtensionStmt), + + AlterExtensionStmt(super::AlterExtensionStmt), + + AlterExtensionContentsStmt(Box), + + CreateEventTrigStmt(super::CreateEventTrigStmt), + + AlterEventTrigStmt(super::AlterEventTrigStmt), + + RefreshMatViewStmt(super::RefreshMatViewStmt), + + ReplicaIdentityStmt(super::ReplicaIdentityStmt), + + AlterSystemStmt(super::AlterSystemStmt), + + CreatePolicyStmt(Box), + + AlterPolicyStmt(Box), + + CreateTransformStmt(super::CreateTransformStmt), + + CreateAmStmt(super::CreateAmStmt), + + CreatePublicationStmt(super::CreatePublicationStmt), + + AlterPublicationStmt(super::AlterPublicationStmt), + + CreateSubscriptionStmt(super::CreateSubscriptionStmt), + + AlterSubscriptionStmt(super::AlterSubscriptionStmt), + + DropSubscriptionStmt(super::DropSubscriptionStmt), + + CreateStatsStmt(super::CreateStatsStmt), + + AlterCollationStmt(super::AlterCollationStmt), + + CallStmt(Box), + + AlterStatsStmt(super::AlterStatsStmt), + + AExpr(Box), + + ColumnRef(super::ColumnRef), + + ParamRef(super::ParamRef), + + AConst(Box), + + FuncCall(Box), + + AStar(super::AStar), + + AIndices(Box), + + AIndirection(Box), + + AArrayExpr(super::AArrayExpr), + + ResTarget(Box), + + MultiAssignRef(Box), + + TypeCast(Box), + + CollateClause(Box), + + SortBy(Box), + + WindowDef(Box), + + RangeSubselect(Box), + + RangeFunction(super::RangeFunction), + + RangeTableSample(Box), + + RangeTableFunc(Box), + + RangeTableFuncCol(Box), + + TypeName(super::TypeName), + + ColumnDef(Box), + + IndexElem(Box), + + Constraint(Box), + + DefElem(Box), + + RangeTblEntry(Box), + + RangeTblFunction(Box), + + TableSampleClause(Box), + + WithCheckOption(Box), + + SortGroupClause(super::SortGroupClause), + + GroupingSet(super::GroupingSet), + + WindowClause(Box), + + ObjectWithArgs(super::ObjectWithArgs), + + AccessPriv(super::AccessPriv), + + CreateOpClassItem(super::CreateOpClassItem), + + TableLikeClause(super::TableLikeClause), + + FunctionParameter(Box), + + LockingClause(super::LockingClause), + + RowMarkClause(super::RowMarkClause), + + XmlSerialize(Box), + + WithClause(super::WithClause), + + InferClause(Box), + + OnConflictClause(Box), + + CommonTableExpr(Box), + + RoleSpec(super::RoleSpec), + + TriggerTransition(super::TriggerTransition), + + PartitionElem(Box), + + PartitionSpec(super::PartitionSpec), + + PartitionBoundSpec(super::PartitionBoundSpec), + + PartitionRangeDatum(Box), + + PartitionCmd(super::PartitionCmd), + + VacuumRelation(super::VacuumRelation), + + InlineCodeBlock(super::InlineCodeBlock), + + CallContext(super::CallContext), + + Integer(super::Integer), + + Float(super::Float), + + String(super::String2), + + BitString(super::BitString), + + Null(super::Null), + + List(super::List), + + IntList(super::IntList), + + OidList(super::OidList), + } +} +#[derive(Clone, PartialEq)] +pub struct Integer { + /// machine integer + pub ival: i32, +} +#[derive(Clone, PartialEq)] +pub struct Float { + /// string + pub str: String, +} +#[derive(Clone, PartialEq)] +pub struct String2 { + /// string + pub str: String, +} +#[derive(Clone, PartialEq)] +pub struct BitString { + /// string + pub str: String, +} +/// intentionally empty +#[derive(Clone, PartialEq)] +pub struct Null {} +#[derive(Clone, PartialEq)] +pub struct List { + pub items: Vec, +} +#[derive(Clone, PartialEq)] +pub struct OidList { + pub items: Vec, +} +#[derive(Clone, PartialEq)] +pub struct IntList { + pub items: Vec, +} +#[derive(Clone, PartialEq)] +pub struct Alias { + pub aliasname: String, + + pub colnames: Vec, +} +#[derive(Clone, PartialEq)] +pub struct RangeVar { + pub catalogname: String, + + pub schemaname: String, + + pub relname: String, + + pub inh: bool, + + pub relpersistence: String, + + pub alias: ::core::option::Option, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct TableFunc { + pub ns_uris: Vec, + + pub ns_names: Vec, + + pub docexpr: ::core::option::Option>, + + pub rowexpr: ::core::option::Option>, + + pub colnames: Vec, + + pub coltypes: Vec, + + pub coltypmods: Vec, + + pub colcollations: Vec, + + pub colexprs: Vec, + + pub coldefexprs: Vec, + + pub notnulls: Vec, + + pub ordinalitycol: i32, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct Expr {} +#[derive(Clone, PartialEq)] +pub struct Var { + pub xpr: ::core::option::Option>, + + pub varno: u32, + + pub varattno: i32, + + pub vartype: u32, + + pub vartypmod: i32, + + pub varcollid: u32, + + pub varlevelsup: u32, + + pub varnosyn: u32, + + pub varattnosyn: i32, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct Param { + pub xpr: ::core::option::Option>, + + pub paramkind: i32, + + pub paramid: i32, + + pub paramtype: u32, + + pub paramtypmod: i32, + + pub paramcollid: u32, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct Aggref { + pub xpr: ::core::option::Option>, + + pub aggfnoid: u32, + + pub aggtype: u32, + + pub aggcollid: u32, + + pub inputcollid: u32, + + pub aggtranstype: u32, + + pub aggargtypes: Vec, + + pub aggdirectargs: Vec, + + pub args: Vec, + + pub aggorder: Vec, + + pub aggdistinct: Vec, + + pub aggfilter: ::core::option::Option>, + + pub aggstar: bool, + + pub aggvariadic: bool, + + pub aggkind: String, + + pub agglevelsup: u32, + + pub aggsplit: i32, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct GroupingFunc { + pub xpr: ::core::option::Option>, + + pub args: Vec, + + pub refs: Vec, + + pub cols: Vec, + + pub agglevelsup: u32, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct WindowFunc { + pub xpr: ::core::option::Option>, + + pub winfnoid: u32, + + pub wintype: u32, + + pub wincollid: u32, + + pub inputcollid: u32, + + pub args: Vec, + + pub aggfilter: ::core::option::Option>, + + pub winref: u32, + + pub winstar: bool, + + pub winagg: bool, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct SubscriptingRef { + pub xpr: ::core::option::Option>, + + pub refcontainertype: u32, + + pub refelemtype: u32, + + pub reftypmod: i32, + + pub refcollid: u32, + + pub refupperindexpr: Vec, + + pub reflowerindexpr: Vec, + + pub refexpr: ::core::option::Option>, + + pub refassgnexpr: ::core::option::Option>, +} +#[derive(Clone, PartialEq)] +pub struct FuncExpr { + pub xpr: ::core::option::Option>, + + pub funcid: u32, + + pub funcresulttype: u32, + + pub funcretset: bool, + + pub funcvariadic: bool, + + pub funcformat: i32, + + pub funccollid: u32, + + pub inputcollid: u32, + + pub args: Vec, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct NamedArgExpr { + pub xpr: ::core::option::Option>, + + pub arg: ::core::option::Option>, + + pub name: String, + + pub argnumber: i32, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct OpExpr { + pub xpr: ::core::option::Option>, + + pub opno: u32, + + pub opfuncid: u32, + + pub opresulttype: u32, + + pub opretset: bool, + + pub opcollid: u32, + + pub inputcollid: u32, + + pub args: Vec, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct DistinctExpr { + pub xpr: ::core::option::Option>, + + pub opno: u32, + + pub opfuncid: u32, + + pub opresulttype: u32, + + pub opretset: bool, + + pub opcollid: u32, + + pub inputcollid: u32, + + pub args: Vec, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct NullIfExpr { + pub xpr: ::core::option::Option>, + + pub opno: u32, + + pub opfuncid: u32, + + pub opresulttype: u32, + + pub opretset: bool, + + pub opcollid: u32, + + pub inputcollid: u32, + + pub args: Vec, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct ScalarArrayOpExpr { + pub xpr: ::core::option::Option>, + + pub opno: u32, + + pub opfuncid: u32, + + pub use_or: bool, + + pub inputcollid: u32, + + pub args: Vec, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct BoolExpr { + pub xpr: ::core::option::Option>, + + pub boolop: i32, + + pub args: Vec, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct SubLink { + pub xpr: ::core::option::Option>, + + pub sub_link_type: i32, + + pub sub_link_id: i32, + + pub testexpr: ::core::option::Option>, + + pub oper_name: Vec, + + pub subselect: ::core::option::Option>, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct SubPlan { + pub xpr: ::core::option::Option>, + + pub sub_link_type: i32, + + pub testexpr: ::core::option::Option>, + + pub param_ids: Vec, + + pub plan_id: i32, + + pub plan_name: String, + + pub first_col_type: u32, + + pub first_col_typmod: i32, + + pub first_col_collation: u32, + + pub use_hash_table: bool, + + pub unknown_eq_false: bool, + + pub parallel_safe: bool, + + pub set_param: Vec, + + pub par_param: Vec, + + pub args: Vec, + + pub startup_cost: f64, + + pub per_call_cost: f64, +} +#[derive(Clone, PartialEq)] +pub struct AlternativeSubPlan { + pub xpr: ::core::option::Option>, + + pub subplans: Vec, +} +#[derive(Clone, PartialEq)] +pub struct FieldSelect { + pub xpr: ::core::option::Option>, + + pub arg: ::core::option::Option>, + + pub fieldnum: i32, + + pub resulttype: u32, + + pub resulttypmod: i32, + + pub resultcollid: u32, +} +#[derive(Clone, PartialEq)] +pub struct FieldStore { + pub xpr: ::core::option::Option>, + + pub arg: ::core::option::Option>, + + pub newvals: Vec, + + pub fieldnums: Vec, + + pub resulttype: u32, +} +#[derive(Clone, PartialEq)] +pub struct RelabelType { + pub xpr: ::core::option::Option>, + + pub arg: ::core::option::Option>, + + pub resulttype: u32, + + pub resulttypmod: i32, + + pub resultcollid: u32, + + pub relabelformat: i32, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct CoerceViaIo { + pub xpr: ::core::option::Option>, + + pub arg: ::core::option::Option>, + + pub resulttype: u32, + + pub resultcollid: u32, + + pub coerceformat: i32, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct ArrayCoerceExpr { + pub xpr: ::core::option::Option>, + + pub arg: ::core::option::Option>, + + pub elemexpr: ::core::option::Option>, + + pub resulttype: u32, + + pub resulttypmod: i32, + + pub resultcollid: u32, + + pub coerceformat: i32, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct ConvertRowtypeExpr { + pub xpr: ::core::option::Option>, + + pub arg: ::core::option::Option>, + + pub resulttype: u32, + + pub convertformat: i32, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct CollateExpr { + pub xpr: ::core::option::Option>, + + pub arg: ::core::option::Option>, + + pub coll_oid: u32, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct CaseExpr { + pub xpr: ::core::option::Option>, + + pub casetype: u32, + + pub casecollid: u32, + + pub arg: ::core::option::Option>, + + pub args: Vec, + + pub defresult: ::core::option::Option>, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct CaseWhen { + pub xpr: ::core::option::Option>, + + pub expr: ::core::option::Option>, + + pub result: ::core::option::Option>, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct CaseTestExpr { + pub xpr: ::core::option::Option>, + + pub type_id: u32, + + pub type_mod: i32, + + pub collation: u32, +} +#[derive(Clone, PartialEq)] +pub struct ArrayExpr { + pub xpr: ::core::option::Option>, + + pub array_typeid: u32, + + pub array_collid: u32, + + pub element_typeid: u32, + + pub elements: Vec, + + pub multidims: bool, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct RowExpr { + pub xpr: ::core::option::Option>, + + pub args: Vec, + + pub row_typeid: u32, + + pub row_format: i32, + + pub colnames: Vec, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct RowCompareExpr { + pub xpr: ::core::option::Option>, + + pub rctype: i32, + + pub opnos: Vec, + + pub opfamilies: Vec, + + pub inputcollids: Vec, + + pub largs: Vec, + + pub rargs: Vec, +} +#[derive(Clone, PartialEq)] +pub struct CoalesceExpr { + pub xpr: ::core::option::Option>, + + pub coalescetype: u32, + + pub coalescecollid: u32, + + pub args: Vec, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct MinMaxExpr { + pub xpr: ::core::option::Option>, + + pub minmaxtype: u32, + + pub minmaxcollid: u32, + + pub inputcollid: u32, + + pub op: i32, + + pub args: Vec, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct SqlValueFunction { + pub xpr: ::core::option::Option>, + + pub op: i32, + + pub r#type: u32, + + pub typmod: i32, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct XmlExpr { + pub xpr: ::core::option::Option>, + + pub op: i32, + + pub name: String, + + pub named_args: Vec, + + pub arg_names: Vec, + + pub args: Vec, + + pub xmloption: i32, + + pub r#type: u32, + + pub typmod: i32, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct NullTest { + pub xpr: ::core::option::Option>, + + pub arg: ::core::option::Option>, + + pub nulltesttype: i32, + + pub argisrow: bool, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct BooleanTest { + pub xpr: ::core::option::Option>, + + pub arg: ::core::option::Option>, + + pub booltesttype: i32, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct CoerceToDomain { + pub xpr: ::core::option::Option>, + + pub arg: ::core::option::Option>, + + pub resulttype: u32, + + pub resulttypmod: i32, + + pub resultcollid: u32, + + pub coercionformat: i32, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct CoerceToDomainValue { + pub xpr: ::core::option::Option>, + + pub type_id: u32, + + pub type_mod: i32, + + pub collation: u32, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct SetToDefault { + pub xpr: ::core::option::Option>, + + pub type_id: u32, + + pub type_mod: i32, + + pub collation: u32, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct CurrentOfExpr { + pub xpr: ::core::option::Option>, + + pub cvarno: u32, + + pub cursor_name: String, + + pub cursor_param: i32, +} +#[derive(Clone, PartialEq)] +pub struct NextValueExpr { + pub xpr: ::core::option::Option>, + + pub seqid: u32, + + pub type_id: u32, +} +#[derive(Clone, PartialEq)] +pub struct InferenceElem { + pub xpr: ::core::option::Option>, + + pub expr: ::core::option::Option>, + + pub infercollid: u32, + + pub inferopclass: u32, +} +#[derive(Clone, PartialEq)] +pub struct TargetEntry { + pub xpr: ::core::option::Option>, + + pub expr: ::core::option::Option>, + + pub resno: i32, + + pub resname: String, + + pub ressortgroupref: u32, + + pub resorigtbl: u32, + + pub resorigcol: i32, + + pub resjunk: bool, +} +#[derive(Clone, PartialEq)] +pub struct RangeTblRef { + pub rtindex: i32, +} +#[derive(Clone, PartialEq)] +pub struct JoinExpr { + pub jointype: i32, + + pub is_natural: bool, + + pub larg: ::core::option::Option>, + + pub rarg: ::core::option::Option>, + + pub using_clause: Vec, + + pub quals: ::core::option::Option>, + + pub alias: ::core::option::Option, + + pub rtindex: i32, +} +#[derive(Clone, PartialEq)] +pub struct FromExpr { + pub fromlist: Vec, + + pub quals: ::core::option::Option>, +} +#[derive(Clone, PartialEq)] +pub struct OnConflictExpr { + pub action: i32, + + pub arbiter_elems: Vec, + + pub arbiter_where: ::core::option::Option>, + + pub constraint: u32, + + pub on_conflict_set: Vec, + + pub on_conflict_where: ::core::option::Option>, + + pub excl_rel_index: i32, + + pub excl_rel_tlist: Vec, +} +#[derive(Clone, PartialEq)] +pub struct IntoClause { + pub rel: ::core::option::Option, + + pub col_names: Vec, + + pub access_method: String, + + pub options: Vec, + + pub on_commit: i32, + + pub table_space_name: String, + + pub view_query: ::core::option::Option>, + + pub skip_data: bool, +} +#[derive(Clone, PartialEq)] +pub struct RawStmt { + pub stmt: ::core::option::Option>, + + pub stmt_location: i32, + + pub stmt_len: i32, +} +#[derive(Clone, PartialEq)] +pub struct Query { + pub command_type: i32, + + pub query_source: i32, + + pub can_set_tag: bool, + + pub utility_stmt: ::core::option::Option>, + + pub result_relation: i32, + + pub has_aggs: bool, + + pub has_window_funcs: bool, + + pub has_target_srfs: bool, + + pub has_sub_links: bool, + + pub has_distinct_on: bool, + + pub has_recursive: bool, + + pub has_modifying_cte: bool, + + pub has_for_update: bool, + + pub has_row_security: bool, + + pub cte_list: Vec, + + pub rtable: Vec, + + pub jointree: ::core::option::Option>, + + pub target_list: Vec, + + pub r#override: i32, + + pub on_conflict: ::core::option::Option>, + + pub returning_list: Vec, + + pub group_clause: Vec, + + pub grouping_sets: Vec, + + pub having_qual: ::core::option::Option>, + + pub window_clause: Vec, + + pub distinct_clause: Vec, + + pub sort_clause: Vec, + + pub limit_offset: ::core::option::Option>, + + pub limit_count: ::core::option::Option>, + + pub limit_option: i32, + + pub row_marks: Vec, + + pub set_operations: ::core::option::Option>, + + pub constraint_deps: Vec, + + pub with_check_options: Vec, + + pub stmt_location: i32, + + pub stmt_len: i32, +} +#[derive(Clone, PartialEq)] +pub struct InsertStmt { + pub relation: ::core::option::Option, + + pub cols: Vec, + + pub select_stmt: ::core::option::Option>, + + pub on_conflict_clause: ::core::option::Option>, + + pub returning_list: Vec, + + pub with_clause: ::core::option::Option, + + pub r#override: i32, +} +#[derive(Clone, PartialEq)] +pub struct DeleteStmt { + pub relation: ::core::option::Option, + + pub using_clause: Vec, + + pub where_clause: ::core::option::Option>, + + pub returning_list: Vec, + + pub with_clause: ::core::option::Option, +} +#[derive(Clone, PartialEq)] +pub struct UpdateStmt { + pub relation: ::core::option::Option, + + pub target_list: Vec, + + pub where_clause: ::core::option::Option>, + + pub from_clause: Vec, + + pub returning_list: Vec, + + pub with_clause: ::core::option::Option, +} +#[derive(Clone, PartialEq)] +pub struct SelectStmt { + pub distinct_clause: Vec, + + pub into_clause: ::core::option::Option>, + + pub target_list: Vec, + + pub from_clause: Vec, + + pub where_clause: ::core::option::Option>, + + pub group_clause: Vec, + + pub having_clause: ::core::option::Option>, + + pub window_clause: Vec, + + pub values_lists: Vec, + + pub sort_clause: Vec, + + pub limit_offset: ::core::option::Option>, + + pub limit_count: ::core::option::Option>, + + pub limit_option: i32, + + pub locking_clause: Vec, + + pub with_clause: ::core::option::Option, + + pub op: i32, + + pub all: bool, + + pub larg: ::core::option::Option>, + + pub rarg: ::core::option::Option>, +} +#[derive(Clone, PartialEq)] +pub struct AlterTableStmt { + pub relation: ::core::option::Option, + + pub cmds: Vec, + + pub relkind: i32, + + pub missing_ok: bool, +} +#[derive(Clone, PartialEq)] +pub struct AlterTableCmd { + pub subtype: i32, + + pub name: String, + + pub num: i32, + + pub newowner: ::core::option::Option, + + pub def: ::core::option::Option>, + + pub behavior: i32, + + pub missing_ok: bool, +} +#[derive(Clone, PartialEq)] +pub struct AlterDomainStmt { + pub subtype: String, + + pub type_name: Vec, + + pub name: String, + + pub def: ::core::option::Option>, + + pub behavior: i32, + + pub missing_ok: bool, +} +#[derive(Clone, PartialEq)] +pub struct SetOperationStmt { + pub op: i32, + + pub all: bool, + + pub larg: ::core::option::Option>, + + pub rarg: ::core::option::Option>, + + pub col_types: Vec, + + pub col_typmods: Vec, + + pub col_collations: Vec, + + pub group_clauses: Vec, +} +#[derive(Clone, PartialEq)] +pub struct GrantStmt { + pub is_grant: bool, + + pub targtype: i32, + + pub objtype: i32, + + pub objects: Vec, + + pub privileges: Vec, + + pub grantees: Vec, + + pub grant_option: bool, + + pub behavior: i32, +} +#[derive(Clone, PartialEq)] +pub struct GrantRoleStmt { + pub granted_roles: Vec, + + pub grantee_roles: Vec, + + pub is_grant: bool, + + pub admin_opt: bool, + + pub grantor: ::core::option::Option, + + pub behavior: i32, +} +#[derive(Clone, PartialEq)] +pub struct AlterDefaultPrivilegesStmt { + pub options: Vec, + + pub action: ::core::option::Option, +} +#[derive(Clone, PartialEq)] +pub struct ClosePortalStmt { + pub portalname: String, +} +#[derive(Clone, PartialEq)] +pub struct ClusterStmt { + pub relation: ::core::option::Option, + + pub indexname: String, + + pub options: i32, +} +#[derive(Clone, PartialEq)] +pub struct CopyStmt { + pub relation: ::core::option::Option, + + pub query: ::core::option::Option>, + + pub attlist: Vec, + + pub is_from: bool, + + pub is_program: bool, + + pub filename: String, + + pub options: Vec, + + pub where_clause: ::core::option::Option>, +} +#[derive(Clone, PartialEq)] +pub struct CreateStmt { + pub relation: ::core::option::Option, + + pub table_elts: Vec, + + pub inh_relations: Vec, + + pub partbound: ::core::option::Option, + + pub partspec: ::core::option::Option, + + pub of_typename: ::core::option::Option, + + pub constraints: Vec, + + pub options: Vec, + + pub oncommit: i32, + + pub tablespacename: String, + + pub access_method: String, + + pub if_not_exists: bool, +} +#[derive(Clone, PartialEq)] +pub struct DefineStmt { + pub kind: i32, + + pub oldstyle: bool, + + pub defnames: Vec, + + pub args: Vec, + + pub definition: Vec, + + pub if_not_exists: bool, + + pub replace: bool, +} +#[derive(Clone, PartialEq)] +pub struct DropStmt { + pub objects: Vec, + + pub remove_type: i32, + + pub behavior: i32, + + pub missing_ok: bool, + + pub concurrent: bool, +} +#[derive(Clone, PartialEq)] +pub struct TruncateStmt { + pub relations: Vec, + + pub restart_seqs: bool, + + pub behavior: i32, +} +#[derive(Clone, PartialEq)] +pub struct CommentStmt { + pub objtype: i32, + + pub object: ::core::option::Option>, + + pub comment: String, +} +#[derive(Clone, PartialEq)] +pub struct FetchStmt { + pub direction: i32, + + pub how_many: i64, + + pub portalname: String, + + pub ismove: bool, +} +#[derive(Clone, PartialEq)] +pub struct IndexStmt { + pub idxname: String, + + pub relation: ::core::option::Option, + + pub access_method: String, + + pub table_space: String, + + pub index_params: Vec, + + pub index_including_params: Vec, + + pub options: Vec, + + pub where_clause: ::core::option::Option>, + + pub exclude_op_names: Vec, + + pub idxcomment: String, + + pub index_oid: u32, + + pub old_node: u32, + + pub old_create_subid: u32, + + pub old_first_relfilenode_subid: u32, + + pub unique: bool, + + pub primary: bool, + + pub isconstraint: bool, + + pub deferrable: bool, + + pub initdeferred: bool, + + pub transformed: bool, + + pub concurrent: bool, + + pub if_not_exists: bool, + + pub reset_default_tblspc: bool, +} +#[derive(Clone, PartialEq)] +pub struct CreateFunctionStmt { + pub is_procedure: bool, + + pub replace: bool, + + pub funcname: Vec, + + pub parameters: Vec, + + pub return_type: ::core::option::Option, + + pub options: Vec, +} +#[derive(Clone, PartialEq)] +pub struct AlterFunctionStmt { + pub objtype: i32, + + pub func: ::core::option::Option, + + pub actions: Vec, +} +#[derive(Clone, PartialEq)] +pub struct DoStmt { + pub args: Vec, +} +#[derive(Clone, PartialEq)] +pub struct RenameStmt { + pub rename_type: i32, + + pub relation_type: i32, + + pub relation: ::core::option::Option, + + pub object: ::core::option::Option>, + + pub subname: String, + + pub newname: String, + + pub behavior: i32, + + pub missing_ok: bool, +} +#[derive(Clone, PartialEq)] +pub struct RuleStmt { + pub relation: ::core::option::Option, + + pub rulename: String, + + pub where_clause: ::core::option::Option>, + + pub event: i32, + + pub instead: bool, + + pub actions: Vec, + + pub replace: bool, +} +#[derive(Clone, PartialEq)] +pub struct NotifyStmt { + pub conditionname: String, + + pub payload: String, +} +#[derive(Clone, PartialEq)] +pub struct ListenStmt { + pub conditionname: String, +} +#[derive(Clone, PartialEq)] +pub struct UnlistenStmt { + pub conditionname: String, +} +#[derive(Clone, PartialEq)] +pub struct TransactionStmt { + pub kind: i32, + + pub options: Vec, + + pub savepoint_name: String, + + pub gid: String, + + pub chain: bool, +} +#[derive(Clone, PartialEq)] +pub struct ViewStmt { + pub view: ::core::option::Option, + + pub aliases: Vec, + + pub query: ::core::option::Option>, + + pub replace: bool, + + pub options: Vec, + + pub with_check_option: i32, +} +#[derive(Clone, PartialEq)] +pub struct LoadStmt { + pub filename: String, +} +#[derive(Clone, PartialEq)] +pub struct CreateDomainStmt { + pub domainname: Vec, + + pub type_name: ::core::option::Option, + + pub coll_clause: ::core::option::Option>, + + pub constraints: Vec, +} +#[derive(Clone, PartialEq)] +pub struct CreatedbStmt { + pub dbname: String, + + pub options: Vec, +} +#[derive(Clone, PartialEq)] +pub struct DropdbStmt { + pub dbname: String, + + pub missing_ok: bool, + + pub options: Vec, +} +#[derive(Clone, PartialEq)] +pub struct VacuumStmt { + pub options: Vec, + + pub rels: Vec, + + pub is_vacuumcmd: bool, +} +#[derive(Clone, PartialEq)] +pub struct ExplainStmt { + pub query: ::core::option::Option>, + + pub options: Vec, +} +#[derive(Clone, PartialEq)] +pub struct CreateTableAsStmt { + pub query: ::core::option::Option>, + + pub into: ::core::option::Option>, + + pub relkind: i32, + + pub is_select_into: bool, + + pub if_not_exists: bool, +} +#[derive(Clone, PartialEq)] +pub struct CreateSeqStmt { + pub sequence: ::core::option::Option, + + pub options: Vec, + + pub owner_id: u32, + + pub for_identity: bool, + + pub if_not_exists: bool, +} +#[derive(Clone, PartialEq)] +pub struct AlterSeqStmt { + pub sequence: ::core::option::Option, + + pub options: Vec, + + pub for_identity: bool, + + pub missing_ok: bool, +} +#[derive(Clone, PartialEq)] +pub struct VariableSetStmt { + pub kind: i32, + + pub name: String, + + pub args: Vec, + + pub is_local: bool, +} +#[derive(Clone, PartialEq)] +pub struct VariableShowStmt { + pub name: String, +} +#[derive(Clone, PartialEq)] +pub struct DiscardStmt { + pub target: i32, +} +#[derive(Clone, PartialEq)] +pub struct CreateTrigStmt { + pub trigname: String, + + pub relation: ::core::option::Option, + + pub funcname: Vec, + + pub args: Vec, + + pub row: bool, + + pub timing: i32, + + pub events: i32, + + pub columns: Vec, + + pub when_clause: ::core::option::Option>, + + pub isconstraint: bool, + + pub transition_rels: Vec, + + pub deferrable: bool, + + pub initdeferred: bool, + + pub constrrel: ::core::option::Option, +} +#[derive(Clone, PartialEq)] +pub struct CreatePLangStmt { + pub replace: bool, + + pub plname: String, + + pub plhandler: Vec, + + pub plinline: Vec, + + pub plvalidator: Vec, + + pub pltrusted: bool, +} +#[derive(Clone, PartialEq)] +pub struct CreateRoleStmt { + pub stmt_type: i32, + + pub role: String, + + pub options: Vec, +} +#[derive(Clone, PartialEq)] +pub struct AlterRoleStmt { + pub role: ::core::option::Option, + + pub options: Vec, + + pub action: i32, +} +#[derive(Clone, PartialEq)] +pub struct DropRoleStmt { + pub roles: Vec, + + pub missing_ok: bool, +} +#[derive(Clone, PartialEq)] +pub struct LockStmt { + pub relations: Vec, + + pub mode: i32, + + pub nowait: bool, +} +#[derive(Clone, PartialEq)] +pub struct ConstraintsSetStmt { + pub constraints: Vec, + + pub deferred: bool, +} +#[derive(Clone, PartialEq)] +pub struct ReindexStmt { + pub kind: i32, + + pub relation: ::core::option::Option, + + pub name: String, + + pub options: i32, + + pub concurrent: bool, +} +#[derive(Clone, PartialEq)] +pub struct CheckPointStmt {} +#[derive(Clone, PartialEq)] +pub struct CreateSchemaStmt { + pub schemaname: String, + + pub authrole: ::core::option::Option, + + pub schema_elts: Vec, + + pub if_not_exists: bool, +} +#[derive(Clone, PartialEq)] +pub struct AlterDatabaseStmt { + pub dbname: String, + + pub options: Vec, +} +#[derive(Clone, PartialEq)] +pub struct AlterDatabaseSetStmt { + pub dbname: String, + + pub setstmt: ::core::option::Option, +} +#[derive(Clone, PartialEq)] +pub struct AlterRoleSetStmt { + pub role: ::core::option::Option, + + pub database: String, + + pub setstmt: ::core::option::Option, +} +#[derive(Clone, PartialEq)] +pub struct CreateConversionStmt { + pub conversion_name: Vec, + + pub for_encoding_name: String, + + pub to_encoding_name: String, + + pub func_name: Vec, + + pub def: bool, +} +#[derive(Clone, PartialEq)] +pub struct CreateCastStmt { + pub sourcetype: ::core::option::Option, + + pub targettype: ::core::option::Option, + + pub func: ::core::option::Option, + + pub context: i32, + + pub inout: bool, +} +#[derive(Clone, PartialEq)] +pub struct CreateOpClassStmt { + pub opclassname: Vec, + + pub opfamilyname: Vec, + + pub amname: String, + + pub datatype: ::core::option::Option, + + pub items: Vec, + + pub is_default: bool, +} +#[derive(Clone, PartialEq)] +pub struct CreateOpFamilyStmt { + pub opfamilyname: Vec, + + pub amname: String, +} +#[derive(Clone, PartialEq)] +pub struct AlterOpFamilyStmt { + pub opfamilyname: Vec, + + pub amname: String, + + pub is_drop: bool, + + pub items: Vec, +} +#[derive(Clone, PartialEq)] +pub struct PrepareStmt { + pub name: String, + + pub argtypes: Vec, + + pub query: ::core::option::Option>, +} +#[derive(Clone, PartialEq)] +pub struct ExecuteStmt { + pub name: String, + + pub params: Vec, +} +#[derive(Clone, PartialEq)] +pub struct DeallocateStmt { + pub name: String, +} +#[derive(Clone, PartialEq)] +pub struct DeclareCursorStmt { + pub portalname: String, + + pub options: i32, + + pub query: ::core::option::Option>, +} +#[derive(Clone, PartialEq)] +pub struct CreateTableSpaceStmt { + pub tablespacename: String, + + pub owner: ::core::option::Option, + + pub location: String, + + pub options: Vec, +} +#[derive(Clone, PartialEq)] +pub struct DropTableSpaceStmt { + pub tablespacename: String, + + pub missing_ok: bool, +} +#[derive(Clone, PartialEq)] +pub struct AlterObjectDependsStmt { + pub object_type: i32, + + pub relation: ::core::option::Option, + + pub object: ::core::option::Option>, + + pub extname: ::core::option::Option>, + + pub remove: bool, +} +#[derive(Clone, PartialEq)] +pub struct AlterObjectSchemaStmt { + pub object_type: i32, + + pub relation: ::core::option::Option, + + pub object: ::core::option::Option>, + + pub newschema: String, + + pub missing_ok: bool, +} +#[derive(Clone, PartialEq)] +pub struct AlterOwnerStmt { + pub object_type: i32, + + pub relation: ::core::option::Option, + + pub object: ::core::option::Option>, + + pub newowner: ::core::option::Option, +} +#[derive(Clone, PartialEq)] +pub struct AlterOperatorStmt { + pub opername: ::core::option::Option, + + pub options: Vec, +} +#[derive(Clone, PartialEq)] +pub struct AlterTypeStmt { + pub type_name: Vec, + + pub options: Vec, +} +#[derive(Clone, PartialEq)] +pub struct DropOwnedStmt { + pub roles: Vec, + + pub behavior: i32, +} +#[derive(Clone, PartialEq)] +pub struct ReassignOwnedStmt { + pub roles: Vec, + + pub newrole: ::core::option::Option, +} +#[derive(Clone, PartialEq)] +pub struct CompositeTypeStmt { + pub typevar: ::core::option::Option, + + pub coldeflist: Vec, +} +#[derive(Clone, PartialEq)] +pub struct CreateEnumStmt { + pub type_name: Vec, + + pub vals: Vec, +} +#[derive(Clone, PartialEq)] +pub struct CreateRangeStmt { + pub type_name: Vec, + + pub params: Vec, +} +#[derive(Clone, PartialEq)] +pub struct AlterEnumStmt { + pub type_name: Vec, + + pub old_val: String, + + pub new_val: String, + + pub new_val_neighbor: String, + + pub new_val_is_after: bool, + + pub skip_if_new_val_exists: bool, +} +#[derive(Clone, PartialEq)] +pub struct AlterTsDictionaryStmt { + pub dictname: Vec, + + pub options: Vec, +} +#[derive(Clone, PartialEq)] +pub struct AlterTsConfigurationStmt { + pub kind: i32, + + pub cfgname: Vec, + + pub tokentype: Vec, + + pub dicts: Vec, + + pub r#override: bool, + + pub replace: bool, + + pub missing_ok: bool, +} +#[derive(Clone, PartialEq)] +pub struct CreateFdwStmt { + pub fdwname: String, + + pub func_options: Vec, + + pub options: Vec, +} +#[derive(Clone, PartialEq)] +pub struct AlterFdwStmt { + pub fdwname: String, + + pub func_options: Vec, + + pub options: Vec, +} +#[derive(Clone, PartialEq)] +pub struct CreateForeignServerStmt { + pub servername: String, + + pub servertype: String, + + pub version: String, + + pub fdwname: String, + + pub if_not_exists: bool, + + pub options: Vec, +} +#[derive(Clone, PartialEq)] +pub struct AlterForeignServerStmt { + pub servername: String, + + pub version: String, + + pub options: Vec, + + pub has_version: bool, +} +#[derive(Clone, PartialEq)] +pub struct CreateUserMappingStmt { + pub user: ::core::option::Option, + + pub servername: String, + + pub if_not_exists: bool, + + pub options: Vec, +} +#[derive(Clone, PartialEq)] +pub struct AlterUserMappingStmt { + pub user: ::core::option::Option, + + pub servername: String, + + pub options: Vec, +} +#[derive(Clone, PartialEq)] +pub struct DropUserMappingStmt { + pub user: ::core::option::Option, + + pub servername: String, + + pub missing_ok: bool, +} +#[derive(Clone, PartialEq)] +pub struct AlterTableSpaceOptionsStmt { + pub tablespacename: String, + + pub options: Vec, + + pub is_reset: bool, +} +#[derive(Clone, PartialEq)] +pub struct AlterTableMoveAllStmt { + pub orig_tablespacename: String, + + pub objtype: i32, + + pub roles: Vec, + + pub new_tablespacename: String, + + pub nowait: bool, +} +#[derive(Clone, PartialEq)] +pub struct SecLabelStmt { + pub objtype: i32, + + pub object: ::core::option::Option>, + + pub provider: String, + + pub label: String, +} +#[derive(Clone, PartialEq)] +pub struct CreateForeignTableStmt { + pub base_stmt: ::core::option::Option, + + pub servername: String, + + pub options: Vec, +} +#[derive(Clone, PartialEq)] +pub struct ImportForeignSchemaStmt { + pub server_name: String, + + pub remote_schema: String, + + pub local_schema: String, + + pub list_type: i32, + + pub table_list: Vec, + + pub options: Vec, +} +#[derive(Clone, PartialEq)] +pub struct CreateExtensionStmt { + pub extname: String, + + pub if_not_exists: bool, + + pub options: Vec, +} +#[derive(Clone, PartialEq)] +pub struct AlterExtensionStmt { + pub extname: String, + + pub options: Vec, +} +#[derive(Clone, PartialEq)] +pub struct AlterExtensionContentsStmt { + pub extname: String, + + pub action: i32, + + pub objtype: i32, + + pub object: ::core::option::Option>, +} +#[derive(Clone, PartialEq)] +pub struct CreateEventTrigStmt { + pub trigname: String, + + pub eventname: String, + + pub whenclause: Vec, + + pub funcname: Vec, +} +#[derive(Clone, PartialEq)] +pub struct AlterEventTrigStmt { + pub trigname: String, + + pub tgenabled: String, +} +#[derive(Clone, PartialEq)] +pub struct RefreshMatViewStmt { + pub concurrent: bool, + + pub skip_data: bool, + + pub relation: ::core::option::Option, +} +#[derive(Clone, PartialEq)] +pub struct ReplicaIdentityStmt { + pub identity_type: String, + + pub name: String, +} +#[derive(Clone, PartialEq)] +pub struct AlterSystemStmt { + pub setstmt: ::core::option::Option, +} +#[derive(Clone, PartialEq)] +pub struct CreatePolicyStmt { + pub policy_name: String, + + pub table: ::core::option::Option, + + pub cmd_name: String, + + pub permissive: bool, + + pub roles: Vec, + + pub qual: ::core::option::Option>, + + pub with_check: ::core::option::Option>, +} +#[derive(Clone, PartialEq)] +pub struct AlterPolicyStmt { + pub policy_name: String, + + pub table: ::core::option::Option, + + pub roles: Vec, + + pub qual: ::core::option::Option>, + + pub with_check: ::core::option::Option>, +} +#[derive(Clone, PartialEq)] +pub struct CreateTransformStmt { + pub replace: bool, + + pub type_name: ::core::option::Option, + + pub lang: String, + + pub fromsql: ::core::option::Option, + + pub tosql: ::core::option::Option, +} +#[derive(Clone, PartialEq)] +pub struct CreateAmStmt { + pub amname: String, + + pub handler_name: Vec, + + pub amtype: String, +} +#[derive(Clone, PartialEq)] +pub struct CreatePublicationStmt { + pub pubname: String, + + pub options: Vec, + + pub tables: Vec, + + pub for_all_tables: bool, +} +#[derive(Clone, PartialEq)] +pub struct AlterPublicationStmt { + pub pubname: String, + + pub options: Vec, + + pub tables: Vec, + + pub for_all_tables: bool, + + pub table_action: i32, +} +#[derive(Clone, PartialEq)] +pub struct CreateSubscriptionStmt { + pub subname: String, + + pub conninfo: String, + + pub publication: Vec, + + pub options: Vec, +} +#[derive(Clone, PartialEq)] +pub struct AlterSubscriptionStmt { + pub kind: i32, + + pub subname: String, + + pub conninfo: String, + + pub publication: Vec, + + pub options: Vec, +} +#[derive(Clone, PartialEq)] +pub struct DropSubscriptionStmt { + pub subname: String, + + pub missing_ok: bool, + + pub behavior: i32, +} +#[derive(Clone, PartialEq)] +pub struct CreateStatsStmt { + pub defnames: Vec, + + pub stat_types: Vec, + + pub exprs: Vec, + + pub relations: Vec, + + pub stxcomment: String, + + pub if_not_exists: bool, +} +#[derive(Clone, PartialEq)] +pub struct AlterCollationStmt { + pub collname: Vec, +} +#[derive(Clone, PartialEq)] +pub struct CallStmt { + pub funccall: ::core::option::Option>, + + pub funcexpr: ::core::option::Option>, +} +#[derive(Clone, PartialEq)] +pub struct AlterStatsStmt { + pub defnames: Vec, + + pub stxstattarget: i32, + + pub missing_ok: bool, +} +#[derive(Clone, PartialEq)] +pub struct AExpr { + pub kind: i32, + + pub name: Vec, + + pub lexpr: ::core::option::Option>, + + pub rexpr: ::core::option::Option>, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct ColumnRef { + pub fields: Vec, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct ParamRef { + pub number: i32, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct AConst { + pub val: ::core::option::Option>, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct FuncCall { + pub funcname: Vec, + + pub args: Vec, + + pub agg_order: Vec, + + pub agg_filter: ::core::option::Option>, + + pub agg_within_group: bool, + + pub agg_star: bool, + + pub agg_distinct: bool, + + pub func_variadic: bool, + + pub over: ::core::option::Option>, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct AStar {} +#[derive(Clone, PartialEq)] +pub struct AIndices { + pub is_slice: bool, + + pub lidx: ::core::option::Option>, + + pub uidx: ::core::option::Option>, +} +#[derive(Clone, PartialEq)] +pub struct AIndirection { + pub arg: ::core::option::Option>, + + pub indirection: Vec, +} +#[derive(Clone, PartialEq)] +pub struct AArrayExpr { + pub elements: Vec, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct ResTarget { + pub name: String, + + pub indirection: Vec, + + pub val: ::core::option::Option>, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct MultiAssignRef { + pub source: ::core::option::Option>, + + pub colno: i32, + + pub ncolumns: i32, +} +#[derive(Clone, PartialEq)] +pub struct TypeCast { + pub arg: ::core::option::Option>, + + pub type_name: ::core::option::Option, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct CollateClause { + pub arg: ::core::option::Option>, + + pub collname: Vec, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct SortBy { + pub node: ::core::option::Option>, + + pub sortby_dir: i32, + + pub sortby_nulls: i32, + + pub use_op: Vec, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct WindowDef { + pub name: String, + + pub refname: String, + + pub partition_clause: Vec, + + pub order_clause: Vec, + + pub frame_options: i32, + + pub start_offset: ::core::option::Option>, + + pub end_offset: ::core::option::Option>, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct RangeSubselect { + pub lateral: bool, + + pub subquery: ::core::option::Option>, + + pub alias: ::core::option::Option, +} +#[derive(Clone, PartialEq)] +pub struct RangeFunction { + pub lateral: bool, + + pub ordinality: bool, + + pub is_rowsfrom: bool, + + pub functions: Vec, + + pub alias: ::core::option::Option, + + pub coldeflist: Vec, +} +#[derive(Clone, PartialEq)] +pub struct RangeTableSample { + pub relation: ::core::option::Option>, + + pub method: Vec, + + pub args: Vec, + + pub repeatable: ::core::option::Option>, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct RangeTableFunc { + pub lateral: bool, + + pub docexpr: ::core::option::Option>, + + pub rowexpr: ::core::option::Option>, + + pub namespaces: Vec, + + pub columns: Vec, + + pub alias: ::core::option::Option, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct RangeTableFuncCol { + pub colname: String, + + pub type_name: ::core::option::Option, + + pub for_ordinality: bool, + + pub is_not_null: bool, + + pub colexpr: ::core::option::Option>, + + pub coldefexpr: ::core::option::Option>, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct TypeName { + pub names: Vec, + + pub type_oid: u32, + + pub setof: bool, + + pub pct_type: bool, + + pub typmods: Vec, + + pub typemod: i32, + + pub array_bounds: Vec, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct ColumnDef { + pub colname: String, + + pub type_name: ::core::option::Option, + + pub inhcount: i32, + + pub is_local: bool, + + pub is_not_null: bool, + + pub is_from_type: bool, + + pub storage: String, + + pub raw_default: ::core::option::Option>, + + pub cooked_default: ::core::option::Option>, + + pub identity: String, + + pub identity_sequence: ::core::option::Option, + + pub generated: String, + + pub coll_clause: ::core::option::Option>, + + pub coll_oid: u32, + + pub constraints: Vec, + + pub fdwoptions: Vec, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct IndexElem { + pub name: String, + + pub expr: ::core::option::Option>, + + pub indexcolname: String, + + pub collation: Vec, + + pub opclass: Vec, + + pub opclassopts: Vec, + + pub ordering: i32, + + pub nulls_ordering: i32, +} +#[derive(Clone, PartialEq)] +pub struct Constraint { + pub contype: i32, + + pub conname: String, + + pub deferrable: bool, + + pub initdeferred: bool, + + pub location: i32, + + pub is_no_inherit: bool, + + pub raw_expr: ::core::option::Option>, + + pub cooked_expr: String, + + pub generated_when: String, + + pub keys: Vec, + + pub including: Vec, + + pub exclusions: Vec, + + pub options: Vec, + + pub indexname: String, + + pub indexspace: String, + + pub reset_default_tblspc: bool, + + pub access_method: String, + + pub where_clause: ::core::option::Option>, + + pub pktable: ::core::option::Option, + + pub fk_attrs: Vec, + + pub pk_attrs: Vec, + + pub fk_matchtype: String, + + pub fk_upd_action: String, + + pub fk_del_action: String, + + pub old_conpfeqop: Vec, + + pub old_pktable_oid: u32, + + pub skip_validation: bool, + + pub initially_valid: bool, +} +#[derive(Clone, PartialEq)] +pub struct DefElem { + pub defnamespace: String, + + pub defname: String, + + pub arg: ::core::option::Option>, + + pub defaction: i32, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct RangeTblEntry { + pub rtekind: i32, + + pub relid: u32, + + pub relkind: String, + + pub rellockmode: i32, + + pub tablesample: ::core::option::Option>, + + pub subquery: ::core::option::Option>, + + pub security_barrier: bool, + + pub jointype: i32, + + pub joinmergedcols: i32, + + pub joinaliasvars: Vec, + + pub joinleftcols: Vec, + + pub joinrightcols: Vec, + + pub functions: Vec, + + pub funcordinality: bool, + + pub tablefunc: ::core::option::Option>, + + pub values_lists: Vec, + + pub ctename: String, + + pub ctelevelsup: u32, + + pub self_reference: bool, + + pub coltypes: Vec, + + pub coltypmods: Vec, + + pub colcollations: Vec, + + pub enrname: String, + + pub enrtuples: f64, + + pub alias: ::core::option::Option, + + pub eref: ::core::option::Option, + + pub lateral: bool, + + pub inh: bool, + + pub in_from_cl: bool, + + pub required_perms: u32, + + pub check_as_user: u32, + + pub selected_cols: Vec, + + pub inserted_cols: Vec, + + pub updated_cols: Vec, + + pub extra_updated_cols: Vec, + + pub security_quals: Vec, +} +#[derive(Clone, PartialEq)] +pub struct RangeTblFunction { + pub funcexpr: ::core::option::Option>, + + pub funccolcount: i32, + + pub funccolnames: Vec, + + pub funccoltypes: Vec, + + pub funccoltypmods: Vec, + + pub funccolcollations: Vec, + + pub funcparams: Vec, +} +#[derive(Clone, PartialEq)] +pub struct TableSampleClause { + pub tsmhandler: u32, + + pub args: Vec, + + pub repeatable: ::core::option::Option>, +} +#[derive(Clone, PartialEq)] +pub struct WithCheckOption { + pub kind: i32, + + pub relname: String, + + pub polname: String, + + pub qual: ::core::option::Option>, + + pub cascaded: bool, +} +#[derive(Clone, PartialEq)] +pub struct SortGroupClause { + pub tle_sort_group_ref: u32, + + pub eqop: u32, + + pub sortop: u32, + + pub nulls_first: bool, + + pub hashable: bool, +} +#[derive(Clone, PartialEq)] +pub struct GroupingSet { + pub kind: i32, + + pub content: Vec, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct WindowClause { + pub name: String, + + pub refname: String, + + pub partition_clause: Vec, + + pub order_clause: Vec, + + pub frame_options: i32, + + pub start_offset: ::core::option::Option>, + + pub end_offset: ::core::option::Option>, + + pub start_in_range_func: u32, + + pub end_in_range_func: u32, + + pub in_range_coll: u32, + + pub in_range_asc: bool, + + pub in_range_nulls_first: bool, + + pub winref: u32, + + pub copied_order: bool, +} +#[derive(Clone, PartialEq)] +pub struct ObjectWithArgs { + pub objname: Vec, + + pub objargs: Vec, + + pub args_unspecified: bool, +} +#[derive(Clone, PartialEq)] +pub struct AccessPriv { + pub priv_name: String, + + pub cols: Vec, +} +#[derive(Clone, PartialEq)] +pub struct CreateOpClassItem { + pub itemtype: i32, + + pub name: ::core::option::Option, + + pub number: i32, + + pub order_family: Vec, + + pub class_args: Vec, + + pub storedtype: ::core::option::Option, +} +#[derive(Clone, PartialEq)] +pub struct TableLikeClause { + pub relation: ::core::option::Option, + + pub options: u32, + + pub relation_oid: u32, +} +#[derive(Clone, PartialEq)] +pub struct FunctionParameter { + pub name: String, + + pub arg_type: ::core::option::Option, + + pub mode: i32, + + pub defexpr: ::core::option::Option>, +} +#[derive(Clone, PartialEq)] +pub struct LockingClause { + pub locked_rels: Vec, + + pub strength: i32, + + pub wait_policy: i32, +} +#[derive(Clone, PartialEq)] +pub struct RowMarkClause { + pub rti: u32, + + pub strength: i32, + + pub wait_policy: i32, + + pub pushed_down: bool, +} +#[derive(Clone, PartialEq)] +pub struct XmlSerialize { + pub xmloption: i32, + + pub expr: ::core::option::Option>, + + pub type_name: ::core::option::Option, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct WithClause { + pub ctes: Vec, + + pub recursive: bool, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct InferClause { + pub index_elems: Vec, + + pub where_clause: ::core::option::Option>, + + pub conname: String, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct OnConflictClause { + pub action: i32, + + pub infer: ::core::option::Option>, + + pub target_list: Vec, + + pub where_clause: ::core::option::Option>, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct CommonTableExpr { + pub ctename: String, + + pub aliascolnames: Vec, + + pub ctematerialized: i32, + + pub ctequery: ::core::option::Option>, + + pub location: i32, + + pub cterecursive: bool, + + pub cterefcount: i32, + + pub ctecolnames: Vec, + + pub ctecoltypes: Vec, + + pub ctecoltypmods: Vec, + + pub ctecolcollations: Vec, +} +#[derive(Clone, PartialEq)] +pub struct RoleSpec { + pub roletype: i32, + + pub rolename: String, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct TriggerTransition { + pub name: String, + + pub is_new: bool, + + pub is_table: bool, +} +#[derive(Clone, PartialEq)] +pub struct PartitionElem { + pub name: String, + + pub expr: ::core::option::Option>, + + pub collation: Vec, + + pub opclass: Vec, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct PartitionSpec { + pub strategy: String, + + pub part_params: Vec, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct PartitionBoundSpec { + pub strategy: String, + + pub is_default: bool, + + pub modulus: i32, + + pub remainder: i32, + + pub listdatums: Vec, + + pub lowerdatums: Vec, + + pub upperdatums: Vec, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct PartitionRangeDatum { + pub kind: i32, + + pub value: ::core::option::Option>, + + pub location: i32, +} +#[derive(Clone, PartialEq)] +pub struct PartitionCmd { + pub name: ::core::option::Option, + + pub bound: ::core::option::Option, +} +#[derive(Clone, PartialEq)] +pub struct VacuumRelation { + pub relation: ::core::option::Option, + + pub oid: u32, + + pub va_cols: Vec, +} +#[derive(Clone, PartialEq)] +pub struct InlineCodeBlock { + pub source_text: String, + + pub lang_oid: u32, + + pub lang_is_trusted: bool, + + pub atomic: bool, +} +#[derive(Clone, PartialEq)] +pub struct CallContext { + pub atomic: bool, +} +#[derive(Clone, PartialEq)] +pub struct ScanToken { + pub start: i32, + + pub end: i32, + + pub token: i32, + + pub keyword_kind: i32, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum OverridingKind { + Undefined = 0, + OverridingNotSet = 1, + OverridingUserValue = 2, + OverridingSystemValue = 3, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum QuerySource { + Undefined = 0, + QsrcOriginal = 1, + QsrcParser = 2, + QsrcInsteadRule = 3, + QsrcQualInsteadRule = 4, + QsrcNonInsteadRule = 5, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum SortByDir { + Undefined = 0, + SortbyDefault = 1, + SortbyAsc = 2, + SortbyDesc = 3, + SortbyUsing = 4, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum SortByNulls { + Undefined = 0, + SortbyNullsDefault = 1, + SortbyNullsFirst = 2, + SortbyNullsLast = 3, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum AExprKind { + Undefined = 0, + AexprOp = 1, + AexprOpAny = 2, + AexprOpAll = 3, + AexprDistinct = 4, + AexprNotDistinct = 5, + AexprNullif = 6, + AexprOf = 7, + AexprIn = 8, + AexprLike = 9, + AexprIlike = 10, + AexprSimilar = 11, + AexprBetween = 12, + AexprNotBetween = 13, + AexprBetweenSym = 14, + AexprNotBetweenSym = 15, + AexprParen = 16, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum RoleSpecType { + Undefined = 0, + RolespecCstring = 1, + RolespecCurrentUser = 2, + RolespecSessionUser = 3, + RolespecPublic = 4, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum TableLikeOption { + Undefined = 0, + CreateTableLikeComments = 1, + CreateTableLikeConstraints = 2, + CreateTableLikeDefaults = 3, + CreateTableLikeGenerated = 4, + CreateTableLikeIdentity = 5, + CreateTableLikeIndexes = 6, + CreateTableLikeStatistics = 7, + CreateTableLikeStorage = 8, + CreateTableLikeAll = 9, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum DefElemAction { + Undefined = 0, + DefelemUnspec = 1, + DefelemSet = 2, + DefelemAdd = 3, + DefelemDrop = 4, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum PartitionRangeDatumKind { + Undefined = 0, + PartitionRangeDatumMinvalue = 1, + PartitionRangeDatumValue = 2, + PartitionRangeDatumMaxvalue = 3, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum RteKind { + RtekindUndefined = 0, + RteRelation = 1, + RteSubquery = 2, + RteJoin = 3, + RteFunction = 4, + RteTablefunc = 5, + RteValues = 6, + RteCte = 7, + RteNamedtuplestore = 8, + RteResult = 9, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum WcoKind { + WcokindUndefined = 0, + WcoViewCheck = 1, + WcoRlsInsertCheck = 2, + WcoRlsUpdateCheck = 3, + WcoRlsConflictCheck = 4, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum GroupingSetKind { + Undefined = 0, + GroupingSetEmpty = 1, + GroupingSetSimple = 2, + GroupingSetRollup = 3, + GroupingSetCube = 4, + GroupingSetSets = 5, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum CteMaterialize { + CtematerializeUndefined = 0, + Default = 1, + Always = 2, + Never = 3, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum SetOperation { + Undefined = 0, + SetopNone = 1, + SetopUnion = 2, + SetopIntersect = 3, + SetopExcept = 4, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum ObjectType { + Undefined = 0, + ObjectAccessMethod = 1, + ObjectAggregate = 2, + ObjectAmop = 3, + ObjectAmproc = 4, + ObjectAttribute = 5, + ObjectCast = 6, + ObjectColumn = 7, + ObjectCollation = 8, + ObjectConversion = 9, + ObjectDatabase = 10, + ObjectDefault = 11, + ObjectDefacl = 12, + ObjectDomain = 13, + ObjectDomconstraint = 14, + ObjectEventTrigger = 15, + ObjectExtension = 16, + ObjectFdw = 17, + ObjectForeignServer = 18, + ObjectForeignTable = 19, + ObjectFunction = 20, + ObjectIndex = 21, + ObjectLanguage = 22, + ObjectLargeobject = 23, + ObjectMatview = 24, + ObjectOpclass = 25, + ObjectOperator = 26, + ObjectOpfamily = 27, + ObjectPolicy = 28, + ObjectProcedure = 29, + ObjectPublication = 30, + ObjectPublicationRel = 31, + ObjectRole = 32, + ObjectRoutine = 33, + ObjectRule = 34, + ObjectSchema = 35, + ObjectSequence = 36, + ObjectSubscription = 37, + ObjectStatisticExt = 38, + ObjectTabconstraint = 39, + ObjectTable = 40, + ObjectTablespace = 41, + ObjectTransform = 42, + ObjectTrigger = 43, + ObjectTsconfiguration = 44, + ObjectTsdictionary = 45, + ObjectTsparser = 46, + ObjectTstemplate = 47, + ObjectType = 48, + ObjectUserMapping = 49, + ObjectView = 50, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum DropBehavior { + Undefined = 0, + DropRestrict = 1, + DropCascade = 2, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum AlterTableType { + Undefined = 0, + AtAddColumn = 1, + AtAddColumnRecurse = 2, + AtAddColumnToView = 3, + AtColumnDefault = 4, + AtCookedColumnDefault = 5, + AtDropNotNull = 6, + AtSetNotNull = 7, + AtDropExpression = 8, + AtCheckNotNull = 9, + AtSetStatistics = 10, + AtSetOptions = 11, + AtResetOptions = 12, + AtSetStorage = 13, + AtDropColumn = 14, + AtDropColumnRecurse = 15, + AtAddIndex = 16, + AtReAddIndex = 17, + AtAddConstraint = 18, + AtAddConstraintRecurse = 19, + AtReAddConstraint = 20, + AtReAddDomainConstraint = 21, + AtAlterConstraint = 22, + AtValidateConstraint = 23, + AtValidateConstraintRecurse = 24, + AtAddIndexConstraint = 25, + AtDropConstraint = 26, + AtDropConstraintRecurse = 27, + AtReAddComment = 28, + AtAlterColumnType = 29, + AtAlterColumnGenericOptions = 30, + AtChangeOwner = 31, + AtClusterOn = 32, + AtDropCluster = 33, + AtSetLogged = 34, + AtSetUnLogged = 35, + AtDropOids = 36, + AtSetTableSpace = 37, + AtSetRelOptions = 38, + AtResetRelOptions = 39, + AtReplaceRelOptions = 40, + AtEnableTrig = 41, + AtEnableAlwaysTrig = 42, + AtEnableReplicaTrig = 43, + AtDisableTrig = 44, + AtEnableTrigAll = 45, + AtDisableTrigAll = 46, + AtEnableTrigUser = 47, + AtDisableTrigUser = 48, + AtEnableRule = 49, + AtEnableAlwaysRule = 50, + AtEnableReplicaRule = 51, + AtDisableRule = 52, + AtAddInherit = 53, + AtDropInherit = 54, + AtAddOf = 55, + AtDropOf = 56, + AtReplicaIdentity = 57, + AtEnableRowSecurity = 58, + AtDisableRowSecurity = 59, + AtForceRowSecurity = 60, + AtNoForceRowSecurity = 61, + AtGenericOptions = 62, + AtAttachPartition = 63, + AtDetachPartition = 64, + AtAddIdentity = 65, + AtSetIdentity = 66, + AtDropIdentity = 67, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum GrantTargetType { + Undefined = 0, + AclTargetObject = 1, + AclTargetAllInSchema = 2, + AclTargetDefaults = 3, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum VariableSetKind { + Undefined = 0, + VarSetValue = 1, + VarSetDefault = 2, + VarSetCurrent = 3, + VarSetMulti = 4, + VarReset = 5, + VarResetAll = 6, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum ConstrType { + Undefined = 0, + ConstrNull = 1, + ConstrNotnull = 2, + ConstrDefault = 3, + ConstrIdentity = 4, + ConstrGenerated = 5, + ConstrCheck = 6, + ConstrPrimary = 7, + ConstrUnique = 8, + ConstrExclusion = 9, + ConstrForeign = 10, + ConstrAttrDeferrable = 11, + ConstrAttrNotDeferrable = 12, + ConstrAttrDeferred = 13, + ConstrAttrImmediate = 14, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum ImportForeignSchemaType { + Undefined = 0, + FdwImportSchemaAll = 1, + FdwImportSchemaLimitTo = 2, + FdwImportSchemaExcept = 3, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum RoleStmtType { + Undefined = 0, + RolestmtRole = 1, + RolestmtUser = 2, + RolestmtGroup = 3, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum FetchDirection { + Undefined = 0, + FetchForward = 1, + FetchBackward = 2, + FetchAbsolute = 3, + FetchRelative = 4, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum FunctionParameterMode { + Undefined = 0, + FuncParamIn = 1, + FuncParamOut = 2, + FuncParamInout = 3, + FuncParamVariadic = 4, + FuncParamTable = 5, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum TransactionStmtKind { + Undefined = 0, + TransStmtBegin = 1, + TransStmtStart = 2, + TransStmtCommit = 3, + TransStmtRollback = 4, + TransStmtSavepoint = 5, + TransStmtRelease = 6, + TransStmtRollbackTo = 7, + TransStmtPrepare = 8, + TransStmtCommitPrepared = 9, + TransStmtRollbackPrepared = 10, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum ViewCheckOption { + Undefined = 0, + NoCheckOption = 1, + LocalCheckOption = 2, + CascadedCheckOption = 3, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum ClusterOption { + Undefined = 0, + CluoptRecheck = 1, + CluoptVerbose = 2, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum DiscardMode { + Undefined = 0, + DiscardAll = 1, + DiscardPlans = 2, + DiscardSequences = 3, + DiscardTemp = 4, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum ReindexObjectType { + Undefined = 0, + ReindexObjectIndex = 1, + ReindexObjectTable = 2, + ReindexObjectSchema = 3, + ReindexObjectSystem = 4, + ReindexObjectDatabase = 5, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum AlterTsConfigType { + AlterTsconfigTypeUndefined = 0, + AlterTsconfigAddMapping = 1, + AlterTsconfigAlterMappingForToken = 2, + AlterTsconfigReplaceDict = 3, + AlterTsconfigReplaceDictForToken = 4, + AlterTsconfigDropMapping = 5, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum AlterSubscriptionType { + Undefined = 0, + AlterSubscriptionOptions = 1, + AlterSubscriptionConnection = 2, + AlterSubscriptionPublication = 3, + AlterSubscriptionRefresh = 4, + AlterSubscriptionEnabled = 5, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum OnCommitAction { + Undefined = 0, + OncommitNoop = 1, + OncommitPreserveRows = 2, + OncommitDeleteRows = 3, + OncommitDrop = 4, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum ParamKind { + Undefined = 0, + ParamExtern = 1, + ParamExec = 2, + ParamSublink = 3, + ParamMultiexpr = 4, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum CoercionContext { + Undefined = 0, + CoercionImplicit = 1, + CoercionAssignment = 2, + CoercionExplicit = 3, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum CoercionForm { + Undefined = 0, + CoerceExplicitCall = 1, + CoerceExplicitCast = 2, + CoerceImplicitCast = 3, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum BoolExprType { + Undefined = 0, + AndExpr = 1, + OrExpr = 2, + NotExpr = 3, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum SubLinkType { + Undefined = 0, + ExistsSublink = 1, + AllSublink = 2, + AnySublink = 3, + RowcompareSublink = 4, + ExprSublink = 5, + MultiexprSublink = 6, + ArraySublink = 7, + CteSublink = 8, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum RowCompareType { + Undefined = 0, + RowcompareLt = 1, + RowcompareLe = 2, + RowcompareEq = 3, + RowcompareGe = 4, + RowcompareGt = 5, + RowcompareNe = 6, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum MinMaxOp { + Undefined = 0, + IsGreatest = 1, + IsLeast = 2, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum SqlValueFunctionOp { + SqlvalueFunctionOpUndefined = 0, + SvfopCurrentDate = 1, + SvfopCurrentTime = 2, + SvfopCurrentTimeN = 3, + SvfopCurrentTimestamp = 4, + SvfopCurrentTimestampN = 5, + SvfopLocaltime = 6, + SvfopLocaltimeN = 7, + SvfopLocaltimestamp = 8, + SvfopLocaltimestampN = 9, + SvfopCurrentRole = 10, + SvfopCurrentUser = 11, + SvfopUser = 12, + SvfopSessionUser = 13, + SvfopCurrentCatalog = 14, + SvfopCurrentSchema = 15, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum XmlExprOp { + Undefined = 0, + IsXmlconcat = 1, + IsXmlelement = 2, + IsXmlforest = 3, + IsXmlparse = 4, + IsXmlpi = 5, + IsXmlroot = 6, + IsXmlserialize = 7, + IsDocument = 8, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum XmlOptionType { + Undefined = 0, + XmloptionDocument = 1, + XmloptionContent = 2, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum NullTestType { + Undefined = 0, + IsNull = 1, + IsNotNull = 2, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum BoolTestType { + Undefined = 0, + IsTrue = 1, + IsNotTrue = 2, + IsFalse = 3, + IsNotFalse = 4, + IsUnknown = 5, + IsNotUnknown = 6, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum CmdType { + Undefined = 0, + CmdUnknown = 1, + CmdSelect = 2, + CmdUpdate = 3, + CmdInsert = 4, + CmdDelete = 5, + CmdUtility = 6, + CmdNothing = 7, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum JoinType { + Undefined = 0, + JoinInner = 1, + JoinLeft = 2, + JoinFull = 3, + JoinRight = 4, + JoinSemi = 5, + JoinAnti = 6, + JoinUniqueOuter = 7, + JoinUniqueInner = 8, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum AggStrategy { + Undefined = 0, + AggPlain = 1, + AggSorted = 2, + AggHashed = 3, + AggMixed = 4, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum AggSplit { + Undefined = 0, + AggsplitSimple = 1, + AggsplitInitialSerial = 2, + AggsplitFinalDeserial = 3, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum SetOpCmd { + Undefined = 0, + SetopcmdIntersect = 1, + SetopcmdIntersectAll = 2, + SetopcmdExcept = 3, + SetopcmdExceptAll = 4, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum SetOpStrategy { + Undefined = 0, + SetopSorted = 1, + SetopHashed = 2, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum OnConflictAction { + Undefined = 0, + OnconflictNone = 1, + OnconflictNothing = 2, + OnconflictUpdate = 3, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum LimitOption { + Undefined = 0, + Default = 1, + Count = 2, + WithTies = 3, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum LockClauseStrength { + Undefined = 0, + LcsNone = 1, + LcsForkeyshare = 2, + LcsForshare = 3, + LcsFornokeyupdate = 4, + LcsForupdate = 5, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum LockWaitPolicy { + Undefined = 0, + LockWaitBlock = 1, + LockWaitSkip = 2, + LockWaitError = 3, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum LockTupleMode { + Undefined = 0, + LockTupleKeyShare = 1, + LockTupleShare = 2, + LockTupleNoKeyExclusive = 3, + LockTupleExclusive = 4, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum KeywordKind { + NoKeyword = 0, + UnreservedKeyword = 1, + ColNameKeyword = 2, + TypeFuncNameKeyword = 3, + ReservedKeyword = 4, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(i32)] +pub enum Token { + Nul = 0, + /// Single-character tokens that are returned 1:1 (identical with "self" list in scan.l) + /// Either supporting syntax, or single-character operators (some can be both) + /// Also see + /// + /// "%" + Ascii37 = 37, + /// "(" + Ascii40 = 40, + /// ")" + Ascii41 = 41, + /// "*" + Ascii42 = 42, + /// "+" + Ascii43 = 43, + /// "," + Ascii44 = 44, + /// "-" + Ascii45 = 45, + /// "." + Ascii46 = 46, + /// "/" + Ascii47 = 47, + /// ":" + Ascii58 = 58, + /// ";" + Ascii59 = 59, + /// "<" + Ascii60 = 60, + /// "=" + Ascii61 = 61, + /// ">" + Ascii62 = 62, + /// "?" + Ascii63 = 63, + /// "[" + Ascii91 = 91, + /// "\" + Ascii92 = 92, + /// "]" + Ascii93 = 93, + /// "^" + Ascii94 = 94, + /// Named tokens in scan.l + Ident = 258, + Uident = 259, + Fconst = 260, + Sconst = 261, + Usconst = 262, + Bconst = 263, + Xconst = 264, + Op = 265, + Iconst = 266, + Param = 267, + Typecast = 268, + DotDot = 269, + ColonEquals = 270, + EqualsGreater = 271, + LessEquals = 272, + GreaterEquals = 273, + NotEquals = 274, + SqlComment = 275, + CComment = 276, + AbortP = 277, + AbsoluteP = 278, + Access = 279, + Action = 280, + AddP = 281, + Admin = 282, + After = 283, + Aggregate = 284, + All = 285, + Also = 286, + Alter = 287, + Always = 288, + Analyse = 289, + Analyze = 290, + And = 291, + Any = 292, + Array = 293, + As = 294, + Asc = 295, + Assertion = 296, + Assignment = 297, + Asymmetric = 298, + At = 299, + Attach = 300, + Attribute = 301, + Authorization = 302, + Backward = 303, + Before = 304, + BeginP = 305, + Between = 306, + Bigint = 307, + Binary = 308, + Bit = 309, + BooleanP = 310, + Both = 311, + By = 312, + Cache = 313, + Call = 314, + Called = 315, + Cascade = 316, + Cascaded = 317, + Case = 318, + Cast = 319, + CatalogP = 320, + Chain = 321, + CharP = 322, + Character = 323, + Characteristics = 324, + Check = 325, + Checkpoint = 326, + Class = 327, + Close = 328, + Cluster = 329, + Coalesce = 330, + Collate = 331, + Collation = 332, + Column = 333, + Columns = 334, + Comment = 335, + Comments = 336, + Commit = 337, + Committed = 338, + Concurrently = 339, + Configuration = 340, + Conflict = 341, + Connection = 342, + Constraint = 343, + Constraints = 344, + ContentP = 345, + ContinueP = 346, + ConversionP = 347, + Copy = 348, + Cost = 349, + Create = 350, + Cross = 351, + Csv = 352, + Cube = 353, + CurrentP = 354, + CurrentCatalog = 355, + CurrentDate = 356, + CurrentRole = 357, + CurrentSchema = 358, + CurrentTime = 359, + CurrentTimestamp = 360, + CurrentUser = 361, + Cursor = 362, + Cycle = 363, + DataP = 364, + Database = 365, + DayP = 366, + Deallocate = 367, + Dec = 368, + DecimalP = 369, + Declare = 370, + Default = 371, + Defaults = 372, + Deferrable = 373, + Deferred = 374, + Definer = 375, + DeleteP = 376, + Delimiter = 377, + Delimiters = 378, + Depends = 379, + Desc = 380, + Detach = 381, + Dictionary = 382, + DisableP = 383, + Discard = 384, + Distinct = 385, + Do = 386, + DocumentP = 387, + DomainP = 388, + DoubleP = 389, + Drop = 390, + Each = 391, + Else = 392, + EnableP = 393, + Encoding = 394, + Encrypted = 395, + EndP = 396, + EnumP = 397, + Escape = 398, + Event = 399, + Except = 400, + Exclude = 401, + Excluding = 402, + Exclusive = 403, + Execute = 404, + Exists = 405, + Explain = 406, + Expression = 407, + Extension = 408, + External = 409, + Extract = 410, + FalseP = 411, + Family = 412, + Fetch = 413, + Filter = 414, + FirstP = 415, + FloatP = 416, + Following = 417, + For = 418, + Force = 419, + Foreign = 420, + Forward = 421, + Freeze = 422, + From = 423, + Full = 424, + Function = 425, + Functions = 426, + Generated = 427, + Global = 428, + Grant = 429, + Granted = 430, + Greatest = 431, + GroupP = 432, + Grouping = 433, + Groups = 434, + Handler = 435, + Having = 436, + HeaderP = 437, + Hold = 438, + HourP = 439, + IdentityP = 440, + IfP = 441, + Ilike = 442, + Immediate = 443, + Immutable = 444, + ImplicitP = 445, + ImportP = 446, + InP = 447, + Include = 448, + Including = 449, + Increment = 450, + Index = 451, + Indexes = 452, + Inherit = 453, + Inherits = 454, + Initially = 455, + InlineP = 456, + InnerP = 457, + Inout = 458, + InputP = 459, + Insensitive = 460, + Insert = 461, + Instead = 462, + IntP = 463, + Integer = 464, + Intersect = 465, + Interval = 466, + Into = 467, + Invoker = 468, + Is = 469, + Isnull = 470, + Isolation = 471, + Join = 472, + Key = 473, + Label = 474, + Language = 475, + LargeP = 476, + LastP = 477, + LateralP = 478, + Leading = 479, + Leakproof = 480, + Least = 481, + Left = 482, + Level = 483, + Like = 484, + Limit = 485, + Listen = 486, + Load = 487, + Local = 488, + Localtime = 489, + Localtimestamp = 490, + Location = 491, + LockP = 492, + Locked = 493, + Logged = 494, + Mapping = 495, + Match = 496, + Materialized = 497, + Maxvalue = 498, + Method = 499, + MinuteP = 500, + Minvalue = 501, + Mode = 502, + MonthP = 503, + Move = 504, + NameP = 505, + Names = 506, + National = 507, + Natural = 508, + Nchar = 509, + New = 510, + Next = 511, + Nfc = 512, + Nfd = 513, + Nfkc = 514, + Nfkd = 515, + No = 516, + None = 517, + Normalize = 518, + Normalized = 519, + Not = 520, + Nothing = 521, + Notify = 522, + Notnull = 523, + Nowait = 524, + NullP = 525, + Nullif = 526, + NullsP = 527, + Numeric = 528, + ObjectP = 529, + Of = 530, + Off = 531, + Offset = 532, + Oids = 533, + Old = 534, + On = 535, + Only = 536, + Operator = 537, + Option = 538, + Options = 539, + Or = 540, + Order = 541, + Ordinality = 542, + Others = 543, + OutP = 544, + OuterP = 545, + Over = 546, + Overlaps = 547, + Overlay = 548, + Overriding = 549, + Owned = 550, + Owner = 551, + Parallel = 552, + Parser = 553, + Partial = 554, + Partition = 555, + Passing = 556, + Password = 557, + Placing = 558, + Plans = 559, + Policy = 560, + Position = 561, + Preceding = 562, + Precision = 563, + Preserve = 564, + Prepare = 565, + Prepared = 566, + Primary = 567, + Prior = 568, + Privileges = 569, + Procedural = 570, + Procedure = 571, + Procedures = 572, + Program = 573, + Publication = 574, + Quote = 575, + Range = 576, + Read = 577, + Real = 578, + Reassign = 579, + Recheck = 580, + Recursive = 581, + Ref = 582, + References = 583, + Referencing = 584, + Refresh = 585, + Reindex = 586, + RelativeP = 587, + Release = 588, + Rename = 589, + Repeatable = 590, + Replace = 591, + Replica = 592, + Reset = 593, + Restart = 594, + Restrict = 595, + Returning = 596, + Returns = 597, + Revoke = 598, + Right = 599, + Role = 600, + Rollback = 601, + Rollup = 602, + Routine = 603, + Routines = 604, + Row = 605, + Rows = 606, + Rule = 607, + Savepoint = 608, + Schema = 609, + Schemas = 610, + Scroll = 611, + Search = 612, + SecondP = 613, + Security = 614, + Select = 615, + Sequence = 616, + Sequences = 617, + Serializable = 618, + Server = 619, + Session = 620, + SessionUser = 621, + Set = 622, + Sets = 623, + Setof = 624, + Share = 625, + Show = 626, + Similar = 627, + Simple = 628, + Skip = 629, + Smallint = 630, + Snapshot = 631, + Some = 632, + SqlP = 633, + Stable = 634, + StandaloneP = 635, + Start = 636, + Statement = 637, + Statistics = 638, + Stdin = 639, + Stdout = 640, + Storage = 641, + Stored = 642, + StrictP = 643, + StripP = 644, + Subscription = 645, + Substring = 646, + Support = 647, + Symmetric = 648, + Sysid = 649, + SystemP = 650, + Table = 651, + Tables = 652, + Tablesample = 653, + Tablespace = 654, + Temp = 655, + Template = 656, + Temporary = 657, + TextP = 658, + Then = 659, + Ties = 660, + Time = 661, + Timestamp = 662, + To = 663, + Trailing = 664, + Transaction = 665, + Transform = 666, + Treat = 667, + Trigger = 668, + Trim = 669, + TrueP = 670, + Truncate = 671, + Trusted = 672, + TypeP = 673, + TypesP = 674, + Uescape = 675, + Unbounded = 676, + Uncommitted = 677, + Unencrypted = 678, + Union = 679, + Unique = 680, + Unknown = 681, + Unlisten = 682, + Unlogged = 683, + Until = 684, + Update = 685, + User = 686, + Using = 687, + Vacuum = 688, + Valid = 689, + Validate = 690, + Validator = 691, + ValueP = 692, + Values = 693, + Varchar = 694, + Variadic = 695, + Varying = 696, + Verbose = 697, + VersionP = 698, + View = 699, + Views = 700, + Volatile = 701, + When = 702, + Where = 703, + WhitespaceP = 704, + Window = 705, + With = 706, + Within = 707, + Without = 708, + Work = 709, + Wrapper = 710, + Write = 711, + XmlP = 712, + Xmlattributes = 713, + Xmlconcat = 714, + Xmlelement = 715, + Xmlexists = 716, + Xmlforest = 717, + Xmlnamespaces = 718, + Xmlparse = 719, + Xmlpi = 720, + Xmlroot = 721, + Xmlserialize = 722, + Xmltable = 723, + YearP = 724, + YesP = 725, + Zone = 726, + NotLa = 727, + NullsLa = 728, + WithLa = 729, + Postfixop = 730, + Uminus = 731, +} From b26c86b51afa013815fc54404ef033f14541c707 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 30 Apr 2022 14:07:43 -0700 Subject: [PATCH 177/401] Update GitHub Actions actions/checkout@v2 to v3 The v2 implementation uses Node 12, which is end-of-life on April 30, 2022. See https://nodejs.org/en/about/releases/. Update to v3, which is based on Node 16 whose support lasts until April 30, 2024. --- .github/workflows/integration.yml | 2 +- .github/workflows/linux.yml | 2 +- .github/workflows/mac.yml | 2 +- .github/workflows/rustdoc_check.yml | 2 +- .github/workflows/upload-assets.yml | 2 +- .github/workflows/windows.yml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index b79221d05436..4d8899b434bb 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -69,7 +69,7 @@ jobs: steps: - name: checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 # Run build - name: install rustup diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 45f63b83c056..6a3f9d89d98f 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -26,7 +26,7 @@ jobs: steps: - name: checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 # Run build - name: install rustup diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml index 55e1cc9539b8..7dfda3142ca9 100644 --- a/.github/workflows/mac.yml +++ b/.github/workflows/mac.yml @@ -23,7 +23,7 @@ jobs: steps: - name: checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 # Run build - name: install rustup diff --git a/.github/workflows/rustdoc_check.yml b/.github/workflows/rustdoc_check.yml index ca96d30f5863..cd0c3218971e 100644 --- a/.github/workflows/rustdoc_check.yml +++ b/.github/workflows/rustdoc_check.yml @@ -11,7 +11,7 @@ jobs: name: rustdoc check steps: - name: checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: install rustup run: | diff --git a/.github/workflows/upload-assets.yml b/.github/workflows/upload-assets.yml index f4dd39444530..25699234a1ec 100644 --- a/.github/workflows/upload-assets.yml +++ b/.github/workflows/upload-assets.yml @@ -31,7 +31,7 @@ jobs: target: x86_64-pc-windows-msvc runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 # Run build - name: install rustup diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index dcb08b5412ea..4ebc29638490 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -33,7 +33,7 @@ jobs: - name: disable git eol translation run: git config --global core.autocrlf false - name: checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 # Run build - name: Install Rustup using win.rustup.rs From c03e1842f637e34b23297fa3a0aeeb0fcdca1c47 Mon Sep 17 00:00:00 2001 From: cuishuang Date: Sat, 7 May 2022 23:56:21 +0800 Subject: [PATCH 178/401] fix some typos Signed-off-by: cuishuang --- src/parse/session.rs | 2 +- tests/rustfmt/main.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/parse/session.rs b/src/parse/session.rs index 7571e6d078a7..55050571db79 100644 --- a/src/parse/session.rs +++ b/src/parse/session.rs @@ -152,7 +152,7 @@ impl ParseSess { /// * `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. + /// * `dir_path` - Module resolution will occur relative to this directory. pub(crate) fn default_submod_path( &self, id: symbol::Ident, diff --git a/tests/rustfmt/main.rs b/tests/rustfmt/main.rs index 450051d2fec6..4c6d52726f3f 100644 --- a/tests/rustfmt/main.rs +++ b/tests/rustfmt/main.rs @@ -143,7 +143,7 @@ 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` + // Module resolution fails because we're unable to find `./a/b.rs` #[cfg(not(windows))] assert!(stderr.contains("a/b.rs does not exist")); #[cfg(windows)] From 3cc1f5ed5b04ea6512b710e231c147b4546f8e52 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Thu, 28 Apr 2022 23:08:22 -0400 Subject: [PATCH 179/401] Add tests for issue 4573 It's unclear which PR resolved this issue, however the behavior of adding inline comments to the next line can't be reproduced. These test cases should serve to prevent a regression. --- tests/target/issue_4573.rs | 245 +++++++++++++++++++++++++++++++++++++ 1 file changed, 245 insertions(+) create mode 100644 tests/target/issue_4573.rs diff --git a/tests/target/issue_4573.rs b/tests/target/issue_4573.rs new file mode 100644 index 000000000000..82cfe4f53598 --- /dev/null +++ b/tests/target/issue_4573.rs @@ -0,0 +1,245 @@ +// rustmft-version:Two +// rustmft-use_small_heuristics:Max +// rustmft-merge_derives:false +// These are the same rustfmt configuration options that are used +// in the comiler as of ce39461ca75a and 8eb7c58dbb7b +// These are commits in https://github.com/rust-lang/rust + +#![no_std] // inner attribute comment +// inner attribute comment +#![no_implicit_prelude] +// post inner attribute comment + +#[cfg(not(miri))] // inline comment +#[no_link] +extern crate foo; + +// before attributes +#[no_link] +// between attributes +#[cfg(not(miri))] // inline comment +extern crate foo as bar; + +#[cfg(not(miri))] // inline comment +// between attribute and use +use foo; + +#[cfg(not(miri))] // inline comment +use foo; + +/* pre attributre */ +#[cfg(not(miri))] +use foo::bar; + +#[cfg(not(miri))] // inline comment +use foo::bar as FooBar; + +#[cfg(not(miri))] // inline comment +#[allow(unused)] +#[deprecated( + since = "5.2", // inline inner comment + note = "FOO was rarely used. Users should instead use BAR" +)] +#[allow(unused)] +static FOO: i32 = 42; + +#[used] +#[export_name = "FOO"] +#[cfg(not(miri))] // inline comment +#[deprecated( + since = "5.2", + note = "FOO was rarely used. Users should instead use BAR" +)] +static FOO: i32 = 42; + +#[cfg(not(miri))] // inline comment +#[export_name = "FOO"] +static BAR: &'static str = "bar"; + +#[cfg(not(miri))] // inline comment +const BAR: i32 = 42; + +#[cfg(not(miri))] // inline comment +#[no_mangle] +#[link_section = ".example_section"] +fn foo(bar: usize) { + #[cfg(not(miri))] // inline comment + println!("hello world!"); +} + +#[cfg(not(miri))] // inline comment +mod foo {} + +#[cfg(not(miri))] // inline comment +extern "C" { + fn my_c_function(x: i32) -> bool; +} + +#[cfg(not(miri))] // inline comment +#[link(name = "CoreFoundation", kind = "framework")] +extern "C" { + + #[link_name = "actual_symbol_name"] // inline comment + // between attribute and function + fn my_c_function(x: i32) -> bool; +} + +#[cfg(not(miri))] // inline comment +pub extern "C" fn callable_from_c(x: i32) -> bool { + x % 3 == 0 +} + +#[cfg(not(miri))] // inline comment +/* between attribute block comment */ +#[no_mangle] +/* between attribute and type */ +type Foo = Bar; + +#[no_mangle] +#[cfg(not(miri))] // inline comment +#[non_exhaustive] // inline comment +enum Foo { + Bar, + Baz, +} + +#[no_mangle] +#[cfg(not(miri))] /* inline comment */ +struct Foo { + x: A, +} + +#[cfg(not(miri))] // inline comment +union Foo { + x: A, + y: B, +} + +#[cfg(not(miri))] // inline comment +trait Foo {} + +#[cfg(not(miri))] // inline comment +trait Foo = Bar + Quux; + +#[cfg(not(miri))] // inline comment +impl Foo {} + +#[cfg(not(miri))] // inline comment +macro_rules! bar { + (3) => {}; +} + +mod nested { + #[cfg(not(miri))] // inline comment + // between attribute and use + use foo; + + #[cfg(not(miri))] // inline comment + use foo; + + #[cfg(not(miri))] // inline comment + use foo::bar; + + #[cfg(not(miri))] // inline comment + use foo::bar as FooBar; + + #[cfg(not(miri))] // inline comment + static FOO: i32 = 42; + + #[cfg(not(miri))] // inline comment + static FOO: i32 = 42; + + #[cfg(not(miri))] // inline comment + static FOO: &'static str = "bar"; + + #[cfg(not(miri))] // inline comment + const FOO: i32 = 42; + + #[cfg(not(miri))] // inline comment + fn foo(bar: usize) { + #[cfg(not(miri))] // inline comment + println!("hello world!"); + } + + #[cfg(not(miri))] // inline comment + mod foo {} + + #[cfg(not(miri))] // inline comment + mod foo {} + + #[cfg(not(miri))] // inline comment + extern "C" { + fn my_c_function(x: i32) -> bool; + } + + #[cfg(not(miri))] // inline comment + #[link(name = "CoreFoundation", kind = "framework")] + extern "C" { + + #[link_name = "actual_symbol_name"] // inline comment + // between attribute and function + fn my_c_function(x: i32) -> bool; + } + + #[cfg(not(miri))] // inline comment + pub extern "C" fn callable_from_c(x: i32) -> bool { + x % 3 == 0 + } + + #[cfg(not(miri))] // inline comment + type Foo = Bar; + + #[cfg(not(miri))] // inline comment + #[non_exhaustive] // inline comment + enum Foo { + // comment + #[attribute_1] + #[attribute_2] // comment + // comment! + Bar, + /* comment */ + #[attribute_1] + #[attribute_2] /* comment */ + #[attribute_3] + #[attribute_4] + /* comment! */ + Baz, + } + + #[cfg(not(miri))] // inline comment + struct Foo { + x: A, + } + + #[cfg(not(miri))] // inline comment + union Foo { + #[attribute_1] + #[attribute_2] /* comment */ + #[attribute_3] + #[attribute_4] // comment + x: A, + y: B, + } + + #[cfg(not(miri))] // inline comment + #[allow(missing_docs)] + trait Foo { + #[must_use] /* comment + * that wrappes to + * the next line */ + fn bar() {} + } + + #[allow(missing_docs)] + #[cfg(not(miri))] // inline comment + trait Foo = Bar + Quux; + + #[allow(missing_docs)] + #[cfg(not(miri))] // inline comment + impl Foo {} + + #[cfg(not(miri))] // inline comment + macro_rules! bar { + (3) => {}; + } +} From c65ba14d692bedab306b0426c36ab8f4fe4cbab2 Mon Sep 17 00:00:00 2001 From: Pascal Seitz Date: Sun, 13 Mar 2022 10:03:50 +0800 Subject: [PATCH 180/401] Fixes #5260 Fixes #5260 by checking if it is part of a type '::' --- src/comment.rs | 2 +- src/string.rs | 45 ++++++++++++++++++++++++++++++++------ tests/source/issue-5260.rs | 14 ++++++++++++ tests/target/issue-5260.rs | 13 +++++++++++ 4 files changed, 66 insertions(+), 8 deletions(-) create mode 100644 tests/source/issue-5260.rs create mode 100644 tests/target/issue-5260.rs diff --git a/src/comment.rs b/src/comment.rs index f9d8a0fa70c0..eb195b1f7628 100644 --- a/src/comment.rs +++ b/src/comment.rs @@ -796,7 +796,7 @@ impl<'a> CommentRewrite<'a> { // 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 + // 4) No URLS were found in the comment let should_wrap_comment = self.fmt.config.wrap_comments() && !is_markdown_header_doc_comment && unicode_str_width(line) > self.fmt.shape.width diff --git a/src/string.rs b/src/string.rs index b65aa5b33b24..78b72a50cb2f 100644 --- a/src/string.rs +++ b/src/string.rs @@ -315,20 +315,21 @@ fn break_string(max_width: usize, trim_end: bool, line_end: &str, input: &[&str] // Found a whitespace and what is on its left side is big enough. Some(index) if index >= MIN_STRING => break_at(index), // No whitespace found, try looking for a punctuation instead - _ => match input[0..max_width_index_in_input] - .iter() - .rposition(|grapheme| is_punctuation(grapheme)) + _ => match (0..max_width_index_in_input) + .rev() + .skip_while(|pos| !is_valid_linebreak(input, *pos)) + .next() { // Found a punctuation and what is on its left side is big enough. Some(index) if index >= MIN_STRING => break_at(index), // Either no boundary character was found to the left of `input[max_chars]`, or the line // got too small. We try searching for a boundary character to the right. - _ => match input[max_width_index_in_input..] - .iter() - .position(|grapheme| is_whitespace(grapheme) || is_punctuation(grapheme)) + _ => match (max_width_index_in_input..input.len()) + .skip_while(|pos| !is_valid_linebreak(input, *pos)) + .next() { // A boundary was found after the line limit - Some(index) => break_at(max_width_index_in_input + index), + Some(index) => break_at(index), // No boundary to the right, the input cannot be broken None => SnippetState::EndOfInput(input.concat()), }, @@ -336,6 +337,23 @@ fn break_string(max_width: usize, trim_end: bool, line_end: &str, input: &[&str] } } +fn is_valid_linebreak(input: &[&str], pos: usize) -> bool { + let is_whitespace = is_whitespace(input[pos]); + if is_whitespace { + return true; + } + let is_punctuation = is_punctuation(input[pos]); + if is_punctuation && !is_part_of_type(input, pos) { + return true; + } + false +} + +fn is_part_of_type(input: &[&str], pos: usize) -> bool { + input.get(pos..=pos + 1) == Some(&[":", ":"]) + || input.get(pos.saturating_sub(1)..=pos) == Some(&[":", ":"]) +} + fn is_new_line(grapheme: &str) -> bool { let bytes = grapheme.as_bytes(); bytes.starts_with(b"\n") || bytes.starts_with(b"\r\n") @@ -369,6 +387,19 @@ mod test { rewrite_string("eq_", &fmt, 2); } + #[test] + fn line_break_at_valid_points_test() { + let string = "[TheName](Dont::break::my::type::That::would::be::very::nice) break here"; + let graphemes = UnicodeSegmentation::graphemes(&*string, false).collect::>(); + assert_eq!( + break_string(20, false, "", &graphemes[..]), + SnippetState::LineEnd( + "[TheName](Dont::break::my::type::That::would::be::very::nice) ".to_string(), + 62 + ) + ); + } + #[test] fn should_break_on_whitespace() { let string = "Placerat felis. Mauris porta ante sagittis purus."; diff --git a/tests/source/issue-5260.rs b/tests/source/issue-5260.rs new file mode 100644 index 000000000000..c0606817290c --- /dev/null +++ b/tests/source/issue-5260.rs @@ -0,0 +1,14 @@ +// rustfmt-wrap_comments: true + +/// [MyType](VeryLongPathToMyType::NoLineBreak::Here::Okay::ThatWouldBeNice::Thanks) +fn documented_with_longtype() { + // # We're using a long type link, rustfmt should not break line + // on the type when `wrap_comments = true` +} + +/// VeryLongPathToMyType::JustMyType::But::VeryVery::Long::NoLineBreak::Here::Okay::ThatWouldBeNice::Thanks +fn documented_with_verylongtype() { + // # We're using a long type link, rustfmt should not break line + // on the type when `wrap_comments = true` +} + diff --git a/tests/target/issue-5260.rs b/tests/target/issue-5260.rs new file mode 100644 index 000000000000..171f6fa51b78 --- /dev/null +++ b/tests/target/issue-5260.rs @@ -0,0 +1,13 @@ +// rustfmt-wrap_comments: true + +/// [MyType](VeryLongPathToMyType::NoLineBreak::Here::Okay::ThatWouldBeNice::Thanks) +fn documented_with_longtype() { + // # We're using a long type link, rustfmt should not break line + // on the type when `wrap_comments = true` +} + +/// VeryLongPathToMyType::JustMyType::But::VeryVery::Long::NoLineBreak::Here::Okay::ThatWouldBeNice::Thanks +fn documented_with_verylongtype() { + // # We're using a long type link, rustfmt should not break line + // on the type when `wrap_comments = true` +} From 84fd4cdd022c7a6234006100b7e547eb2b178634 Mon Sep 17 00:00:00 2001 From: PSeitz Date: Mon, 9 May 2022 04:54:15 +0200 Subject: [PATCH 181/401] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b2336085835..0db1dffd6e0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### 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) +- Fixes an issue where types would be incorrectly wrapped in comments [#5260](https://github.com/rust-lang/rustfmt/issues/5260) ## [1.4.38] 2021-10-20 From 17003ce66b5737a71d0a08832995dc11df497ee2 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Mon, 9 May 2022 17:10:53 -0700 Subject: [PATCH 182/401] In the docs for `hex_literal_case`, show the default as a possible value --- Configurations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Configurations.md b/Configurations.md index dc8d38267a83..7afd4b5cd6a9 100644 --- a/Configurations.md +++ b/Configurations.md @@ -1065,7 +1065,7 @@ See also: [`tab_spaces`](#tab_spaces). Control the case of the letters in hexadecimal literal values - **Default value**: `Preserve` -- **Possible values**: `Upper`, `Lower` +- **Possible values**: `Preserve`, `Upper`, `Lower` - **Stable**: No (tracking issue: [#5081](https://github.com/rust-lang/rustfmt/issues/5081)) ## `hide_parse_errors` From 8a4c05865be17bac75b8d53eae5be18d749a0f5c Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Thu, 12 May 2022 00:44:25 +0900 Subject: [PATCH 183/401] Use the traits added to the Rust 2021 Edition prelude Follow up https://github.com/rust-lang/rust/pull/96861. This PR uses the traits added to the Rust 2021 Edition prelude. > The `TryInto`, `TryFrom` and `FromIterator` traits are now part of the prelude. https://doc.rust-lang.org/edition-guide/rust-2021/prelude.html --- src/cargo-fmt/main.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/cargo-fmt/main.rs b/src/cargo-fmt/main.rs index 3542536f29b6..55fd75f6de9b 100644 --- a/src/cargo-fmt/main.rs +++ b/src/cargo-fmt/main.rs @@ -10,7 +10,6 @@ use std::ffi::OsStr; use std::fs; use std::hash::{Hash, Hasher}; use std::io::{self, Write}; -use std::iter::FromIterator; use std::path::{Path, PathBuf}; use std::process::Command; use std::str; From b08b2daeb6da2f77be50a53cdca2098fb3bb11b2 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Thu, 31 Mar 2022 18:15:51 -0400 Subject: [PATCH 184/401] Add test for issue 3937 Closes 3937 It's unclear which change fixed the `format_code_in_doc_comments=true` issue brought up in this issue, however I'm unable to reproduce the error on the current master. The added test cases should serve to prevent a regression. --- tests/target/issue_3937.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 tests/target/issue_3937.rs diff --git a/tests/target/issue_3937.rs b/tests/target/issue_3937.rs new file mode 100644 index 000000000000..806731085005 --- /dev/null +++ b/tests/target/issue_3937.rs @@ -0,0 +1,13 @@ +// rustfmt-format_code_in_doc_comments:true + +struct Foo { + // a: i32, + // + // b: i32, +} + +struct Foo { + a: i32, + // + // b: i32, +} From 95837832f4bb89a681fd7fae57dad42fca497e23 Mon Sep 17 00:00:00 2001 From: David Lattimore Date: Sat, 30 Apr 2022 15:12:06 +1000 Subject: [PATCH 185/401] import_granularity: Don't normalize imports with comments --- src/imports.rs | 17 +++++++- tests/source/imports_granularity_crate.rs | 28 +++++++++++++ tests/source/imports_granularity_item.rs | 28 +++++++++++++ tests/source/imports_granularity_module.rs | 28 +++++++++++++ tests/source/imports_granularity_one.rs | 28 +++++++++++++ tests/target/imports_granularity_crate.rs | 31 +++++++++++++++ tests/target/imports_granularity_item.rs | 32 +++++++++++++++ tests/target/imports_granularity_module.rs | 39 ++++++++++++++++-- tests/target/imports_granularity_one.rs | 46 ++++++++++++++++++---- 9 files changed, 264 insertions(+), 13 deletions(-) diff --git a/src/imports.rs b/src/imports.rs index efe4e9498c90..962f2126c66c 100644 --- a/src/imports.rs +++ b/src/imports.rs @@ -181,6 +181,14 @@ impl UseSegment { } }) } + + fn contains_comment(&self) -> bool { + if let UseSegment::List(list) = self { + list.iter().any(|subtree| subtree.contains_comment()) + } else { + false + } + } } pub(crate) fn normalize_use_trees_with_granularity( @@ -197,7 +205,7 @@ pub(crate) fn normalize_use_trees_with_granularity( let mut result = Vec::with_capacity(use_trees.len()); for use_tree in use_trees { - if use_tree.has_comment() || use_tree.attrs.is_some() { + if use_tree.contains_comment() || use_tree.attrs.is_some() { result.push(use_tree); continue; } @@ -556,6 +564,10 @@ impl UseTree { self.list_item.as_ref().map_or(false, ListItem::has_comment) } + fn contains_comment(&self) -> bool { + self.has_comment() || self.path.iter().any(|path| path.contains_comment()) + } + fn same_visibility(&self, other: &UseTree) -> bool { match (&self.visibility, &other.visibility) { ( @@ -582,6 +594,7 @@ impl UseTree { if self.path.is_empty() || other.path.is_empty() || self.attrs.is_some() + || self.contains_comment() || !self.same_visibility(other) { false @@ -597,7 +610,7 @@ impl UseTree { } fn flatten(self, import_granularity: ImportGranularity) -> Vec { - if self.path.is_empty() { + if self.path.is_empty() || self.contains_comment() { return vec![self]; } match self.path.clone().last().unwrap() { diff --git a/tests/source/imports_granularity_crate.rs b/tests/source/imports_granularity_crate.rs index d16681b01b56..f6f7761e82ee 100644 --- a/tests/source/imports_granularity_crate.rs +++ b/tests/source/imports_granularity_crate.rs @@ -35,3 +35,31 @@ use j::{a::{self}}; use {k::{a, b}, l::{a, b}}; use {k::{c, d}, l::{c, d}}; + +use b::{f::g, h::{i, j} /* After b::h group */}; +use b::e; +use b::{/* Before b::l group */ l::{self, m, n::o, p::*}, q}; +use b::d; +use b::r; // After b::r +use b::q::{self /* After b::q::self */}; +use b::u::{ + a, + b, +}; +use b::t::{ + // Before b::t::a + a, + b, +}; +use b::s::{ + a, + b, // After b::s::b +}; +use b::v::{ + // Before b::v::a + a, + // Before b::v::b + b, +}; +use b::t::{/* Before b::t::self */ self}; +use b::c; diff --git a/tests/source/imports_granularity_item.rs b/tests/source/imports_granularity_item.rs index d0e94df66ae2..b82c0d33cafd 100644 --- a/tests/source/imports_granularity_item.rs +++ b/tests/source/imports_granularity_item.rs @@ -4,3 +4,31 @@ use a::{b, c, d}; use a::{f::g, h::{i, j}}; use a::{l::{self, m, n::o, p::*}}; use a::q::{self}; + +use b::{f::g, h::{i, j} /* After b::h group */}; +use b::e; +use b::{/* Before b::l group */ l::{self, m, n::o, p::*}, q}; +use b::d; +use b::r; // After b::r +use b::q::{self /* After b::q::self */}; +use b::u::{ + a, + b, +}; +use b::t::{ + // Before b::t::a + a, + b, +}; +use b::s::{ + a, + b, // After b::s::b +}; +use b::v::{ + // Before b::v::a + a, + // Before b::v::b + b, +}; +use b::t::{/* Before b::t::self */ self}; +use b::c; diff --git a/tests/source/imports_granularity_module.rs b/tests/source/imports_granularity_module.rs index 2d7bb299aaac..c7f68cea6d47 100644 --- a/tests/source/imports_granularity_module.rs +++ b/tests/source/imports_granularity_module.rs @@ -17,3 +17,31 @@ use bar::{ c::d, e::f, }; + +use b::{f::g, h::{i, j} /* After b::h group */}; +use b::e; +use b::{/* Before b::l group */ l::{self, m, n::o, p::*}, q}; +use b::d; +use b::r; // After b::r +use b::q::{self /* After b::q::self */}; +use b::u::{ + a, + b, +}; +use b::t::{ + // Before b::t::a + a, + b, +}; +use b::s::{ + a, + b, // After b::s::b +}; +use b::v::{ + // Before b::v::a + a, + // Before b::v::b + b, +}; +use b::t::{/* Before b::t::self */ self}; +use b::c; diff --git a/tests/source/imports_granularity_one.rs b/tests/source/imports_granularity_one.rs index c21707df3954..4d5a47956413 100644 --- a/tests/source/imports_granularity_one.rs +++ b/tests/source/imports_granularity_one.rs @@ -58,3 +58,31 @@ use a::{ }; use b as x; use a::ad::ada; + +use b::{f::g, h::{i, j} /* After b::h group */}; +use b::e; +use b::{/* Before b::l group */ l::{self, m, n::o, p::*}, q}; +use b::d; +use b::r; // After b::r +use b::q::{self /* After b::q::self */}; +use b::u::{ + a, + b, +}; +use b::t::{ + // Before b::t::a + a, + b, +}; +use b::s::{ + a, + b, // After b::s::b +}; +use b::v::{ + // Before b::v::a + a, + // Before b::v::b + b, +}; +use b::t::{/* Before b::t::self */ self}; +use b::c; diff --git a/tests/target/imports_granularity_crate.rs b/tests/target/imports_granularity_crate.rs index d75906d30f18..36e01558ff04 100644 --- a/tests/target/imports_granularity_crate.rs +++ b/tests/target/imports_granularity_crate.rs @@ -26,3 +26,34 @@ use j::a::{self}; use k::{a, b, c, d}; use l::{a, b, c, d}; + +use b::q::{self /* After b::q::self */}; +use b::r; // After b::r +use b::s::{ + a, + b, // After b::s::b +}; +use b::t::{/* Before b::t::self */ self}; +use b::t::{ + // Before b::t::a + a, + b, +}; +use b::v::{ + // Before b::v::a + a, + // Before b::v::b + b, +}; +use b::{ + c, d, e, + u::{a, b}, +}; +use b::{ + f::g, + h::{i, j}, /* After b::h group */ +}; +use b::{ + /* Before b::l group */ l::{self, m, n::o, p::*}, + q, +}; diff --git a/tests/target/imports_granularity_item.rs b/tests/target/imports_granularity_item.rs index eace785e6705..d2f5496fdaca 100644 --- a/tests/target/imports_granularity_item.rs +++ b/tests/target/imports_granularity_item.rs @@ -11,3 +11,35 @@ use a::l::n::o; use a::l::p::*; use a::l::{self}; use a::q::{self}; + +use b::c; +use b::d; +use b::e; +use b::q::{self /* After b::q::self */}; +use b::r; // After b::r +use b::s::{ + a, + b, // After b::s::b +}; +use b::t::{/* Before b::t::self */ self}; +use b::t::{ + // Before b::t::a + a, + b, +}; +use b::u::a; +use b::u::b; +use b::v::{ + // Before b::v::a + a, + // Before b::v::b + b, +}; +use b::{ + f::g, + h::{i, j}, /* After b::h group */ +}; +use b::{ + /* Before b::l group */ l::{self, m, n::o, p::*}, + q, +}; diff --git a/tests/target/imports_granularity_module.rs b/tests/target/imports_granularity_module.rs index e4e1a299e586..14f341016ff9 100644 --- a/tests/target/imports_granularity_module.rs +++ b/tests/target/imports_granularity_module.rs @@ -17,6 +17,39 @@ use foo::e; #[cfg(test)] use foo::{a::b, c::d}; -use bar::a::b; -use bar::c::d; -use bar::e::f; +use bar::{ + // comment + a::b, + // more comment + c::d, + e::f, +}; + +use b::q::{self /* After b::q::self */}; +use b::r; // After b::r +use b::s::{ + a, + b, // After b::s::b +}; +use b::t::{/* Before b::t::self */ self}; +use b::t::{ + // Before b::t::a + a, + b, +}; +use b::u::{a, b}; +use b::v::{ + // Before b::v::a + a, + // Before b::v::b + b, +}; +use b::{c, d, e}; +use b::{ + f::g, + h::{i, j}, /* After b::h group */ +}; +use b::{ + /* Before b::l group */ l::{self, m, n::o, p::*}, + q, +}; diff --git a/tests/target/imports_granularity_one.rs b/tests/target/imports_granularity_one.rs index 78ec5e7325c5..da4c6678db14 100644 --- a/tests/target/imports_granularity_one.rs +++ b/tests/target/imports_granularity_one.rs @@ -68,12 +68,42 @@ use { c::{self, ca}, }; -use { - a::{ - aa::{aaa, aab}, - ab, - ac::aca, - ad::ada, - }, - b as x, +use a::{ + // some comment + aa::{aaa, aab}, + ab, + // another comment + ac::aca, +}; +use {a::ad::ada, b as x}; + +use b::q::{self /* After b::q::self */}; +use b::r; // After b::r +use b::s::{ + a, + b, // After b::s::b +}; +use b::t::{/* Before b::t::self */ self}; +use b::t::{ + // Before b::t::a + a, + b, +}; +use b::v::{ + // Before b::v::a + a, + // Before b::v::b + b, +}; +use b::{ + c, d, e, + u::{a, b}, +}; +use b::{ + f::g, + h::{i, j}, /* After b::h group */ +}; +use b::{ + /* Before b::l group */ l::{self, m, n::o, p::*}, + q, }; From 0b6659cf02fd9bbdec1c0a5a90b48d1aff1ed5f8 Mon Sep 17 00:00:00 2001 From: Alexander Pozdneev Date: Sun, 22 May 2022 21:41:03 +0100 Subject: [PATCH 186/401] Update Configurations.md --- Configurations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Configurations.md b/Configurations.md index 7afd4b5cd6a9..5380275f8f9a 100644 --- a/Configurations.md +++ b/Configurations.md @@ -10,7 +10,7 @@ reorder_imports = false ``` Each configuration option is either stable or unstable. -Stable options can be used directly, while unstable options are opt-in. +Stable options can always be used, while unstable ones are only available on a nightly toolchain, and opt-in. To enable unstable options, set `unstable_features = true` in `rustfmt.toml` or pass `--unstable-features` to rustfmt. # Configuration Options From 37b48ca4b20fa9873297bcef9f25abf3fcdbd475 Mon Sep 17 00:00:00 2001 From: Alexander Pozdneev Date: Mon, 23 May 2022 10:07:19 +0100 Subject: [PATCH 187/401] Update Configurations.md Co-authored-by: Yacin Tmimi --- Configurations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Configurations.md b/Configurations.md index 5380275f8f9a..b3463fad19bd 100644 --- a/Configurations.md +++ b/Configurations.md @@ -10,7 +10,7 @@ reorder_imports = false ``` Each configuration option is either stable or unstable. -Stable options can always be used, while unstable ones are only available on a nightly toolchain, and opt-in. +Stable options can always be used, while unstable options are only available on a nightly toolchain and must be opted into. To enable unstable options, set `unstable_features = true` in `rustfmt.toml` or pass `--unstable-features` to rustfmt. # Configuration Options From a5cfd4d14522ae064c55ac776731b069ae91bb9c Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Sat, 9 Apr 2022 00:31:24 -0400 Subject: [PATCH 188/401] Updated Unreleased CHANGELOG entries from revision v1.4.38..2d9bc460 --- CHANGELOG.md | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0db1dffd6e0a..729c7516d1fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,62 @@ ## [Unreleased] +### Changed + +- Also apply `empty_item_single_line=true` to trait definitions to match the behavior of empty functions, struct, enums, and impls [#5047](https://github.com/rust-lang/rustfmt/issues/5047) + ### Fixed +- Block indent line breaks for type alias impl traits (TAITs) when `version = "Two"` [#5027](https://github.com/rust-lang/rustfmt/issues/5027) +- Retain trailing comments in module when using `#![rustfmt::skip]` [#5033](https://github.com/rust-lang/rustfmt/issues/5033) +- Remove trailing whitespace when formatting a where clause with an empty right hand side [#5012](https://github.com/rust-lang/rustfmt/issues/5012) [#4850](https://github.com/rust-lang/rustfmt/issues/4850) +- Fix various module resolution issues: + - Handle external mods imported via external->inline load hierarchy [#5063](https://github.com/rust-lang/rustfmt/issues/5063) + - Resolve sub modules of integration tests [#5119](https://github.com/rust-lang/rustfmt/issues/5119) + - Module resolution will fallback to the current search directory if a relative directory search results in a `FileNotFound` error [#5198](https://github.com/rust-lang/rustfmt/issues/5198) + - Give clearer error messsaeg when a module is found in two places (e.g `x.rs` and `x/mod.rs`) [#5167](https://github.com/rust-lang/rustfmt/issues/5167) +- Prevent rustfmt from adding an `impl Trait` definition into types [#5086](https://github.com/rust-lang/rustfmt/issues/5086) +- Correctly format associated type in macro body [#4823](https://github.com/rust-lang/rustfmt/issues/4823) +- Fix cases where `normalize_comments=true` would de-normalizes some comments [#4909](https://github.com/rust-lang/rustfmt/issues/4909) +- Prevent rustfmt from wrapping reference style links [#5095](https://github.com/rust-lang/rustfmt/issues/5095) and [#4933](https://github.com/rust-lang/rustfmt/issues/4933) +- Prevent rustfmt from always adding an empty comment line when formatting itemized blocks [#5088](https://github.com/rust-lang/rustfmt/issues/5088) +- Don't format files annotated with an inner `#![rustfmt::skip]` attribute [PR #5094](https://github.com/rust-lang/rustfmt/pull/5094) +- Remove duplicate comma when struct pattern ends with `..` and `trailing_comma=Always` [#5066](https://github.com/rust-lang/rustfmt/issues/5066) +- Fix static async closure qualifier order [#5149](https://github.com/rust-lang/rustfmt/issues/5149) +- Retain qualified path when rewriting struct literal expressions [#5151](https://github.com/rust-lang/rustfmt/issues/5151) +- Do not flatten match arm block with leading attributes [#4109](https://github.com/rust-lang/rustfmt/issues/4109) +- Backport json emitter and stdin changes [PR #5054](https://github.com/rust-lang/rustfmt/pull/5054) + - Make `--check` work when running rustfmt with input from stdin [PR #3896](https://github.com/rust-lang/rustfmt/pull/3896) + - Fix `--check` with the `--files-with-diff` flag [PR #3910](https://github.com/rust-lang/rustfmt/pull/3910) + - Produce valid JSON when using the JSON emitter [PR #3953](https://github.com/rust-lang/rustfmt/pull/3953) + - Fix newlines in JSON output [PR #4262](https://github.com/rust-lang/rustfmt/pull/4262) + - Use `` when emitting stdin as filename [PR #4298](https://github.com/rust-lang/rustfmt/pull/4298) +- Generate output when formatting `@generated` input via stdin [#5172](https://github.com/rust-lang/rustfmt/issues/5172) +- Fix comment indentation in empty structs [#4854](https://github.com/rust-lang/rustfmt/issues/4854) +- Prevent adding whitespace when rewriting `ast::Param` [#5125](https://github.com/rust-lang/rustfmt/issues/5125) +- Prevent panic when `wrap_comments=true` and non-ascii character at comment wrap boundary [#5023](https://github.com/rust-lang/rustfmt/issues/5023) +- Retain trailing commas in inline post comments [#5042](https://github.com/rust-lang/rustfmt/issues/5042) +- Fix `import_granularity` option when the use tree has an alias [#5131](https://github.com/rust-lang/rustfmt/issues/5131) +- Don't merge gereric items into their doc comments [#5122](https://github.com/rust-lang/rustfmt/issues/5122) - 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) +- Handle markdown block quotes when `wrap_comments=true` [#5157](https://github.com/rust-lang/rustfmt/issues/5157) +- Long markdown headers are no longer wrapped when `wrap_comments=true` [#5238](https://github.com/rust-lang/rustfmt/issues/5238) +- Retain trailing comma when struct fields are followed by an empty line [#4791](https://github.com/rust-lang/rustfmt/issues/4791) [#4928](https://github.com/rust-lang/rustfmt/issues/4928) +- Fix compile error when `imports_granularity=Module` and path contains self [#4681](https://github.com/rust-lang/rustfmt/issues/4681) - Fixes an issue where types would be incorrectly wrapped in comments [#5260](https://github.com/rust-lang/rustfmt/issues/5260) +- Don't duplicate parts of `const` expressions when formatting generic arguments [#5273](https://github.com/rust-lang/rustfmt/issues/5273) +- Prevent merging derives when using `#[rustfmt::skip::attributes(derive)]` [#5270](https://github.com/rust-lang/rustfmt/issues/5270) +- Retain trailing `;` when rewriting macro call in extern block [#5281](https://github.com/rust-lang/rustfmt/issues/5281) +- Add a newline when formatting struct fields preceeded by doc comments and inline comments [#5215](https://github.com/rust-lang/rustfmt/issues/5215) + +### Added + +- Added `One` as a new [group_imports](https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#group_imports) option to create a single group for all imports [PR #4966](https://github.com/rust-lang/rustfmt/pull/4966) +- Add [short_array_element_width_threshold](https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#short_array_element_width_threshold) config option [PR #5228](https://github.com/rust-lang/rustfmt/pull/5228) + +### Removed + +- Remove rustfmt executable path from usage string [#5214](https://github.com/rust-lang/rustfmt/issues/5214) ## [1.4.38] 2021-10-20 From 73be264f9f8be070bd2764323911f94f1529db4d Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Thu, 26 May 2022 16:06:46 -0400 Subject: [PATCH 189/401] Rephrase changelog entries to give more context --- CHANGELOG.md | 72 +++++++++++++++++++++++++++------------------------- 1 file changed, 37 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 729c7516d1fe..e729bfb84868 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,60 +4,62 @@ ### Changed -- Also apply `empty_item_single_line=true` to trait definitions to match the behavior of empty functions, struct, enums, and impls [#5047](https://github.com/rust-lang/rustfmt/issues/5047) +- Also apply `empty_item_single_line=true` to trait definitions to match the behavior of empty functions, structs, enums, and impls [#5047](https://github.com/rust-lang/rustfmt/issues/5047) ### Fixed -- Block indent line breaks for type alias impl traits (TAITs) when `version = "Two"` [#5027](https://github.com/rust-lang/rustfmt/issues/5027) -- Retain trailing comments in module when using `#![rustfmt::skip]` [#5033](https://github.com/rust-lang/rustfmt/issues/5033) -- Remove trailing whitespace when formatting a where clause with an empty right hand side [#5012](https://github.com/rust-lang/rustfmt/issues/5012) [#4850](https://github.com/rust-lang/rustfmt/issues/4850) -- Fix various module resolution issues: +- Prevent rustfmt from removing trailing comments at the end of files annotated with inner `#![rustfmt::skip]` attributes [#5033](https://github.com/rust-lang/rustfmt/issues/5033) +- Fixed various `error[internal]: left behind trailing whitespace"` issues: + - Remove trailing whitespace when formatting a where clause who's bounds have an empty right hand side [#5012](https://github.com/rust-lang/rustfmt/issues/5012) [#4850](https://github.com/rust-lang/rustfmt/issues/4850) + - Prevent rustfmt from adding an empty comment line when rewriting markdown lists at the start of doc comments. This issue was triggered when `wrap_comments=true` [#5088](https://github.com/rust-lang/rustfmt/issues/5088) +- Prevent adding a block indented newline before a function parameter with a complex type that was formatted over multiple lines [#5125](https://github.com/rust-lang/rustfmt/issues/5125) +- Fix various module resolution issues preventing rustfmt from finding modules that should be formatted: - Handle external mods imported via external->inline load hierarchy [#5063](https://github.com/rust-lang/rustfmt/issues/5063) - Resolve sub modules of integration tests [#5119](https://github.com/rust-lang/rustfmt/issues/5119) - Module resolution will fallback to the current search directory if a relative directory search results in a `FileNotFound` error [#5198](https://github.com/rust-lang/rustfmt/issues/5198) - - Give clearer error messsaeg when a module is found in two places (e.g `x.rs` and `x/mod.rs`) [#5167](https://github.com/rust-lang/rustfmt/issues/5167) -- Prevent rustfmt from adding an `impl Trait` definition into types [#5086](https://github.com/rust-lang/rustfmt/issues/5086) -- Correctly format associated type in macro body [#4823](https://github.com/rust-lang/rustfmt/issues/4823) -- Fix cases where `normalize_comments=true` would de-normalizes some comments [#4909](https://github.com/rust-lang/rustfmt/issues/4909) -- Prevent rustfmt from wrapping reference style links [#5095](https://github.com/rust-lang/rustfmt/issues/5095) and [#4933](https://github.com/rust-lang/rustfmt/issues/4933) -- Prevent rustfmt from always adding an empty comment line when formatting itemized blocks [#5088](https://github.com/rust-lang/rustfmt/issues/5088) -- Don't format files annotated with an inner `#![rustfmt::skip]` attribute [PR #5094](https://github.com/rust-lang/rustfmt/pull/5094) -- Remove duplicate comma when struct pattern ends with `..` and `trailing_comma=Always` [#5066](https://github.com/rust-lang/rustfmt/issues/5066) -- Fix static async closure qualifier order [#5149](https://github.com/rust-lang/rustfmt/issues/5149) -- Retain qualified path when rewriting struct literal expressions [#5151](https://github.com/rust-lang/rustfmt/issues/5151) -- Do not flatten match arm block with leading attributes [#4109](https://github.com/rust-lang/rustfmt/issues/4109) +- Give users a clearer error message when resolving a module who's file path is ambiguous (e.g `x.rs` and `x/mod.rs`). Before users were given a `File not found` error message which was confusing [#5167](https://github.com/rust-lang/rustfmt/issues/5167) +- Fix various issues related to type aliases: + - Prevent rustfmt from adding `= impl` to associated types defined in macro bodies [#4823](https://github.com/rust-lang/rustfmt/issues/4823) + - Properly block indent type alias impl traits (TAITs) that wrap to the next line when `version=Two` is set. Before any trait bounds that wrapped to the next line would not be indented [#5027](https://github.com/rust-lang/rustfmt/issues/5027) + - Prevent rustfmt from adding an `impl Trait` definition into types [#5086](https://github.com/rust-lang/rustfmt/issues/5086) +- Fix cases where `normalize_comments=true` would de-normalizes some comments by changing inline comments into block comments [#4909](https://github.com/rust-lang/rustfmt/issues/4909) +- Prevent rustfmt from wrapping the content of markdown [reference-style links](https://www.markdownguide.org/basic-syntax/#reference-style-links) in doc comments [#5095](https://github.com/rust-lang/rustfmt/issues/5095) [#4933](https://github.com/rust-lang/rustfmt/issues/4933) +- Don't format files annotated with inner `#![rustfmt::skip]` attribute [PR #5094](https://github.com/rust-lang/rustfmt/pull/5094) +- Prevent duplicate comma when struct pattern ends with `..` and `trailing_comma=Always`. For example, `let Foo { a, .. } = b;` would become `let Foo { a,, .. } = b;` [#5066](https://github.com/rust-lang/rustfmt/issues/5066) +- Fix the order of `static` and `async` keywords when rewriting static async closures. The correct order is `static` and then `async` (e.g `static async || {}`) [#5149](https://github.com/rust-lang/rustfmt/issues/5149) +- Retain the fully qualified path segment when rewriting struct literals in expression position. Now `::Type` is not rewritten as `Trait::Type` [#5151](https://github.com/rust-lang/rustfmt/issues/5151) +- Do not remove match arm braces from a match arm with a single `ast::ExprKind::Block` that has leading attributes. Removing the braces could lead to code that does not compile. Now rustfmt will leave the outer `{}` in place when formatting `=> {#[allow(unsafe_code)]unsafe {}}` [#4109](https://github.com/rust-lang/rustfmt/issues/4109) - Backport json emitter and stdin changes [PR #5054](https://github.com/rust-lang/rustfmt/pull/5054) - Make `--check` work when running rustfmt with input from stdin [PR #3896](https://github.com/rust-lang/rustfmt/pull/3896) - Fix `--check` with the `--files-with-diff` flag [PR #3910](https://github.com/rust-lang/rustfmt/pull/3910) - Produce valid JSON when using the JSON emitter [PR #3953](https://github.com/rust-lang/rustfmt/pull/3953) - Fix newlines in JSON output [PR #4262](https://github.com/rust-lang/rustfmt/pull/4262) - Use `` when emitting stdin as filename [PR #4298](https://github.com/rust-lang/rustfmt/pull/4298) -- Generate output when formatting `@generated` input via stdin [#5172](https://github.com/rust-lang/rustfmt/issues/5172) -- Fix comment indentation in empty structs [#4854](https://github.com/rust-lang/rustfmt/issues/4854) -- Prevent adding whitespace when rewriting `ast::Param` [#5125](https://github.com/rust-lang/rustfmt/issues/5125) -- Prevent panic when `wrap_comments=true` and non-ascii character at comment wrap boundary [#5023](https://github.com/rust-lang/rustfmt/issues/5023) -- Retain trailing commas in inline post comments [#5042](https://github.com/rust-lang/rustfmt/issues/5042) -- Fix `import_granularity` option when the use tree has an alias [#5131](https://github.com/rust-lang/rustfmt/issues/5131) -- Don't merge gereric items into their doc comments [#5122](https://github.com/rust-lang/rustfmt/issues/5122) -- 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) -- Handle markdown block quotes when `wrap_comments=true` [#5157](https://github.com/rust-lang/rustfmt/issues/5157) -- Long markdown headers are no longer wrapped when `wrap_comments=true` [#5238](https://github.com/rust-lang/rustfmt/issues/5238) -- Retain trailing comma when struct fields are followed by an empty line [#4791](https://github.com/rust-lang/rustfmt/issues/4791) [#4928](https://github.com/rust-lang/rustfmt/issues/4928) -- Fix compile error when `imports_granularity=Module` and path contains self [#4681](https://github.com/rust-lang/rustfmt/issues/4681) -- Fixes an issue where types would be incorrectly wrapped in comments [#5260](https://github.com/rust-lang/rustfmt/issues/5260) -- Don't duplicate parts of `const` expressions when formatting generic arguments [#5273](https://github.com/rust-lang/rustfmt/issues/5273) -- Prevent merging derives when using `#[rustfmt::skip::attributes(derive)]` [#5270](https://github.com/rust-lang/rustfmt/issues/5270) -- Retain trailing `;` when rewriting macro call in extern block [#5281](https://github.com/rust-lang/rustfmt/issues/5281) -- Add a newline when formatting struct fields preceeded by doc comments and inline comments [#5215](https://github.com/rust-lang/rustfmt/issues/5215) +- Always generate some output when formatting `@generated` files via stdin even when `format_generated_files=false`. Not producing output caused rust-analyzer to delete the file content [rust-lang/rust-analyzer](https://github.com/rust-lang/rust-analyzer/issues/11285) [#5172](https://github.com/rust-lang/rustfmt/issues/5172) +- Properly block indent multi-line comments in empty struct definitions. Previously, only the first comment line would be block indented. All other comment lines would be aligned with the struct definition [#4854](https://github.com/rust-lang/rustfmt/issues/4854) +- Prevent rustfmt from wrapping a comment at a byte position inside a non-ascii character when `wrap_comments=true`. This prevents rustfmt from panicking when breaking on the invalid position [#5023](https://github.com/rust-lang/rustfmt/issues/5023) +- Prevent rustfmt from removing commented out trailing separators (e.g commas) when rewriting lists. For example, remove the comma from a comment like this `// ...,` would lead to a scenario where the entire list could not be rewritten because the content of the comment changed [#5042](https://github.com/rust-lang/rustfmt/issues/5042) +- Fix panic when `import_granularity` was set to `Module`, `One`, or `Crate` and the import use declaration contained an alias `use crate a::b as b1` [#5131](https://github.com/rust-lang/rustfmt/issues/5131) +- Add a newline between generic parameters and their doc comments to prevent the generic parameters from being merged into their doc comments [#5122](https://github.com/rust-lang/rustfmt/issues/5122) +- Fixes indentation issue where string literals manually broken with line continuation characters (`\`) would be incorrectly indented in macro definitions when setting `format_strings=true`[#4036](https://github.com/rust-lang/rustfmt/issues/4036) +- Properly wrap and format long markdown block quotes when `wrap_comments=true` [#5157](https://github.com/rust-lang/rustfmt/issues/5157) +- Prevent rustfmt from wrapping markdown headers even when `wrap_comments=true`. Wrapping the markdown headers would prevent them from being properly rendered with rustdoc [#5238](https://github.com/rust-lang/rustfmt/issues/5238) +- Prevent rustfmt from removing commas between struct fields when those fields were also separated by an empty line [#4791](https://github.com/rust-lang/rustfmt/issues/4791) [#4928](https://github.com/rust-lang/rustfmt/issues/4928) +- Fix compiler error caused when formatting imports with `imports_granularity=Module` and a path containing `self`. Given the following import `use crate::lexer::{self, tokens::TokenData};`, rustfmt would transform the `self` import into `use crate::lexer::self;`. Now rustfmt produces `use crate::lexer::{self};` [#4681](https://github.com/rust-lang/rustfmt/issues/4681) +- Prevent rustfmt from breaking long type links in doc comments on namespace qualifiers (`::`) when `wrap_comments=true`. Breaking these long type links over multiple lines prevented them from being properly rendered in rustdoc [#5260](https://github.com/rust-lang/rustfmt/issues/5260) +- Correctly find the start of struct bodies after any generic `const` parameters. Naively searching for an opening `{` lead to issues since generic `const` parameters are also defined with `{}` (e.g. `struct Example {}`) [#5273](https://github.com/rust-lang/rustfmt/issues/5273) +- Prevent rustfmt from merging derives when using inner or outer `rustfmt::skip::attributes` attributes. For example, `#[rustfmt::skip::attributes(derive)]` [#5270](https://github.com/rust-lang/rustfmt/issues/5270) +- Retain trailing `;` when rewriting macro calls in extern blocks. For example, `extern "C" { x!(-); }`[#5281](https://github.com/rust-lang/rustfmt/issues/5281) +- Add a newline when formatting struct fields preceded by both doc comments and inline comments to prevent the field from being merged into the inline comment. This was not an issue when a struct was preceded by just a doc comment or just an inline comment [#5215](https://github.com/rust-lang/rustfmt/issues/5215) ### Added - Added `One` as a new [group_imports](https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#group_imports) option to create a single group for all imports [PR #4966](https://github.com/rust-lang/rustfmt/pull/4966) -- Add [short_array_element_width_threshold](https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#short_array_element_width_threshold) config option [PR #5228](https://github.com/rust-lang/rustfmt/pull/5228) +- Add [short_array_element_width_threshold](https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#short_array_element_width_threshold) config option to give users more control over when `Mixed` list formatting is used [PR #5228](https://github.com/rust-lang/rustfmt/pull/5228) ### Removed -- Remove rustfmt executable path from usage string [#5214](https://github.com/rust-lang/rustfmt/issues/5214) +- Remove rustfmt binary path from the usage string when running `rustfmt --help` [#5214](https://github.com/rust-lang/rustfmt/issues/5214) ## [1.4.38] 2021-10-20 From 3e38399ed89da3b7ed68b062ad82bdc0f2068685 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Thu, 26 May 2022 21:56:35 -0500 Subject: [PATCH 190/401] docs: clarify imports_granularity behavior with comments --- CHANGELOG.md | 1 + Configurations.md | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e729bfb84868..3664571fe64b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ ### Fixed +- Don't change granularity of imports containing comments with `imports_granularity` if doing so could lose or misplace those comments [#5311](https://github.com/rust-lang/rustfmt/pull/5311) - Prevent rustfmt from removing trailing comments at the end of files annotated with inner `#![rustfmt::skip]` attributes [#5033](https://github.com/rust-lang/rustfmt/issues/5033) - Fixed various `error[internal]: left behind trailing whitespace"` issues: - Remove trailing whitespace when formatting a where clause who's bounds have an empty right hand side [#5012](https://github.com/rust-lang/rustfmt/issues/5012) [#4850](https://github.com/rust-lang/rustfmt/issues/4850) diff --git a/Configurations.md b/Configurations.md index b3463fad19bd..669da79087c9 100644 --- a/Configurations.md +++ b/Configurations.md @@ -1705,6 +1705,8 @@ How imports should be grouped into `use` statements. Imports will be merged or s - **Possible values**: `Preserve`, `Crate`, `Module`, `Item`, `One` - **Stable**: No (tracking issue: [#4991](https://github.com/rust-lang/rustfmt/issues/4991)) +Note that rustfmt will not modify the granularity of imports containing comments if doing so could potentially lose or misplace said comments. + #### `Preserve` (default): Do not change the granularity of any imports and preserve the original structure written by the developer. From 4c8db8593905d09a872601c445005a594ac632dc Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Sun, 29 May 2022 21:35:12 -0500 Subject: [PATCH 191/401] feat: remove report_todo option --- Configurations.md | 17 ------ src/config/mod.rs | 3 - src/formatting.rs | 4 +- src/issues.rs | 110 ++++++----------------------------- src/test/mod.rs | 5 +- tests/config/small_tabs.toml | 1 - 6 files changed, 20 insertions(+), 120 deletions(-) diff --git a/Configurations.md b/Configurations.md index 669da79087c9..72499618755e 100644 --- a/Configurations.md +++ b/Configurations.md @@ -2180,23 +2180,6 @@ Warns about any comments containing `FIXME` in them when set to `"Always"`. If it contains a `#X` (with `X` being a number) in parentheses following the `FIXME`, `"Unnumbered"` will ignore it. -See also [`report_todo`](#report_todo). - - -## `report_todo` - -Report `TODO` items in comments. - -- **Default value**: `"Never"` -- **Possible values**: `"Always"`, `"Unnumbered"`, `"Never"` -- **Stable**: No (tracking issue: [#3393](https://github.com/rust-lang/rustfmt/issues/3393)) - -Warns about any comments containing `TODO` in them when set to `"Always"`. If -it contains a `#X` (with `X` being a number) in parentheses following the -`TODO`, `"Unnumbered"` will ignore it. - -See also [`report_fixme`](#report_fixme). - ## `required_version` Require a specific version of rustfmt. If you want to make sure that the diff --git a/src/config/mod.rs b/src/config/mod.rs index 18e1854612bf..4c14d7356891 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -164,8 +164,6 @@ create_config! { error_on_unformatted: bool, false, false, "Error if unable to get comments or string literals within max_width, \ or they are left with trailing whitespaces"; - report_todo: ReportTactic, ReportTactic::Never, false, - "Report all, none or unnumbered occurrences of TODO in source file comments"; report_fixme: ReportTactic, ReportTactic::Never, false, "Report all, none or unnumbered occurrences of FIXME in source file comments"; ignore: IgnoreList, IgnoreList::default(), false, @@ -625,7 +623,6 @@ skip_children = false hide_parse_errors = false error_on_line_overflow = false error_on_unformatted = false -report_todo = "Never" report_fixme = "Never" ignore = [] emit_mode = "Files" diff --git a/src/formatting.rs b/src/formatting.rs index 281d3e4e808a..8e6999643b1e 100644 --- a/src/formatting.rs +++ b/src/formatting.rs @@ -497,7 +497,7 @@ impl<'a> FormatLines<'a> { skipped_range: &'a [(usize, usize)], config: &'a Config, ) -> FormatLines<'a> { - let issue_seeker = BadIssueSeeker::new(config.report_todo(), config.report_fixme()); + let issue_seeker = BadIssueSeeker::new(config.report_fixme()); FormatLines { name, skipped_range, @@ -537,7 +537,7 @@ impl<'a> FormatLines<'a> { } if self.allow_issue_seek && self.format_line { - // Add warnings for bad todos/ fixmes + // Add warnings for bad fixmes if let Some(issue) = self.issue_seeker.inspect(c) { self.push_err(ErrorKind::BadIssue(issue), false, false); } diff --git a/src/issues.rs b/src/issues.rs index 33fb5522aeae..3c39d813a6f9 100644 --- a/src/issues.rs +++ b/src/issues.rs @@ -6,7 +6,6 @@ use std::fmt; use crate::config::ReportTactic; -const TO_DO_CHARS: &[char] = &['t', 'o', 'd', 'o']; const FIX_ME_CHARS: &[char] = &['f', 'i', 'x', 'm', 'e']; // Enabled implementation detail is here because it is @@ -17,7 +16,7 @@ fn is_enabled(report_tactic: ReportTactic) -> bool { #[derive(Clone, Copy)] enum Seeking { - Issue { todo_idx: usize, fixme_idx: usize }, + Issue { fixme_idx: usize }, Number { issue: Issue, part: NumberPart }, } @@ -40,7 +39,6 @@ pub struct Issue { impl fmt::Display for Issue { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { let msg = match self.issue_type { - IssueType::Todo => "TODO", IssueType::Fixme => "FIXME", }; let details = if self.missing_number { @@ -55,7 +53,6 @@ impl fmt::Display for Issue { #[derive(PartialEq, Eq, Debug, Clone, Copy)] enum IssueType { - Todo, Fixme, } @@ -67,35 +64,27 @@ enum IssueClassification { pub(crate) struct BadIssueSeeker { state: Seeking, - report_todo: ReportTactic, report_fixme: ReportTactic, } impl BadIssueSeeker { - pub(crate) fn new(report_todo: ReportTactic, report_fixme: ReportTactic) -> BadIssueSeeker { + pub(crate) fn new(report_fixme: ReportTactic) -> BadIssueSeeker { BadIssueSeeker { - state: Seeking::Issue { - todo_idx: 0, - fixme_idx: 0, - }, - report_todo, + state: Seeking::Issue { fixme_idx: 0 }, report_fixme, } } pub(crate) fn is_disabled(&self) -> bool { - !is_enabled(self.report_todo) && !is_enabled(self.report_fixme) + !is_enabled(self.report_fixme) } // Check whether or not the current char is conclusive evidence for an // unnumbered TO-DO or FIX-ME. pub(crate) fn inspect(&mut self, c: char) -> Option { match self.state { - Seeking::Issue { - todo_idx, - fixme_idx, - } => { - self.state = self.inspect_issue(c, todo_idx, fixme_idx); + Seeking::Issue { fixme_idx } => { + self.state = self.inspect_issue(c, fixme_idx); } Seeking::Number { issue, part } => { let result = self.inspect_number(c, issue, part); @@ -104,10 +93,7 @@ impl BadIssueSeeker { return None; } - self.state = Seeking::Issue { - todo_idx: 0, - fixme_idx: 0, - }; + self.state = Seeking::Issue { fixme_idx: 0 }; if let IssueClassification::Bad(issue) = result { return Some(issue); @@ -118,21 +104,9 @@ impl BadIssueSeeker { None } - fn inspect_issue(&mut self, c: char, mut todo_idx: usize, mut fixme_idx: usize) -> Seeking { + fn inspect_issue(&mut self, c: char, mut fixme_idx: usize) -> Seeking { if let Some(lower_case_c) = c.to_lowercase().next() { - if is_enabled(self.report_todo) && lower_case_c == TO_DO_CHARS[todo_idx] { - todo_idx += 1; - if todo_idx == TO_DO_CHARS.len() { - return Seeking::Number { - issue: Issue { - issue_type: IssueType::Todo, - missing_number: matches!(self.report_todo, ReportTactic::Unnumbered), - }, - part: NumberPart::OpenParen, - }; - } - fixme_idx = 0; - } else if is_enabled(self.report_fixme) && lower_case_c == FIX_ME_CHARS[fixme_idx] { + if is_enabled(self.report_fixme) && lower_case_c == FIX_ME_CHARS[fixme_idx] { // Exploit the fact that the character sets of todo and fixme // are disjoint by adding else. fixme_idx += 1; @@ -145,17 +119,12 @@ impl BadIssueSeeker { part: NumberPart::OpenParen, }; } - todo_idx = 0; } else { - todo_idx = 0; fixme_idx = 0; } } - Seeking::Issue { - todo_idx, - fixme_idx, - } + Seeking::Issue { fixme_idx } } fn inspect_number( @@ -206,7 +175,7 @@ impl BadIssueSeeker { #[test] fn find_unnumbered_issue() { fn check_fail(text: &str, failing_pos: usize) { - let mut seeker = BadIssueSeeker::new(ReportTactic::Unnumbered, ReportTactic::Unnumbered); + let mut seeker = BadIssueSeeker::new(ReportTactic::Unnumbered); assert_eq!( Some(failing_pos), text.find(|c| seeker.inspect(c).is_some()) @@ -214,12 +183,10 @@ fn find_unnumbered_issue() { } fn check_pass(text: &str) { - let mut seeker = BadIssueSeeker::new(ReportTactic::Unnumbered, ReportTactic::Unnumbered); + let mut seeker = BadIssueSeeker::new(ReportTactic::Unnumbered); assert_eq!(None, text.find(|c| seeker.inspect(c).is_some())); } - check_fail("TODO\n", 4); - check_pass(" TO FIX DOME\n"); check_fail(" \n FIXME\n", 8); check_fail("FIXME(\n", 6); check_fail("FIXME(#\n", 7); @@ -228,71 +195,28 @@ fn find_unnumbered_issue() { check_pass("FIXME(#1222)\n"); check_fail("FIXME(#12\n22)\n", 9); check_pass("FIXME(@maintainer, #1222, hello)\n"); - check_fail("TODO(#22) FIXME\n", 15); } #[test] fn find_issue() { - fn is_bad_issue(text: &str, report_todo: ReportTactic, report_fixme: ReportTactic) -> bool { - let mut seeker = BadIssueSeeker::new(report_todo, report_fixme); + fn is_bad_issue(text: &str, report_fixme: ReportTactic) -> bool { + let mut seeker = BadIssueSeeker::new(report_fixme); text.chars().any(|c| seeker.inspect(c).is_some()) } - assert!(is_bad_issue( - "TODO(@maintainer, #1222, hello)\n", - ReportTactic::Always, - ReportTactic::Never, - )); - - assert!(!is_bad_issue( - "TODO: no number\n", - ReportTactic::Never, - ReportTactic::Always, - )); - - assert!(!is_bad_issue( - "Todo: mixed case\n", - ReportTactic::Never, - ReportTactic::Always, - )); - - assert!(is_bad_issue( - "This is a FIXME(#1)\n", - ReportTactic::Never, - ReportTactic::Always, - )); + assert!(is_bad_issue("This is a FIXME(#1)\n", ReportTactic::Always)); assert!(is_bad_issue( "This is a FixMe(#1) mixed case\n", - ReportTactic::Never, ReportTactic::Always, )); - assert!(!is_bad_issue( - "bad FIXME\n", - ReportTactic::Always, - ReportTactic::Never, - )); + assert!(!is_bad_issue("bad FIXME\n", ReportTactic::Never)); } #[test] fn issue_type() { - let mut seeker = BadIssueSeeker::new(ReportTactic::Always, ReportTactic::Never); - let expected = Some(Issue { - issue_type: IssueType::Todo, - missing_number: false, - }); - - assert_eq!( - expected, - "TODO(#100): more awesomeness" - .chars() - .map(|c| seeker.inspect(c)) - .find(Option::is_some) - .unwrap() - ); - - let mut seeker = BadIssueSeeker::new(ReportTactic::Never, ReportTactic::Unnumbered); + let mut seeker = BadIssueSeeker::new(ReportTactic::Unnumbered); let expected = Some(Issue { issue_type: IssueType::Fixme, missing_number: true, diff --git a/src/test/mod.rs b/src/test/mod.rs index 4bad8e71481e..0eda9e4b116a 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -9,7 +9,7 @@ use std::process::{Command, Stdio}; use std::str::Chars; use std::thread; -use crate::config::{Color, Config, EmitMode, FileName, NewlineStyle, ReportTactic}; +use crate::config::{Color, Config, EmitMode, FileName, NewlineStyle}; use crate::formatting::{ReportedErrors, SourceFile}; use crate::rustfmt_diff::{make_diff, print_diff, DiffLine, Mismatch, ModifiedChunk, OutputWriter}; use crate::source_file; @@ -688,9 +688,6 @@ fn read_config(filename: &Path) -> Config { } } - // Don't generate warnings for to-do items. - config.set().report_todo(ReportTactic::Never); - config } diff --git a/tests/config/small_tabs.toml b/tests/config/small_tabs.toml index 35c8fd864676..598edda6abee 100644 --- a/tests/config/small_tabs.toml +++ b/tests/config/small_tabs.toml @@ -6,7 +6,6 @@ brace_style = "SameLineWhere" fn_args_layout = "Tall" trailing_comma = "Vertical" indent_style = "Block" -report_todo = "Always" report_fixme = "Never" reorder_imports = false format_strings = true From 825561deb86524107f1c22b59bddb16703d2c790 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Sun, 29 May 2022 21:48:59 -0500 Subject: [PATCH 192/401] feat: remove report_fixme option --- Configurations.md | 12 ------ src/config/mod.rs | 3 -- src/formatting.rs | 2 +- src/issues.rs | 84 ++++++++---------------------------- tests/config/small_tabs.toml | 1 - 5 files changed, 19 insertions(+), 83 deletions(-) diff --git a/Configurations.md b/Configurations.md index 72499618755e..edb2d1f75751 100644 --- a/Configurations.md +++ b/Configurations.md @@ -2168,18 +2168,6 @@ mod sit; **Note** `mod` with `#[macro_export]` will not be reordered since that could change the semantics of the original source code. -## `report_fixme` - -Report `FIXME` items in comments. - -- **Default value**: `"Never"` -- **Possible values**: `"Always"`, `"Unnumbered"`, `"Never"` -- **Stable**: No (tracking issue: [#3394](https://github.com/rust-lang/rustfmt/issues/3394)) - -Warns about any comments containing `FIXME` in them when set to `"Always"`. If -it contains a `#X` (with `X` being a number) in parentheses following the -`FIXME`, `"Unnumbered"` will ignore it. - ## `required_version` Require a specific version of rustfmt. If you want to make sure that the diff --git a/src/config/mod.rs b/src/config/mod.rs index 4c14d7356891..fc724beae60a 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -164,8 +164,6 @@ create_config! { error_on_unformatted: bool, false, false, "Error if unable to get comments or string literals within max_width, \ or they are left with trailing whitespaces"; - report_fixme: ReportTactic, ReportTactic::Never, false, - "Report all, none or unnumbered occurrences of FIXME in source file comments"; ignore: IgnoreList, IgnoreList::default(), false, "Skip formatting the specified files and directories"; @@ -623,7 +621,6 @@ skip_children = false hide_parse_errors = false error_on_line_overflow = false error_on_unformatted = false -report_fixme = "Never" ignore = [] emit_mode = "Files" make_backup = false diff --git a/src/formatting.rs b/src/formatting.rs index 8e6999643b1e..72bbe240f7b9 100644 --- a/src/formatting.rs +++ b/src/formatting.rs @@ -497,7 +497,7 @@ impl<'a> FormatLines<'a> { skipped_range: &'a [(usize, usize)], config: &'a Config, ) -> FormatLines<'a> { - let issue_seeker = BadIssueSeeker::new(config.report_fixme()); + let issue_seeker = BadIssueSeeker::new(); FormatLines { name, skipped_range, diff --git a/src/issues.rs b/src/issues.rs index 3c39d813a6f9..d95a80fe7fd9 100644 --- a/src/issues.rs +++ b/src/issues.rs @@ -6,8 +6,6 @@ use std::fmt; use crate::config::ReportTactic; -const FIX_ME_CHARS: &[char] = &['f', 'i', 'x', 'm', 'e']; - // Enabled implementation detail is here because it is // irrelevant outside the issues module fn is_enabled(report_tactic: ReportTactic) -> bool { @@ -16,7 +14,7 @@ fn is_enabled(report_tactic: ReportTactic) -> bool { #[derive(Clone, Copy)] enum Seeking { - Issue { fixme_idx: usize }, + Issue {}, Number { issue: Issue, part: NumberPart }, } @@ -30,7 +28,7 @@ enum NumberPart { #[derive(PartialEq, Eq, Debug, Clone, Copy)] pub struct Issue { - issue_type: IssueType, + issue_type: Option, // Indicates whether we're looking for issues with missing numbers, or // all issues of this type. missing_number: bool, @@ -39,7 +37,7 @@ pub struct Issue { impl fmt::Display for Issue { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { let msg = match self.issue_type { - IssueType::Fixme => "FIXME", + _ => "", }; let details = if self.missing_number { " without issue number" @@ -52,9 +50,7 @@ impl fmt::Display for Issue { } #[derive(PartialEq, Eq, Debug, Clone, Copy)] -enum IssueType { - Fixme, -} +enum IssueType {} enum IssueClassification { Good, @@ -64,27 +60,25 @@ enum IssueClassification { pub(crate) struct BadIssueSeeker { state: Seeking, - report_fixme: ReportTactic, } impl BadIssueSeeker { - pub(crate) fn new(report_fixme: ReportTactic) -> BadIssueSeeker { + pub(crate) fn new() -> BadIssueSeeker { BadIssueSeeker { - state: Seeking::Issue { fixme_idx: 0 }, - report_fixme, + state: Seeking::Issue {}, } } pub(crate) fn is_disabled(&self) -> bool { - !is_enabled(self.report_fixme) + true } // Check whether or not the current char is conclusive evidence for an // unnumbered TO-DO or FIX-ME. pub(crate) fn inspect(&mut self, c: char) -> Option { match self.state { - Seeking::Issue { fixme_idx } => { - self.state = self.inspect_issue(c, fixme_idx); + Seeking::Issue {} => { + self.state = self.inspect_issue(c, 0); } Seeking::Number { issue, part } => { let result = self.inspect_number(c, issue, part); @@ -93,7 +87,7 @@ impl BadIssueSeeker { return None; } - self.state = Seeking::Issue { fixme_idx: 0 }; + self.state = Seeking::Issue {}; if let IssueClassification::Bad(issue) = result { return Some(issue); @@ -106,25 +100,10 @@ impl BadIssueSeeker { fn inspect_issue(&mut self, c: char, mut fixme_idx: usize) -> Seeking { if let Some(lower_case_c) = c.to_lowercase().next() { - if is_enabled(self.report_fixme) && lower_case_c == FIX_ME_CHARS[fixme_idx] { - // Exploit the fact that the character sets of todo and fixme - // are disjoint by adding else. - fixme_idx += 1; - if fixme_idx == FIX_ME_CHARS.len() { - return Seeking::Number { - issue: Issue { - issue_type: IssueType::Fixme, - missing_number: matches!(self.report_fixme, ReportTactic::Unnumbered), - }, - part: NumberPart::OpenParen, - }; - } - } else { - fixme_idx = 0; - } + fixme_idx = 0; } - Seeking::Issue { fixme_idx } + Seeking::Issue {} } fn inspect_number( @@ -175,7 +154,7 @@ impl BadIssueSeeker { #[test] fn find_unnumbered_issue() { fn check_fail(text: &str, failing_pos: usize) { - let mut seeker = BadIssueSeeker::new(ReportTactic::Unnumbered); + let mut seeker = BadIssueSeeker::new(); assert_eq!( Some(failing_pos), text.find(|c| seeker.inspect(c).is_some()) @@ -183,51 +162,24 @@ fn find_unnumbered_issue() { } fn check_pass(text: &str) { - let mut seeker = BadIssueSeeker::new(ReportTactic::Unnumbered); + let mut seeker = BadIssueSeeker::new(); assert_eq!(None, text.find(|c| seeker.inspect(c).is_some())); } - - check_fail(" \n FIXME\n", 8); - check_fail("FIXME(\n", 6); - check_fail("FIXME(#\n", 7); - check_fail("FIXME(#1\n", 8); - check_fail("FIXME(#)1\n", 7); - check_pass("FIXME(#1222)\n"); - check_fail("FIXME(#12\n22)\n", 9); - check_pass("FIXME(@maintainer, #1222, hello)\n"); } #[test] fn find_issue() { - fn is_bad_issue(text: &str, report_fixme: ReportTactic) -> bool { - let mut seeker = BadIssueSeeker::new(report_fixme); + fn is_bad_issue(text: &str) -> bool { + let mut seeker = BadIssueSeeker::new(); text.chars().any(|c| seeker.inspect(c).is_some()) } - - assert!(is_bad_issue("This is a FIXME(#1)\n", ReportTactic::Always)); - - assert!(is_bad_issue( - "This is a FixMe(#1) mixed case\n", - ReportTactic::Always, - )); - - assert!(!is_bad_issue("bad FIXME\n", ReportTactic::Never)); } #[test] fn issue_type() { - let mut seeker = BadIssueSeeker::new(ReportTactic::Unnumbered); + let seeker = BadIssueSeeker::new(); let expected = Some(Issue { - issue_type: IssueType::Fixme, + issue_type: None, missing_number: true, }); - - assert_eq!( - expected, - "Test. FIXME: bad, bad, not good" - .chars() - .map(|c| seeker.inspect(c)) - .find(Option::is_some) - .unwrap() - ); } diff --git a/tests/config/small_tabs.toml b/tests/config/small_tabs.toml index 598edda6abee..c3cfd34317a3 100644 --- a/tests/config/small_tabs.toml +++ b/tests/config/small_tabs.toml @@ -6,6 +6,5 @@ brace_style = "SameLineWhere" fn_args_layout = "Tall" trailing_comma = "Vertical" indent_style = "Block" -report_fixme = "Never" reorder_imports = false format_strings = true From 5e4296767fadf87cea31c22edaef1a924dfbad40 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Sun, 29 May 2022 21:54:00 -0500 Subject: [PATCH 193/401] refactor: remove code for bad issue (e.g. todo/fixme) reporting --- src/format_report_formatter.rs | 2 +- src/formatting.rs | 14 --- src/issues.rs | 185 --------------------------------- src/lib.rs | 8 +- 4 files changed, 2 insertions(+), 207 deletions(-) delete mode 100644 src/issues.rs diff --git a/src/format_report_formatter.rs b/src/format_report_formatter.rs index 90406cdb95e2..9809f72f5ae6 100644 --- a/src/format_report_formatter.rs +++ b/src/format_report_formatter.rs @@ -146,6 +146,6 @@ fn error_kind_to_snippet_annotation_type(error_kind: &ErrorKind) -> AnnotationTy | ErrorKind::BadAttr | ErrorKind::InvalidGlobPattern(_) | ErrorKind::VersionMismatch => AnnotationType::Error, - ErrorKind::BadIssue(_) | ErrorKind::DeprecatedAttr => AnnotationType::Warning, + ErrorKind::DeprecatedAttr => AnnotationType::Warning, } } diff --git a/src/formatting.rs b/src/formatting.rs index 72bbe240f7b9..869c6db647d0 100644 --- a/src/formatting.rs +++ b/src/formatting.rs @@ -13,7 +13,6 @@ use self::newline_style::apply_newline_style; use crate::comment::{CharClasses, FullCodeCharKind}; use crate::config::{Config, FileName, Verbosity}; use crate::formatting::generated::is_generated_file; -use crate::issues::BadIssueSeeker; use crate::modules::Module; use crate::parse::parser::{DirectoryOwnership, Parser, ParserError}; use crate::parse::session::ParseSess; @@ -332,7 +331,6 @@ impl FormattingError { ErrorKind::LineOverflow(found, max) => (max, found - max), ErrorKind::TrailingWhitespace | ErrorKind::DeprecatedAttr - | ErrorKind::BadIssue(_) | ErrorKind::BadAttr | ErrorKind::LostComment | ErrorKind::LicenseCheck => { @@ -483,11 +481,9 @@ struct FormatLines<'a> { cur_line: usize, newline_count: usize, errors: Vec, - issue_seeker: BadIssueSeeker, line_buffer: String, current_line_contains_string_literal: bool, format_line: bool, - allow_issue_seek: bool, config: &'a Config, } @@ -497,7 +493,6 @@ impl<'a> FormatLines<'a> { skipped_range: &'a [(usize, usize)], config: &'a Config, ) -> FormatLines<'a> { - let issue_seeker = BadIssueSeeker::new(); FormatLines { name, skipped_range, @@ -506,8 +501,6 @@ impl<'a> FormatLines<'a> { cur_line: 1, newline_count: 0, errors: vec![], - allow_issue_seek: !issue_seeker.is_disabled(), - issue_seeker, line_buffer: String::with_capacity(config.max_width() * 2), current_line_contains_string_literal: false, format_line: config.file_lines().contains_line(name, 1), @@ -536,13 +529,6 @@ impl<'a> FormatLines<'a> { continue; } - if self.allow_issue_seek && self.format_line { - // Add warnings for bad fixmes - if let Some(issue) = self.issue_seeker.inspect(c) { - self.push_err(ErrorKind::BadIssue(issue), false, false); - } - } - if c == '\n' { self.new_line(kind); } else { diff --git a/src/issues.rs b/src/issues.rs deleted file mode 100644 index d95a80fe7fd9..000000000000 --- a/src/issues.rs +++ /dev/null @@ -1,185 +0,0 @@ -// Objects for seeking through a char stream for occurrences of TODO and FIXME. -// Depending on the loaded configuration, may also check that these have an -// associated issue number. - -use std::fmt; - -use crate::config::ReportTactic; - -// Enabled implementation detail is here because it is -// irrelevant outside the issues module -fn is_enabled(report_tactic: ReportTactic) -> bool { - report_tactic != ReportTactic::Never -} - -#[derive(Clone, Copy)] -enum Seeking { - Issue {}, - Number { issue: Issue, part: NumberPart }, -} - -#[derive(Clone, Copy)] -enum NumberPart { - OpenParen, - Pound, - Number, - CloseParen, -} - -#[derive(PartialEq, Eq, Debug, Clone, Copy)] -pub struct Issue { - issue_type: Option, - // Indicates whether we're looking for issues with missing numbers, or - // all issues of this type. - missing_number: bool, -} - -impl fmt::Display for Issue { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - let msg = match self.issue_type { - _ => "", - }; - let details = if self.missing_number { - " without issue number" - } else { - "" - }; - - write!(fmt, "{}{}", msg, details) - } -} - -#[derive(PartialEq, Eq, Debug, Clone, Copy)] -enum IssueType {} - -enum IssueClassification { - Good, - Bad(Issue), - None, -} - -pub(crate) struct BadIssueSeeker { - state: Seeking, -} - -impl BadIssueSeeker { - pub(crate) fn new() -> BadIssueSeeker { - BadIssueSeeker { - state: Seeking::Issue {}, - } - } - - pub(crate) fn is_disabled(&self) -> bool { - true - } - - // Check whether or not the current char is conclusive evidence for an - // unnumbered TO-DO or FIX-ME. - pub(crate) fn inspect(&mut self, c: char) -> Option { - match self.state { - Seeking::Issue {} => { - self.state = self.inspect_issue(c, 0); - } - Seeking::Number { issue, part } => { - let result = self.inspect_number(c, issue, part); - - if let IssueClassification::None = result { - return None; - } - - self.state = Seeking::Issue {}; - - if let IssueClassification::Bad(issue) = result { - return Some(issue); - } - } - } - - None - } - - fn inspect_issue(&mut self, c: char, mut fixme_idx: usize) -> Seeking { - if let Some(lower_case_c) = c.to_lowercase().next() { - fixme_idx = 0; - } - - Seeking::Issue {} - } - - fn inspect_number( - &mut self, - c: char, - issue: Issue, - mut part: NumberPart, - ) -> IssueClassification { - if !issue.missing_number || c == '\n' { - return IssueClassification::Bad(issue); - } else if c == ')' { - return if let NumberPart::CloseParen = part { - IssueClassification::Good - } else { - IssueClassification::Bad(issue) - }; - } - - match part { - NumberPart::OpenParen => { - if c != '(' { - return IssueClassification::Bad(issue); - } else { - part = NumberPart::Pound; - } - } - NumberPart::Pound => { - if c == '#' { - part = NumberPart::Number; - } - } - NumberPart::Number => { - if ('0'..='9').contains(&c) { - part = NumberPart::CloseParen; - } else { - return IssueClassification::Bad(issue); - } - } - NumberPart::CloseParen => {} - } - - self.state = Seeking::Number { part, issue }; - - IssueClassification::None - } -} - -#[test] -fn find_unnumbered_issue() { - fn check_fail(text: &str, failing_pos: usize) { - let mut seeker = BadIssueSeeker::new(); - assert_eq!( - Some(failing_pos), - text.find(|c| seeker.inspect(c).is_some()) - ); - } - - fn check_pass(text: &str) { - let mut seeker = BadIssueSeeker::new(); - assert_eq!(None, text.find(|c| seeker.inspect(c).is_some())); - } -} - -#[test] -fn find_issue() { - fn is_bad_issue(text: &str) -> bool { - let mut seeker = BadIssueSeeker::new(); - text.chars().any(|c| seeker.inspect(c).is_some()) - } -} - -#[test] -fn issue_type() { - let seeker = BadIssueSeeker::new(); - let expected = Some(Issue { - issue_type: None, - missing_number: true, - }); -} diff --git a/src/lib.rs b/src/lib.rs index ad23b16e02ec..f46a6914dd8e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,7 +39,6 @@ use thiserror::Error; use crate::comment::LineClasses; use crate::emitter::Emitter; use crate::formatting::{FormatErrorMap, FormattingError, ReportedErrors, SourceFile}; -use crate::issues::Issue; use crate::modules::ModuleResolutionError; use crate::parse::parser::DirectoryOwnership; use crate::shape::Indent; @@ -69,7 +68,6 @@ mod format_report_formatter; pub(crate) mod formatting; mod ignore_path; mod imports; -mod issues; mod items; mod lists; mod macros; @@ -110,9 +108,6 @@ pub enum ErrorKind { /// Line ends in whitespace. #[error("left behind trailing whitespace")] TrailingWhitespace, - /// TODO or FIXME item without an issue number. - #[error("found {0}")] - BadIssue(Issue), /// License check has failed. #[error("license check failed")] LicenseCheck, @@ -236,8 +231,7 @@ impl FormatReport { ErrorKind::LostComment => { errs.has_unformatted_code_errors = true; } - ErrorKind::BadIssue(_) - | ErrorKind::LicenseCheck + ErrorKind::LicenseCheck | ErrorKind::DeprecatedAttr | ErrorKind::BadAttr | ErrorKind::VersionMismatch => { From 79515f17ed4661da864347c90c76c51f9bf86069 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Sun, 29 May 2022 22:05:57 -0500 Subject: [PATCH 194/401] feat: remove license_template_path config option --- Configurations.md | 20 -- src/config/config_type.rs | 22 -- src/config/license.rs | 265 ------------------ src/config/mod.rs | 32 --- src/format_report_formatter.rs | 1 - src/formatting.rs | 20 +- src/lib.rs | 8 +- tests/config/issue-3802.toml | 2 - tests/license-template/lt.txt | 2 - .../license-templates/empty_license_path.rs | 5 - tests/source/license-templates/license.rs | 6 - .../license-templates/empty_license_path.rs | 5 - tests/target/license-templates/license.rs | 6 - 13 files changed, 3 insertions(+), 391 deletions(-) delete mode 100644 src/config/license.rs delete mode 100644 tests/config/issue-3802.toml delete mode 100644 tests/license-template/lt.txt delete mode 100644 tests/source/license-templates/empty_license_path.rs delete mode 100644 tests/source/license-templates/license.rs delete mode 100644 tests/target/license-templates/empty_license_path.rs delete mode 100644 tests/target/license-templates/license.rs diff --git a/Configurations.md b/Configurations.md index edb2d1f75751..8c84614352ca 100644 --- a/Configurations.md +++ b/Configurations.md @@ -1473,26 +1473,6 @@ use core::slice; #[cfg(feature = "alloc")] use core::slice; ``` -## `license_template_path` - -Check whether beginnings of files match a license template. - -- **Default value**: `""` -- **Possible values**: path to a license template file -- **Stable**: No (tracking issue: [#3352](https://github.com/rust-lang/rustfmt/issues/3352)) - -A license template is a plain text file which is matched literally against the -beginning of each source file, except for `{}`-delimited blocks, which are -matched as regular expressions. The following license template therefore -matches strings like `// Copyright 2017 The Rust Project Developers.`, `// -Copyright 2018 The Rust Project Developers.`, etc.: - -``` -// Copyright {\d+} The Rust Project Developers. -``` - -`\{`, `\}` and `\\` match literal braces / backslashes. - ## `match_arm_blocks` Controls whether arm bodies are wrapped in cases where the first line of the body cannot fit on the same line as the `=>` operator. diff --git a/src/config/config_type.rs b/src/config/config_type.rs index 7fc4486ddcd3..e37ed798cb55 100644 --- a/src/config/config_type.rs +++ b/src/config/config_type.rs @@ -61,9 +61,6 @@ macro_rules! create_config { #[derive(Clone)] #[allow(unreachable_pub)] pub struct Config { - // if a license_template_path has been specified, successfully read, parsed and compiled - // into a regex, it will be stored here - pub license_template: Option, // For each config item, we store a bool indicating whether it has // been accessed and the value, and a bool whether the option was // manually initialised, or taken from the default, @@ -104,7 +101,6 @@ macro_rules! create_config { | "struct_variant_width" | "array_width" | "chain_width" => self.0.set_heuristics(), - "license_template_path" => self.0.set_license_template(), "merge_imports" => self.0.set_merge_imports(), &_ => (), } @@ -163,7 +159,6 @@ macro_rules! create_config { } )+ self.set_heuristics(); - self.set_license_template(); self.set_ignore(dir); self.set_merge_imports(); self @@ -247,7 +242,6 @@ macro_rules! create_config { | "struct_variant_width" | "array_width" | "chain_width" => self.set_heuristics(), - "license_template_path" => self.set_license_template(), "merge_imports" => self.set_merge_imports(), &_ => (), } @@ -386,21 +380,6 @@ macro_rules! create_config { }; } - fn set_license_template(&mut self) { - if self.was_set().license_template_path() { - let lt_path = self.license_template_path(); - if lt_path.len() > 0 { - match license::load_and_compile_template(<_path) { - Ok(re) => self.license_template = Some(re), - Err(msg) => eprintln!("Warning for license template file {:?}: {}", - lt_path, msg), - } - } else { - self.license_template = None; - } - } - } - fn set_ignore(&mut self, dir: &Path) { self.ignore.2.add_prefix(dir); } @@ -437,7 +416,6 @@ macro_rules! create_config { impl Default for Config { fn default() -> Config { Config { - license_template: None, $( $i: (Cell::new(false), false, $def, $stb), )+ diff --git a/src/config/license.rs b/src/config/license.rs deleted file mode 100644 index c7feb502ea91..000000000000 --- a/src/config/license.rs +++ /dev/null @@ -1,265 +0,0 @@ -use std::fmt; -use std::fs::File; -use std::io; -use std::io::Read; - -use regex::Regex; - -#[derive(Debug)] -pub(crate) enum LicenseError { - IO(io::Error), - Regex(regex::Error), - Parse(String), -} - -impl fmt::Display for LicenseError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - LicenseError::IO(ref err) => err.fmt(f), - LicenseError::Regex(ref err) => err.fmt(f), - LicenseError::Parse(ref err) => write!(f, "parsing failed, {}", err), - } - } -} - -impl From for LicenseError { - fn from(err: io::Error) -> LicenseError { - LicenseError::IO(err) - } -} - -impl From for LicenseError { - fn from(err: regex::Error) -> LicenseError { - LicenseError::Regex(err) - } -} - -// the template is parsed using a state machine -enum ParsingState { - Lit, - LitEsc, - // the u32 keeps track of brace nesting - Re(u32), - ReEsc(u32), - Abort(String), -} - -use self::ParsingState::*; - -pub(crate) struct TemplateParser { - parsed: String, - buffer: String, - state: ParsingState, - linum: u32, - open_brace_line: u32, -} - -impl TemplateParser { - fn new() -> Self { - Self { - parsed: "^".to_owned(), - buffer: String::new(), - state: Lit, - linum: 1, - // keeps track of last line on which a regex placeholder was started - open_brace_line: 0, - } - } - - /// Converts a license template into a string which can be turned into a regex. - /// - /// The license template could use regex syntax directly, but that would require a lot of manual - /// escaping, which is inconvenient. It is therefore literal by default, with optional regex - /// subparts delimited by `{` and `}`. Additionally: - /// - /// - to insert literal `{`, `}` or `\`, escape it with `\` - /// - an empty regex placeholder (`{}`) is shorthand for `{.*?}` - /// - /// This function parses this input format and builds a properly escaped *string* representation - /// of the equivalent regular expression. It **does not** however guarantee that the returned - /// string is a syntactically valid regular expression. - /// - /// # Examples - /// - /// ```text - /// assert_eq!( - /// TemplateParser::parse( - /// r" - /// // Copyright {\d+} The \} Rust \\ Project \{ Developers. See the {([A-Z]+)} - /// // file at the top-level directory of this distribution and at - /// // {}. - /// // - /// // Licensed under the Apache License, Version 2.0 or the MIT license - /// // , at your - /// // option. This file may not be copied, modified, or distributed - /// // except according to those terms. - /// " - /// ).unwrap(), - /// r"^ - /// // Copyright \d+ The \} Rust \\ Project \{ Developers\. See the ([A-Z]+) - /// // file at the top\-level directory of this distribution and at - /// // .*?\. - /// // - /// // Licensed under the Apache License, Version 2\.0 or the MIT license - /// // , at your - /// // option\. This file may not be copied, modified, or distributed - /// // except according to those terms\. - /// " - /// ); - /// ``` - pub(crate) fn parse(template: &str) -> Result { - let mut parser = Self::new(); - for chr in template.chars() { - if chr == '\n' { - parser.linum += 1; - } - parser.state = match parser.state { - Lit => parser.trans_from_lit(chr), - LitEsc => parser.trans_from_litesc(chr), - Re(brace_nesting) => parser.trans_from_re(chr, brace_nesting), - ReEsc(brace_nesting) => parser.trans_from_reesc(chr, brace_nesting), - Abort(msg) => return Err(LicenseError::Parse(msg)), - }; - } - // check if we've ended parsing in a valid state - match parser.state { - Abort(msg) => return Err(LicenseError::Parse(msg)), - Re(_) | ReEsc(_) => { - return Err(LicenseError::Parse(format!( - "escape or balance opening brace on l. {}", - parser.open_brace_line - ))); - } - LitEsc => { - return Err(LicenseError::Parse(format!( - "incomplete escape sequence on l. {}", - parser.linum - ))); - } - _ => (), - } - parser.parsed.push_str(®ex::escape(&parser.buffer)); - - Ok(parser.parsed) - } - - fn trans_from_lit(&mut self, chr: char) -> ParsingState { - match chr { - '{' => { - self.parsed.push_str(®ex::escape(&self.buffer)); - self.buffer.clear(); - self.open_brace_line = self.linum; - Re(1) - } - '}' => Abort(format!( - "escape or balance closing brace on l. {}", - self.linum - )), - '\\' => LitEsc, - _ => { - self.buffer.push(chr); - Lit - } - } - } - - fn trans_from_litesc(&mut self, chr: char) -> ParsingState { - self.buffer.push(chr); - Lit - } - - fn trans_from_re(&mut self, chr: char, brace_nesting: u32) -> ParsingState { - match chr { - '{' => { - self.buffer.push(chr); - Re(brace_nesting + 1) - } - '}' => { - match brace_nesting { - 1 => { - // default regex for empty placeholder {} - if self.buffer.is_empty() { - self.parsed.push_str(".*?"); - } else { - self.parsed.push_str(&self.buffer); - } - self.buffer.clear(); - Lit - } - _ => { - self.buffer.push(chr); - Re(brace_nesting - 1) - } - } - } - '\\' => { - self.buffer.push(chr); - ReEsc(brace_nesting) - } - _ => { - self.buffer.push(chr); - Re(brace_nesting) - } - } - } - - fn trans_from_reesc(&mut self, chr: char, brace_nesting: u32) -> ParsingState { - self.buffer.push(chr); - Re(brace_nesting) - } -} - -pub(crate) fn load_and_compile_template(path: &str) -> Result { - let mut lt_file = File::open(&path)?; - let mut lt_str = String::new(); - lt_file.read_to_string(&mut lt_str)?; - let lt_parsed = TemplateParser::parse(<_str)?; - Ok(Regex::new(<_parsed)?) -} - -#[cfg(test)] -mod test { - use super::TemplateParser; - - #[test] - fn test_parse_license_template() { - assert_eq!( - TemplateParser::parse("literal (.*)").unwrap(), - r"^literal \(\.\*\)" - ); - assert_eq!( - TemplateParser::parse(r"escaping \}").unwrap(), - r"^escaping \}" - ); - assert!(TemplateParser::parse("unbalanced } without escape").is_err()); - assert_eq!( - TemplateParser::parse(r"{\d+} place{-?}holder{s?}").unwrap(), - r"^\d+ place-?holders?" - ); - assert_eq!(TemplateParser::parse("default {}").unwrap(), "^default .*?"); - assert_eq!( - TemplateParser::parse(r"unbalanced nested braces {\{{3}}").unwrap(), - r"^unbalanced nested braces \{{3}" - ); - assert_eq!( - &TemplateParser::parse("parsing error }") - .unwrap_err() - .to_string(), - "parsing failed, escape or balance closing brace on l. 1" - ); - assert_eq!( - &TemplateParser::parse("parsing error {\nsecond line") - .unwrap_err() - .to_string(), - "parsing failed, escape or balance opening brace on l. 1" - ); - assert_eq!( - &TemplateParser::parse(r"parsing error \") - .unwrap_err() - .to_string(), - "parsing failed, incomplete escape sequence on l. 1" - ); - } -} diff --git a/src/config/mod.rs b/src/config/mod.rs index fc724beae60a..a51695281878 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -5,7 +5,6 @@ use std::io::{Error, ErrorKind, Read}; use std::path::{Path, PathBuf}; use std::{env, fs}; -use regex::Regex; use thiserror::Error; use crate::config::config_type::ConfigType; @@ -22,7 +21,6 @@ pub(crate) mod config_type; pub(crate) mod options; pub(crate) mod file_lines; -pub(crate) mod license; pub(crate) mod lists; // This macro defines configuration options used in rustfmt. Each option @@ -63,8 +61,6 @@ create_config! { "Maximum length of comments. No effect unless wrap_comments = true"; normalize_comments: bool, false, false, "Convert /* */ comments to // comments where possible"; normalize_doc_attributes: bool, false, false, "Normalize doc attributes as doc comments"; - license_template_path: String, String::default(), false, - "Beginning of file must match license template"; format_strings: bool, false, false, "Format string literals where necessary"; format_macro_matchers: bool, false, false, "Format the metavariable matching patterns in macros"; @@ -414,8 +410,6 @@ mod test { create_config! { // Options that are used by the generated functions max_width: usize, 100, true, "Maximum width of each line"; - license_template_path: String, String::default(), false, - "Beginning of file must match license template"; required_version: String, env!("CARGO_PKG_VERSION").to_owned(), false, "Require a specific version of rustfmt."; ignore: IgnoreList, IgnoreList::default(), false, @@ -520,31 +514,6 @@ mod test { assert_eq!(s.contains("(unstable)"), true); } - #[test] - fn test_empty_string_license_template_path() { - let toml = r#"license_template_path = """#; - let config = Config::from_toml(toml, Path::new("")).unwrap(); - assert!(config.license_template.is_none()); - } - - #[nightly_only_test] - #[test] - fn test_valid_license_template_path() { - let toml = r#"license_template_path = "tests/license-template/lt.txt""#; - let config = Config::from_toml(toml, Path::new("")).unwrap(); - assert!(config.license_template.is_some()); - } - - #[nightly_only_test] - #[test] - fn test_override_existing_license_with_no_license() { - let toml = r#"license_template_path = "tests/license-template/lt.txt""#; - let mut config = Config::from_toml(toml, Path::new("")).unwrap(); - assert!(config.license_template.is_some()); - config.override_value("license_template_path", ""); - assert!(config.license_template.is_none()); - } - #[test] fn test_dump_default_config() { let default_config = format!( @@ -566,7 +535,6 @@ format_code_in_doc_comments = false comment_width = 80 normalize_comments = false normalize_doc_attributes = false -license_template_path = "" format_strings = false format_macro_matchers = false format_macro_bodies = true diff --git a/src/format_report_formatter.rs b/src/format_report_formatter.rs index 9809f72f5ae6..fd536d4df41a 100644 --- a/src/format_report_formatter.rs +++ b/src/format_report_formatter.rs @@ -142,7 +142,6 @@ fn error_kind_to_snippet_annotation_type(error_kind: &ErrorKind) -> AnnotationTy | ErrorKind::ModuleResolutionError(_) | ErrorKind::ParseError | ErrorKind::LostComment - | ErrorKind::LicenseCheck | ErrorKind::BadAttr | ErrorKind::InvalidGlobPattern(_) | ErrorKind::VersionMismatch => AnnotationType::Error, diff --git a/src/formatting.rs b/src/formatting.rs index 869c6db647d0..e6995210a943 100644 --- a/src/formatting.rs +++ b/src/formatting.rs @@ -332,8 +332,7 @@ impl FormattingError { ErrorKind::TrailingWhitespace | ErrorKind::DeprecatedAttr | ErrorKind::BadAttr - | ErrorKind::LostComment - | ErrorKind::LicenseCheck => { + | ErrorKind::LostComment => { let trailing_ws_start = self .line_buffer .rfind(|c: char| !c.is_whitespace()) @@ -365,7 +364,7 @@ pub(crate) struct ReportedErrors { // Code contains macro call that was unable to format. pub(crate) has_macro_format_failure: bool, - // Failed a check, such as the license check or other opt-in checking. + // Failed an opt-in checking. pub(crate) has_check_errors: bool, /// Formatted code differs from existing code (--check only). @@ -461,7 +460,6 @@ fn format_lines( report: &FormatReport, ) { let mut formatter = FormatLines::new(name, skipped_range, config); - formatter.check_license(text); formatter.iterate(text); if formatter.newline_count > 1 { @@ -508,20 +506,6 @@ impl<'a> FormatLines<'a> { } } - fn check_license(&mut self, text: &mut String) { - if let Some(ref license_template) = self.config.license_template { - if !license_template.is_match(text) { - self.errors.push(FormattingError { - line: self.cur_line, - kind: ErrorKind::LicenseCheck, - is_comment: false, - is_string: false, - line_buffer: String::new(), - }); - } - } - } - // Iterate over the chars in the file map. fn iterate(&mut self, text: &mut String) { for (kind, c) in CharClasses::new(text.chars()) { diff --git a/src/lib.rs b/src/lib.rs index f46a6914dd8e..495010a297df 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -108,9 +108,6 @@ pub enum ErrorKind { /// Line ends in whitespace. #[error("left behind trailing whitespace")] TrailingWhitespace, - /// License check has failed. - #[error("license check failed")] - LicenseCheck, /// Used deprecated skip attribute. #[error("`rustfmt_skip` is deprecated; use `rustfmt::skip`")] DeprecatedAttr, @@ -231,10 +228,7 @@ impl FormatReport { ErrorKind::LostComment => { errs.has_unformatted_code_errors = true; } - ErrorKind::LicenseCheck - | ErrorKind::DeprecatedAttr - | ErrorKind::BadAttr - | ErrorKind::VersionMismatch => { + ErrorKind::DeprecatedAttr | ErrorKind::BadAttr | ErrorKind::VersionMismatch => { errs.has_check_errors = true; } _ => {} diff --git a/tests/config/issue-3802.toml b/tests/config/issue-3802.toml deleted file mode 100644 index 74ee8b010dd0..000000000000 --- a/tests/config/issue-3802.toml +++ /dev/null @@ -1,2 +0,0 @@ -unstable_features = true -license_template_path = "" diff --git a/tests/license-template/lt.txt b/tests/license-template/lt.txt deleted file mode 100644 index ea4390371a09..000000000000 --- a/tests/license-template/lt.txt +++ /dev/null @@ -1,2 +0,0 @@ -// rustfmt-license_template_path: tests/license-template/lt.txt -// Copyright {\d+} The rustfmt developers. diff --git a/tests/source/license-templates/empty_license_path.rs b/tests/source/license-templates/empty_license_path.rs deleted file mode 100644 index d3a91e4231d1..000000000000 --- a/tests/source/license-templates/empty_license_path.rs +++ /dev/null @@ -1,5 +0,0 @@ -// rustfmt-config: issue-3802.toml - -fn main() { -println!("Hello world!"); -} diff --git a/tests/source/license-templates/license.rs b/tests/source/license-templates/license.rs deleted file mode 100644 index 6816011c60dc..000000000000 --- a/tests/source/license-templates/license.rs +++ /dev/null @@ -1,6 +0,0 @@ -// rustfmt-license_template_path: tests/license-template/lt.txt -// Copyright 2019 The rustfmt developers. - -fn main() { -println!("Hello world!"); -} diff --git a/tests/target/license-templates/empty_license_path.rs b/tests/target/license-templates/empty_license_path.rs deleted file mode 100644 index 950f103ed392..000000000000 --- a/tests/target/license-templates/empty_license_path.rs +++ /dev/null @@ -1,5 +0,0 @@ -// rustfmt-config: issue-3802.toml - -fn main() { - println!("Hello world!"); -} diff --git a/tests/target/license-templates/license.rs b/tests/target/license-templates/license.rs deleted file mode 100644 index 7169c7b25761..000000000000 --- a/tests/target/license-templates/license.rs +++ /dev/null @@ -1,6 +0,0 @@ -// rustfmt-license_template_path: tests/license-template/lt.txt -// Copyright 2019 The rustfmt developers. - -fn main() { - println!("Hello world!"); -} From 64f1f57a4837694cb2449d6bc1f614e60c6ff42c Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Sat, 4 Jun 2022 10:08:33 -0500 Subject: [PATCH 195/401] fix: handle inner ignore attribute on stdin --- src/formatting.rs | 28 +++++++++++++++++++++------- src/test/mod.rs | 24 ++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/src/formatting.rs b/src/formatting.rs index e6995210a943..23d90d9e2e06 100644 --- a/src/formatting.rs +++ b/src/formatting.rs @@ -40,12 +40,10 @@ impl<'b, T: Write + 'b> Session<'b, T> { rustc_span::create_session_if_not_set_then(self.config.edition().into(), |_| { if self.config.disable_all_formatting() { // When the input is from stdin, echo back the input. - if let Input::Text(ref buf) = input { - if let Err(e) = io::stdout().write_all(buf.as_bytes()) { - return Err(From::from(e)); - } - } - return Ok(FormatReport::new()); + return match input { + Input::Text(ref buf) => echo_back_stdin(buf), + _ => Ok(FormatReport::new()), + }; } let config = &self.config.clone(); @@ -94,6 +92,13 @@ fn should_skip_module( false } +fn echo_back_stdin(input: &str) -> Result { + if let Err(e) = io::stdout().write_all(input.as_bytes()) { + return Err(From::from(e)); + } + Ok(FormatReport::new()) +} + // Format an entire crate (or subset of the module tree). fn format_project( input: Input, @@ -136,7 +141,8 @@ fn format_project( .visit_crate(&krate)? .into_iter() .filter(|(path, module)| { - !should_skip_module(config, &context, input_is_stdin, &main_file, path, module) + input_is_stdin + || !should_skip_module(config, &context, input_is_stdin, &main_file, path, module) }) .collect::>(); @@ -146,6 +152,14 @@ fn format_project( context.parse_session.set_silent_emitter(); for (path, module) in files { + if input_is_stdin && contains_skip(module.attrs()) { + return echo_back_stdin( + context + .parse_session + .snippet_provider(module.span) + .entire_snippet(), + ); + } should_emit_verbose(input_is_stdin, config, || println!("Formatting {}", path)); context.format_file(path, &module, is_macro_def)?; } diff --git a/src/test/mod.rs b/src/test/mod.rs index 0eda9e4b116a..18ec8620facd 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -578,6 +578,30 @@ fn stdin_generated_files_issue_5172() { ); } +#[test] +fn stdin_handles_mod_inner_ignore_attr() { + // see https://github.com/rust-lang/rustfmt/issues/5368 + init_log(); + let input = String::from("#![rustfmt::skip]\n\nfn main() { }"); + let mut child = Command::new(rustfmt().to_str().unwrap()) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn() + .expect("failed to execute child"); + + { + let stdin = child.stdin.as_mut().expect("failed to get stdin"); + stdin + .write_all(input.as_bytes()) + .expect("failed to write stdin"); + } + + let output = child.wait_with_output().expect("failed to wait on child"); + assert!(output.status.success()); + assert!(output.stderr.is_empty()); + assert_eq!(input, String::from_utf8(output.stdout).unwrap()); +} + #[test] fn format_lines_errors_are_reported() { init_log(); From aedb396063e2cd166e3cc0bfd762bf382996cd03 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Tue, 7 Jun 2022 22:14:08 -0500 Subject: [PATCH 196/401] chore: bump toolchain --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index 94b57d506c20..813e5e2c10fe 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-03-27" +channel = "nightly-2022-06-06" components = ["rustc-dev"] From 5fa2727ddeef534a7cd437f9e288c221a2cf0b6a Mon Sep 17 00:00:00 2001 From: Urgau Date: Wed, 8 Jun 2022 18:15:22 +0200 Subject: [PATCH 197/401] Remove useless conditional compilation --- src/test/mod.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/test/mod.rs b/src/test/mod.rs index 18ec8620facd..6b5bc2b30dd5 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -978,12 +978,6 @@ fn rustfmt() -> PathBuf { // Chop off `deps`. me.pop(); - // If we run `cargo test --release`, we might only have a release build. - if cfg!(release) { - // `../release/` - me.pop(); - me.push("release"); - } me.push("rustfmt"); assert!( me.is_file() || me.with_extension("exe").is_file(), From 7d34cfaf2c31dc90f6dc8fc1cdc001fcbd7ebbfa Mon Sep 17 00:00:00 2001 From: David Bar-On <61089727+davidBar-On@users.noreply.github.com> Date: Tue, 16 Mar 2021 03:57:04 +0200 Subject: [PATCH 198/401] Dedup `imports_granularity = "Item"` (#4737) * Fix for issue 4725 - dedup Item imports_granularity (2nd version) * Use unique() instead of unique_by() --- src/imports.rs | 15 ++++++++++++++- .../source/{ => imports}/imports-impl-only-use.rs | 0 .../imports-reorder-lines-and-items.rs | 0 .../source/{ => imports}/imports-reorder-lines.rs | 0 tests/source/{ => imports}/imports-reorder.rs | 0 tests/source/{ => imports}/imports.rs | 0 .../source/{ => imports}/imports_block_indent.rs | 0 .../{ => imports}/imports_granularity_crate.rs | 0 .../imports_granularity_default-with-dups.rs | 6 ++++++ ..._item-with-dups-StdExternalCrate-no-reorder.rs | 13 +++++++++++++ .../imports/imports_granularity_item-with-dups.rs | 11 +++++++++++ .../{ => imports}/imports_granularity_item.rs | 0 .../{ => imports}/imports_granularity_module.rs | 0 .../{ => imports}/import-fencepost-length.rs | 0 .../target/{ => imports}/imports-impl-only-use.rs | 0 .../imports-reorder-lines-and-items.rs | 0 .../target/{ => imports}/imports-reorder-lines.rs | 0 tests/target/{ => imports}/imports-reorder.rs | 0 tests/target/{ => imports}/imports.rs | 0 .../target/{ => imports}/imports_2021_edition.rs | 0 .../target/{ => imports}/imports_block_indent.rs | 0 .../{ => imports}/imports_granularity_crate.rs | 0 .../imports_granularity_default-with-dups.rs | 6 ++++++ ..._item-with-dups-StdExternalCrate-no-reorder.rs | 7 +++++++ .../imports/imports_granularity_item-with-dups.rs | 5 +++++ .../{ => imports}/imports_granularity_item.rs | 0 .../{ => imports}/imports_granularity_module.rs | 0 27 files changed, 62 insertions(+), 1 deletion(-) rename tests/source/{ => imports}/imports-impl-only-use.rs (100%) rename tests/source/{ => imports}/imports-reorder-lines-and-items.rs (100%) rename tests/source/{ => imports}/imports-reorder-lines.rs (100%) rename tests/source/{ => imports}/imports-reorder.rs (100%) rename tests/source/{ => imports}/imports.rs (100%) rename tests/source/{ => imports}/imports_block_indent.rs (100%) rename tests/source/{ => imports}/imports_granularity_crate.rs (100%) create mode 100644 tests/source/imports/imports_granularity_default-with-dups.rs create mode 100644 tests/source/imports/imports_granularity_item-with-dups-StdExternalCrate-no-reorder.rs create mode 100644 tests/source/imports/imports_granularity_item-with-dups.rs rename tests/source/{ => imports}/imports_granularity_item.rs (100%) rename tests/source/{ => imports}/imports_granularity_module.rs (100%) rename tests/target/{ => imports}/import-fencepost-length.rs (100%) rename tests/target/{ => imports}/imports-impl-only-use.rs (100%) rename tests/target/{ => imports}/imports-reorder-lines-and-items.rs (100%) rename tests/target/{ => imports}/imports-reorder-lines.rs (100%) rename tests/target/{ => imports}/imports-reorder.rs (100%) rename tests/target/{ => imports}/imports.rs (100%) rename tests/target/{ => imports}/imports_2021_edition.rs (100%) rename tests/target/{ => imports}/imports_block_indent.rs (100%) rename tests/target/{ => imports}/imports_granularity_crate.rs (100%) create mode 100644 tests/target/imports/imports_granularity_default-with-dups.rs create mode 100644 tests/target/imports/imports_granularity_item-with-dups-StdExternalCrate-no-reorder.rs create mode 100644 tests/target/imports/imports_granularity_item-with-dups.rs rename tests/target/{ => imports}/imports_granularity_item.rs (100%) rename tests/target/{ => imports}/imports_granularity_module.rs (100%) diff --git a/src/imports.rs b/src/imports.rs index 962f2126c66c..559ed3917dba 100644 --- a/src/imports.rs +++ b/src/imports.rs @@ -2,6 +2,10 @@ use std::borrow::Cow; use std::cmp::Ordering; use std::fmt; +use core::hash::{Hash, Hasher}; + +use itertools::Itertools; + use rustc_ast::ast::{self, UseTreeKind}; use rustc_span::{ symbol::{self, sym}, @@ -87,7 +91,7 @@ impl<'a> FmtVisitor<'a> { // sorting. // FIXME we do a lot of allocation to make our own representation. -#[derive(Clone, Eq, PartialEq)] +#[derive(Clone, Eq, Hash, PartialEq)] pub(crate) enum UseSegment { Ident(String, Option), Slf(Option), @@ -232,10 +236,13 @@ fn flatten_use_trees( use_trees: Vec, import_granularity: ImportGranularity, ) -> Vec { + // Return non-sorted single occurance of the use-trees text string; + // order is by first occurance of the use-tree. use_trees .into_iter() .flat_map(|tree| tree.flatten(import_granularity)) .map(UseTree::nest_trailing_self) + .unique() .collect() } @@ -780,6 +787,12 @@ fn merge_use_trees_inner(trees: &mut Vec, use_tree: UseTree, merge_by: trees.sort(); } +impl Hash for UseTree { + fn hash(&self, state: &mut H) { + self.path.hash(state); + } +} + impl PartialOrd for UseSegment { fn partial_cmp(&self, other: &UseSegment) -> Option { Some(self.cmp(other)) diff --git a/tests/source/imports-impl-only-use.rs b/tests/source/imports/imports-impl-only-use.rs similarity index 100% rename from tests/source/imports-impl-only-use.rs rename to tests/source/imports/imports-impl-only-use.rs diff --git a/tests/source/imports-reorder-lines-and-items.rs b/tests/source/imports/imports-reorder-lines-and-items.rs similarity index 100% rename from tests/source/imports-reorder-lines-and-items.rs rename to tests/source/imports/imports-reorder-lines-and-items.rs diff --git a/tests/source/imports-reorder-lines.rs b/tests/source/imports/imports-reorder-lines.rs similarity index 100% rename from tests/source/imports-reorder-lines.rs rename to tests/source/imports/imports-reorder-lines.rs diff --git a/tests/source/imports-reorder.rs b/tests/source/imports/imports-reorder.rs similarity index 100% rename from tests/source/imports-reorder.rs rename to tests/source/imports/imports-reorder.rs diff --git a/tests/source/imports.rs b/tests/source/imports/imports.rs similarity index 100% rename from tests/source/imports.rs rename to tests/source/imports/imports.rs diff --git a/tests/source/imports_block_indent.rs b/tests/source/imports/imports_block_indent.rs similarity index 100% rename from tests/source/imports_block_indent.rs rename to tests/source/imports/imports_block_indent.rs diff --git a/tests/source/imports_granularity_crate.rs b/tests/source/imports/imports_granularity_crate.rs similarity index 100% rename from tests/source/imports_granularity_crate.rs rename to tests/source/imports/imports_granularity_crate.rs diff --git a/tests/source/imports/imports_granularity_default-with-dups.rs b/tests/source/imports/imports_granularity_default-with-dups.rs new file mode 100644 index 000000000000..cbb21a9f1b38 --- /dev/null +++ b/tests/source/imports/imports_granularity_default-with-dups.rs @@ -0,0 +1,6 @@ +use crate::lexer; +use crate::lexer::tokens::TokenData; +use crate::lexer::{tokens::TokenData}; +use crate::lexer::self; +use crate::lexer::{self}; +use crate::lexer::{self, tokens::TokenData}; diff --git a/tests/source/imports/imports_granularity_item-with-dups-StdExternalCrate-no-reorder.rs b/tests/source/imports/imports_granularity_item-with-dups-StdExternalCrate-no-reorder.rs new file mode 100644 index 000000000000..e23705a884fe --- /dev/null +++ b/tests/source/imports/imports_granularity_item-with-dups-StdExternalCrate-no-reorder.rs @@ -0,0 +1,13 @@ +// rustfmt-imports_granularity: Item +// rustfmt-reorder_imports: false +// rustfmt-group_imports: StdExternalCrate + +use crate::lexer; +use crate::lexer; +use crate::lexer::tokens::TokenData; +use crate::lexer::{tokens::TokenData}; +use crate::lexer::self; +use crate::lexer; +use crate::lexer; +use crate::lexer::{self}; +use crate::lexer::{self, tokens::TokenData}; diff --git a/tests/source/imports/imports_granularity_item-with-dups.rs b/tests/source/imports/imports_granularity_item-with-dups.rs new file mode 100644 index 000000000000..3e9589c299f6 --- /dev/null +++ b/tests/source/imports/imports_granularity_item-with-dups.rs @@ -0,0 +1,11 @@ +// rustfmt-imports_granularity: Item + +use crate::lexer; +use crate::lexer; +use crate::lexer::tokens::TokenData; +use crate::lexer::{tokens::TokenData}; +use crate::lexer::self; +use crate::lexer; +use crate::lexer; +use crate::lexer::{self}; +use crate::lexer::{self, tokens::TokenData}; diff --git a/tests/source/imports_granularity_item.rs b/tests/source/imports/imports_granularity_item.rs similarity index 100% rename from tests/source/imports_granularity_item.rs rename to tests/source/imports/imports_granularity_item.rs diff --git a/tests/source/imports_granularity_module.rs b/tests/source/imports/imports_granularity_module.rs similarity index 100% rename from tests/source/imports_granularity_module.rs rename to tests/source/imports/imports_granularity_module.rs diff --git a/tests/target/import-fencepost-length.rs b/tests/target/imports/import-fencepost-length.rs similarity index 100% rename from tests/target/import-fencepost-length.rs rename to tests/target/imports/import-fencepost-length.rs diff --git a/tests/target/imports-impl-only-use.rs b/tests/target/imports/imports-impl-only-use.rs similarity index 100% rename from tests/target/imports-impl-only-use.rs rename to tests/target/imports/imports-impl-only-use.rs diff --git a/tests/target/imports-reorder-lines-and-items.rs b/tests/target/imports/imports-reorder-lines-and-items.rs similarity index 100% rename from tests/target/imports-reorder-lines-and-items.rs rename to tests/target/imports/imports-reorder-lines-and-items.rs diff --git a/tests/target/imports-reorder-lines.rs b/tests/target/imports/imports-reorder-lines.rs similarity index 100% rename from tests/target/imports-reorder-lines.rs rename to tests/target/imports/imports-reorder-lines.rs diff --git a/tests/target/imports-reorder.rs b/tests/target/imports/imports-reorder.rs similarity index 100% rename from tests/target/imports-reorder.rs rename to tests/target/imports/imports-reorder.rs diff --git a/tests/target/imports.rs b/tests/target/imports/imports.rs similarity index 100% rename from tests/target/imports.rs rename to tests/target/imports/imports.rs diff --git a/tests/target/imports_2021_edition.rs b/tests/target/imports/imports_2021_edition.rs similarity index 100% rename from tests/target/imports_2021_edition.rs rename to tests/target/imports/imports_2021_edition.rs diff --git a/tests/target/imports_block_indent.rs b/tests/target/imports/imports_block_indent.rs similarity index 100% rename from tests/target/imports_block_indent.rs rename to tests/target/imports/imports_block_indent.rs diff --git a/tests/target/imports_granularity_crate.rs b/tests/target/imports/imports_granularity_crate.rs similarity index 100% rename from tests/target/imports_granularity_crate.rs rename to tests/target/imports/imports_granularity_crate.rs diff --git a/tests/target/imports/imports_granularity_default-with-dups.rs b/tests/target/imports/imports_granularity_default-with-dups.rs new file mode 100644 index 000000000000..5da6d588e6dd --- /dev/null +++ b/tests/target/imports/imports_granularity_default-with-dups.rs @@ -0,0 +1,6 @@ +use crate::lexer; +use crate::lexer; +use crate::lexer::tokens::TokenData; +use crate::lexer::tokens::TokenData; +use crate::lexer::{self}; +use crate::lexer::{self, tokens::TokenData}; diff --git a/tests/target/imports/imports_granularity_item-with-dups-StdExternalCrate-no-reorder.rs b/tests/target/imports/imports_granularity_item-with-dups-StdExternalCrate-no-reorder.rs new file mode 100644 index 000000000000..ed4df544d6f9 --- /dev/null +++ b/tests/target/imports/imports_granularity_item-with-dups-StdExternalCrate-no-reorder.rs @@ -0,0 +1,7 @@ +// rustfmt-imports_granularity: Item +// rustfmt-reorder_imports: false +// rustfmt-group_imports: StdExternalCrate + +use crate::lexer; +use crate::lexer::tokens::TokenData; +use crate::lexer::{self}; diff --git a/tests/target/imports/imports_granularity_item-with-dups.rs b/tests/target/imports/imports_granularity_item-with-dups.rs new file mode 100644 index 000000000000..00df37f93322 --- /dev/null +++ b/tests/target/imports/imports_granularity_item-with-dups.rs @@ -0,0 +1,5 @@ +// rustfmt-imports_granularity: Item + +use crate::lexer; +use crate::lexer::tokens::TokenData; +use crate::lexer::{self}; diff --git a/tests/target/imports_granularity_item.rs b/tests/target/imports/imports_granularity_item.rs similarity index 100% rename from tests/target/imports_granularity_item.rs rename to tests/target/imports/imports_granularity_item.rs diff --git a/tests/target/imports_granularity_module.rs b/tests/target/imports/imports_granularity_module.rs similarity index 100% rename from tests/target/imports_granularity_module.rs rename to tests/target/imports/imports_granularity_module.rs From 7b73b60faca71d01d900e49831fcb84553e93019 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Sun, 12 Jun 2022 21:04:12 -0500 Subject: [PATCH 199/401] chore: prep v1.5.0 release --- CHANGELOG.md | 21 +++++++++++++++++++-- Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3664571fe64b..bfc155cd656a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,12 +2,18 @@ ## [Unreleased] +## [1.5.0] 2022-06-13 + ### Changed -- Also apply `empty_item_single_line=true` to trait definitions to match the behavior of empty functions, structs, enums, and impls [#5047](https://github.com/rust-lang/rustfmt/issues/5047) +- Simplify the rustfmt help text by eliding the full path to the rustfmt binary path from the usage string when running `rustfmt --help` [#5214](https://github.com/rust-lang/rustfmt/issues/5214) ### Fixed +- Remove duplicate imports when `imports_granularity` is set to `Item` [#4725](https://github.com/rust-lang/rustfmt/issues/4725) +- Properly handle stdin input containing an inner skip attribute [#5368](https://github.com/rust-lang/rustfmt/issues/5368) +- Maintain attributes on imports when `imports_granularity` is set to `Item` [#5030](https://github.com/rust-lang/rustfmt/issues/5030) +- Format empty trait definitions as a single line when both `empty_item_single_line` is enabled and `brace_style` is set to `AlwaysNextLine` [#5047](https://github.com/rust-lang/rustfmt/issues/5047) - Don't change granularity of imports containing comments with `imports_granularity` if doing so could lose or misplace those comments [#5311](https://github.com/rust-lang/rustfmt/pull/5311) - Prevent rustfmt from removing trailing comments at the end of files annotated with inner `#![rustfmt::skip]` attributes [#5033](https://github.com/rust-lang/rustfmt/issues/5033) - Fixed various `error[internal]: left behind trailing whitespace"` issues: @@ -60,7 +66,18 @@ ### Removed -- Remove rustfmt binary path from the usage string when running `rustfmt --help` [#5214](https://github.com/rust-lang/rustfmt/issues/5214) +- Removed unstable, nightly-only config option `report_todo` [#5101](https://github.com/rust-lang/rustfmt/issues/5101) +- Removed unstable, nightly-only config option `report_fixme` [#5102](https://github.com/rust-lang/rustfmt/issues/5102) +- Removed unstable, nightly-only config option `license_template_path` [#5103](https://github.com/rust-lang/rustfmt/issues/5103) + +### Misc + +- Improved performance when formatting large and deeply nested expression trees, often found in generated code, which have many expressions that exceed `max_width` [#5128](https://github.com/rust-lang/rustfmt/issues/5128), [#4867](https://github.com/rust-lang/rustfmt/issues/4867), [#4476](https://github.com/rust-lang/rustfmt/issues/4476), [#5139](https://github.com/rust-lang/rustfmt/pull/5139) + +### Install/Download Options +- **rustup (nightly)** - *pending* +- **GitHub Release Binaries** - [Release v1.5.0](https://github.com/rust-lang/rustfmt/releases/tag/v1.5.0) +- **Build from source** - [Tag v1.5.0](https://github.com/rust-lang/rustfmt/tree/v1.5.0), see instructions for how to [install rustfmt from source][install-from-source] ## [1.4.38] 2021-10-20 diff --git a/Cargo.lock b/Cargo.lock index 4eaaee74cf96..639d35886dcd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -485,7 +485,7 @@ dependencies = [ [[package]] name = "rustfmt-nightly" -version = "1.4.38" +version = "1.5.0" dependencies = [ "annotate-snippets", "anyhow", diff --git a/Cargo.toml b/Cargo.toml index 0be9723bc4dc..f26e98240623 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rustfmt-nightly" -version = "1.4.38" +version = "1.5.0" description = "Tool to find and fix Rust formatting issues" repository = "https://github.com/rust-lang/rustfmt" readme = "README.md" From 1a6146e53638e04ed86ceec417d4197440ead828 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Mon, 13 Jun 2022 12:57:34 -0400 Subject: [PATCH 200/401] Pass `--locked` when installing rustfmt in integration tests There was recently an issue where `cargo install` was installing a newer version of a dependency than the one listed in our Cargo.toml. The newer version added deprecation warnings that caused our continuous integration tests to break. As mentioned in the `cargo help install` docs, passing the `--locked` flag should force cargo to use the `Cargo.lock` file included with the repository. --- ci/integration.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/integration.sh b/ci/integration.sh index 0269e3ee4af9..562d5d70c70b 100755 --- a/ci/integration.sh +++ b/ci/integration.sh @@ -15,7 +15,7 @@ set -ex # it again. # #which cargo-fmt || cargo install --force -CFG_RELEASE=nightly CFG_RELEASE_CHANNEL=nightly cargo install --path . --force +CFG_RELEASE=nightly CFG_RELEASE_CHANNEL=nightly cargo install --path . --force --locked echo "Integration tests for: ${INTEGRATION}" cargo fmt -- --version From 5ae94cc6b878f8004e3d354d77991146448c1f82 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Mon, 13 Jun 2022 08:34:47 -0400 Subject: [PATCH 201/401] Backport 3795 fix sorting of use statements with raw identifiers --- src/imports.rs | 24 ++++++++++++++++-------- tests/source/imports_raw_identifiers.rs | 3 +++ tests/target/imports_raw_identifiers.rs | 3 +++ 3 files changed, 22 insertions(+), 8 deletions(-) create mode 100644 tests/source/imports_raw_identifiers.rs create mode 100644 tests/target/imports_raw_identifiers.rs diff --git a/src/imports.rs b/src/imports.rs index 559ed3917dba..6742b14c5a37 100644 --- a/src/imports.rs +++ b/src/imports.rs @@ -815,9 +815,16 @@ impl Ord for UseSegment { match (self, other) { (&Slf(ref a), &Slf(ref b)) | (&Super(ref a), &Super(ref b)) - | (&Crate(ref a), &Crate(ref b)) => a.cmp(b), + | (&Crate(ref a), &Crate(ref b)) => match (a, b) { + (Some(sa), Some(sb)) => { + sa.trim_start_matches("r#").cmp(sb.trim_start_matches("r#")) + } + (_, _) => a.cmp(b), + }, (&Glob, &Glob) => Ordering::Equal, - (&Ident(ref ia, ref aa), &Ident(ref ib, ref ab)) => { + (&Ident(ref pia, ref aa), &Ident(ref pib, ref ab)) => { + let ia = pia.trim_start_matches("r#"); + let ib = pib.trim_start_matches("r#"); // snake_case < CamelCase < UPPER_SNAKE_CASE if ia.starts_with(char::is_uppercase) && ib.starts_with(char::is_lowercase) { return Ordering::Greater; @@ -835,13 +842,14 @@ impl Ord for UseSegment { if ident_ord != Ordering::Equal { return ident_ord; } - if aa.is_none() && ab.is_some() { - return Ordering::Less; + match (aa, ab) { + (None, Some(_)) => Ordering::Less, + (Some(_), None) => Ordering::Greater, + (Some(aas), Some(abs)) => aas + .trim_start_matches("r#") + .cmp(abs.trim_start_matches("r#")), + (None, None) => Ordering::Equal, } - if aa.is_some() && ab.is_none() { - return Ordering::Greater; - } - aa.cmp(ab) } (&List(ref a), &List(ref b)) => { for (a, b) in a.iter().zip(b.iter()) { diff --git a/tests/source/imports_raw_identifiers.rs b/tests/source/imports_raw_identifiers.rs new file mode 100644 index 000000000000..d5857290b628 --- /dev/null +++ b/tests/source/imports_raw_identifiers.rs @@ -0,0 +1,3 @@ +use websocket::client::ClientBuilder; +use websocket::r#async::futures::Stream; +use websocket::result::WebSocketError; diff --git a/tests/target/imports_raw_identifiers.rs b/tests/target/imports_raw_identifiers.rs new file mode 100644 index 000000000000..6ae085c8a5f6 --- /dev/null +++ b/tests/target/imports_raw_identifiers.rs @@ -0,0 +1,3 @@ +use websocket::r#async::futures::Stream; +use websocket::client::ClientBuilder; +use websocket::result::WebSocketError; From 795efb206892041e81107cc58797ea61fd1c7b16 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Mon, 13 Jun 2022 10:53:53 -0400 Subject: [PATCH 202/401] Add `Version` information to `UseSegment` There are some proposed import sorting changes for raw identifier `r#`. These changes constitute a breaking change, and need to be version gagted. Before version gating those changes we add the version information to the `UseSegment`. --- src/imports.rs | 311 ++++++++++++++++++++++++++++++++----------------- src/reorder.rs | 10 +- 2 files changed, 206 insertions(+), 115 deletions(-) diff --git a/src/imports.rs b/src/imports.rs index 6742b14c5a37..58c5f31995ee 100644 --- a/src/imports.rs +++ b/src/imports.rs @@ -15,7 +15,7 @@ use rustc_span::{ use crate::comment::combine_strs_with_missing_comments; use crate::config::lists::*; use crate::config::ImportGranularity; -use crate::config::{Edition, IndentStyle}; +use crate::config::{Edition, IndentStyle, Version}; use crate::lists::{ definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator, }; @@ -92,7 +92,7 @@ impl<'a> FmtVisitor<'a> { // FIXME we do a lot of allocation to make our own representation. #[derive(Clone, Eq, Hash, PartialEq)] -pub(crate) enum UseSegment { +pub(crate) enum UseSegmentKind { Ident(String, Option), Slf(Option), Super(Option), @@ -101,6 +101,12 @@ pub(crate) enum UseSegment { List(Vec), } +#[derive(Clone, Eq, PartialEq)] +pub(crate) struct UseSegment { + pub(crate) kind: UseSegmentKind, + pub(crate) version: Version, +} + #[derive(Clone)] pub(crate) struct UseTree { pub(crate) path: Vec, @@ -134,34 +140,38 @@ impl Spanned for UseTree { impl UseSegment { // Clone a version of self with any top-level alias removed. fn remove_alias(&self) -> UseSegment { - match *self { - UseSegment::Ident(ref s, _) => UseSegment::Ident(s.clone(), None), - UseSegment::Slf(_) => UseSegment::Slf(None), - UseSegment::Super(_) => UseSegment::Super(None), - UseSegment::Crate(_) => UseSegment::Crate(None), - _ => self.clone(), + let kind = match self.kind { + UseSegmentKind::Ident(ref s, _) => UseSegmentKind::Ident(s.clone(), None), + UseSegmentKind::Slf(_) => UseSegmentKind::Slf(None), + UseSegmentKind::Super(_) => UseSegmentKind::Super(None), + UseSegmentKind::Crate(_) => UseSegmentKind::Crate(None), + _ => return self.clone(), + }; + UseSegment { + kind, + version: self.version, } } // Check if self == other with their aliases removed. fn equal_except_alias(&self, other: &Self) -> bool { - match (self, other) { - (UseSegment::Ident(ref s1, _), UseSegment::Ident(ref s2, _)) => s1 == s2, - (UseSegment::Slf(_), UseSegment::Slf(_)) - | (UseSegment::Super(_), UseSegment::Super(_)) - | (UseSegment::Crate(_), UseSegment::Crate(_)) - | (UseSegment::Glob, UseSegment::Glob) => true, - (UseSegment::List(ref list1), UseSegment::List(ref list2)) => list1 == list2, + match (&self.kind, &other.kind) { + (UseSegmentKind::Ident(ref s1, _), UseSegmentKind::Ident(ref s2, _)) => s1 == s2, + (UseSegmentKind::Slf(_), UseSegmentKind::Slf(_)) + | (UseSegmentKind::Super(_), UseSegmentKind::Super(_)) + | (UseSegmentKind::Crate(_), UseSegmentKind::Crate(_)) + | (UseSegmentKind::Glob, UseSegmentKind::Glob) => true, + (UseSegmentKind::List(ref list1), UseSegmentKind::List(ref list2)) => list1 == list2, _ => false, } } fn get_alias(&self) -> Option<&str> { - match self { - UseSegment::Ident(_, a) - | UseSegment::Slf(a) - | UseSegment::Super(a) - | UseSegment::Crate(a) => a.as_deref(), + match &self.kind { + UseSegmentKind::Ident(_, a) + | UseSegmentKind::Slf(a) + | UseSegmentKind::Super(a) + | UseSegmentKind::Crate(a) => a.as_deref(), _ => None, } } @@ -175,19 +185,24 @@ impl UseSegment { if name.is_empty() || name == "{{root}}" { return None; } - Some(match name { - "self" => UseSegment::Slf(None), - "super" => UseSegment::Super(None), - "crate" => UseSegment::Crate(None), + let kind = match name { + "self" => UseSegmentKind::Slf(None), + "super" => UseSegmentKind::Super(None), + "crate" => UseSegmentKind::Crate(None), _ => { let mod_sep = if modsep { "::" } else { "" }; - UseSegment::Ident(format!("{}{}", mod_sep, name), None) + UseSegmentKind::Ident(format!("{}{}", mod_sep, name), None) } + }; + + Some(UseSegment { + kind, + version: context.config.version(), }) } fn contains_comment(&self) -> bool { - if let UseSegment::List(list) = self { + if let UseSegmentKind::List(list) = &self.kind { list.iter().any(|subtree| subtree.contains_comment()) } else { false @@ -254,20 +269,38 @@ impl fmt::Debug for UseTree { impl fmt::Debug for UseSegment { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self, f) + fmt::Display::fmt(&self.kind, f) } } impl fmt::Display for UseSegment { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.kind, f) + } +} + +impl Hash for UseSegment { + fn hash(&self, state: &mut H) { + self.kind.hash(state); + } +} + +impl fmt::Debug for UseSegmentKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl fmt::Display for UseSegmentKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { - UseSegment::Glob => write!(f, "*"), - 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"), - UseSegment::List(ref list) => { + UseSegmentKind::Glob => write!(f, "*"), + UseSegmentKind::Ident(ref s, Some(ref alias)) => write!(f, "{} as {}", s, alias), + UseSegmentKind::Ident(ref s, None) => write!(f, "{}", s), + UseSegmentKind::Slf(..) => write!(f, "self"), + UseSegmentKind::Super(..) => write!(f, "super"), + UseSegmentKind::Crate(..) => write!(f, "crate"), + UseSegmentKind::List(ref list) => { write!(f, "{{")?; for (i, item) in list.iter().enumerate() { if i != 0 { @@ -411,13 +444,19 @@ impl UseTree { } } + let version = context.config.version(); + match a.kind { UseTreeKind::Glob => { // in case of a global path and the glob starts at the root, e.g., "::*" if a.prefix.segments.len() == 1 && leading_modsep { - result.path.push(UseSegment::Ident("".to_owned(), None)); + let kind = UseSegmentKind::Ident("".to_owned(), None); + result.path.push(UseSegment { kind, version }); } - result.path.push(UseSegment::Glob); + result.path.push(UseSegment { + kind: UseSegmentKind::Glob, + version, + }); } UseTreeKind::Nested(ref list) => { // Extract comments between nested use items. @@ -438,16 +477,18 @@ impl UseTree { // in case of a global path and the nested list starts at the root, // e.g., "::{foo, bar}" if a.prefix.segments.len() == 1 && leading_modsep { - result.path.push(UseSegment::Ident("".to_owned(), None)); + let kind = UseSegmentKind::Ident("".to_owned(), None); + result.path.push(UseSegment { kind, version }); } - result.path.push(UseSegment::List( + let kind = UseSegmentKind::List( list.iter() .zip(items) .map(|(t, list_item)| { Self::from_ast(context, &t.0, Some(list_item), None, None, None) }) .collect(), - )); + ); + result.path.push(UseSegment { kind, version }); } UseTreeKind::Simple(ref rename, ..) => { // If the path has leading double colons and is composed of only 2 segments, then we @@ -469,13 +510,15 @@ impl UseTree { Some(rewrite_ident(context, ident).to_owned()) } }); - let segment = match name.as_ref() { - "self" => UseSegment::Slf(alias), - "super" => UseSegment::Super(alias), - "crate" => UseSegment::Crate(alias), - _ => UseSegment::Ident(name, alias), + let kind = match name.as_ref() { + "self" => UseSegmentKind::Slf(alias), + "super" => UseSegmentKind::Super(alias), + "crate" => UseSegmentKind::Crate(alias), + _ => UseSegmentKind::Ident(name, alias), }; + let segment = UseSegment { kind, version }; + // `name` is already in result. result.path.pop(); result.path.push(segment); @@ -492,13 +535,13 @@ impl UseTree { let mut aliased_self = false; // Remove foo::{} or self without attributes. - match last { + match last.kind { _ if self.attrs.is_some() => (), - UseSegment::List(ref list) if list.is_empty() => { + UseSegmentKind::List(ref list) if list.is_empty() => { self.path = vec![]; return self; } - UseSegment::Slf(None) if self.path.is_empty() && self.visibility.is_some() => { + UseSegmentKind::Slf(None) if self.path.is_empty() && self.visibility.is_some() => { self.path = vec![]; return self; } @@ -506,15 +549,19 @@ impl UseTree { } // Normalise foo::self -> foo. - if let UseSegment::Slf(None) = last { + if let UseSegmentKind::Slf(None) = last.kind { if !self.path.is_empty() { return self; } } // Normalise foo::self as bar -> foo as bar. - if let UseSegment::Slf(_) = last { - if let Some(UseSegment::Ident(_, None)) = self.path.last() { + if let UseSegmentKind::Slf(_) = last.kind { + if let Some(UseSegment { + kind: UseSegmentKind::Ident(_, None), + .. + }) = self.path.last() + { aliased_self = true; } } @@ -522,9 +569,12 @@ impl UseTree { let mut done = false; if aliased_self { match self.path.last_mut() { - Some(UseSegment::Ident(_, ref mut old_rename)) => { + Some(UseSegment { + kind: UseSegmentKind::Ident(_, ref mut old_rename), + .. + }) => { assert!(old_rename.is_none()); - if let UseSegment::Slf(Some(rename)) = last.clone() { + if let UseSegmentKind::Slf(Some(rename)) = last.clone().kind { *old_rename = Some(rename); done = true; } @@ -538,15 +588,15 @@ impl UseTree { } // Normalise foo::{bar} -> foo::bar - if let UseSegment::List(ref list) = last { + if let UseSegmentKind::List(ref list) = last.kind { if list.len() == 1 && list[0].to_string() != "self" { normalize_sole_list = true; } } if normalize_sole_list { - match last { - UseSegment::List(list) => { + match last.kind { + UseSegmentKind::List(list) => { for seg in &list[0].path { self.path.push(seg.clone()); } @@ -557,10 +607,13 @@ impl UseTree { } // Recursively normalize elements of a list use (including sorting the list). - if let UseSegment::List(list) = last { + if let UseSegmentKind::List(list) = last.kind { let mut list = list.into_iter().map(UseTree::normalize).collect::>(); list.sort(); - last = UseSegment::List(list); + last = UseSegment { + kind: UseSegmentKind::List(list), + version: last.version, + }; } self.path.push(last); @@ -620,10 +673,10 @@ impl UseTree { if self.path.is_empty() || self.contains_comment() { return vec![self]; } - match self.path.clone().last().unwrap() { - UseSegment::List(list) => { + match &self.path.clone().last().unwrap().kind { + UseSegmentKind::List(list) => { if list.len() == 1 && list[0].path.len() == 1 { - if let UseSegment::Slf(..) = list[0].path[0] { + if let UseSegmentKind::Slf(..) = list[0].path[0].kind { return vec![self]; }; } @@ -671,12 +724,15 @@ impl UseTree { /// 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() { + if let Some(UseSegment { + kind: UseSegmentKind::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, - )])); + let version = self_segment.version; + let kind = UseSegmentKind::List(vec![UseTree::from_path(vec![self_segment], DUMMY_SP)]); + self.path.push(UseSegment { kind, version }); } self } @@ -692,7 +748,8 @@ fn merge_rest( return None; } if a.len() != len && b.len() != len { - if let UseSegment::List(ref list) = a[len] { + let version = a[len].version; + if let UseSegmentKind::List(ref list) = a[len].kind { let mut list = list.clone(); merge_use_trees_inner( &mut list, @@ -700,7 +757,8 @@ fn merge_rest( merge_by, ); let mut new_path = b[..len].to_vec(); - new_path.push(UseSegment::List(list)); + let kind = UseSegmentKind::List(list); + new_path.push(UseSegment { kind, version }); return Some(new_path); } } else if len == 1 { @@ -709,15 +767,28 @@ fn merge_rest( } else { (&b[0], &a[1..]) }; + let kind = UseSegmentKind::Slf(common.get_alias().map(ToString::to_string)); + let version = a[0].version; let mut list = vec![UseTree::from_path( - vec![UseSegment::Slf(common.get_alias().map(ToString::to_string))], + vec![UseSegment { kind, version }], DUMMY_SP, )]; match rest { - [UseSegment::List(rest_list)] => list.extend(rest_list.clone()), + [ + UseSegment { + kind: UseSegmentKind::List(rest_list), + .. + }, + ] => list.extend(rest_list.clone()), _ => list.push(UseTree::from_path(rest.to_vec(), DUMMY_SP)), } - return Some(vec![b[0].clone(), UseSegment::List(list)]); + return Some(vec![ + b[0].clone(), + UseSegment { + kind: UseSegmentKind::List(list), + version, + }, + ]); } else { len -= 1; } @@ -727,7 +798,9 @@ fn merge_rest( ]; list.sort(); let mut new_path = b[..len].to_vec(); - new_path.push(UseSegment::List(list)); + let kind = UseSegmentKind::List(list); + let version = a[0].version; + new_path.push(UseSegment { kind, version }); Some(new_path) } @@ -805,24 +878,24 @@ impl PartialOrd for UseTree { } impl Ord for UseSegment { fn cmp(&self, other: &UseSegment) -> Ordering { - use self::UseSegment::*; + use self::UseSegmentKind::*; fn is_upper_snake_case(s: &str) -> bool { s.chars() .all(|c| c.is_uppercase() || c == '_' || c.is_numeric()) } - match (self, other) { - (&Slf(ref a), &Slf(ref b)) - | (&Super(ref a), &Super(ref b)) - | (&Crate(ref a), &Crate(ref b)) => match (a, b) { + match (&self.kind, &other.kind) { + (Slf(ref a), Slf(ref b)) + | (Super(ref a), Super(ref b)) + | (Crate(ref a), Crate(ref b)) => match (a, b) { (Some(sa), Some(sb)) => { sa.trim_start_matches("r#").cmp(sb.trim_start_matches("r#")) } (_, _) => a.cmp(b), }, - (&Glob, &Glob) => Ordering::Equal, - (&Ident(ref pia, ref aa), &Ident(ref pib, ref ab)) => { + (Glob, Glob) => Ordering::Equal, + (Ident(ref pia, ref aa), Ident(ref pib, ref ab)) => { let ia = pia.trim_start_matches("r#"); let ib = pib.trim_start_matches("r#"); // snake_case < CamelCase < UPPER_SNAKE_CASE @@ -851,7 +924,7 @@ impl Ord for UseSegment { (None, None) => Ordering::Equal, } } - (&List(ref a), &List(ref b)) => { + (List(ref a), List(ref b)) => { for (a, b) in a.iter().zip(b.iter()) { let ord = a.cmp(b); if ord != Ordering::Equal { @@ -861,16 +934,16 @@ impl Ord for UseSegment { a.len().cmp(&b.len()) } - (&Slf(_), _) => Ordering::Less, - (_, &Slf(_)) => Ordering::Greater, - (&Super(_), _) => Ordering::Less, - (_, &Super(_)) => Ordering::Greater, - (&Crate(_), _) => Ordering::Less, - (_, &Crate(_)) => Ordering::Greater, - (&Ident(..), _) => Ordering::Less, - (_, &Ident(..)) => Ordering::Greater, - (&Glob, _) => Ordering::Less, - (_, &Glob) => Ordering::Greater, + (Slf(_), _) => Ordering::Less, + (_, Slf(_)) => Ordering::Greater, + (Super(_), _) => Ordering::Less, + (_, Super(_)) => Ordering::Greater, + (Crate(_), _) => Ordering::Less, + (_, Crate(_)) => Ordering::Greater, + (Ident(..), _) => Ordering::Less, + (_, Ident(..)) => Ordering::Greater, + (Glob, _) => Ordering::Less, + (_, Glob) => Ordering::Greater, } } } @@ -914,7 +987,7 @@ fn rewrite_nested_use_tree( } let has_nested_list = use_tree_list.iter().any(|use_segment| { use_segment.path.last().map_or(false, |last_segment| { - matches!(last_segment, UseSegment::List(..)) + matches!(last_segment.kind, UseSegmentKind::List(..)) }) }); @@ -965,17 +1038,19 @@ fn rewrite_nested_use_tree( impl Rewrite for UseSegment { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { - Some(match self { - UseSegment::Ident(ref ident, Some(ref rename)) => format!("{} as {}", ident, rename), - UseSegment::Ident(ref ident, None) => ident.clone(), - UseSegment::Slf(Some(ref rename)) => format!("self as {}", rename), - UseSegment::Slf(None) => "self".to_owned(), - UseSegment::Super(Some(ref rename)) => format!("super as {}", rename), - UseSegment::Super(None) => "super".to_owned(), - UseSegment::Crate(Some(ref rename)) => format!("crate as {}", rename), - UseSegment::Crate(None) => "crate".to_owned(), - UseSegment::Glob => "*".to_owned(), - UseSegment::List(ref use_tree_list) => rewrite_nested_use_tree( + Some(match self.kind { + UseSegmentKind::Ident(ref ident, Some(ref rename)) => { + format!("{} as {}", ident, rename) + } + UseSegmentKind::Ident(ref ident, None) => ident.clone(), + UseSegmentKind::Slf(Some(ref rename)) => format!("self as {}", rename), + UseSegmentKind::Slf(None) => "self".to_owned(), + UseSegmentKind::Super(Some(ref rename)) => format!("super as {}", rename), + UseSegmentKind::Super(None) => "super".to_owned(), + UseSegmentKind::Crate(Some(ref rename)) => format!("crate as {}", rename), + UseSegmentKind::Crate(None) => "crate".to_owned(), + UseSegmentKind::Glob => "*".to_owned(), + UseSegmentKind::List(ref use_tree_list) => rewrite_nested_use_tree( context, use_tree_list, // 1 = "{" and "}" @@ -1024,6 +1099,7 @@ mod test { struct Parser<'a> { input: Peekable>, + version: Version, } impl<'a> Parser<'a> { @@ -1036,34 +1112,40 @@ mod test { } fn push_segment( + &self, result: &mut Vec, buf: &mut String, alias_buf: &mut Option, ) { + let version = self.version; if !buf.is_empty() { let mut alias = None; swap(alias_buf, &mut alias); match buf.as_ref() { "self" => { - result.push(UseSegment::Slf(alias)); + let kind = UseSegmentKind::Slf(alias); + result.push(UseSegment { kind, version }); *buf = String::new(); *alias_buf = None; } "super" => { - result.push(UseSegment::Super(alias)); + let kind = UseSegmentKind::Super(alias); + result.push(UseSegment { kind, version }); *buf = String::new(); *alias_buf = None; } "crate" => { - result.push(UseSegment::Crate(alias)); + let kind = UseSegmentKind::Crate(alias); + result.push(UseSegment { kind, version }); *buf = String::new(); *alias_buf = None; } _ => { let mut name = String::new(); swap(buf, &mut name); - result.push(UseSegment::Ident(name, alias)); + let kind = UseSegmentKind::Ident(name, alias); + result.push(UseSegment { kind, version }); } } } @@ -1078,21 +1160,29 @@ mod test { '{' => { assert!(buf.is_empty()); self.bump(); - result.push(UseSegment::List(self.parse_list())); + let kind = UseSegmentKind::List(self.parse_list()); + result.push(UseSegment { + kind, + version: self.version, + }); self.eat('}'); } '*' => { assert!(buf.is_empty()); self.bump(); - result.push(UseSegment::Glob); + let kind = UseSegmentKind::Glob; + result.push(UseSegment { + kind, + version: self.version, + }); } ':' => { self.bump(); self.eat(':'); - Self::push_segment(&mut result, &mut buf, &mut alias_buf); + self.push_segment(&mut result, &mut buf, &mut alias_buf); } '}' | ',' => { - Self::push_segment(&mut result, &mut buf, &mut alias_buf); + self.push_segment(&mut result, &mut buf, &mut alias_buf); return UseTree { path: result, span: DUMMY_SP, @@ -1118,7 +1208,7 @@ mod test { } } } - Self::push_segment(&mut result, &mut buf, &mut alias_buf); + self.push_segment(&mut result, &mut buf, &mut alias_buf); UseTree { path: result, span: DUMMY_SP, @@ -1144,6 +1234,7 @@ mod test { let mut parser = Parser { input: s.chars().peekable(), + version: Version::One, }; parser.parse_in_list() } diff --git a/src/reorder.rs b/src/reorder.rs index 8ae297de25bc..9e4a668aa493 100644 --- a/src/reorder.rs +++ b/src/reorder.rs @@ -12,7 +12,7 @@ use rustc_ast::ast; use rustc_span::{symbol::sym, Span}; use crate::config::{Config, GroupImportsTactic}; -use crate::imports::{normalize_use_trees_with_granularity, UseSegment, UseTree}; +use crate::imports::{normalize_use_trees_with_granularity, UseSegmentKind, UseTree}; use crate::items::{is_mod_decl, rewrite_extern_crate, rewrite_mod}; use crate::lists::{itemize_list, write_list, ListFormatting, ListItem}; use crate::rewrite::RewriteContext; @@ -182,16 +182,16 @@ fn group_imports(uts: Vec) -> Vec> { external_imports.push(ut); continue; } - match &ut.path[0] { - UseSegment::Ident(id, _) => match id.as_ref() { + match &ut.path[0].kind { + UseSegmentKind::Ident(id, _) => match id.as_ref() { "std" | "alloc" | "core" => std_imports.push(ut), _ => external_imports.push(ut), }, - UseSegment::Slf(_) | UseSegment::Super(_) | UseSegment::Crate(_) => { + UseSegmentKind::Slf(_) | UseSegmentKind::Super(_) | UseSegmentKind::Crate(_) => { local_imports.push(ut) } // These are probably illegal here - UseSegment::Glob | UseSegment::List(_) => external_imports.push(ut), + UseSegmentKind::Glob | UseSegmentKind::List(_) => external_imports.push(ut), } } From e44380b34167ff554ca7bd6ccd446a511927e8aa Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Mon, 13 Jun 2022 11:36:20 -0400 Subject: [PATCH 203/401] Version gate raw identifier use statement sorting When useing `version=One` rustfmt will treat the leading `r#` as part of the `UseSegment` used for sorting. When using `version=Two` rustfmt will ignore the leading `r#` and only consider the name of the identifier when sorting the `UseSegment`. --- src/imports.rs | 24 ++++++++++++++----- .../version_One.rs} | 2 ++ .../imports_raw_identifiers/version_Two.rs} | 4 +++- .../imports_raw_identifiers/version_One.rs | 5 ++++ .../imports_raw_identifiers/version_Two.rs | 5 ++++ 5 files changed, 33 insertions(+), 7 deletions(-) rename tests/source/{imports_raw_identifiers.rs => imports_raw_identifiers/version_One.rs} (83%) rename tests/{target/imports_raw_identifiers.rs => source/imports_raw_identifiers/version_Two.rs} (83%) create mode 100644 tests/target/imports_raw_identifiers/version_One.rs create mode 100644 tests/target/imports_raw_identifiers/version_Two.rs diff --git a/src/imports.rs b/src/imports.rs index 58c5f31995ee..8d41c881589e 100644 --- a/src/imports.rs +++ b/src/imports.rs @@ -890,14 +890,21 @@ impl Ord for UseSegment { | (Super(ref a), Super(ref b)) | (Crate(ref a), Crate(ref b)) => match (a, b) { (Some(sa), Some(sb)) => { - sa.trim_start_matches("r#").cmp(sb.trim_start_matches("r#")) + if self.version == Version::Two { + sa.trim_start_matches("r#").cmp(sb.trim_start_matches("r#")) + } else { + a.cmp(b) + } } (_, _) => a.cmp(b), }, (Glob, Glob) => Ordering::Equal, (Ident(ref pia, ref aa), Ident(ref pib, ref ab)) => { - let ia = pia.trim_start_matches("r#"); - let ib = pib.trim_start_matches("r#"); + let (ia, ib) = if self.version == Version::Two { + (pia.trim_start_matches("r#"), pib.trim_start_matches("r#")) + } else { + (pia.as_str(), pib.as_str()) + }; // snake_case < CamelCase < UPPER_SNAKE_CASE if ia.starts_with(char::is_uppercase) && ib.starts_with(char::is_lowercase) { return Ordering::Greater; @@ -918,9 +925,14 @@ impl Ord for UseSegment { match (aa, ab) { (None, Some(_)) => Ordering::Less, (Some(_), None) => Ordering::Greater, - (Some(aas), Some(abs)) => aas - .trim_start_matches("r#") - .cmp(abs.trim_start_matches("r#")), + (Some(aas), Some(abs)) => { + if self.version == Version::Two { + aas.trim_start_matches("r#") + .cmp(abs.trim_start_matches("r#")) + } else { + aas.cmp(abs) + } + } (None, None) => Ordering::Equal, } } diff --git a/tests/source/imports_raw_identifiers.rs b/tests/source/imports_raw_identifiers/version_One.rs similarity index 83% rename from tests/source/imports_raw_identifiers.rs rename to tests/source/imports_raw_identifiers/version_One.rs index d5857290b628..bc4b5b135696 100644 --- a/tests/source/imports_raw_identifiers.rs +++ b/tests/source/imports_raw_identifiers/version_One.rs @@ -1,3 +1,5 @@ +// rustfmt-version:One + use websocket::client::ClientBuilder; use websocket::r#async::futures::Stream; use websocket::result::WebSocketError; diff --git a/tests/target/imports_raw_identifiers.rs b/tests/source/imports_raw_identifiers/version_Two.rs similarity index 83% rename from tests/target/imports_raw_identifiers.rs rename to tests/source/imports_raw_identifiers/version_Two.rs index 6ae085c8a5f6..88e7fbd01ca6 100644 --- a/tests/target/imports_raw_identifiers.rs +++ b/tests/source/imports_raw_identifiers/version_Two.rs @@ -1,3 +1,5 @@ -use websocket::r#async::futures::Stream; +// rustfmt-version:Two + use websocket::client::ClientBuilder; +use websocket::r#async::futures::Stream; use websocket::result::WebSocketError; diff --git a/tests/target/imports_raw_identifiers/version_One.rs b/tests/target/imports_raw_identifiers/version_One.rs new file mode 100644 index 000000000000..bc4b5b135696 --- /dev/null +++ b/tests/target/imports_raw_identifiers/version_One.rs @@ -0,0 +1,5 @@ +// rustfmt-version:One + +use websocket::client::ClientBuilder; +use websocket::r#async::futures::Stream; +use websocket::result::WebSocketError; diff --git a/tests/target/imports_raw_identifiers/version_Two.rs b/tests/target/imports_raw_identifiers/version_Two.rs new file mode 100644 index 000000000000..22bfe93122f9 --- /dev/null +++ b/tests/target/imports_raw_identifiers/version_Two.rs @@ -0,0 +1,5 @@ +// rustfmt-version:Two + +use websocket::r#async::futures::Stream; +use websocket::client::ClientBuilder; +use websocket::result::WebSocketError; From 33c60740d3e1387a5979cac93785e605de8470e0 Mon Sep 17 00:00:00 2001 From: sec65 <106604020+sec65@users.noreply.github.com> Date: Fri, 17 Jun 2022 05:15:16 +0200 Subject: [PATCH 204/401] Add width for codeblocks in comments (#5372) * add doc_comment_code_block_width configuration * updated config docu * Updated docu and changed tests to config folder --- Configurations.md | 8 +++++ src/comment.rs | 4 +++ src/config/mod.rs | 3 ++ .../doc_comment_code_block_width/100.rs | 16 ++++++++++ .../100_greater_max_width.rs | 17 +++++++++++ .../doc_comment_code_block_width/50.rs | 16 ++++++++++ .../doc_comment_code_block_width/100.rs | 16 ++++++++++ .../100_greater_max_width.rs | 29 +++++++++++++++++++ .../doc_comment_code_block_width/50.rs | 22 ++++++++++++++ 9 files changed, 131 insertions(+) create mode 100644 tests/source/configs/doc_comment_code_block_width/100.rs create mode 100644 tests/source/configs/doc_comment_code_block_width/100_greater_max_width.rs create mode 100644 tests/source/configs/doc_comment_code_block_width/50.rs create mode 100644 tests/target/configs/doc_comment_code_block_width/100.rs create mode 100644 tests/target/configs/doc_comment_code_block_width/100_greater_max_width.rs create mode 100644 tests/target/configs/doc_comment_code_block_width/50.rs diff --git a/Configurations.md b/Configurations.md index 8c84614352ca..8b96b9d36892 100644 --- a/Configurations.md +++ b/Configurations.md @@ -926,6 +926,14 @@ fn add_one(x: i32) -> i32 { } ``` +## `doc_comment_code_block_width` + +Max width for code snippets included in doc comments. Only used if [`format_code_in_doc_comments`](#format_code_in_doc_comments) is true. + +- **Default value**: `100` +- **Possible values**: any positive integer that is less than or equal to the value specified for [`max_width`](#max_width) +- **Stable**: No (tracking issue: [#5359](https://github.com/rust-lang/rustfmt/issues/5359)) + ## `format_generated_files` Format generated files. A file is considered generated diff --git a/src/comment.rs b/src/comment.rs index eb195b1f7628..4d565afc1e02 100644 --- a/src/comment.rs +++ b/src/comment.rs @@ -730,6 +730,10 @@ impl<'a> CommentRewrite<'a> { { let mut config = self.fmt.config.clone(); config.set().wrap_comments(false); + let comment_max_width = config + .doc_comment_code_block_width() + .min(config.max_width()); + config.set().max_width(comment_max_width); if let Some(s) = crate::format_code_block(&self.code_block_buffer, &config, false) { diff --git a/src/config/mod.rs b/src/config/mod.rs index a51695281878..f49c18d3a460 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -57,6 +57,8 @@ create_config! { // Comments. macros, and strings wrap_comments: bool, false, false, "Break comments to fit on the line"; format_code_in_doc_comments: bool, false, false, "Format the code snippet in doc comments."; + doc_comment_code_block_width: usize, 100, false, "Maximum width for code snippets in doc \ + comments. No effect unless format_code_in_doc_comments = true"; comment_width: usize, 80, false, "Maximum length of comments. No effect unless wrap_comments = true"; normalize_comments: bool, false, false, "Convert /* */ comments to // comments where possible"; @@ -532,6 +534,7 @@ chain_width = 60 single_line_if_else_max_width = 50 wrap_comments = false format_code_in_doc_comments = false +doc_comment_code_block_width = 100 comment_width = 80 normalize_comments = false normalize_doc_attributes = false diff --git a/tests/source/configs/doc_comment_code_block_width/100.rs b/tests/source/configs/doc_comment_code_block_width/100.rs new file mode 100644 index 000000000000..515780761670 --- /dev/null +++ b/tests/source/configs/doc_comment_code_block_width/100.rs @@ -0,0 +1,16 @@ +// rustfmt-format_code_in_doc_comments: true +// rustfmt-doc_comment_code_block_width: 100 + +/// ```rust +/// impl Test { +/// pub const fn from_bytes(v: &[u8]) -> Result { +/// Self::from_bytes_manual_slice(v, 0, v.len() ) +/// } +/// } +/// ``` + +impl Test { + pub const fn from_bytes(v: &[u8]) -> Result { + Self::from_bytes_manual_slice(v, 0, v.len() ) + } +} diff --git a/tests/source/configs/doc_comment_code_block_width/100_greater_max_width.rs b/tests/source/configs/doc_comment_code_block_width/100_greater_max_width.rs new file mode 100644 index 000000000000..96505c69714e --- /dev/null +++ b/tests/source/configs/doc_comment_code_block_width/100_greater_max_width.rs @@ -0,0 +1,17 @@ +// rustfmt-max_width: 50 +// rustfmt-format_code_in_doc_comments: true +// rustfmt-doc_comment_code_block_width: 100 + +/// ```rust +/// impl Test { +/// pub const fn from_bytes(v: &[u8]) -> Result { +/// Self::from_bytes_manual_slice(v, 0, v.len() ) +/// } +/// } +/// ``` + +impl Test { + pub const fn from_bytes(v: &[u8]) -> Result { + Self::from_bytes_manual_slice(v, 0, v.len() ) + } +} diff --git a/tests/source/configs/doc_comment_code_block_width/50.rs b/tests/source/configs/doc_comment_code_block_width/50.rs new file mode 100644 index 000000000000..2c6307951c84 --- /dev/null +++ b/tests/source/configs/doc_comment_code_block_width/50.rs @@ -0,0 +1,16 @@ +// rustfmt-format_code_in_doc_comments: true +// rustfmt-doc_comment_code_block_width: 50 + +/// ```rust +/// impl Test { +/// pub const fn from_bytes(v: &[u8]) -> Result { +/// Self::from_bytes_manual_slice(v, 0, v.len() ) +/// } +/// } +/// ``` + +impl Test { + pub const fn from_bytes(v: &[u8]) -> Result { + Self::from_bytes_manual_slice(v, 0, v.len() ) + } +} diff --git a/tests/target/configs/doc_comment_code_block_width/100.rs b/tests/target/configs/doc_comment_code_block_width/100.rs new file mode 100644 index 000000000000..c010a28aab61 --- /dev/null +++ b/tests/target/configs/doc_comment_code_block_width/100.rs @@ -0,0 +1,16 @@ +// rustfmt-format_code_in_doc_comments: true +// rustfmt-doc_comment_code_block_width: 100 + +/// ```rust +/// impl Test { +/// pub const fn from_bytes(v: &[u8]) -> Result { +/// Self::from_bytes_manual_slice(v, 0, v.len()) +/// } +/// } +/// ``` + +impl Test { + pub const fn from_bytes(v: &[u8]) -> Result { + Self::from_bytes_manual_slice(v, 0, v.len()) + } +} diff --git a/tests/target/configs/doc_comment_code_block_width/100_greater_max_width.rs b/tests/target/configs/doc_comment_code_block_width/100_greater_max_width.rs new file mode 100644 index 000000000000..6bcb99b915ff --- /dev/null +++ b/tests/target/configs/doc_comment_code_block_width/100_greater_max_width.rs @@ -0,0 +1,29 @@ +// rustfmt-max_width: 50 +// rustfmt-format_code_in_doc_comments: true +// rustfmt-doc_comment_code_block_width: 100 + +/// ```rust +/// impl Test { +/// pub const fn from_bytes( +/// v: &[u8], +/// ) -> Result { +/// Self::from_bytes_manual_slice( +/// v, +/// 0, +/// v.len(), +/// ) +/// } +/// } +/// ``` + +impl Test { + pub const fn from_bytes( + v: &[u8], + ) -> Result { + Self::from_bytes_manual_slice( + v, + 0, + v.len(), + ) + } +} diff --git a/tests/target/configs/doc_comment_code_block_width/50.rs b/tests/target/configs/doc_comment_code_block_width/50.rs new file mode 100644 index 000000000000..e8ab6f28bdc5 --- /dev/null +++ b/tests/target/configs/doc_comment_code_block_width/50.rs @@ -0,0 +1,22 @@ +// rustfmt-format_code_in_doc_comments: true +// rustfmt-doc_comment_code_block_width: 50 + +/// ```rust +/// impl Test { +/// pub const fn from_bytes( +/// v: &[u8], +/// ) -> Result { +/// Self::from_bytes_manual_slice( +/// v, +/// 0, +/// v.len(), +/// ) +/// } +/// } +/// ``` + +impl Test { + pub const fn from_bytes(v: &[u8]) -> Result { + Self::from_bytes_manual_slice(v, 0, v.len()) + } +} From 3de1a095e0ed52ade1b88d165d44d80455d3e6c5 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Thu, 16 Jun 2022 08:51:19 -0400 Subject: [PATCH 205/401] Set `package.metadata.rust-analyzer.rustc_private=true` in Cargo.toml By setting this value in the Cargo.toml rust-analyzer understands that rustfmt uses compiler-internals using `extern crate`. This is a universal step that all developers will need to take in order to get better type hints and code completion suggestions for compiler-internals in their editor. **Note**: users will also need to install the `rustc-dev` component via `rustup` and add the following to their rust-analyzer configuration: ```json { "rust-analyzer.rustcSource": "discover", "rust-analyzer.updates.channel": "nightly" } ``` --- Cargo.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index f26e98240623..4f5127e1de2a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -65,3 +65,7 @@ rustfmt-config_proc_macro = { version = "0.2", path = "config_proc_macro" } rustc-workspace-hack = "1.0.0" # Rustc dependencies are loaded from the sysroot, Cargo doesn't know about them. + +[package.metadata.rust-analyzer] +# This package uses #[feature(rustc_private)] +rustc_private = true From 2c8b3bef2bb5d708e047afc4d0dadf0a85528470 Mon Sep 17 00:00:00 2001 From: Tom Milligan Date: Tue, 21 Jun 2022 16:23:13 +0100 Subject: [PATCH 206/401] ci: include config_proc_macro crate in ci (#5389) * config_proc_macro: fix failing doctests * ci: include config_proc_macro crate in ci * [review] working native windows ci * [fix] add --locked file for ci * [fix] quoting of cmd variables --- .github/workflows/linux.yml | 14 ++--------- .github/workflows/mac.yml | 10 ++------ .github/workflows/windows.yml | 11 ++------- ci/build_and_test.bat | 14 +++++++++++ ci/build_and_test.sh | 18 ++++++++++++++ config_proc_macro/Cargo.lock | 44 +++++++++++++++++------------------ config_proc_macro/src/lib.rs | 4 ++++ 7 files changed, 64 insertions(+), 51 deletions(-) create mode 100755 ci/build_and_test.bat create mode 100755 ci/build_and_test.sh diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 6a3f9d89d98f..bce9b0c8d5a9 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -35,15 +35,5 @@ jobs: sh rustup-init.sh -y --default-toolchain none rustup target add ${{ matrix.target }} - - name: build - run: | - rustc -Vv - cargo -V - cargo build - env: - RUSTFLAGS: '-D warnings' - - - name: test - run: cargo test - env: - RUSTFLAGS: '-D warnings' + - name: Build and Test + run: ./ci/build_and_test.sh diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml index 7dfda3142ca9..89a980c42c5a 100644 --- a/.github/workflows/mac.yml +++ b/.github/workflows/mac.yml @@ -32,11 +32,5 @@ jobs: sh rustup-init.sh -y --default-toolchain none rustup target add ${{ matrix.target }} - - name: build - run: | - rustc -Vv - cargo -V - cargo build - - - name: test - run: cargo test + - name: Build and Test + run: ./ci/build_and_test.sh diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 4ebc29638490..ec37c714b085 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -57,13 +57,6 @@ jobs: if: matrix.target == 'x86_64-pc-windows-gnu' && matrix.channel == 'nightly' shell: bash - - name: build - run: | - rustc -Vv - cargo -V - cargo build - shell: cmd - - - name: test - run: cargo test + - name: Build and Test shell: cmd + run: ci\build_and_test.bat diff --git a/ci/build_and_test.bat b/ci/build_and_test.bat new file mode 100755 index 000000000000..ef41017783fe --- /dev/null +++ b/ci/build_and_test.bat @@ -0,0 +1,14 @@ +set "RUSTFLAGS=-D warnings" + +:: Print version information +rustc -Vv || exit /b 1 +cargo -V || exit /b 1 + +:: Build and test main crate +cargo build --locked || exit /b 1 +cargo test || exit /b 1 + +:: Build and test other crates +cd config_proc_macro || exit /b 1 +cargo build --locked || exit /b 1 +cargo test || exit /b 1 diff --git a/ci/build_and_test.sh b/ci/build_and_test.sh new file mode 100755 index 000000000000..8fa0f67b0d02 --- /dev/null +++ b/ci/build_and_test.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +set -euo pipefail + +export RUSTFLAGS="-D warnings" + +# Print version information +rustc -Vv +cargo -V + +# Build and test main crate +cargo build --locked +cargo test + +# Build and test other crates +cd config_proc_macro +cargo build --locked +cargo test diff --git a/config_proc_macro/Cargo.lock b/config_proc_macro/Cargo.lock index abcf9654e5d7..ecf561f28fb6 100644 --- a/config_proc_macro/Cargo.lock +++ b/config_proc_macro/Cargo.lock @@ -1,68 +1,68 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "proc-macro2" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e98a83a9f9b331f54b924e68a66acb1bb35cb01fb0a23645139967abefb697e8" dependencies = [ - "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid", ] [[package]] name = "quote" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" dependencies = [ - "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", ] [[package]] name = "rustfmt-config_proc_macro" -version = "0.1.2" +version = "0.2.0" dependencies = [ - "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "serde", + "syn", ] [[package]] name = "serde" version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fec2851eb56d010dc9a21b89ca53ee75e6528bab60c11e89d38390904982da9f" dependencies = [ - "serde_derive 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4dc18c61206b08dc98216c98faa0232f4337e1e1b8574551d5bad29ea1b425" dependencies = [ - "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "syn" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf" dependencies = [ - "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "unicode-xid", ] [[package]] name = "unicode-xid" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" - -[metadata] -"checksum proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e98a83a9f9b331f54b924e68a66acb1bb35cb01fb0a23645139967abefb697e8" -"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" -"checksum serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)" = "fec2851eb56d010dc9a21b89ca53ee75e6528bab60c11e89d38390904982da9f" -"checksum serde_derive 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)" = "cb4dc18c61206b08dc98216c98faa0232f4337e1e1b8574551d5bad29ea1b425" -"checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf" -"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" diff --git a/config_proc_macro/src/lib.rs b/config_proc_macro/src/lib.rs index 513018213192..e772c53f4236 100644 --- a/config_proc_macro/src/lib.rs +++ b/config_proc_macro/src/lib.rs @@ -29,6 +29,8 @@ pub fn config_type(_args: TokenStream, input: TokenStream) -> TokenStream { /// Used to conditionally output the TokenStream for tests that need to be run on nightly only. /// /// ```rust +/// # use rustfmt_config_proc_macro::nightly_only_test; +/// /// #[nightly_only_test] /// #[test] /// fn test_needs_nightly_rustfmt() { @@ -49,6 +51,8 @@ pub fn nightly_only_test(_args: TokenStream, input: TokenStream) -> TokenStream /// Used to conditionally output the TokenStream for tests that need to be run on stable only. /// /// ```rust +/// # use rustfmt_config_proc_macro::stable_only_test; +/// /// #[stable_only_test] /// #[test] /// fn test_needs_stable_rustfmt() { From 0156575a32511a2d58d129415e9ce31ccb80e7e8 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Wed, 22 Jun 2022 13:36:01 -0400 Subject: [PATCH 207/401] Revert "Memoize format_expr" Fixes 5399 Memoizing expressions lead to cases where rustfmt's stability guarantees were violated. This reverts commit a37d3ab0e1c7c05f1a6410fb7ddf5539f0d030f8. --- src/expr.rs | 51 +- src/formatting.rs | 2 - src/rewrite.rs | 13 - src/shape.rs | 4 +- src/visitor.rs | 7 +- tests/source/performance/issue-4476.rs | 638 --- tests/source/performance/issue-5128.rs | 5127 ------------------------ tests/target/performance/issue-4476.rs | 705 ---- tests/target/performance/issue-4867.rs | 13 - tests/target/performance/issue-5128.rs | 4898 ---------------------- 10 files changed, 4 insertions(+), 11454 deletions(-) delete mode 100644 tests/source/performance/issue-4476.rs delete mode 100644 tests/source/performance/issue-5128.rs delete mode 100644 tests/target/performance/issue-4476.rs delete mode 100644 tests/target/performance/issue-4867.rs delete mode 100644 tests/target/performance/issue-5128.rs diff --git a/src/expr.rs b/src/expr.rs index 4ccf1ca70c9d..e4cc93026f10 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -1,6 +1,5 @@ use std::borrow::Cow; use std::cmp::min; -use std::collections::HashMap; use itertools::Itertools; use rustc_ast::token::{Delimiter, LitKind}; @@ -23,7 +22,7 @@ use crate::macros::{rewrite_macro, MacroPosition}; use crate::matches::rewrite_match; use crate::overflow::{self, IntoOverflowableItem, OverflowableItem}; use crate::pairs::{rewrite_all_pairs, rewrite_pair, PairParts}; -use crate::rewrite::{QueryId, Rewrite, RewriteContext}; +use crate::rewrite::{Rewrite, RewriteContext}; use crate::shape::{Indent, Shape}; use crate::source_map::{LineRangeUtils, SpanUtils}; use crate::spanned::Spanned; @@ -54,54 +53,6 @@ pub(crate) fn format_expr( expr_type: ExprType, context: &RewriteContext<'_>, shape: Shape, -) -> Option { - // when max_width is tight, we should check all possible formattings, in order to find - // if we can fit expression in the limit. Doing it recursively takes exponential time - // relative to input size, and people hit it with rustfmt takes minutes in #4476 #4867 #5128 - // By memoization of format_expr function, we format each pair of expression and shape - // only once, so worst case execution time becomes O(n*max_width^3). - if context.inside_macro() || context.is_macro_def { - // span ids are not unique in macros, so we don't memoize result of them. - return format_expr_inner(expr, expr_type, context, shape); - } - let clean; - let query_id = QueryId { - shape, - span: expr.span, - }; - if let Some(map) = context.memoize.take() { - if let Some(r) = map.get(&query_id) { - let r = r.clone(); - context.memoize.set(Some(map)); // restore map in the memoize cell for other users - return r; - } - context.memoize.set(Some(map)); - clean = false; - } else { - context.memoize.set(Some(HashMap::default())); - clean = true; // We got None, so we are the top level called function. When - // this function finishes, no one is interested in what is in the map, because - // all of them are sub expressions of this top level expression, and this is - // done. So we should clean up memoize map to save some memory. - } - - let r = format_expr_inner(expr, expr_type, context, shape); - if clean { - context.memoize.set(None); - } else { - if let Some(mut map) = context.memoize.take() { - map.insert(query_id, r.clone()); // insert the result in the memoize map - context.memoize.set(Some(map)); // so it won't be computed again - } - } - r -} - -fn format_expr_inner( - expr: &ast::Expr, - expr_type: ExprType, - context: &RewriteContext<'_>, - shape: Shape, ) -> Option { skip_out_of_file_lines_range!(context, expr.span); diff --git a/src/formatting.rs b/src/formatting.rs index e644ea50effd..1dfd8a514f0b 100644 --- a/src/formatting.rs +++ b/src/formatting.rs @@ -2,7 +2,6 @@ use std::collections::HashMap; use std::io::{self, Write}; -use std::rc::Rc; use std::time::{Duration, Instant}; use rustc_ast::ast; @@ -203,7 +202,6 @@ impl<'a, T: FormatHandler + 'a> FormatContext<'a, T> { self.config, &snippet_provider, self.report.clone(), - Rc::default(), ); visitor.skip_context.update_with_attrs(&self.krate.attrs); visitor.is_macro_def = is_macro_def; diff --git a/src/rewrite.rs b/src/rewrite.rs index f97df70cc6a7..4a3bd129d16f 100644 --- a/src/rewrite.rs +++ b/src/rewrite.rs @@ -12,7 +12,6 @@ use crate::shape::Shape; use crate::skip::SkipContext; use crate::visitor::SnippetProvider; use crate::FormatReport; -use rustc_data_structures::stable_map::FxHashMap; pub(crate) trait Rewrite { /// Rewrite self into shape. @@ -25,22 +24,10 @@ impl Rewrite for ptr::P { } } -#[derive(Clone, PartialEq, Eq, Hash)] -pub(crate) struct QueryId { - pub(crate) shape: Shape, - pub(crate) span: Span, -} - -// We use Option instead of HashMap, because in case of `None` -// the function clean the memoize map, but it doesn't clean when -// there is `Some(empty)`, so they are different. -pub(crate) type Memoize = Rc>>>>; - #[derive(Clone)] pub(crate) struct RewriteContext<'a> { pub(crate) parse_sess: &'a ParseSess, pub(crate) config: &'a Config, - pub(crate) memoize: Memoize, pub(crate) inside_macro: Rc>, // Force block indent style even if we are using visual indent style. pub(crate) use_block: Cell, diff --git a/src/shape.rs b/src/shape.rs index b3f785a9470e..4376fd12b526 100644 --- a/src/shape.rs +++ b/src/shape.rs @@ -4,7 +4,7 @@ use std::ops::{Add, Sub}; use crate::Config; -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, Debug)] pub(crate) struct Indent { // Width of the block indent, in characters. Must be a multiple of // Config::tab_spaces. @@ -139,7 +139,7 @@ impl Sub for Indent { // 8096 is close enough to infinite for rustfmt. const INFINITE_SHAPE_WIDTH: usize = 8096; -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, Debug)] pub(crate) struct Shape { pub(crate) width: usize, // The current indentation of code. diff --git a/src/visitor.rs b/src/visitor.rs index 3ff56d52f92d..9a0e0752c12f 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -17,7 +17,7 @@ use crate::items::{ use crate::macros::{macro_style, rewrite_macro, rewrite_macro_def, MacroPosition}; use crate::modules::Module; use crate::parse::session::ParseSess; -use crate::rewrite::{Memoize, Rewrite, RewriteContext}; +use crate::rewrite::{Rewrite, RewriteContext}; use crate::shape::{Indent, Shape}; use crate::skip::{is_skip_attr, SkipContext}; use crate::source_map::{LineRangeUtils, SpanUtils}; @@ -71,7 +71,6 @@ impl SnippetProvider { pub(crate) struct FmtVisitor<'a> { parent_context: Option<&'a RewriteContext<'a>>, - pub(crate) memoize: Memoize, pub(crate) parse_sess: &'a ParseSess, pub(crate) buffer: String, pub(crate) last_pos: BytePos, @@ -759,7 +758,6 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { ctx.config, ctx.snippet_provider, ctx.report.clone(), - ctx.memoize.clone(), ); visitor.skip_context.update(ctx.skip_context.clone()); visitor.set_parent_context(ctx); @@ -771,12 +769,10 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { config: &'a Config, snippet_provider: &'a SnippetProvider, report: FormatReport, - memoize: Memoize, ) -> FmtVisitor<'a> { FmtVisitor { parent_context: None, parse_sess: parse_session, - memoize, buffer: String::with_capacity(snippet_provider.big_snippet.len() * 2), last_pos: BytePos(0), block_indent: Indent::empty(), @@ -999,7 +995,6 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { RewriteContext { parse_sess: self.parse_sess, config: self.config, - memoize: self.memoize.clone(), inside_macro: Rc::new(Cell::new(false)), use_block: Cell::new(false), is_if_else_block: Cell::new(false), diff --git a/tests/source/performance/issue-4476.rs b/tests/source/performance/issue-4476.rs deleted file mode 100644 index 8da3f19b62d6..000000000000 --- a/tests/source/performance/issue-4476.rs +++ /dev/null @@ -1,638 +0,0 @@ -use super::SemverParser; - -#[allow(dead_code, non_camel_case_types)] -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub enum Rule { - EOI, - range_set, - logical_or, - range, - empty, - hyphen, - simple, - primitive, - primitive_op, - partial, - xr, - xr_op, - nr, - tilde, - caret, - qualifier, - parts, - part, - space, -} -#[allow(clippy::all)] -impl ::pest::Parser for SemverParser { - fn parse<'i>( - rule: Rule, - input: &'i str, - ) -> ::std::result::Result<::pest::iterators::Pairs<'i, Rule>, ::pest::error::Error> { - mod rules { - pub mod hidden { - use super::super::Rule; - #[inline] - #[allow(dead_code, non_snake_case, unused_variables)] - pub fn skip( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - Ok(state) - } - } - pub mod visible { - use super::super::Rule; - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn range_set( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::range_set, |state| { - state.sequence(|state| { - self::SOI(state) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - self::space(state).and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state).and_then(|state| self::space(state)) - }) - }) - }) - }) - }) - }) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::range(state)) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - state - .sequence(|state| { - self::logical_or(state) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::range(state)) - }) - .and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state).and_then(|state| { - state.sequence(|state| { - self::logical_or(state) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::range(state)) - }) - }) - }) - }) - }) - }) - }) - }) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - self::space(state).and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state).and_then(|state| self::space(state)) - }) - }) - }) - }) - }) - }) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::EOI(state)) - }) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn logical_or( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::logical_or, |state| { - state.sequence(|state| { - state - .sequence(|state| { - state.optional(|state| { - self::space(state).and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state).and_then(|state| self::space(state)) - }) - }) - }) - }) - }) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| state.match_string("||")) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - self::space(state).and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state).and_then(|state| self::space(state)) - }) - }) - }) - }) - }) - }) - }) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn range( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::range, |state| { - self::hyphen(state) - .or_else(|state| { - state.sequence(|state| { - self::simple(state) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - state - .sequence(|state| { - state - .optional(|state| state.match_string(",")) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - self::space(state) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - self::space(state).and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state) - .and_then(|state| self::space(state)) - }) - }) - }) - }) - }) - }) - }) - }) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::simple(state)) - }) - .and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state).and_then(|state| { - state.sequence(|state| { - state - .optional(|state| state.match_string(",")) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - self::space(state) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - self::space(state).and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state) - .and_then(|state| self::space(state)) - }) - }) - }) - }) - }) - }) - }) - }) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::simple(state)) - }) - }) - }) - }) - }) - }) - }) - }) - }) - }) - .or_else(|state| self::empty(state)) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn empty( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::empty, |state| state.match_string("")) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn hyphen( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::hyphen, |state| { - state.sequence(|state| { - self::partial(state) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - self::space(state) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - self::space(state).and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state).and_then(|state| self::space(state)) - }) - }) - }) - }) - }) - }) - }) - }) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| state.match_string("-")) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - self::space(state) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - self::space(state).and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state).and_then(|state| self::space(state)) - }) - }) - }) - }) - }) - }) - }) - }) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::partial(state)) - }) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn simple( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::simple, |state| { - self::primitive(state) - .or_else(|state| self::partial(state)) - .or_else(|state| self::tilde(state)) - .or_else(|state| self::caret(state)) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn primitive( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::primitive, |state| { - state.sequence(|state| { - self::primitive_op(state) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - self::space(state).and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state).and_then(|state| self::space(state)) - }) - }) - }) - }) - }) - }) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::partial(state)) - }) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn primitive_op( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::primitive_op, |state| { - state - .match_string("<=") - .or_else(|state| state.match_string(">=")) - .or_else(|state| state.match_string(">")) - .or_else(|state| state.match_string("<")) - .or_else(|state| state.match_string("=")) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn partial( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::partial, |state| { - state.sequence(|state| { - self::xr(state) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.optional(|state| { - state.sequence(|state| { - state - .match_string(".") - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::xr(state)) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.optional(|state| { - state.sequence(|state| { - state - .match_string(".") - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::xr(state)) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| state.optional(|state| self::qualifier(state))) - }) - }) - }) - }) - }) - }) - }) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn xr( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::xr, |state| { - self::xr_op(state).or_else(|state| self::nr(state)) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn xr_op( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::xr_op, |state| { - state - .match_string("x") - .or_else(|state| state.match_string("X")) - .or_else(|state| state.match_string("*")) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn nr( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::nr, |state| { - state.match_string("0").or_else(|state| { - state.sequence(|state| { - state - .match_range('1'..'9') - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - state.match_range('0'..'9').and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state) - .and_then(|state| state.match_range('0'..'9')) - }) - }) - }) - }) - }) - }) - }) - }) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn tilde( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::tilde, |state| { - state.sequence(|state| { - state - .match_string("~>") - .or_else(|state| state.match_string("~")) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - self::space(state).and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state).and_then(|state| self::space(state)) - }) - }) - }) - }) - }) - }) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::partial(state)) - }) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn caret( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::caret, |state| { - state.sequence(|state| { - state - .match_string("^") - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - self::space(state).and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state).and_then(|state| self::space(state)) - }) - }) - }) - }) - }) - }) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::partial(state)) - }) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn qualifier( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::qualifier, |state| { - state.sequence(|state| { - state - .match_string("-") - .or_else(|state| state.match_string("+")) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::parts(state)) - }) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn parts( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::parts, |state| { - state.sequence(|state| { - self::part(state) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - state - .sequence(|state| { - state - .match_string(".") - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::part(state)) - }) - .and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state).and_then(|state| { - state.sequence(|state| { - state - .match_string(".") - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::part(state)) - }) - }) - }) - }) - }) - }) - }) - }) - }) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn part( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::part, |state| { - self::nr(state).or_else(|state| { - state.sequence(|state| { - state - .match_string("-") - .or_else(|state| state.match_range('0'..'9')) - .or_else(|state| state.match_range('A'..'Z')) - .or_else(|state| state.match_range('a'..'z')) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - state - .match_string("-") - .or_else(|state| state.match_range('0'..'9')) - .or_else(|state| state.match_range('A'..'Z')) - .or_else(|state| state.match_range('a'..'z')) - .and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state).and_then(|state| { - state - .match_string("-") - .or_else(|state| state.match_range('0'..'9')) - .or_else(|state| state.match_range('A'..'Z')) - .or_else(|state| state.match_range('a'..'z')) - }) - }) - }) - }) - }) - }) - }) - }) - }) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn space( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state - .match_string(" ") - .or_else(|state| state.match_string("\t")) - } - #[inline] - #[allow(dead_code, non_snake_case, unused_variables)] - pub fn EOI( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::EOI, |state| state.end_of_input()) - } - #[inline] - #[allow(dead_code, non_snake_case, unused_variables)] - pub fn SOI( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.start_of_input() - } - } - pub use self::visible::*; - } - ::pest::state(input, |state| match rule { - Rule::range_set => rules::range_set(state), - Rule::logical_or => rules::logical_or(state), - Rule::range => rules::range(state), - Rule::empty => rules::empty(state), - Rule::hyphen => rules::hyphen(state), - Rule::simple => rules::simple(state), - Rule::primitive => rules::primitive(state), - Rule::primitive_op => rules::primitive_op(state), - Rule::partial => rules::partial(state), - Rule::xr => rules::xr(state), - Rule::xr_op => rules::xr_op(state), - Rule::nr => rules::nr(state), - Rule::tilde => rules::tilde(state), - Rule::caret => rules::caret(state), - Rule::qualifier => rules::qualifier(state), - Rule::parts => rules::parts(state), - Rule::part => rules::part(state), - Rule::space => rules::space(state), - Rule::EOI => rules::EOI(state), - }) - } -} \ No newline at end of file diff --git a/tests/source/performance/issue-5128.rs b/tests/source/performance/issue-5128.rs deleted file mode 100644 index 3adce49601c0..000000000000 --- a/tests/source/performance/issue-5128.rs +++ /dev/null @@ -1,5127 +0,0 @@ - -fn takes_a_long_time_to_rustfmt() { - let inner_cte = vec![Node { - node: Some(node::Node::CommonTableExpr(Box::new(CommonTableExpr { - ctename: String::from("ranked_by_age_within_key"), - aliascolnames: vec![], - ctematerialized: CteMaterialize::Default as i32, - ctequery: Some(Box::new(Node { - node: Some(node::Node::SelectStmt(Box::new(SelectStmt { - distinct_clause: vec![], - into_clause: None, - target_list: vec![ - Node { - node: Some(node::Node::ResTarget(Box::new(ResTarget { - name: String::from(""), - indirection: vec![], - val: Some(Box::new(Node { - node: Some(node::Node::ColumnRef(ColumnRef { - fields: vec![Node { - node: Some(node::Node::AStar(AStar{})) - }], - location: 80 - })) - })), - location: 80 - }))) - }, - Node { - node: Some(node::Node::ResTarget(Box::new(ResTarget { - name: String::from("rank_in_key"), - indirection: vec![], - val: Some(Box::new(Node { - node: Some(node::Node::FuncCall(Box::new(FuncCall { - funcname: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("row_number") - })) - }], - args: vec![], - agg_order: vec![], - agg_filter: None, - agg_within_group: false, - agg_star: false, - agg_distinct: false, - func_variadic: false, - over: Some(Box::new(WindowDef { - name: String::from(""), - refname: String::from(""), - partition_clause: vec![ - Node { - node: Some(node::Node::ColumnRef(ColumnRef { - fields: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("synthetic_key") - })) - }], location: 123 - })) - }], order_clause: vec![Node { - node: Some(node::Node::SortBy(Box::new(SortBy { - node: Some(Box::new(Node { - node: Some(node::Node::ColumnRef(ColumnRef { - fields: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("logical_timestamp") - })) - }], location: 156 - })) - })), - sortby_dir: SortByDir::SortbyDesc as i32, - sortby_nulls: SortByNulls::SortbyNullsDefault as i32, - use_op: vec![], - location: -1 - }))) - }], frame_options: 1058, start_offset: None, end_offset: None, location: 109 - })), - location: 91 - }))) - })), - location: 91 - }))) - }], - from_clause: vec![Node { - node: Some(node::Node::RangeVar(RangeVar { - catalogname: String::from(""), schemaname: String::from("_supertables"), relname: String::from("9999-9999-9999"), inh: true, relpersistence: String::from("p"), alias: None, location: 206 - })) - }], - where_clause: Some(Box::new(Node { - node: Some(node::Node::AExpr(Box::new(AExpr { - kind: AExprKind::AexprOp as i32, - name: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("<=") - })) - }], - lexpr: Some(Box::new(Node { - node: Some(node::Node::ColumnRef(ColumnRef { - fields: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("logical_timestamp") - })) - }], - location: 250 - })) - })), - rexpr: Some(Box::new(Node { - node: Some(node::Node::AConst(Box::new(AConst { - val: Some(Box::new(Node { - node: Some(node::Node::Integer(Integer { - ival: 9000 - })) - })), - location: 271 - }))) - })), - location: 268 - }))) - })), - group_clause: vec![], - having_clause: None, - window_clause: vec![], - values_lists: vec![], - sort_clause: vec![], - limit_offset: None, - limit_count: None, - limit_option: LimitOption::Default as i32, - locking_clause: vec![], - with_clause: None, - op: SetOperation::SetopNone as i32, - all: false, - larg: None, - rarg: None - }))), - })), - location: 29, - cterecursive: false, - cterefcount: 0, - ctecolnames: vec![], - ctecoltypes: vec![], - ctecoltypmods: vec![], - ctecolcollations: vec![], - }))), - }]; - let outer_cte = vec![Node { - node: Some(node::Node::CommonTableExpr(Box::new(CommonTableExpr { - ctename: String::from("table_name"), - aliascolnames: vec![], - ctematerialized: CteMaterialize::Default as i32, - ctequery: Some(Box::new(Node { - node: Some(node::Node::SelectStmt(Box::new(SelectStmt { - distinct_clause: vec![], - into_clause: None, - target_list: vec![ - Node { - node: Some(node::Node::ResTarget(Box::new(ResTarget { - name: String::from("column1"), - indirection: vec![], - val: Some(Box::new(Node { - node: Some(node::Node::ColumnRef(ColumnRef { - fields: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("c1"), - })), - }], - location: 301, - })), - })), - location: 301, - }))), - }, - Node { - node: Some(node::Node::ResTarget(Box::new(ResTarget { - name: String::from("column2"), - indirection: vec![], - val: Some(Box::new(Node { - node: Some(node::Node::ColumnRef(ColumnRef { - fields: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("c2"), - })), - }], - location: 324, - })), - })), - location: 324, - }))), - }, - ], - from_clause: vec![Node { - node: Some(node::Node::RangeVar(RangeVar { - catalogname: String::from(""), - schemaname: String::from(""), - relname: String::from("ranked_by_age_within_key"), - inh: true, - relpersistence: String::from("p"), - alias: None, - location: 347, - })), - }], - where_clause: Some(Box::new(Node { - node: Some(node::Node::BoolExpr(Box::new(BoolExpr { - xpr: None, - boolop: BoolExprType::AndExpr as i32, - args: vec![ - Node { - node: Some(node::Node::AExpr(Box::new(AExpr { - kind: AExprKind::AexprOp as i32, - name: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("="), - })), - }], - lexpr: Some(Box::new(Node { - node: Some(node::Node::ColumnRef(ColumnRef { - fields: vec![Node { - node: Some(node::Node::String( - String2 { - str: String::from("rank_in_key"), - }, - )), - }], - location: 382, - })), - })), - rexpr: Some(Box::new(Node { - node: Some(node::Node::AConst(Box::new(AConst { - val: Some(Box::new(Node { - node: Some(node::Node::Integer( - Integer { ival: 1 }, - )), - })), - location: 396, - }))), - })), - location: 394, - }))), - }, - Node { - node: Some(node::Node::AExpr(Box::new(AExpr { - kind: AExprKind::AexprOp as i32, - name: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("="), - })), - }], - lexpr: Some(Box::new(Node { - node: Some(node::Node::ColumnRef(ColumnRef { - fields: vec![Node { - node: Some(node::Node::String( - String2 { - str: String::from("is_deleted"), - }, - )), - }], - location: 402, - })), - })), - rexpr: Some(Box::new(Node { - node: Some(node::Node::TypeCast(Box::new( - TypeCast { - arg: Some(Box::new(Node { - node: Some(node::Node::AConst( - Box::new(AConst { - val: Some(Box::new(Node { - node: Some( - node::Node::String( - String2 { - str: - String::from( - "f", - ), - }, - ), - ), - })), - location: 415, - }), - )), - })), - type_name: Some(TypeName { - names: vec![ - Node { - node: Some(node::Node::String( - String2 { - str: String::from( - "pg_catalog", - ), - }, - )), - }, - Node { - node: Some(node::Node::String( - String2 { - str: String::from( - "bool", - ), - }, - )), - }, - ], - type_oid: 0, - setof: false, - pct_type: false, - typmods: vec![], - typemod: -1, - array_bounds: vec![], - location: -1, - }), - location: -1, - }, - ))), - })), - location: 413, - }))), - }, - ], - location: 398, - }))), - })), - group_clause: vec![], - having_clause: None, - window_clause: vec![], - values_lists: vec![], - sort_clause: vec![], - limit_offset: None, - limit_count: None, - limit_option: LimitOption::Default as i32, - locking_clause: vec![], - with_clause: Some(WithClause { - ctes: inner_cte, - recursive: false, - location: 24, - }), - op: SetOperation::SetopNone as i32, - all: false, - larg: None, - rarg: None, - }))), - })), - location: 5, - cterecursive: false, - cterefcount: 0, - ctecolnames: vec![], - ctecoltypes: vec![], - ctecoltypmods: vec![], - ctecolcollations: vec![], - }))), - }]; - let expected_result = ParseResult { - version: 130003, - stmts: vec![RawStmt { - stmt: Some(Box::new(Node { - node: Some(node::Node::SelectStmt(Box::new(SelectStmt { - distinct_clause: vec![], - into_clause: None, - - target_list: vec![Node { - node: Some(node::Node::ResTarget(Box::new(ResTarget { - name: String::from(""), - indirection: vec![], - val: Some(Box::new(Node { - node: Some(node::Node::ColumnRef(ColumnRef { - fields: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("column1"), - })), - }], - location: 430, - })), - })), - location: 430, - }))), - }], - from_clause: vec![Node { - node: Some(node::Node::RangeVar(RangeVar { - catalogname: String::from(""), - schemaname: String::from(""), - relname: String::from("table_name"), - inh: true, - relpersistence: String::from("p"), - alias: None, - location: 443, - })), - }], - where_clause: Some(Box::new(Node { - node: Some(node::Node::AExpr(Box::new(AExpr { - kind: AExprKind::AexprOp as i32, - name: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from(">"), - })), - }], - lexpr: Some(Box::new(Node { - node: Some(node::Node::ColumnRef(ColumnRef { - fields: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("column2"), - })), - }], - location: 460, - })), - })), - rexpr: Some(Box::new(Node { - node: Some(node::Node::AConst(Box::new(AConst { - val: Some(Box::new(Node { - node: Some(node::Node::Integer(Integer { - ival: 9000, - })), - })), - location: 470, - }))), - })), - location: 468, - }))), - })), - group_clause: vec![], - having_clause: None, - window_clause: vec![], - values_lists: vec![], - sort_clause: vec![], - limit_offset: None, - limit_count: None, - limit_option: LimitOption::Default as i32, - locking_clause: vec![], - with_clause: Some(WithClause { - ctes: outer_cte, - recursive: false, - location: 0, - }), - op: SetOperation::SetopNone as i32, - all: false, - larg: None, - rarg: None, - }))), - })), - stmt_location: 0, - stmt_len: 0, - }], - }; - -} -#[derive(Clone, PartialEq)] -pub struct ParseResult { - - pub version: i32, - - pub stmts: Vec, -} -#[derive(Clone, PartialEq)] -pub struct ScanResult { - - pub version: i32, - - pub tokens: Vec, -} -#[derive(Clone, PartialEq)] -pub struct Node { - pub node: ::core::option::Option, -} -/// Nested message and enum types in `Node`. -pub mod node { - #[derive(Clone, PartialEq)] - pub enum Node { - - Alias(super::Alias), - - RangeVar(super::RangeVar), - - TableFunc(Box), - - Expr(super::Expr), - - Var(Box), - - Param(Box), - - Aggref(Box), - - GroupingFunc(Box), - - WindowFunc(Box), - - SubscriptingRef(Box), - - FuncExpr(Box), - - NamedArgExpr(Box), - - OpExpr(Box), - - DistinctExpr(Box), - - NullIfExpr(Box), - - ScalarArrayOpExpr(Box), - - BoolExpr(Box), - - SubLink(Box), - - SubPlan(Box), - - AlternativeSubPlan(Box), - - FieldSelect(Box), - - FieldStore(Box), - - RelabelType(Box), - - CoerceViaIo(Box), - - ArrayCoerceExpr(Box), - - ConvertRowtypeExpr(Box), - - CollateExpr(Box), - - CaseExpr(Box), - - CaseWhen(Box), - - CaseTestExpr(Box), - - ArrayExpr(Box), - - RowExpr(Box), - - RowCompareExpr(Box), - - CoalesceExpr(Box), - - MinMaxExpr(Box), - - SqlvalueFunction(Box), - - XmlExpr(Box), - - NullTest(Box), - - BooleanTest(Box), - - CoerceToDomain(Box), - - CoerceToDomainValue(Box), - - SetToDefault(Box), - - CurrentOfExpr(Box), - - NextValueExpr(Box), - - InferenceElem(Box), - - TargetEntry(Box), - - RangeTblRef(super::RangeTblRef), - - JoinExpr(Box), - - FromExpr(Box), - - OnConflictExpr(Box), - - IntoClause(Box), - - RawStmt(Box), - - Query(Box), - - InsertStmt(Box), - - DeleteStmt(Box), - - UpdateStmt(Box), - - SelectStmt(Box), - - AlterTableStmt(super::AlterTableStmt), - - AlterTableCmd(Box), - - AlterDomainStmt(Box), - - SetOperationStmt(Box), - - GrantStmt(super::GrantStmt), - - GrantRoleStmt(super::GrantRoleStmt), - - AlterDefaultPrivilegesStmt(super::AlterDefaultPrivilegesStmt), - - ClosePortalStmt(super::ClosePortalStmt), - - ClusterStmt(super::ClusterStmt), - - CopyStmt(Box), - - CreateStmt(super::CreateStmt), - - DefineStmt(super::DefineStmt), - - DropStmt(super::DropStmt), - - TruncateStmt(super::TruncateStmt), - - CommentStmt(Box), - - FetchStmt(super::FetchStmt), - - IndexStmt(Box), - - CreateFunctionStmt(super::CreateFunctionStmt), - - AlterFunctionStmt(super::AlterFunctionStmt), - - DoStmt(super::DoStmt), - - RenameStmt(Box), - - RuleStmt(Box), - - NotifyStmt(super::NotifyStmt), - - ListenStmt(super::ListenStmt), - - UnlistenStmt(super::UnlistenStmt), - - TransactionStmt(super::TransactionStmt), - - ViewStmt(Box), - - LoadStmt(super::LoadStmt), - - CreateDomainStmt(Box), - - CreatedbStmt(super::CreatedbStmt), - - DropdbStmt(super::DropdbStmt), - - VacuumStmt(super::VacuumStmt), - - ExplainStmt(Box), - - CreateTableAsStmt(Box), - - CreateSeqStmt(super::CreateSeqStmt), - - AlterSeqStmt(super::AlterSeqStmt), - - VariableSetStmt(super::VariableSetStmt), - - VariableShowStmt(super::VariableShowStmt), - - DiscardStmt(super::DiscardStmt), - - CreateTrigStmt(Box), - - CreatePlangStmt(super::CreatePLangStmt), - - CreateRoleStmt(super::CreateRoleStmt), - - AlterRoleStmt(super::AlterRoleStmt), - - DropRoleStmt(super::DropRoleStmt), - - LockStmt(super::LockStmt), - - ConstraintsSetStmt(super::ConstraintsSetStmt), - - ReindexStmt(super::ReindexStmt), - - CheckPointStmt(super::CheckPointStmt), - - CreateSchemaStmt(super::CreateSchemaStmt), - - AlterDatabaseStmt(super::AlterDatabaseStmt), - - AlterDatabaseSetStmt(super::AlterDatabaseSetStmt), - - AlterRoleSetStmt(super::AlterRoleSetStmt), - - CreateConversionStmt(super::CreateConversionStmt), - - CreateCastStmt(super::CreateCastStmt), - - CreateOpClassStmt(super::CreateOpClassStmt), - - CreateOpFamilyStmt(super::CreateOpFamilyStmt), - - AlterOpFamilyStmt(super::AlterOpFamilyStmt), - - PrepareStmt(Box), - - ExecuteStmt(super::ExecuteStmt), - - DeallocateStmt(super::DeallocateStmt), - - DeclareCursorStmt(Box), - - CreateTableSpaceStmt(super::CreateTableSpaceStmt), - - DropTableSpaceStmt(super::DropTableSpaceStmt), - - AlterObjectDependsStmt(Box), - - AlterObjectSchemaStmt(Box), - - AlterOwnerStmt(Box), - - AlterOperatorStmt(super::AlterOperatorStmt), - - AlterTypeStmt(super::AlterTypeStmt), - - DropOwnedStmt(super::DropOwnedStmt), - - ReassignOwnedStmt(super::ReassignOwnedStmt), - - CompositeTypeStmt(super::CompositeTypeStmt), - - CreateEnumStmt(super::CreateEnumStmt), - - CreateRangeStmt(super::CreateRangeStmt), - - AlterEnumStmt(super::AlterEnumStmt), - - AlterTsdictionaryStmt(super::AlterTsDictionaryStmt), - - AlterTsconfigurationStmt(super::AlterTsConfigurationStmt), - - CreateFdwStmt(super::CreateFdwStmt), - - AlterFdwStmt(super::AlterFdwStmt), - - CreateForeignServerStmt(super::CreateForeignServerStmt), - - AlterForeignServerStmt(super::AlterForeignServerStmt), - - CreateUserMappingStmt(super::CreateUserMappingStmt), - - AlterUserMappingStmt(super::AlterUserMappingStmt), - - DropUserMappingStmt(super::DropUserMappingStmt), - - AlterTableSpaceOptionsStmt(super::AlterTableSpaceOptionsStmt), - - AlterTableMoveAllStmt(super::AlterTableMoveAllStmt), - - SecLabelStmt(Box), - - CreateForeignTableStmt(super::CreateForeignTableStmt), - - ImportForeignSchemaStmt(super::ImportForeignSchemaStmt), - - CreateExtensionStmt(super::CreateExtensionStmt), - - AlterExtensionStmt(super::AlterExtensionStmt), - - AlterExtensionContentsStmt(Box), - - CreateEventTrigStmt(super::CreateEventTrigStmt), - - AlterEventTrigStmt(super::AlterEventTrigStmt), - - RefreshMatViewStmt(super::RefreshMatViewStmt), - - ReplicaIdentityStmt(super::ReplicaIdentityStmt), - - AlterSystemStmt(super::AlterSystemStmt), - - CreatePolicyStmt(Box), - - AlterPolicyStmt(Box), - - CreateTransformStmt(super::CreateTransformStmt), - - CreateAmStmt(super::CreateAmStmt), - - CreatePublicationStmt(super::CreatePublicationStmt), - - AlterPublicationStmt(super::AlterPublicationStmt), - - CreateSubscriptionStmt(super::CreateSubscriptionStmt), - - AlterSubscriptionStmt(super::AlterSubscriptionStmt), - - DropSubscriptionStmt(super::DropSubscriptionStmt), - - CreateStatsStmt(super::CreateStatsStmt), - - AlterCollationStmt(super::AlterCollationStmt), - - CallStmt(Box), - - AlterStatsStmt(super::AlterStatsStmt), - - AExpr(Box), - - ColumnRef(super::ColumnRef), - - ParamRef(super::ParamRef), - - AConst(Box), - - FuncCall(Box), - - AStar(super::AStar), - - AIndices(Box), - - AIndirection(Box), - - AArrayExpr(super::AArrayExpr), - - ResTarget(Box), - - MultiAssignRef(Box), - - TypeCast(Box), - - CollateClause(Box), - - SortBy(Box), - - WindowDef(Box), - - RangeSubselect(Box), - - RangeFunction(super::RangeFunction), - - RangeTableSample(Box), - - RangeTableFunc(Box), - - RangeTableFuncCol(Box), - - TypeName(super::TypeName), - - ColumnDef(Box), - - IndexElem(Box), - - Constraint(Box), - - DefElem(Box), - - RangeTblEntry(Box), - - RangeTblFunction(Box), - - TableSampleClause(Box), - - WithCheckOption(Box), - - SortGroupClause(super::SortGroupClause), - - GroupingSet(super::GroupingSet), - - WindowClause(Box), - - ObjectWithArgs(super::ObjectWithArgs), - - AccessPriv(super::AccessPriv), - - CreateOpClassItem(super::CreateOpClassItem), - - TableLikeClause(super::TableLikeClause), - - FunctionParameter(Box), - - LockingClause(super::LockingClause), - - RowMarkClause(super::RowMarkClause), - - XmlSerialize(Box), - - WithClause(super::WithClause), - - InferClause(Box), - - OnConflictClause(Box), - - CommonTableExpr(Box), - - RoleSpec(super::RoleSpec), - - TriggerTransition(super::TriggerTransition), - - PartitionElem(Box), - - PartitionSpec(super::PartitionSpec), - - PartitionBoundSpec(super::PartitionBoundSpec), - - PartitionRangeDatum(Box), - - PartitionCmd(super::PartitionCmd), - - VacuumRelation(super::VacuumRelation), - - InlineCodeBlock(super::InlineCodeBlock), - - CallContext(super::CallContext), - - Integer(super::Integer), - - Float(super::Float), - - String(super::String2), - - BitString(super::BitString), - - Null(super::Null), - - List(super::List), - - IntList(super::IntList), - - OidList(super::OidList), - } -} -#[derive(Clone, PartialEq)] -pub struct Integer { - /// machine integer - - pub ival: i32, -} -#[derive(Clone, PartialEq)] -pub struct Float { - /// string - - pub str: String, -} -#[derive(Clone, PartialEq)] -pub struct String2 { - /// string - - pub str: String, -} -#[derive(Clone, PartialEq)] -pub struct BitString { - /// string - - pub str: String, -} -/// intentionally empty -#[derive(Clone, PartialEq)] -pub struct Null {} -#[derive(Clone, PartialEq)] -pub struct List { - - pub items: Vec, -} -#[derive(Clone, PartialEq)] -pub struct OidList { - - pub items: Vec, -} -#[derive(Clone, PartialEq)] -pub struct IntList { - - pub items: Vec, -} -#[derive(Clone, PartialEq)] -pub struct Alias { - - pub aliasname: String, - - pub colnames: Vec, -} -#[derive(Clone, PartialEq)] -pub struct RangeVar { - - pub catalogname: String, - - pub schemaname: String, - - pub relname: String, - - pub inh: bool, - - pub relpersistence: String, - - pub alias: ::core::option::Option, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct TableFunc { - - pub ns_uris: Vec, - - pub ns_names: Vec, - - pub docexpr: ::core::option::Option>, - - pub rowexpr: ::core::option::Option>, - - pub colnames: Vec, - - pub coltypes: Vec, - - pub coltypmods: Vec, - - pub colcollations: Vec, - - pub colexprs: Vec, - - pub coldefexprs: Vec, - - pub notnulls: Vec, - - pub ordinalitycol: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct Expr {} -#[derive(Clone, PartialEq)] -pub struct Var { - - pub xpr: ::core::option::Option>, - - pub varno: u32, - - pub varattno: i32, - - pub vartype: u32, - - pub vartypmod: i32, - - pub varcollid: u32, - - pub varlevelsup: u32, - - pub varnosyn: u32, - - pub varattnosyn: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct Param { - - pub xpr: ::core::option::Option>, - - pub paramkind: i32, - - pub paramid: i32, - - pub paramtype: u32, - - pub paramtypmod: i32, - - pub paramcollid: u32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct Aggref { - - pub xpr: ::core::option::Option>, - - pub aggfnoid: u32, - - pub aggtype: u32, - - pub aggcollid: u32, - - pub inputcollid: u32, - - pub aggtranstype: u32, - - pub aggargtypes: Vec, - - pub aggdirectargs: Vec, - - pub args: Vec, - - pub aggorder: Vec, - - pub aggdistinct: Vec, - - pub aggfilter: ::core::option::Option>, - - pub aggstar: bool, - - pub aggvariadic: bool, - - pub aggkind: String, - - pub agglevelsup: u32, - - pub aggsplit: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct GroupingFunc { - - pub xpr: ::core::option::Option>, - - pub args: Vec, - - pub refs: Vec, - - pub cols: Vec, - - pub agglevelsup: u32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct WindowFunc { - - pub xpr: ::core::option::Option>, - - pub winfnoid: u32, - - pub wintype: u32, - - pub wincollid: u32, - - pub inputcollid: u32, - - pub args: Vec, - - pub aggfilter: ::core::option::Option>, - - pub winref: u32, - - pub winstar: bool, - - pub winagg: bool, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct SubscriptingRef { - - pub xpr: ::core::option::Option>, - - pub refcontainertype: u32, - - pub refelemtype: u32, - - pub reftypmod: i32, - - pub refcollid: u32, - - pub refupperindexpr: Vec, - - pub reflowerindexpr: Vec, - - pub refexpr: ::core::option::Option>, - - pub refassgnexpr: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct FuncExpr { - - pub xpr: ::core::option::Option>, - - pub funcid: u32, - - pub funcresulttype: u32, - - pub funcretset: bool, - - pub funcvariadic: bool, - - pub funcformat: i32, - - pub funccollid: u32, - - pub inputcollid: u32, - - pub args: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct NamedArgExpr { - - pub xpr: ::core::option::Option>, - - pub arg: ::core::option::Option>, - - pub name: String, - - pub argnumber: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct OpExpr { - - pub xpr: ::core::option::Option>, - - pub opno: u32, - - pub opfuncid: u32, - - pub opresulttype: u32, - - pub opretset: bool, - - pub opcollid: u32, - - pub inputcollid: u32, - - pub args: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct DistinctExpr { - - pub xpr: ::core::option::Option>, - - pub opno: u32, - - pub opfuncid: u32, - - pub opresulttype: u32, - - pub opretset: bool, - - pub opcollid: u32, - - pub inputcollid: u32, - - pub args: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct NullIfExpr { - - pub xpr: ::core::option::Option>, - - pub opno: u32, - - pub opfuncid: u32, - - pub opresulttype: u32, - - pub opretset: bool, - - pub opcollid: u32, - - pub inputcollid: u32, - - pub args: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct ScalarArrayOpExpr { - - pub xpr: ::core::option::Option>, - - pub opno: u32, - - pub opfuncid: u32, - - pub use_or: bool, - - pub inputcollid: u32, - - pub args: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct BoolExpr { - - pub xpr: ::core::option::Option>, - - pub boolop: i32, - - pub args: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct SubLink { - - pub xpr: ::core::option::Option>, - - pub sub_link_type: i32, - - pub sub_link_id: i32, - - pub testexpr: ::core::option::Option>, - - pub oper_name: Vec, - - pub subselect: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct SubPlan { - - pub xpr: ::core::option::Option>, - - pub sub_link_type: i32, - - pub testexpr: ::core::option::Option>, - - pub param_ids: Vec, - - pub plan_id: i32, - - pub plan_name: String, - - pub first_col_type: u32, - - pub first_col_typmod: i32, - - pub first_col_collation: u32, - - pub use_hash_table: bool, - - pub unknown_eq_false: bool, - - pub parallel_safe: bool, - - pub set_param: Vec, - - pub par_param: Vec, - - pub args: Vec, - - pub startup_cost: f64, - - pub per_call_cost: f64, -} -#[derive(Clone, PartialEq)] -pub struct AlternativeSubPlan { - - pub xpr: ::core::option::Option>, - - pub subplans: Vec, -} -#[derive(Clone, PartialEq)] -pub struct FieldSelect { - - pub xpr: ::core::option::Option>, - - pub arg: ::core::option::Option>, - - pub fieldnum: i32, - - pub resulttype: u32, - - pub resulttypmod: i32, - - pub resultcollid: u32, -} -#[derive(Clone, PartialEq)] -pub struct FieldStore { - - pub xpr: ::core::option::Option>, - - pub arg: ::core::option::Option>, - - pub newvals: Vec, - - pub fieldnums: Vec, - - pub resulttype: u32, -} -#[derive(Clone, PartialEq)] -pub struct RelabelType { - - pub xpr: ::core::option::Option>, - - pub arg: ::core::option::Option>, - - pub resulttype: u32, - - pub resulttypmod: i32, - - pub resultcollid: u32, - - pub relabelformat: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct CoerceViaIo { - - pub xpr: ::core::option::Option>, - - pub arg: ::core::option::Option>, - - pub resulttype: u32, - - pub resultcollid: u32, - - pub coerceformat: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct ArrayCoerceExpr { - - pub xpr: ::core::option::Option>, - - pub arg: ::core::option::Option>, - - pub elemexpr: ::core::option::Option>, - - pub resulttype: u32, - - pub resulttypmod: i32, - - pub resultcollid: u32, - - pub coerceformat: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct ConvertRowtypeExpr { - - pub xpr: ::core::option::Option>, - - pub arg: ::core::option::Option>, - - pub resulttype: u32, - - pub convertformat: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct CollateExpr { - - pub xpr: ::core::option::Option>, - - pub arg: ::core::option::Option>, - - pub coll_oid: u32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct CaseExpr { - - pub xpr: ::core::option::Option>, - - pub casetype: u32, - - pub casecollid: u32, - - pub arg: ::core::option::Option>, - - pub args: Vec, - - pub defresult: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct CaseWhen { - - pub xpr: ::core::option::Option>, - - pub expr: ::core::option::Option>, - - pub result: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct CaseTestExpr { - - pub xpr: ::core::option::Option>, - - pub type_id: u32, - - pub type_mod: i32, - - pub collation: u32, -} -#[derive(Clone, PartialEq)] -pub struct ArrayExpr { - - pub xpr: ::core::option::Option>, - - pub array_typeid: u32, - - pub array_collid: u32, - - pub element_typeid: u32, - - pub elements: Vec, - - pub multidims: bool, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct RowExpr { - - pub xpr: ::core::option::Option>, - - pub args: Vec, - - pub row_typeid: u32, - - pub row_format: i32, - - pub colnames: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct RowCompareExpr { - - pub xpr: ::core::option::Option>, - - pub rctype: i32, - - pub opnos: Vec, - - pub opfamilies: Vec, - - pub inputcollids: Vec, - - pub largs: Vec, - - pub rargs: Vec, -} -#[derive(Clone, PartialEq)] -pub struct CoalesceExpr { - - pub xpr: ::core::option::Option>, - - pub coalescetype: u32, - - pub coalescecollid: u32, - - pub args: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct MinMaxExpr { - - pub xpr: ::core::option::Option>, - - pub minmaxtype: u32, - - pub minmaxcollid: u32, - - pub inputcollid: u32, - - pub op: i32, - - pub args: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct SqlValueFunction { - - pub xpr: ::core::option::Option>, - - pub op: i32, - - pub r#type: u32, - - pub typmod: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct XmlExpr { - - pub xpr: ::core::option::Option>, - - pub op: i32, - - pub name: String, - - pub named_args: Vec, - - pub arg_names: Vec, - - pub args: Vec, - - pub xmloption: i32, - - pub r#type: u32, - - pub typmod: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct NullTest { - - pub xpr: ::core::option::Option>, - - pub arg: ::core::option::Option>, - - pub nulltesttype: i32, - - pub argisrow: bool, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct BooleanTest { - - pub xpr: ::core::option::Option>, - - pub arg: ::core::option::Option>, - - pub booltesttype: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct CoerceToDomain { - - pub xpr: ::core::option::Option>, - - pub arg: ::core::option::Option>, - - pub resulttype: u32, - - pub resulttypmod: i32, - - pub resultcollid: u32, - - pub coercionformat: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct CoerceToDomainValue { - - pub xpr: ::core::option::Option>, - - pub type_id: u32, - - pub type_mod: i32, - - pub collation: u32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct SetToDefault { - - pub xpr: ::core::option::Option>, - - pub type_id: u32, - - pub type_mod: i32, - - pub collation: u32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct CurrentOfExpr { - - pub xpr: ::core::option::Option>, - - pub cvarno: u32, - - pub cursor_name: String, - - pub cursor_param: i32, -} -#[derive(Clone, PartialEq)] -pub struct NextValueExpr { - - pub xpr: ::core::option::Option>, - - pub seqid: u32, - - pub type_id: u32, -} -#[derive(Clone, PartialEq)] -pub struct InferenceElem { - - pub xpr: ::core::option::Option>, - - pub expr: ::core::option::Option>, - - pub infercollid: u32, - - pub inferopclass: u32, -} -#[derive(Clone, PartialEq)] -pub struct TargetEntry { - - pub xpr: ::core::option::Option>, - - pub expr: ::core::option::Option>, - - pub resno: i32, - - pub resname: String, - - pub ressortgroupref: u32, - - pub resorigtbl: u32, - - pub resorigcol: i32, - - pub resjunk: bool, -} -#[derive(Clone, PartialEq)] -pub struct RangeTblRef { - - pub rtindex: i32, -} -#[derive(Clone, PartialEq)] -pub struct JoinExpr { - - pub jointype: i32, - - pub is_natural: bool, - - pub larg: ::core::option::Option>, - - pub rarg: ::core::option::Option>, - - pub using_clause: Vec, - - pub quals: ::core::option::Option>, - - pub alias: ::core::option::Option, - - pub rtindex: i32, -} -#[derive(Clone, PartialEq)] -pub struct FromExpr { - - pub fromlist: Vec, - - pub quals: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct OnConflictExpr { - - pub action: i32, - - pub arbiter_elems: Vec, - - pub arbiter_where: ::core::option::Option>, - - pub constraint: u32, - - pub on_conflict_set: Vec, - - pub on_conflict_where: ::core::option::Option>, - - pub excl_rel_index: i32, - - pub excl_rel_tlist: Vec, -} -#[derive(Clone, PartialEq)] -pub struct IntoClause { - - pub rel: ::core::option::Option, - - pub col_names: Vec, - - pub access_method: String, - - pub options: Vec, - - pub on_commit: i32, - - pub table_space_name: String, - - pub view_query: ::core::option::Option>, - - pub skip_data: bool, -} -#[derive(Clone, PartialEq)] -pub struct RawStmt { - - pub stmt: ::core::option::Option>, - - pub stmt_location: i32, - - pub stmt_len: i32, -} -#[derive(Clone, PartialEq)] -pub struct Query { - - pub command_type: i32, - - pub query_source: i32, - - pub can_set_tag: bool, - - pub utility_stmt: ::core::option::Option>, - - pub result_relation: i32, - - pub has_aggs: bool, - - pub has_window_funcs: bool, - - pub has_target_srfs: bool, - - pub has_sub_links: bool, - - pub has_distinct_on: bool, - - pub has_recursive: bool, - - pub has_modifying_cte: bool, - - pub has_for_update: bool, - - pub has_row_security: bool, - - pub cte_list: Vec, - - pub rtable: Vec, - - pub jointree: ::core::option::Option>, - - pub target_list: Vec, - - pub r#override: i32, - - pub on_conflict: ::core::option::Option>, - - pub returning_list: Vec, - - pub group_clause: Vec, - - pub grouping_sets: Vec, - - pub having_qual: ::core::option::Option>, - - pub window_clause: Vec, - - pub distinct_clause: Vec, - - pub sort_clause: Vec, - - pub limit_offset: ::core::option::Option>, - - pub limit_count: ::core::option::Option>, - - pub limit_option: i32, - - pub row_marks: Vec, - - pub set_operations: ::core::option::Option>, - - pub constraint_deps: Vec, - - pub with_check_options: Vec, - - pub stmt_location: i32, - - pub stmt_len: i32, -} -#[derive(Clone, PartialEq)] -pub struct InsertStmt { - - pub relation: ::core::option::Option, - - pub cols: Vec, - - pub select_stmt: ::core::option::Option>, - - pub on_conflict_clause: ::core::option::Option>, - - pub returning_list: Vec, - - pub with_clause: ::core::option::Option, - - pub r#override: i32, -} -#[derive(Clone, PartialEq)] -pub struct DeleteStmt { - - pub relation: ::core::option::Option, - - pub using_clause: Vec, - - pub where_clause: ::core::option::Option>, - - pub returning_list: Vec, - - pub with_clause: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct UpdateStmt { - - pub relation: ::core::option::Option, - - pub target_list: Vec, - - pub where_clause: ::core::option::Option>, - - pub from_clause: Vec, - - pub returning_list: Vec, - - pub with_clause: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct SelectStmt { - - pub distinct_clause: Vec, - - pub into_clause: ::core::option::Option>, - - pub target_list: Vec, - - pub from_clause: Vec, - - pub where_clause: ::core::option::Option>, - - pub group_clause: Vec, - - pub having_clause: ::core::option::Option>, - - pub window_clause: Vec, - - pub values_lists: Vec, - - pub sort_clause: Vec, - - pub limit_offset: ::core::option::Option>, - - pub limit_count: ::core::option::Option>, - - pub limit_option: i32, - - pub locking_clause: Vec, - - pub with_clause: ::core::option::Option, - - pub op: i32, - - pub all: bool, - - pub larg: ::core::option::Option>, - - pub rarg: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct AlterTableStmt { - - pub relation: ::core::option::Option, - - pub cmds: Vec, - - pub relkind: i32, - - pub missing_ok: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterTableCmd { - - pub subtype: i32, - - pub name: String, - - pub num: i32, - - pub newowner: ::core::option::Option, - - pub def: ::core::option::Option>, - - pub behavior: i32, - - pub missing_ok: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterDomainStmt { - - pub subtype: String, - - pub type_name: Vec, - - pub name: String, - - pub def: ::core::option::Option>, - - pub behavior: i32, - - pub missing_ok: bool, -} -#[derive(Clone, PartialEq)] -pub struct SetOperationStmt { - - pub op: i32, - - pub all: bool, - - pub larg: ::core::option::Option>, - - pub rarg: ::core::option::Option>, - - pub col_types: Vec, - - pub col_typmods: Vec, - - pub col_collations: Vec, - - pub group_clauses: Vec, -} -#[derive(Clone, PartialEq)] -pub struct GrantStmt { - - pub is_grant: bool, - - pub targtype: i32, - - pub objtype: i32, - - pub objects: Vec, - - pub privileges: Vec, - - pub grantees: Vec, - - pub grant_option: bool, - - pub behavior: i32, -} -#[derive(Clone, PartialEq)] -pub struct GrantRoleStmt { - - pub granted_roles: Vec, - - pub grantee_roles: Vec, - - pub is_grant: bool, - - pub admin_opt: bool, - - pub grantor: ::core::option::Option, - - pub behavior: i32, -} -#[derive(Clone, PartialEq)] -pub struct AlterDefaultPrivilegesStmt { - - pub options: Vec, - - pub action: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct ClosePortalStmt { - - pub portalname: String, -} -#[derive(Clone, PartialEq)] -pub struct ClusterStmt { - - pub relation: ::core::option::Option, - - pub indexname: String, - - pub options: i32, -} -#[derive(Clone, PartialEq)] -pub struct CopyStmt { - - pub relation: ::core::option::Option, - - pub query: ::core::option::Option>, - - pub attlist: Vec, - - pub is_from: bool, - - pub is_program: bool, - - pub filename: String, - - pub options: Vec, - - pub where_clause: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct CreateStmt { - - pub relation: ::core::option::Option, - - pub table_elts: Vec, - - pub inh_relations: Vec, - - pub partbound: ::core::option::Option, - - pub partspec: ::core::option::Option, - - pub of_typename: ::core::option::Option, - - pub constraints: Vec, - - pub options: Vec, - - pub oncommit: i32, - - pub tablespacename: String, - - pub access_method: String, - - pub if_not_exists: bool, -} -#[derive(Clone, PartialEq)] -pub struct DefineStmt { - - pub kind: i32, - - pub oldstyle: bool, - - pub defnames: Vec, - - pub args: Vec, - - pub definition: Vec, - - pub if_not_exists: bool, - - pub replace: bool, -} -#[derive(Clone, PartialEq)] -pub struct DropStmt { - - pub objects: Vec, - - pub remove_type: i32, - - pub behavior: i32, - - pub missing_ok: bool, - - pub concurrent: bool, -} -#[derive(Clone, PartialEq)] -pub struct TruncateStmt { - - pub relations: Vec, - - pub restart_seqs: bool, - - pub behavior: i32, -} -#[derive(Clone, PartialEq)] -pub struct CommentStmt { - - pub objtype: i32, - - pub object: ::core::option::Option>, - - pub comment: String, -} -#[derive(Clone, PartialEq)] -pub struct FetchStmt { - - pub direction: i32, - - pub how_many: i64, - - pub portalname: String, - - pub ismove: bool, -} -#[derive(Clone, PartialEq)] -pub struct IndexStmt { - - pub idxname: String, - - pub relation: ::core::option::Option, - - pub access_method: String, - - pub table_space: String, - - pub index_params: Vec, - - pub index_including_params: Vec, - - pub options: Vec, - - pub where_clause: ::core::option::Option>, - - pub exclude_op_names: Vec, - - pub idxcomment: String, - - pub index_oid: u32, - - pub old_node: u32, - - pub old_create_subid: u32, - - pub old_first_relfilenode_subid: u32, - - pub unique: bool, - - pub primary: bool, - - pub isconstraint: bool, - - pub deferrable: bool, - - pub initdeferred: bool, - - pub transformed: bool, - - pub concurrent: bool, - - pub if_not_exists: bool, - - pub reset_default_tblspc: bool, -} -#[derive(Clone, PartialEq)] -pub struct CreateFunctionStmt { - - pub is_procedure: bool, - - pub replace: bool, - - pub funcname: Vec, - - pub parameters: Vec, - - pub return_type: ::core::option::Option, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterFunctionStmt { - - pub objtype: i32, - - pub func: ::core::option::Option, - - pub actions: Vec, -} -#[derive(Clone, PartialEq)] -pub struct DoStmt { - - pub args: Vec, -} -#[derive(Clone, PartialEq)] -pub struct RenameStmt { - - pub rename_type: i32, - - pub relation_type: i32, - - pub relation: ::core::option::Option, - - pub object: ::core::option::Option>, - - pub subname: String, - - pub newname: String, - - pub behavior: i32, - - pub missing_ok: bool, -} -#[derive(Clone, PartialEq)] -pub struct RuleStmt { - - pub relation: ::core::option::Option, - - pub rulename: String, - - pub where_clause: ::core::option::Option>, - - pub event: i32, - - pub instead: bool, - - pub actions: Vec, - - pub replace: bool, -} -#[derive(Clone, PartialEq)] -pub struct NotifyStmt { - - pub conditionname: String, - - pub payload: String, -} -#[derive(Clone, PartialEq)] -pub struct ListenStmt { - - pub conditionname: String, -} -#[derive(Clone, PartialEq)] -pub struct UnlistenStmt { - - pub conditionname: String, -} -#[derive(Clone, PartialEq)] -pub struct TransactionStmt { - - pub kind: i32, - - pub options: Vec, - - pub savepoint_name: String, - - pub gid: String, - - pub chain: bool, -} -#[derive(Clone, PartialEq)] -pub struct ViewStmt { - - pub view: ::core::option::Option, - - pub aliases: Vec, - - pub query: ::core::option::Option>, - - pub replace: bool, - - pub options: Vec, - - pub with_check_option: i32, -} -#[derive(Clone, PartialEq)] -pub struct LoadStmt { - - pub filename: String, -} -#[derive(Clone, PartialEq)] -pub struct CreateDomainStmt { - - pub domainname: Vec, - - pub type_name: ::core::option::Option, - - pub coll_clause: ::core::option::Option>, - - pub constraints: Vec, -} -#[derive(Clone, PartialEq)] -pub struct CreatedbStmt { - - pub dbname: String, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct DropdbStmt { - - pub dbname: String, - - pub missing_ok: bool, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct VacuumStmt { - - pub options: Vec, - - pub rels: Vec, - - pub is_vacuumcmd: bool, -} -#[derive(Clone, PartialEq)] -pub struct ExplainStmt { - - pub query: ::core::option::Option>, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct CreateTableAsStmt { - - pub query: ::core::option::Option>, - - pub into: ::core::option::Option>, - - pub relkind: i32, - - pub is_select_into: bool, - - pub if_not_exists: bool, -} -#[derive(Clone, PartialEq)] -pub struct CreateSeqStmt { - - pub sequence: ::core::option::Option, - - pub options: Vec, - - pub owner_id: u32, - - pub for_identity: bool, - - pub if_not_exists: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterSeqStmt { - - pub sequence: ::core::option::Option, - - pub options: Vec, - - pub for_identity: bool, - - pub missing_ok: bool, -} -#[derive(Clone, PartialEq)] -pub struct VariableSetStmt { - - pub kind: i32, - - pub name: String, - - pub args: Vec, - - pub is_local: bool, -} -#[derive(Clone, PartialEq)] -pub struct VariableShowStmt { - - pub name: String, -} -#[derive(Clone, PartialEq)] -pub struct DiscardStmt { - - pub target: i32, -} -#[derive(Clone, PartialEq)] -pub struct CreateTrigStmt { - - pub trigname: String, - - pub relation: ::core::option::Option, - - pub funcname: Vec, - - pub args: Vec, - - pub row: bool, - - pub timing: i32, - - pub events: i32, - - pub columns: Vec, - - pub when_clause: ::core::option::Option>, - - pub isconstraint: bool, - - pub transition_rels: Vec, - - pub deferrable: bool, - - pub initdeferred: bool, - - pub constrrel: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct CreatePLangStmt { - - pub replace: bool, - - pub plname: String, - - pub plhandler: Vec, - - pub plinline: Vec, - - pub plvalidator: Vec, - - pub pltrusted: bool, -} -#[derive(Clone, PartialEq)] -pub struct CreateRoleStmt { - - pub stmt_type: i32, - - pub role: String, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterRoleStmt { - - pub role: ::core::option::Option, - - pub options: Vec, - - pub action: i32, -} -#[derive(Clone, PartialEq)] -pub struct DropRoleStmt { - - pub roles: Vec, - - pub missing_ok: bool, -} -#[derive(Clone, PartialEq)] -pub struct LockStmt { - - pub relations: Vec, - - pub mode: i32, - - pub nowait: bool, -} -#[derive(Clone, PartialEq)] -pub struct ConstraintsSetStmt { - - pub constraints: Vec, - - pub deferred: bool, -} -#[derive(Clone, PartialEq)] -pub struct ReindexStmt { - - pub kind: i32, - - pub relation: ::core::option::Option, - - pub name: String, - - pub options: i32, - - pub concurrent: bool, -} -#[derive(Clone, PartialEq)] -pub struct CheckPointStmt {} -#[derive(Clone, PartialEq)] -pub struct CreateSchemaStmt { - - pub schemaname: String, - - pub authrole: ::core::option::Option, - - pub schema_elts: Vec, - - pub if_not_exists: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterDatabaseStmt { - - pub dbname: String, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterDatabaseSetStmt { - - pub dbname: String, - - pub setstmt: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct AlterRoleSetStmt { - - pub role: ::core::option::Option, - - pub database: String, - - pub setstmt: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct CreateConversionStmt { - - pub conversion_name: Vec, - - pub for_encoding_name: String, - - pub to_encoding_name: String, - - pub func_name: Vec, - - pub def: bool, -} -#[derive(Clone, PartialEq)] -pub struct CreateCastStmt { - - pub sourcetype: ::core::option::Option, - - pub targettype: ::core::option::Option, - - pub func: ::core::option::Option, - - pub context: i32, - - pub inout: bool, -} -#[derive(Clone, PartialEq)] -pub struct CreateOpClassStmt { - - pub opclassname: Vec, - - pub opfamilyname: Vec, - - pub amname: String, - - pub datatype: ::core::option::Option, - - pub items: Vec, - - pub is_default: bool, -} -#[derive(Clone, PartialEq)] -pub struct CreateOpFamilyStmt { - - pub opfamilyname: Vec, - - pub amname: String, -} -#[derive(Clone, PartialEq)] -pub struct AlterOpFamilyStmt { - - pub opfamilyname: Vec, - - pub amname: String, - - pub is_drop: bool, - - pub items: Vec, -} -#[derive(Clone, PartialEq)] -pub struct PrepareStmt { - - pub name: String, - - pub argtypes: Vec, - - pub query: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct ExecuteStmt { - - pub name: String, - - pub params: Vec, -} -#[derive(Clone, PartialEq)] -pub struct DeallocateStmt { - - pub name: String, -} -#[derive(Clone, PartialEq)] -pub struct DeclareCursorStmt { - - pub portalname: String, - - pub options: i32, - - pub query: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct CreateTableSpaceStmt { - - pub tablespacename: String, - - pub owner: ::core::option::Option, - - pub location: String, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct DropTableSpaceStmt { - - pub tablespacename: String, - - pub missing_ok: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterObjectDependsStmt { - - pub object_type: i32, - - pub relation: ::core::option::Option, - - pub object: ::core::option::Option>, - - pub extname: ::core::option::Option>, - - pub remove: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterObjectSchemaStmt { - - pub object_type: i32, - - pub relation: ::core::option::Option, - - pub object: ::core::option::Option>, - - pub newschema: String, - - pub missing_ok: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterOwnerStmt { - - pub object_type: i32, - - pub relation: ::core::option::Option, - - pub object: ::core::option::Option>, - - pub newowner: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct AlterOperatorStmt { - - pub opername: ::core::option::Option, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterTypeStmt { - - pub type_name: Vec, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct DropOwnedStmt { - - pub roles: Vec, - - pub behavior: i32, -} -#[derive(Clone, PartialEq)] -pub struct ReassignOwnedStmt { - - pub roles: Vec, - - pub newrole: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct CompositeTypeStmt { - - pub typevar: ::core::option::Option, - - pub coldeflist: Vec, -} -#[derive(Clone, PartialEq)] -pub struct CreateEnumStmt { - - pub type_name: Vec, - - pub vals: Vec, -} -#[derive(Clone, PartialEq)] -pub struct CreateRangeStmt { - - pub type_name: Vec, - - pub params: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterEnumStmt { - - pub type_name: Vec, - - pub old_val: String, - - pub new_val: String, - - pub new_val_neighbor: String, - - pub new_val_is_after: bool, - - pub skip_if_new_val_exists: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterTsDictionaryStmt { - - pub dictname: Vec, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterTsConfigurationStmt { - - pub kind: i32, - - pub cfgname: Vec, - - pub tokentype: Vec, - - pub dicts: Vec, - - pub r#override: bool, - - pub replace: bool, - - pub missing_ok: bool, -} -#[derive(Clone, PartialEq)] -pub struct CreateFdwStmt { - - pub fdwname: String, - - pub func_options: Vec, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterFdwStmt { - - pub fdwname: String, - - pub func_options: Vec, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct CreateForeignServerStmt { - - pub servername: String, - - pub servertype: String, - - pub version: String, - - pub fdwname: String, - - pub if_not_exists: bool, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterForeignServerStmt { - - pub servername: String, - - pub version: String, - - pub options: Vec, - - pub has_version: bool, -} -#[derive(Clone, PartialEq)] -pub struct CreateUserMappingStmt { - - pub user: ::core::option::Option, - - pub servername: String, - - pub if_not_exists: bool, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterUserMappingStmt { - - pub user: ::core::option::Option, - - pub servername: String, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct DropUserMappingStmt { - - pub user: ::core::option::Option, - - pub servername: String, - - pub missing_ok: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterTableSpaceOptionsStmt { - - pub tablespacename: String, - - pub options: Vec, - - pub is_reset: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterTableMoveAllStmt { - - pub orig_tablespacename: String, - - pub objtype: i32, - - pub roles: Vec, - - pub new_tablespacename: String, - - pub nowait: bool, -} -#[derive(Clone, PartialEq)] -pub struct SecLabelStmt { - - pub objtype: i32, - - pub object: ::core::option::Option>, - - pub provider: String, - - pub label: String, -} -#[derive(Clone, PartialEq)] -pub struct CreateForeignTableStmt { - - pub base_stmt: ::core::option::Option, - - pub servername: String, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct ImportForeignSchemaStmt { - - pub server_name: String, - - pub remote_schema: String, - - pub local_schema: String, - - pub list_type: i32, - - pub table_list: Vec, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct CreateExtensionStmt { - - pub extname: String, - - pub if_not_exists: bool, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterExtensionStmt { - - pub extname: String, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterExtensionContentsStmt { - - pub extname: String, - - pub action: i32, - - pub objtype: i32, - - pub object: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct CreateEventTrigStmt { - - pub trigname: String, - - pub eventname: String, - - pub whenclause: Vec, - - pub funcname: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterEventTrigStmt { - - pub trigname: String, - - pub tgenabled: String, -} -#[derive(Clone, PartialEq)] -pub struct RefreshMatViewStmt { - - pub concurrent: bool, - - pub skip_data: bool, - - pub relation: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct ReplicaIdentityStmt { - - pub identity_type: String, - - pub name: String, -} -#[derive(Clone, PartialEq)] -pub struct AlterSystemStmt { - - pub setstmt: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct CreatePolicyStmt { - - pub policy_name: String, - - pub table: ::core::option::Option, - - pub cmd_name: String, - - pub permissive: bool, - - pub roles: Vec, - - pub qual: ::core::option::Option>, - - pub with_check: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct AlterPolicyStmt { - - pub policy_name: String, - - pub table: ::core::option::Option, - - pub roles: Vec, - - pub qual: ::core::option::Option>, - - pub with_check: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct CreateTransformStmt { - - pub replace: bool, - - pub type_name: ::core::option::Option, - - pub lang: String, - - pub fromsql: ::core::option::Option, - - pub tosql: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct CreateAmStmt { - - pub amname: String, - - pub handler_name: Vec, - - pub amtype: String, -} -#[derive(Clone, PartialEq)] -pub struct CreatePublicationStmt { - - pub pubname: String, - - pub options: Vec, - - pub tables: Vec, - - pub for_all_tables: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterPublicationStmt { - - pub pubname: String, - - pub options: Vec, - - pub tables: Vec, - - pub for_all_tables: bool, - - pub table_action: i32, -} -#[derive(Clone, PartialEq)] -pub struct CreateSubscriptionStmt { - - pub subname: String, - - pub conninfo: String, - - pub publication: Vec, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterSubscriptionStmt { - - pub kind: i32, - - pub subname: String, - - pub conninfo: String, - - pub publication: Vec, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct DropSubscriptionStmt { - - pub subname: String, - - pub missing_ok: bool, - - pub behavior: i32, -} -#[derive(Clone, PartialEq)] -pub struct CreateStatsStmt { - - pub defnames: Vec, - - pub stat_types: Vec, - - pub exprs: Vec, - - pub relations: Vec, - - pub stxcomment: String, - - pub if_not_exists: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterCollationStmt { - - pub collname: Vec, -} -#[derive(Clone, PartialEq)] -pub struct CallStmt { - - pub funccall: ::core::option::Option>, - - pub funcexpr: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct AlterStatsStmt { - - pub defnames: Vec, - - pub stxstattarget: i32, - - pub missing_ok: bool, -} -#[derive(Clone, PartialEq)] -pub struct AExpr { - - pub kind: i32, - - pub name: Vec, - - pub lexpr: ::core::option::Option>, - - pub rexpr: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct ColumnRef { - - pub fields: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct ParamRef { - - pub number: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct AConst { - - pub val: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct FuncCall { - - pub funcname: Vec, - - pub args: Vec, - - pub agg_order: Vec, - - pub agg_filter: ::core::option::Option>, - - pub agg_within_group: bool, - - pub agg_star: bool, - - pub agg_distinct: bool, - - pub func_variadic: bool, - - pub over: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct AStar {} -#[derive(Clone, PartialEq)] -pub struct AIndices { - - pub is_slice: bool, - - pub lidx: ::core::option::Option>, - - pub uidx: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct AIndirection { - - pub arg: ::core::option::Option>, - - pub indirection: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AArrayExpr { - - pub elements: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct ResTarget { - - pub name: String, - - pub indirection: Vec, - - pub val: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct MultiAssignRef { - - pub source: ::core::option::Option>, - - pub colno: i32, - - pub ncolumns: i32, -} -#[derive(Clone, PartialEq)] -pub struct TypeCast { - - pub arg: ::core::option::Option>, - - pub type_name: ::core::option::Option, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct CollateClause { - - pub arg: ::core::option::Option>, - - pub collname: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct SortBy { - - pub node: ::core::option::Option>, - - pub sortby_dir: i32, - - pub sortby_nulls: i32, - - pub use_op: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct WindowDef { - - pub name: String, - - pub refname: String, - - pub partition_clause: Vec, - - pub order_clause: Vec, - - pub frame_options: i32, - - pub start_offset: ::core::option::Option>, - - pub end_offset: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct RangeSubselect { - - pub lateral: bool, - - pub subquery: ::core::option::Option>, - - pub alias: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct RangeFunction { - - pub lateral: bool, - - pub ordinality: bool, - - pub is_rowsfrom: bool, - - pub functions: Vec, - - pub alias: ::core::option::Option, - - pub coldeflist: Vec, -} -#[derive(Clone, PartialEq)] -pub struct RangeTableSample { - - pub relation: ::core::option::Option>, - - pub method: Vec, - - pub args: Vec, - - pub repeatable: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct RangeTableFunc { - - pub lateral: bool, - - pub docexpr: ::core::option::Option>, - - pub rowexpr: ::core::option::Option>, - - pub namespaces: Vec, - - pub columns: Vec, - - pub alias: ::core::option::Option, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct RangeTableFuncCol { - - pub colname: String, - - pub type_name: ::core::option::Option, - - pub for_ordinality: bool, - - pub is_not_null: bool, - - pub colexpr: ::core::option::Option>, - - pub coldefexpr: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct TypeName { - - pub names: Vec, - - pub type_oid: u32, - - pub setof: bool, - - pub pct_type: bool, - - pub typmods: Vec, - - pub typemod: i32, - - pub array_bounds: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct ColumnDef { - - pub colname: String, - - pub type_name: ::core::option::Option, - - pub inhcount: i32, - - pub is_local: bool, - - pub is_not_null: bool, - - pub is_from_type: bool, - - pub storage: String, - - pub raw_default: ::core::option::Option>, - - pub cooked_default: ::core::option::Option>, - - pub identity: String, - - pub identity_sequence: ::core::option::Option, - - pub generated: String, - - pub coll_clause: ::core::option::Option>, - - pub coll_oid: u32, - - pub constraints: Vec, - - pub fdwoptions: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct IndexElem { - - pub name: String, - - pub expr: ::core::option::Option>, - - pub indexcolname: String, - - pub collation: Vec, - - pub opclass: Vec, - - pub opclassopts: Vec, - - pub ordering: i32, - - pub nulls_ordering: i32, -} -#[derive(Clone, PartialEq)] -pub struct Constraint { - - pub contype: i32, - - pub conname: String, - - pub deferrable: bool, - - pub initdeferred: bool, - - pub location: i32, - - pub is_no_inherit: bool, - - pub raw_expr: ::core::option::Option>, - - pub cooked_expr: String, - - pub generated_when: String, - - pub keys: Vec, - - pub including: Vec, - - pub exclusions: Vec, - - pub options: Vec, - - pub indexname: String, - - pub indexspace: String, - - pub reset_default_tblspc: bool, - - pub access_method: String, - - pub where_clause: ::core::option::Option>, - - pub pktable: ::core::option::Option, - - pub fk_attrs: Vec, - - pub pk_attrs: Vec, - - pub fk_matchtype: String, - - pub fk_upd_action: String, - - pub fk_del_action: String, - - pub old_conpfeqop: Vec, - - pub old_pktable_oid: u32, - - pub skip_validation: bool, - - pub initially_valid: bool, -} -#[derive(Clone, PartialEq)] -pub struct DefElem { - - pub defnamespace: String, - - pub defname: String, - - pub arg: ::core::option::Option>, - - pub defaction: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct RangeTblEntry { - - pub rtekind: i32, - - pub relid: u32, - - pub relkind: String, - - pub rellockmode: i32, - - pub tablesample: ::core::option::Option>, - - pub subquery: ::core::option::Option>, - - pub security_barrier: bool, - - pub jointype: i32, - - pub joinmergedcols: i32, - - pub joinaliasvars: Vec, - - pub joinleftcols: Vec, - - pub joinrightcols: Vec, - - pub functions: Vec, - - pub funcordinality: bool, - - pub tablefunc: ::core::option::Option>, - - pub values_lists: Vec, - - pub ctename: String, - - pub ctelevelsup: u32, - - pub self_reference: bool, - - pub coltypes: Vec, - - pub coltypmods: Vec, - - pub colcollations: Vec, - - pub enrname: String, - - pub enrtuples: f64, - - pub alias: ::core::option::Option, - - pub eref: ::core::option::Option, - - pub lateral: bool, - - pub inh: bool, - - pub in_from_cl: bool, - - pub required_perms: u32, - - pub check_as_user: u32, - - pub selected_cols: Vec, - - pub inserted_cols: Vec, - - pub updated_cols: Vec, - - pub extra_updated_cols: Vec, - - pub security_quals: Vec, -} -#[derive(Clone, PartialEq)] -pub struct RangeTblFunction { - - pub funcexpr: ::core::option::Option>, - - pub funccolcount: i32, - - pub funccolnames: Vec, - - pub funccoltypes: Vec, - - pub funccoltypmods: Vec, - - pub funccolcollations: Vec, - - pub funcparams: Vec, -} -#[derive(Clone, PartialEq)] -pub struct TableSampleClause { - - pub tsmhandler: u32, - - pub args: Vec, - - pub repeatable: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct WithCheckOption { - - pub kind: i32, - - pub relname: String, - - pub polname: String, - - pub qual: ::core::option::Option>, - - pub cascaded: bool, -} -#[derive(Clone, PartialEq)] -pub struct SortGroupClause { - - pub tle_sort_group_ref: u32, - - pub eqop: u32, - - pub sortop: u32, - - pub nulls_first: bool, - - pub hashable: bool, -} -#[derive(Clone, PartialEq)] -pub struct GroupingSet { - - pub kind: i32, - - pub content: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct WindowClause { - - pub name: String, - - pub refname: String, - - pub partition_clause: Vec, - - pub order_clause: Vec, - - pub frame_options: i32, - - pub start_offset: ::core::option::Option>, - - pub end_offset: ::core::option::Option>, - - pub start_in_range_func: u32, - - pub end_in_range_func: u32, - - pub in_range_coll: u32, - - pub in_range_asc: bool, - - pub in_range_nulls_first: bool, - - pub winref: u32, - - pub copied_order: bool, -} -#[derive(Clone, PartialEq)] -pub struct ObjectWithArgs { - - pub objname: Vec, - - pub objargs: Vec, - - pub args_unspecified: bool, -} -#[derive(Clone, PartialEq)] -pub struct AccessPriv { - - pub priv_name: String, - - pub cols: Vec, -} -#[derive(Clone, PartialEq)] -pub struct CreateOpClassItem { - - pub itemtype: i32, - - pub name: ::core::option::Option, - - pub number: i32, - - pub order_family: Vec, - - pub class_args: Vec, - - pub storedtype: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct TableLikeClause { - - pub relation: ::core::option::Option, - - pub options: u32, - - pub relation_oid: u32, -} -#[derive(Clone, PartialEq)] -pub struct FunctionParameter { - - pub name: String, - - pub arg_type: ::core::option::Option, - - pub mode: i32, - - pub defexpr: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct LockingClause { - - pub locked_rels: Vec, - - pub strength: i32, - - pub wait_policy: i32, -} -#[derive(Clone, PartialEq)] -pub struct RowMarkClause { - - pub rti: u32, - - pub strength: i32, - - pub wait_policy: i32, - - pub pushed_down: bool, -} -#[derive(Clone, PartialEq)] -pub struct XmlSerialize { - - pub xmloption: i32, - - pub expr: ::core::option::Option>, - - pub type_name: ::core::option::Option, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct WithClause { - - pub ctes: Vec, - - pub recursive: bool, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct InferClause { - - pub index_elems: Vec, - - pub where_clause: ::core::option::Option>, - - pub conname: String, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct OnConflictClause { - - pub action: i32, - - pub infer: ::core::option::Option>, - - pub target_list: Vec, - - pub where_clause: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct CommonTableExpr { - - pub ctename: String, - - pub aliascolnames: Vec, - - pub ctematerialized: i32, - - pub ctequery: ::core::option::Option>, - - pub location: i32, - - pub cterecursive: bool, - - pub cterefcount: i32, - - pub ctecolnames: Vec, - - pub ctecoltypes: Vec, - - pub ctecoltypmods: Vec, - - pub ctecolcollations: Vec, -} -#[derive(Clone, PartialEq)] -pub struct RoleSpec { - - pub roletype: i32, - - pub rolename: String, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct TriggerTransition { - - pub name: String, - - pub is_new: bool, - - pub is_table: bool, -} -#[derive(Clone, PartialEq)] -pub struct PartitionElem { - - pub name: String, - - pub expr: ::core::option::Option>, - - pub collation: Vec, - - pub opclass: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct PartitionSpec { - - pub strategy: String, - - pub part_params: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct PartitionBoundSpec { - - pub strategy: String, - - pub is_default: bool, - - pub modulus: i32, - - pub remainder: i32, - - pub listdatums: Vec, - - pub lowerdatums: Vec, - - pub upperdatums: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct PartitionRangeDatum { - - pub kind: i32, - - pub value: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct PartitionCmd { - - pub name: ::core::option::Option, - - pub bound: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct VacuumRelation { - - pub relation: ::core::option::Option, - - pub oid: u32, - - pub va_cols: Vec, -} -#[derive(Clone, PartialEq)] -pub struct InlineCodeBlock { - - pub source_text: String, - - pub lang_oid: u32, - - pub lang_is_trusted: bool, - - pub atomic: bool, -} -#[derive(Clone, PartialEq)] -pub struct CallContext { - - pub atomic: bool, -} -#[derive(Clone, PartialEq)] -pub struct ScanToken { - - pub start: i32, - - pub end: i32, - - pub token: i32, - - pub keyword_kind: i32, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum OverridingKind { - Undefined = 0, - OverridingNotSet = 1, - OverridingUserValue = 2, - OverridingSystemValue = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum QuerySource { - Undefined = 0, - QsrcOriginal = 1, - QsrcParser = 2, - QsrcInsteadRule = 3, - QsrcQualInsteadRule = 4, - QsrcNonInsteadRule = 5, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum SortByDir { - Undefined = 0, - SortbyDefault = 1, - SortbyAsc = 2, - SortbyDesc = 3, - SortbyUsing = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum SortByNulls { - Undefined = 0, - SortbyNullsDefault = 1, - SortbyNullsFirst = 2, - SortbyNullsLast = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum AExprKind { - Undefined = 0, - AexprOp = 1, - AexprOpAny = 2, - AexprOpAll = 3, - AexprDistinct = 4, - AexprNotDistinct = 5, - AexprNullif = 6, - AexprOf = 7, - AexprIn = 8, - AexprLike = 9, - AexprIlike = 10, - AexprSimilar = 11, - AexprBetween = 12, - AexprNotBetween = 13, - AexprBetweenSym = 14, - AexprNotBetweenSym = 15, - AexprParen = 16, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum RoleSpecType { - Undefined = 0, - RolespecCstring = 1, - RolespecCurrentUser = 2, - RolespecSessionUser = 3, - RolespecPublic = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum TableLikeOption { - Undefined = 0, - CreateTableLikeComments = 1, - CreateTableLikeConstraints = 2, - CreateTableLikeDefaults = 3, - CreateTableLikeGenerated = 4, - CreateTableLikeIdentity = 5, - CreateTableLikeIndexes = 6, - CreateTableLikeStatistics = 7, - CreateTableLikeStorage = 8, - CreateTableLikeAll = 9, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum DefElemAction { - Undefined = 0, - DefelemUnspec = 1, - DefelemSet = 2, - DefelemAdd = 3, - DefelemDrop = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum PartitionRangeDatumKind { - Undefined = 0, - PartitionRangeDatumMinvalue = 1, - PartitionRangeDatumValue = 2, - PartitionRangeDatumMaxvalue = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum RteKind { - RtekindUndefined = 0, - RteRelation = 1, - RteSubquery = 2, - RteJoin = 3, - RteFunction = 4, - RteTablefunc = 5, - RteValues = 6, - RteCte = 7, - RteNamedtuplestore = 8, - RteResult = 9, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum WcoKind { - WcokindUndefined = 0, - WcoViewCheck = 1, - WcoRlsInsertCheck = 2, - WcoRlsUpdateCheck = 3, - WcoRlsConflictCheck = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum GroupingSetKind { - Undefined = 0, - GroupingSetEmpty = 1, - GroupingSetSimple = 2, - GroupingSetRollup = 3, - GroupingSetCube = 4, - GroupingSetSets = 5, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum CteMaterialize { - CtematerializeUndefined = 0, - Default = 1, - Always = 2, - Never = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum SetOperation { - Undefined = 0, - SetopNone = 1, - SetopUnion = 2, - SetopIntersect = 3, - SetopExcept = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum ObjectType { - Undefined = 0, - ObjectAccessMethod = 1, - ObjectAggregate = 2, - ObjectAmop = 3, - ObjectAmproc = 4, - ObjectAttribute = 5, - ObjectCast = 6, - ObjectColumn = 7, - ObjectCollation = 8, - ObjectConversion = 9, - ObjectDatabase = 10, - ObjectDefault = 11, - ObjectDefacl = 12, - ObjectDomain = 13, - ObjectDomconstraint = 14, - ObjectEventTrigger = 15, - ObjectExtension = 16, - ObjectFdw = 17, - ObjectForeignServer = 18, - ObjectForeignTable = 19, - ObjectFunction = 20, - ObjectIndex = 21, - ObjectLanguage = 22, - ObjectLargeobject = 23, - ObjectMatview = 24, - ObjectOpclass = 25, - ObjectOperator = 26, - ObjectOpfamily = 27, - ObjectPolicy = 28, - ObjectProcedure = 29, - ObjectPublication = 30, - ObjectPublicationRel = 31, - ObjectRole = 32, - ObjectRoutine = 33, - ObjectRule = 34, - ObjectSchema = 35, - ObjectSequence = 36, - ObjectSubscription = 37, - ObjectStatisticExt = 38, - ObjectTabconstraint = 39, - ObjectTable = 40, - ObjectTablespace = 41, - ObjectTransform = 42, - ObjectTrigger = 43, - ObjectTsconfiguration = 44, - ObjectTsdictionary = 45, - ObjectTsparser = 46, - ObjectTstemplate = 47, - ObjectType = 48, - ObjectUserMapping = 49, - ObjectView = 50, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum DropBehavior { - Undefined = 0, - DropRestrict = 1, - DropCascade = 2, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum AlterTableType { - Undefined = 0, - AtAddColumn = 1, - AtAddColumnRecurse = 2, - AtAddColumnToView = 3, - AtColumnDefault = 4, - AtCookedColumnDefault = 5, - AtDropNotNull = 6, - AtSetNotNull = 7, - AtDropExpression = 8, - AtCheckNotNull = 9, - AtSetStatistics = 10, - AtSetOptions = 11, - AtResetOptions = 12, - AtSetStorage = 13, - AtDropColumn = 14, - AtDropColumnRecurse = 15, - AtAddIndex = 16, - AtReAddIndex = 17, - AtAddConstraint = 18, - AtAddConstraintRecurse = 19, - AtReAddConstraint = 20, - AtReAddDomainConstraint = 21, - AtAlterConstraint = 22, - AtValidateConstraint = 23, - AtValidateConstraintRecurse = 24, - AtAddIndexConstraint = 25, - AtDropConstraint = 26, - AtDropConstraintRecurse = 27, - AtReAddComment = 28, - AtAlterColumnType = 29, - AtAlterColumnGenericOptions = 30, - AtChangeOwner = 31, - AtClusterOn = 32, - AtDropCluster = 33, - AtSetLogged = 34, - AtSetUnLogged = 35, - AtDropOids = 36, - AtSetTableSpace = 37, - AtSetRelOptions = 38, - AtResetRelOptions = 39, - AtReplaceRelOptions = 40, - AtEnableTrig = 41, - AtEnableAlwaysTrig = 42, - AtEnableReplicaTrig = 43, - AtDisableTrig = 44, - AtEnableTrigAll = 45, - AtDisableTrigAll = 46, - AtEnableTrigUser = 47, - AtDisableTrigUser = 48, - AtEnableRule = 49, - AtEnableAlwaysRule = 50, - AtEnableReplicaRule = 51, - AtDisableRule = 52, - AtAddInherit = 53, - AtDropInherit = 54, - AtAddOf = 55, - AtDropOf = 56, - AtReplicaIdentity = 57, - AtEnableRowSecurity = 58, - AtDisableRowSecurity = 59, - AtForceRowSecurity = 60, - AtNoForceRowSecurity = 61, - AtGenericOptions = 62, - AtAttachPartition = 63, - AtDetachPartition = 64, - AtAddIdentity = 65, - AtSetIdentity = 66, - AtDropIdentity = 67, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum GrantTargetType { - Undefined = 0, - AclTargetObject = 1, - AclTargetAllInSchema = 2, - AclTargetDefaults = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum VariableSetKind { - Undefined = 0, - VarSetValue = 1, - VarSetDefault = 2, - VarSetCurrent = 3, - VarSetMulti = 4, - VarReset = 5, - VarResetAll = 6, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum ConstrType { - Undefined = 0, - ConstrNull = 1, - ConstrNotnull = 2, - ConstrDefault = 3, - ConstrIdentity = 4, - ConstrGenerated = 5, - ConstrCheck = 6, - ConstrPrimary = 7, - ConstrUnique = 8, - ConstrExclusion = 9, - ConstrForeign = 10, - ConstrAttrDeferrable = 11, - ConstrAttrNotDeferrable = 12, - ConstrAttrDeferred = 13, - ConstrAttrImmediate = 14, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum ImportForeignSchemaType { - Undefined = 0, - FdwImportSchemaAll = 1, - FdwImportSchemaLimitTo = 2, - FdwImportSchemaExcept = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum RoleStmtType { - Undefined = 0, - RolestmtRole = 1, - RolestmtUser = 2, - RolestmtGroup = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum FetchDirection { - Undefined = 0, - FetchForward = 1, - FetchBackward = 2, - FetchAbsolute = 3, - FetchRelative = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum FunctionParameterMode { - Undefined = 0, - FuncParamIn = 1, - FuncParamOut = 2, - FuncParamInout = 3, - FuncParamVariadic = 4, - FuncParamTable = 5, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum TransactionStmtKind { - Undefined = 0, - TransStmtBegin = 1, - TransStmtStart = 2, - TransStmtCommit = 3, - TransStmtRollback = 4, - TransStmtSavepoint = 5, - TransStmtRelease = 6, - TransStmtRollbackTo = 7, - TransStmtPrepare = 8, - TransStmtCommitPrepared = 9, - TransStmtRollbackPrepared = 10, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum ViewCheckOption { - Undefined = 0, - NoCheckOption = 1, - LocalCheckOption = 2, - CascadedCheckOption = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum ClusterOption { - Undefined = 0, - CluoptRecheck = 1, - CluoptVerbose = 2, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum DiscardMode { - Undefined = 0, - DiscardAll = 1, - DiscardPlans = 2, - DiscardSequences = 3, - DiscardTemp = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum ReindexObjectType { - Undefined = 0, - ReindexObjectIndex = 1, - ReindexObjectTable = 2, - ReindexObjectSchema = 3, - ReindexObjectSystem = 4, - ReindexObjectDatabase = 5, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum AlterTsConfigType { - AlterTsconfigTypeUndefined = 0, - AlterTsconfigAddMapping = 1, - AlterTsconfigAlterMappingForToken = 2, - AlterTsconfigReplaceDict = 3, - AlterTsconfigReplaceDictForToken = 4, - AlterTsconfigDropMapping = 5, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum AlterSubscriptionType { - Undefined = 0, - AlterSubscriptionOptions = 1, - AlterSubscriptionConnection = 2, - AlterSubscriptionPublication = 3, - AlterSubscriptionRefresh = 4, - AlterSubscriptionEnabled = 5, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum OnCommitAction { - Undefined = 0, - OncommitNoop = 1, - OncommitPreserveRows = 2, - OncommitDeleteRows = 3, - OncommitDrop = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum ParamKind { - Undefined = 0, - ParamExtern = 1, - ParamExec = 2, - ParamSublink = 3, - ParamMultiexpr = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum CoercionContext { - Undefined = 0, - CoercionImplicit = 1, - CoercionAssignment = 2, - CoercionExplicit = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum CoercionForm { - Undefined = 0, - CoerceExplicitCall = 1, - CoerceExplicitCast = 2, - CoerceImplicitCast = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum BoolExprType { - Undefined = 0, - AndExpr = 1, - OrExpr = 2, - NotExpr = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum SubLinkType { - Undefined = 0, - ExistsSublink = 1, - AllSublink = 2, - AnySublink = 3, - RowcompareSublink = 4, - ExprSublink = 5, - MultiexprSublink = 6, - ArraySublink = 7, - CteSublink = 8, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum RowCompareType { - Undefined = 0, - RowcompareLt = 1, - RowcompareLe = 2, - RowcompareEq = 3, - RowcompareGe = 4, - RowcompareGt = 5, - RowcompareNe = 6, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum MinMaxOp { - Undefined = 0, - IsGreatest = 1, - IsLeast = 2, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum SqlValueFunctionOp { - SqlvalueFunctionOpUndefined = 0, - SvfopCurrentDate = 1, - SvfopCurrentTime = 2, - SvfopCurrentTimeN = 3, - SvfopCurrentTimestamp = 4, - SvfopCurrentTimestampN = 5, - SvfopLocaltime = 6, - SvfopLocaltimeN = 7, - SvfopLocaltimestamp = 8, - SvfopLocaltimestampN = 9, - SvfopCurrentRole = 10, - SvfopCurrentUser = 11, - SvfopUser = 12, - SvfopSessionUser = 13, - SvfopCurrentCatalog = 14, - SvfopCurrentSchema = 15, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum XmlExprOp { - Undefined = 0, - IsXmlconcat = 1, - IsXmlelement = 2, - IsXmlforest = 3, - IsXmlparse = 4, - IsXmlpi = 5, - IsXmlroot = 6, - IsXmlserialize = 7, - IsDocument = 8, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum XmlOptionType { - Undefined = 0, - XmloptionDocument = 1, - XmloptionContent = 2, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum NullTestType { - Undefined = 0, - IsNull = 1, - IsNotNull = 2, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum BoolTestType { - Undefined = 0, - IsTrue = 1, - IsNotTrue = 2, - IsFalse = 3, - IsNotFalse = 4, - IsUnknown = 5, - IsNotUnknown = 6, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum CmdType { - Undefined = 0, - CmdUnknown = 1, - CmdSelect = 2, - CmdUpdate = 3, - CmdInsert = 4, - CmdDelete = 5, - CmdUtility = 6, - CmdNothing = 7, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum JoinType { - Undefined = 0, - JoinInner = 1, - JoinLeft = 2, - JoinFull = 3, - JoinRight = 4, - JoinSemi = 5, - JoinAnti = 6, - JoinUniqueOuter = 7, - JoinUniqueInner = 8, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum AggStrategy { - Undefined = 0, - AggPlain = 1, - AggSorted = 2, - AggHashed = 3, - AggMixed = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum AggSplit { - Undefined = 0, - AggsplitSimple = 1, - AggsplitInitialSerial = 2, - AggsplitFinalDeserial = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum SetOpCmd { - Undefined = 0, - SetopcmdIntersect = 1, - SetopcmdIntersectAll = 2, - SetopcmdExcept = 3, - SetopcmdExceptAll = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum SetOpStrategy { - Undefined = 0, - SetopSorted = 1, - SetopHashed = 2, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum OnConflictAction { - Undefined = 0, - OnconflictNone = 1, - OnconflictNothing = 2, - OnconflictUpdate = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum LimitOption { - Undefined = 0, - Default = 1, - Count = 2, - WithTies = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum LockClauseStrength { - Undefined = 0, - LcsNone = 1, - LcsForkeyshare = 2, - LcsForshare = 3, - LcsFornokeyupdate = 4, - LcsForupdate = 5, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum LockWaitPolicy { - Undefined = 0, - LockWaitBlock = 1, - LockWaitSkip = 2, - LockWaitError = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum LockTupleMode { - Undefined = 0, - LockTupleKeyShare = 1, - LockTupleShare = 2, - LockTupleNoKeyExclusive = 3, - LockTupleExclusive = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum KeywordKind { - NoKeyword = 0, - UnreservedKeyword = 1, - ColNameKeyword = 2, - TypeFuncNameKeyword = 3, - ReservedKeyword = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum Token { - Nul = 0, - /// Single-character tokens that are returned 1:1 (identical with "self" list in scan.l) - /// Either supporting syntax, or single-character operators (some can be both) - /// Also see - /// - /// "%" - Ascii37 = 37, - /// "(" - Ascii40 = 40, - /// ")" - Ascii41 = 41, - /// "*" - Ascii42 = 42, - /// "+" - Ascii43 = 43, - /// "," - Ascii44 = 44, - /// "-" - Ascii45 = 45, - /// "." - Ascii46 = 46, - /// "/" - Ascii47 = 47, - /// ":" - Ascii58 = 58, - /// ";" - Ascii59 = 59, - /// "<" - Ascii60 = 60, - /// "=" - Ascii61 = 61, - /// ">" - Ascii62 = 62, - /// "?" - Ascii63 = 63, - /// "[" - Ascii91 = 91, - /// "\" - Ascii92 = 92, - /// "]" - Ascii93 = 93, - /// "^" - Ascii94 = 94, - /// Named tokens in scan.l - Ident = 258, - Uident = 259, - Fconst = 260, - Sconst = 261, - Usconst = 262, - Bconst = 263, - Xconst = 264, - Op = 265, - Iconst = 266, - Param = 267, - Typecast = 268, - DotDot = 269, - ColonEquals = 270, - EqualsGreater = 271, - LessEquals = 272, - GreaterEquals = 273, - NotEquals = 274, - SqlComment = 275, - CComment = 276, - AbortP = 277, - AbsoluteP = 278, - Access = 279, - Action = 280, - AddP = 281, - Admin = 282, - After = 283, - Aggregate = 284, - All = 285, - Also = 286, - Alter = 287, - Always = 288, - Analyse = 289, - Analyze = 290, - And = 291, - Any = 292, - Array = 293, - As = 294, - Asc = 295, - Assertion = 296, - Assignment = 297, - Asymmetric = 298, - At = 299, - Attach = 300, - Attribute = 301, - Authorization = 302, - Backward = 303, - Before = 304, - BeginP = 305, - Between = 306, - Bigint = 307, - Binary = 308, - Bit = 309, - BooleanP = 310, - Both = 311, - By = 312, - Cache = 313, - Call = 314, - Called = 315, - Cascade = 316, - Cascaded = 317, - Case = 318, - Cast = 319, - CatalogP = 320, - Chain = 321, - CharP = 322, - Character = 323, - Characteristics = 324, - Check = 325, - Checkpoint = 326, - Class = 327, - Close = 328, - Cluster = 329, - Coalesce = 330, - Collate = 331, - Collation = 332, - Column = 333, - Columns = 334, - Comment = 335, - Comments = 336, - Commit = 337, - Committed = 338, - Concurrently = 339, - Configuration = 340, - Conflict = 341, - Connection = 342, - Constraint = 343, - Constraints = 344, - ContentP = 345, - ContinueP = 346, - ConversionP = 347, - Copy = 348, - Cost = 349, - Create = 350, - Cross = 351, - Csv = 352, - Cube = 353, - CurrentP = 354, - CurrentCatalog = 355, - CurrentDate = 356, - CurrentRole = 357, - CurrentSchema = 358, - CurrentTime = 359, - CurrentTimestamp = 360, - CurrentUser = 361, - Cursor = 362, - Cycle = 363, - DataP = 364, - Database = 365, - DayP = 366, - Deallocate = 367, - Dec = 368, - DecimalP = 369, - Declare = 370, - Default = 371, - Defaults = 372, - Deferrable = 373, - Deferred = 374, - Definer = 375, - DeleteP = 376, - Delimiter = 377, - Delimiters = 378, - Depends = 379, - Desc = 380, - Detach = 381, - Dictionary = 382, - DisableP = 383, - Discard = 384, - Distinct = 385, - Do = 386, - DocumentP = 387, - DomainP = 388, - DoubleP = 389, - Drop = 390, - Each = 391, - Else = 392, - EnableP = 393, - Encoding = 394, - Encrypted = 395, - EndP = 396, - EnumP = 397, - Escape = 398, - Event = 399, - Except = 400, - Exclude = 401, - Excluding = 402, - Exclusive = 403, - Execute = 404, - Exists = 405, - Explain = 406, - Expression = 407, - Extension = 408, - External = 409, - Extract = 410, - FalseP = 411, - Family = 412, - Fetch = 413, - Filter = 414, - FirstP = 415, - FloatP = 416, - Following = 417, - For = 418, - Force = 419, - Foreign = 420, - Forward = 421, - Freeze = 422, - From = 423, - Full = 424, - Function = 425, - Functions = 426, - Generated = 427, - Global = 428, - Grant = 429, - Granted = 430, - Greatest = 431, - GroupP = 432, - Grouping = 433, - Groups = 434, - Handler = 435, - Having = 436, - HeaderP = 437, - Hold = 438, - HourP = 439, - IdentityP = 440, - IfP = 441, - Ilike = 442, - Immediate = 443, - Immutable = 444, - ImplicitP = 445, - ImportP = 446, - InP = 447, - Include = 448, - Including = 449, - Increment = 450, - Index = 451, - Indexes = 452, - Inherit = 453, - Inherits = 454, - Initially = 455, - InlineP = 456, - InnerP = 457, - Inout = 458, - InputP = 459, - Insensitive = 460, - Insert = 461, - Instead = 462, - IntP = 463, - Integer = 464, - Intersect = 465, - Interval = 466, - Into = 467, - Invoker = 468, - Is = 469, - Isnull = 470, - Isolation = 471, - Join = 472, - Key = 473, - Label = 474, - Language = 475, - LargeP = 476, - LastP = 477, - LateralP = 478, - Leading = 479, - Leakproof = 480, - Least = 481, - Left = 482, - Level = 483, - Like = 484, - Limit = 485, - Listen = 486, - Load = 487, - Local = 488, - Localtime = 489, - Localtimestamp = 490, - Location = 491, - LockP = 492, - Locked = 493, - Logged = 494, - Mapping = 495, - Match = 496, - Materialized = 497, - Maxvalue = 498, - Method = 499, - MinuteP = 500, - Minvalue = 501, - Mode = 502, - MonthP = 503, - Move = 504, - NameP = 505, - Names = 506, - National = 507, - Natural = 508, - Nchar = 509, - New = 510, - Next = 511, - Nfc = 512, - Nfd = 513, - Nfkc = 514, - Nfkd = 515, - No = 516, - None = 517, - Normalize = 518, - Normalized = 519, - Not = 520, - Nothing = 521, - Notify = 522, - Notnull = 523, - Nowait = 524, - NullP = 525, - Nullif = 526, - NullsP = 527, - Numeric = 528, - ObjectP = 529, - Of = 530, - Off = 531, - Offset = 532, - Oids = 533, - Old = 534, - On = 535, - Only = 536, - Operator = 537, - Option = 538, - Options = 539, - Or = 540, - Order = 541, - Ordinality = 542, - Others = 543, - OutP = 544, - OuterP = 545, - Over = 546, - Overlaps = 547, - Overlay = 548, - Overriding = 549, - Owned = 550, - Owner = 551, - Parallel = 552, - Parser = 553, - Partial = 554, - Partition = 555, - Passing = 556, - Password = 557, - Placing = 558, - Plans = 559, - Policy = 560, - Position = 561, - Preceding = 562, - Precision = 563, - Preserve = 564, - Prepare = 565, - Prepared = 566, - Primary = 567, - Prior = 568, - Privileges = 569, - Procedural = 570, - Procedure = 571, - Procedures = 572, - Program = 573, - Publication = 574, - Quote = 575, - Range = 576, - Read = 577, - Real = 578, - Reassign = 579, - Recheck = 580, - Recursive = 581, - Ref = 582, - References = 583, - Referencing = 584, - Refresh = 585, - Reindex = 586, - RelativeP = 587, - Release = 588, - Rename = 589, - Repeatable = 590, - Replace = 591, - Replica = 592, - Reset = 593, - Restart = 594, - Restrict = 595, - Returning = 596, - Returns = 597, - Revoke = 598, - Right = 599, - Role = 600, - Rollback = 601, - Rollup = 602, - Routine = 603, - Routines = 604, - Row = 605, - Rows = 606, - Rule = 607, - Savepoint = 608, - Schema = 609, - Schemas = 610, - Scroll = 611, - Search = 612, - SecondP = 613, - Security = 614, - Select = 615, - Sequence = 616, - Sequences = 617, - Serializable = 618, - Server = 619, - Session = 620, - SessionUser = 621, - Set = 622, - Sets = 623, - Setof = 624, - Share = 625, - Show = 626, - Similar = 627, - Simple = 628, - Skip = 629, - Smallint = 630, - Snapshot = 631, - Some = 632, - SqlP = 633, - Stable = 634, - StandaloneP = 635, - Start = 636, - Statement = 637, - Statistics = 638, - Stdin = 639, - Stdout = 640, - Storage = 641, - Stored = 642, - StrictP = 643, - StripP = 644, - Subscription = 645, - Substring = 646, - Support = 647, - Symmetric = 648, - Sysid = 649, - SystemP = 650, - Table = 651, - Tables = 652, - Tablesample = 653, - Tablespace = 654, - Temp = 655, - Template = 656, - Temporary = 657, - TextP = 658, - Then = 659, - Ties = 660, - Time = 661, - Timestamp = 662, - To = 663, - Trailing = 664, - Transaction = 665, - Transform = 666, - Treat = 667, - Trigger = 668, - Trim = 669, - TrueP = 670, - Truncate = 671, - Trusted = 672, - TypeP = 673, - TypesP = 674, - Uescape = 675, - Unbounded = 676, - Uncommitted = 677, - Unencrypted = 678, - Union = 679, - Unique = 680, - Unknown = 681, - Unlisten = 682, - Unlogged = 683, - Until = 684, - Update = 685, - User = 686, - Using = 687, - Vacuum = 688, - Valid = 689, - Validate = 690, - Validator = 691, - ValueP = 692, - Values = 693, - Varchar = 694, - Variadic = 695, - Varying = 696, - Verbose = 697, - VersionP = 698, - View = 699, - Views = 700, - Volatile = 701, - When = 702, - Where = 703, - WhitespaceP = 704, - Window = 705, - With = 706, - Within = 707, - Without = 708, - Work = 709, - Wrapper = 710, - Write = 711, - XmlP = 712, - Xmlattributes = 713, - Xmlconcat = 714, - Xmlelement = 715, - Xmlexists = 716, - Xmlforest = 717, - Xmlnamespaces = 718, - Xmlparse = 719, - Xmlpi = 720, - Xmlroot = 721, - Xmlserialize = 722, - Xmltable = 723, - YearP = 724, - YesP = 725, - Zone = 726, - NotLa = 727, - NullsLa = 728, - WithLa = 729, - Postfixop = 730, - Uminus = 731, -} diff --git a/tests/target/performance/issue-4476.rs b/tests/target/performance/issue-4476.rs deleted file mode 100644 index 30567f2644b7..000000000000 --- a/tests/target/performance/issue-4476.rs +++ /dev/null @@ -1,705 +0,0 @@ -use super::SemverParser; - -#[allow(dead_code, non_camel_case_types)] -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub enum Rule { - EOI, - range_set, - logical_or, - range, - empty, - hyphen, - simple, - primitive, - primitive_op, - partial, - xr, - xr_op, - nr, - tilde, - caret, - qualifier, - parts, - part, - space, -} -#[allow(clippy::all)] -impl ::pest::Parser for SemverParser { - fn parse<'i>( - rule: Rule, - input: &'i str, - ) -> ::std::result::Result<::pest::iterators::Pairs<'i, Rule>, ::pest::error::Error> { - mod rules { - pub mod hidden { - use super::super::Rule; - #[inline] - #[allow(dead_code, non_snake_case, unused_variables)] - pub fn skip( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - Ok(state) - } - } - pub mod visible { - use super::super::Rule; - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn range_set( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::range_set, |state| { - state.sequence(|state| { - self::SOI(state) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - self::space(state).and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state) - .and_then(|state| self::space(state)) - }) - }) - }) - }) - }) - }) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::range(state)) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - state - .sequence(|state| { - self::logical_or(state) - .and_then(|state| { - super::hidden::skip(state) - }) - .and_then(|state| self::range(state)) - }) - .and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state).and_then( - |state| { - state.sequence(|state| { - self::logical_or(state) - .and_then(|state| { - super::hidden::skip( - state, - ) - }) - .and_then(|state| { - self::range(state) - }) - }) - }, - ) - }) - }) - }) - }) - }) - }) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - self::space(state).and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state) - .and_then(|state| self::space(state)) - }) - }) - }) - }) - }) - }) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::EOI(state)) - }) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn logical_or( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::logical_or, |state| { - state.sequence(|state| { - state - .sequence(|state| { - state.optional(|state| { - self::space(state).and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state) - .and_then(|state| self::space(state)) - }) - }) - }) - }) - }) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| state.match_string("||")) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - self::space(state).and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state) - .and_then(|state| self::space(state)) - }) - }) - }) - }) - }) - }) - }) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn range( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::range, |state| { - self::hyphen(state) - .or_else(|state| { - state.sequence(|state| { - self::simple(state) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - state - .sequence(|state| { - state - .optional(|state| { - state.match_string(",") - }) - .and_then(|state| { - super::hidden::skip(state) - }) - .and_then(|state| { - state.sequence(|state| { - self::space(state) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - self::space(state).and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state) - .and_then(|state| self::space(state)) - }) - }) - }) - }) - }) - }) - }) - }) - .and_then(|state| { - super::hidden::skip(state) - }) - .and_then(|state| { - self::simple(state) - }) - }) - .and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state) - .and_then(|state| { - state.sequence( - |state| { - state - .optional(|state| state.match_string(",")) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - self::space(state) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - self::space(state).and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state) - .and_then(|state| self::space(state)) - }) - }) - }) - }) - }) - }) - }) - }) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::simple(state)) - }, - ) - }) - }) - }) - }) - }) - }) - }) - }) - }) - .or_else(|state| self::empty(state)) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn empty( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::empty, |state| state.match_string("")) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn hyphen( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::hyphen, |state| { - state.sequence(|state| { - self::partial(state) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - self::space(state) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - self::space(state).and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state) - .and_then(|state| { - self::space(state) - }) - }) - }) - }) - }) - }) - }) - }) - }) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| state.match_string("-")) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - self::space(state) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - self::space(state).and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state) - .and_then(|state| { - self::space(state) - }) - }) - }) - }) - }) - }) - }) - }) - }) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::partial(state)) - }) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn simple( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::simple, |state| { - self::primitive(state) - .or_else(|state| self::partial(state)) - .or_else(|state| self::tilde(state)) - .or_else(|state| self::caret(state)) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn primitive( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::primitive, |state| { - state.sequence(|state| { - self::primitive_op(state) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - self::space(state).and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state) - .and_then(|state| self::space(state)) - }) - }) - }) - }) - }) - }) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::partial(state)) - }) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn primitive_op( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::primitive_op, |state| { - state - .match_string("<=") - .or_else(|state| state.match_string(">=")) - .or_else(|state| state.match_string(">")) - .or_else(|state| state.match_string("<")) - .or_else(|state| state.match_string("=")) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn partial( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::partial, |state| { - state.sequence(|state| { - self::xr(state) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.optional(|state| { - state.sequence(|state| { - state - .match_string(".") - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::xr(state)) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.optional(|state| { - state.sequence(|state| { - state - .match_string(".") - .and_then(|state| { - super::hidden::skip(state) - }) - .and_then(|state| self::xr(state)) - .and_then(|state| { - super::hidden::skip(state) - }) - .and_then(|state| { - state.optional(|state| { - self::qualifier(state) - }) - }) - }) - }) - }) - }) - }) - }) - }) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn xr( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::xr, |state| { - self::xr_op(state).or_else(|state| self::nr(state)) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn xr_op( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::xr_op, |state| { - state - .match_string("x") - .or_else(|state| state.match_string("X")) - .or_else(|state| state.match_string("*")) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn nr( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::nr, |state| { - state.match_string("0").or_else(|state| { - state.sequence(|state| { - state - .match_range('1'..'9') - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - state.match_range('0'..'9').and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state).and_then( - |state| state.match_range('0'..'9'), - ) - }) - }) - }) - }) - }) - }) - }) - }) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn tilde( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::tilde, |state| { - state.sequence(|state| { - state - .match_string("~>") - .or_else(|state| state.match_string("~")) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - self::space(state).and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state) - .and_then(|state| self::space(state)) - }) - }) - }) - }) - }) - }) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::partial(state)) - }) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn caret( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::caret, |state| { - state.sequence(|state| { - state - .match_string("^") - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - self::space(state).and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state) - .and_then(|state| self::space(state)) - }) - }) - }) - }) - }) - }) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::partial(state)) - }) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn qualifier( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::qualifier, |state| { - state.sequence(|state| { - state - .match_string("-") - .or_else(|state| state.match_string("+")) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::parts(state)) - }) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn parts( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::parts, |state| { - state.sequence(|state| { - self::part(state) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - state - .sequence(|state| { - state - .match_string(".") - .and_then(|state| { - super::hidden::skip(state) - }) - .and_then(|state| self::part(state)) - }) - .and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state).and_then( - |state| { - state.sequence(|state| { - state - .match_string(".") - .and_then(|state| { - super::hidden::skip( - state, - ) - }) - .and_then(|state| { - self::part(state) - }) - }) - }, - ) - }) - }) - }) - }) - }) - }) - }) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn part( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::part, |state| { - self::nr(state).or_else(|state| { - state.sequence(|state| { - state - .match_string("-") - .or_else(|state| state.match_range('0'..'9')) - .or_else(|state| state.match_range('A'..'Z')) - .or_else(|state| state.match_range('a'..'z')) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - state - .match_string("-") - .or_else(|state| state.match_range('0'..'9')) - .or_else(|state| state.match_range('A'..'Z')) - .or_else(|state| state.match_range('a'..'z')) - .and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state).and_then( - |state| { - state - .match_string("-") - .or_else(|state| { - state.match_range( - '0'..'9', - ) - }) - .or_else(|state| { - state.match_range( - 'A'..'Z', - ) - }) - .or_else(|state| { - state.match_range( - 'a'..'z', - ) - }) - }, - ) - }) - }) - }) - }) - }) - }) - }) - }) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn space( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state - .match_string(" ") - .or_else(|state| state.match_string("\t")) - } - #[inline] - #[allow(dead_code, non_snake_case, unused_variables)] - pub fn EOI( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::EOI, |state| state.end_of_input()) - } - #[inline] - #[allow(dead_code, non_snake_case, unused_variables)] - pub fn SOI( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.start_of_input() - } - } - pub use self::visible::*; - } - ::pest::state(input, |state| match rule { - Rule::range_set => rules::range_set(state), - Rule::logical_or => rules::logical_or(state), - Rule::range => rules::range(state), - Rule::empty => rules::empty(state), - Rule::hyphen => rules::hyphen(state), - Rule::simple => rules::simple(state), - Rule::primitive => rules::primitive(state), - Rule::primitive_op => rules::primitive_op(state), - Rule::partial => rules::partial(state), - Rule::xr => rules::xr(state), - Rule::xr_op => rules::xr_op(state), - Rule::nr => rules::nr(state), - Rule::tilde => rules::tilde(state), - Rule::caret => rules::caret(state), - Rule::qualifier => rules::qualifier(state), - Rule::parts => rules::parts(state), - Rule::part => rules::part(state), - Rule::space => rules::space(state), - Rule::EOI => rules::EOI(state), - }) - } -} diff --git a/tests/target/performance/issue-4867.rs b/tests/target/performance/issue-4867.rs deleted file mode 100644 index 336dae1b64ab..000000000000 --- a/tests/target/performance/issue-4867.rs +++ /dev/null @@ -1,13 +0,0 @@ -mod modA { - mod modB { - mod modC { - mod modD { - mod modE { - fn func() { - state . rule (Rule :: myrule , | state | { state . sequence (| state | { state . sequence (| state | { state . match_string ("abc") . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { state . match_string ("def") }) }) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { state . sequence (| state | { state . optional (| state | { state . sequence (| state | { state . match_string ("abc") . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { state . match_string ("def") }) }) . and_then (| state | { state . repeat (| state | { state . sequence (| state | { super :: hidden :: skip (state) . and_then (| state | { state . sequence (| state | { state . match_string ("abc") . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { state . match_string ("def") }) }) }) }) }) }) }) }) }) }) }); - } - } - } - } - } -} diff --git a/tests/target/performance/issue-5128.rs b/tests/target/performance/issue-5128.rs deleted file mode 100644 index ba9ebfc6243f..000000000000 --- a/tests/target/performance/issue-5128.rs +++ /dev/null @@ -1,4898 +0,0 @@ -fn takes_a_long_time_to_rustfmt() { - let inner_cte = vec![Node { - node: Some(node::Node::CommonTableExpr(Box::new(CommonTableExpr { - ctename: String::from("ranked_by_age_within_key"), - aliascolnames: vec![], - ctematerialized: CteMaterialize::Default as i32, - ctequery: Some(Box::new(Node { - node: Some(node::Node::SelectStmt(Box::new(SelectStmt { - distinct_clause: vec![], - into_clause: None, - target_list: vec![ - Node { - node: Some(node::Node::ResTarget(Box::new(ResTarget { - name: String::from(""), - indirection: vec![], - val: Some(Box::new(Node { - node: Some(node::Node::ColumnRef(ColumnRef { - fields: vec![Node { - node: Some(node::Node::AStar(AStar {})), - }], - location: 80, - })), - })), - location: 80, - }))), - }, - Node { - node: Some(node::Node::ResTarget(Box::new(ResTarget { - name: String::from("rank_in_key"), - indirection: vec![], - val: Some(Box::new(Node { - node: Some(node::Node::FuncCall(Box::new(FuncCall { - funcname: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("row_number"), - })), - }], - args: vec![], - agg_order: vec![], - agg_filter: None, - agg_within_group: false, - agg_star: false, - agg_distinct: false, - func_variadic: false, - over: Some(Box::new(WindowDef { - name: String::from(""), - refname: String::from(""), - partition_clause: vec![Node { - node: Some(node::Node::ColumnRef(ColumnRef { - fields: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("synthetic_key"), - })), - }], - location: 123, - })), - }], - order_clause: vec![Node { - node: Some(node::Node::SortBy(Box::new(SortBy { - node: Some(Box::new(Node { - node: Some(node::Node::ColumnRef( - ColumnRef { - fields: vec![Node { - node: Some(node::Node::String( - String2 { - str: String::from( - "logical_timestamp", - ), - }, - )), - }], - location: 156, - }, - )), - })), - sortby_dir: SortByDir::SortbyDesc as i32, - sortby_nulls: SortByNulls::SortbyNullsDefault - as i32, - use_op: vec![], - location: -1, - }))), - }], - frame_options: 1058, - start_offset: None, - end_offset: None, - location: 109, - })), - location: 91, - }))), - })), - location: 91, - }))), - }, - ], - from_clause: vec![Node { - node: Some(node::Node::RangeVar(RangeVar { - catalogname: String::from(""), - schemaname: String::from("_supertables"), - relname: String::from("9999-9999-9999"), - inh: true, - relpersistence: String::from("p"), - alias: None, - location: 206, - })), - }], - where_clause: Some(Box::new(Node { - node: Some(node::Node::AExpr(Box::new(AExpr { - kind: AExprKind::AexprOp as i32, - name: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("<="), - })), - }], - lexpr: Some(Box::new(Node { - node: Some(node::Node::ColumnRef(ColumnRef { - fields: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("logical_timestamp"), - })), - }], - location: 250, - })), - })), - rexpr: Some(Box::new(Node { - node: Some(node::Node::AConst(Box::new(AConst { - val: Some(Box::new(Node { - node: Some(node::Node::Integer(Integer { ival: 9000 })), - })), - location: 271, - }))), - })), - location: 268, - }))), - })), - group_clause: vec![], - having_clause: None, - window_clause: vec![], - values_lists: vec![], - sort_clause: vec![], - limit_offset: None, - limit_count: None, - limit_option: LimitOption::Default as i32, - locking_clause: vec![], - with_clause: None, - op: SetOperation::SetopNone as i32, - all: false, - larg: None, - rarg: None, - }))), - })), - location: 29, - cterecursive: false, - cterefcount: 0, - ctecolnames: vec![], - ctecoltypes: vec![], - ctecoltypmods: vec![], - ctecolcollations: vec![], - }))), - }]; - let outer_cte = vec![Node { - node: Some(node::Node::CommonTableExpr(Box::new(CommonTableExpr { - ctename: String::from("table_name"), - aliascolnames: vec![], - ctematerialized: CteMaterialize::Default as i32, - ctequery: Some(Box::new(Node { - node: Some(node::Node::SelectStmt(Box::new(SelectStmt { - distinct_clause: vec![], - into_clause: None, - target_list: vec![ - Node { - node: Some(node::Node::ResTarget(Box::new(ResTarget { - name: String::from("column1"), - indirection: vec![], - val: Some(Box::new(Node { - node: Some(node::Node::ColumnRef(ColumnRef { - fields: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("c1"), - })), - }], - location: 301, - })), - })), - location: 301, - }))), - }, - Node { - node: Some(node::Node::ResTarget(Box::new(ResTarget { - name: String::from("column2"), - indirection: vec![], - val: Some(Box::new(Node { - node: Some(node::Node::ColumnRef(ColumnRef { - fields: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("c2"), - })), - }], - location: 324, - })), - })), - location: 324, - }))), - }, - ], - from_clause: vec![Node { - node: Some(node::Node::RangeVar(RangeVar { - catalogname: String::from(""), - schemaname: String::from(""), - relname: String::from("ranked_by_age_within_key"), - inh: true, - relpersistence: String::from("p"), - alias: None, - location: 347, - })), - }], - where_clause: Some(Box::new(Node { - node: Some(node::Node::BoolExpr(Box::new(BoolExpr { - xpr: None, - boolop: BoolExprType::AndExpr as i32, - args: vec![ - Node { - node: Some(node::Node::AExpr(Box::new(AExpr { - kind: AExprKind::AexprOp as i32, - name: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("="), - })), - }], - lexpr: Some(Box::new(Node { - node: Some(node::Node::ColumnRef(ColumnRef { - fields: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("rank_in_key"), - })), - }], - location: 382, - })), - })), - rexpr: Some(Box::new(Node { - node: Some(node::Node::AConst(Box::new(AConst { - val: Some(Box::new(Node { - node: Some(node::Node::Integer(Integer { - ival: 1, - })), - })), - location: 396, - }))), - })), - location: 394, - }))), - }, - Node { - node: Some(node::Node::AExpr(Box::new(AExpr { - kind: AExprKind::AexprOp as i32, - name: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("="), - })), - }], - lexpr: Some(Box::new(Node { - node: Some(node::Node::ColumnRef(ColumnRef { - fields: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("is_deleted"), - })), - }], - location: 402, - })), - })), - rexpr: Some(Box::new(Node { - node: Some(node::Node::TypeCast(Box::new(TypeCast { - arg: Some(Box::new(Node { - node: Some(node::Node::AConst(Box::new( - AConst { - val: Some(Box::new(Node { - node: Some(node::Node::String( - String2 { - str: String::from("f"), - }, - )), - })), - location: 415, - }, - ))), - })), - type_name: Some(TypeName { - names: vec![ - Node { - node: Some(node::Node::String( - String2 { - str: String::from("pg_catalog"), - }, - )), - }, - Node { - node: Some(node::Node::String( - String2 { - str: String::from("bool"), - }, - )), - }, - ], - type_oid: 0, - setof: false, - pct_type: false, - typmods: vec![], - typemod: -1, - array_bounds: vec![], - location: -1, - }), - location: -1, - }))), - })), - location: 413, - }))), - }, - ], - location: 398, - }))), - })), - group_clause: vec![], - having_clause: None, - window_clause: vec![], - values_lists: vec![], - sort_clause: vec![], - limit_offset: None, - limit_count: None, - limit_option: LimitOption::Default as i32, - locking_clause: vec![], - with_clause: Some(WithClause { - ctes: inner_cte, - recursive: false, - location: 24, - }), - op: SetOperation::SetopNone as i32, - all: false, - larg: None, - rarg: None, - }))), - })), - location: 5, - cterecursive: false, - cterefcount: 0, - ctecolnames: vec![], - ctecoltypes: vec![], - ctecoltypmods: vec![], - ctecolcollations: vec![], - }))), - }]; - let expected_result = ParseResult { - version: 130003, - stmts: vec![RawStmt { - stmt: Some(Box::new(Node { - node: Some(node::Node::SelectStmt(Box::new(SelectStmt { - distinct_clause: vec![], - into_clause: None, - - target_list: vec![Node { - node: Some(node::Node::ResTarget(Box::new(ResTarget { - name: String::from(""), - indirection: vec![], - val: Some(Box::new(Node { - node: Some(node::Node::ColumnRef(ColumnRef { - fields: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("column1"), - })), - }], - location: 430, - })), - })), - location: 430, - }))), - }], - from_clause: vec![Node { - node: Some(node::Node::RangeVar(RangeVar { - catalogname: String::from(""), - schemaname: String::from(""), - relname: String::from("table_name"), - inh: true, - relpersistence: String::from("p"), - alias: None, - location: 443, - })), - }], - where_clause: Some(Box::new(Node { - node: Some(node::Node::AExpr(Box::new(AExpr { - kind: AExprKind::AexprOp as i32, - name: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from(">"), - })), - }], - lexpr: Some(Box::new(Node { - node: Some(node::Node::ColumnRef(ColumnRef { - fields: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("column2"), - })), - }], - location: 460, - })), - })), - rexpr: Some(Box::new(Node { - node: Some(node::Node::AConst(Box::new(AConst { - val: Some(Box::new(Node { - node: Some(node::Node::Integer(Integer { ival: 9000 })), - })), - location: 470, - }))), - })), - location: 468, - }))), - })), - group_clause: vec![], - having_clause: None, - window_clause: vec![], - values_lists: vec![], - sort_clause: vec![], - limit_offset: None, - limit_count: None, - limit_option: LimitOption::Default as i32, - locking_clause: vec![], - with_clause: Some(WithClause { - ctes: outer_cte, - recursive: false, - location: 0, - }), - op: SetOperation::SetopNone as i32, - all: false, - larg: None, - rarg: None, - }))), - })), - stmt_location: 0, - stmt_len: 0, - }], - }; -} -#[derive(Clone, PartialEq)] -pub struct ParseResult { - pub version: i32, - - pub stmts: Vec, -} -#[derive(Clone, PartialEq)] -pub struct ScanResult { - pub version: i32, - - pub tokens: Vec, -} -#[derive(Clone, PartialEq)] -pub struct Node { - pub node: ::core::option::Option, -} -/// Nested message and enum types in `Node`. -pub mod node { - #[derive(Clone, PartialEq)] - pub enum Node { - Alias(super::Alias), - - RangeVar(super::RangeVar), - - TableFunc(Box), - - Expr(super::Expr), - - Var(Box), - - Param(Box), - - Aggref(Box), - - GroupingFunc(Box), - - WindowFunc(Box), - - SubscriptingRef(Box), - - FuncExpr(Box), - - NamedArgExpr(Box), - - OpExpr(Box), - - DistinctExpr(Box), - - NullIfExpr(Box), - - ScalarArrayOpExpr(Box), - - BoolExpr(Box), - - SubLink(Box), - - SubPlan(Box), - - AlternativeSubPlan(Box), - - FieldSelect(Box), - - FieldStore(Box), - - RelabelType(Box), - - CoerceViaIo(Box), - - ArrayCoerceExpr(Box), - - ConvertRowtypeExpr(Box), - - CollateExpr(Box), - - CaseExpr(Box), - - CaseWhen(Box), - - CaseTestExpr(Box), - - ArrayExpr(Box), - - RowExpr(Box), - - RowCompareExpr(Box), - - CoalesceExpr(Box), - - MinMaxExpr(Box), - - SqlvalueFunction(Box), - - XmlExpr(Box), - - NullTest(Box), - - BooleanTest(Box), - - CoerceToDomain(Box), - - CoerceToDomainValue(Box), - - SetToDefault(Box), - - CurrentOfExpr(Box), - - NextValueExpr(Box), - - InferenceElem(Box), - - TargetEntry(Box), - - RangeTblRef(super::RangeTblRef), - - JoinExpr(Box), - - FromExpr(Box), - - OnConflictExpr(Box), - - IntoClause(Box), - - RawStmt(Box), - - Query(Box), - - InsertStmt(Box), - - DeleteStmt(Box), - - UpdateStmt(Box), - - SelectStmt(Box), - - AlterTableStmt(super::AlterTableStmt), - - AlterTableCmd(Box), - - AlterDomainStmt(Box), - - SetOperationStmt(Box), - - GrantStmt(super::GrantStmt), - - GrantRoleStmt(super::GrantRoleStmt), - - AlterDefaultPrivilegesStmt(super::AlterDefaultPrivilegesStmt), - - ClosePortalStmt(super::ClosePortalStmt), - - ClusterStmt(super::ClusterStmt), - - CopyStmt(Box), - - CreateStmt(super::CreateStmt), - - DefineStmt(super::DefineStmt), - - DropStmt(super::DropStmt), - - TruncateStmt(super::TruncateStmt), - - CommentStmt(Box), - - FetchStmt(super::FetchStmt), - - IndexStmt(Box), - - CreateFunctionStmt(super::CreateFunctionStmt), - - AlterFunctionStmt(super::AlterFunctionStmt), - - DoStmt(super::DoStmt), - - RenameStmt(Box), - - RuleStmt(Box), - - NotifyStmt(super::NotifyStmt), - - ListenStmt(super::ListenStmt), - - UnlistenStmt(super::UnlistenStmt), - - TransactionStmt(super::TransactionStmt), - - ViewStmt(Box), - - LoadStmt(super::LoadStmt), - - CreateDomainStmt(Box), - - CreatedbStmt(super::CreatedbStmt), - - DropdbStmt(super::DropdbStmt), - - VacuumStmt(super::VacuumStmt), - - ExplainStmt(Box), - - CreateTableAsStmt(Box), - - CreateSeqStmt(super::CreateSeqStmt), - - AlterSeqStmt(super::AlterSeqStmt), - - VariableSetStmt(super::VariableSetStmt), - - VariableShowStmt(super::VariableShowStmt), - - DiscardStmt(super::DiscardStmt), - - CreateTrigStmt(Box), - - CreatePlangStmt(super::CreatePLangStmt), - - CreateRoleStmt(super::CreateRoleStmt), - - AlterRoleStmt(super::AlterRoleStmt), - - DropRoleStmt(super::DropRoleStmt), - - LockStmt(super::LockStmt), - - ConstraintsSetStmt(super::ConstraintsSetStmt), - - ReindexStmt(super::ReindexStmt), - - CheckPointStmt(super::CheckPointStmt), - - CreateSchemaStmt(super::CreateSchemaStmt), - - AlterDatabaseStmt(super::AlterDatabaseStmt), - - AlterDatabaseSetStmt(super::AlterDatabaseSetStmt), - - AlterRoleSetStmt(super::AlterRoleSetStmt), - - CreateConversionStmt(super::CreateConversionStmt), - - CreateCastStmt(super::CreateCastStmt), - - CreateOpClassStmt(super::CreateOpClassStmt), - - CreateOpFamilyStmt(super::CreateOpFamilyStmt), - - AlterOpFamilyStmt(super::AlterOpFamilyStmt), - - PrepareStmt(Box), - - ExecuteStmt(super::ExecuteStmt), - - DeallocateStmt(super::DeallocateStmt), - - DeclareCursorStmt(Box), - - CreateTableSpaceStmt(super::CreateTableSpaceStmt), - - DropTableSpaceStmt(super::DropTableSpaceStmt), - - AlterObjectDependsStmt(Box), - - AlterObjectSchemaStmt(Box), - - AlterOwnerStmt(Box), - - AlterOperatorStmt(super::AlterOperatorStmt), - - AlterTypeStmt(super::AlterTypeStmt), - - DropOwnedStmt(super::DropOwnedStmt), - - ReassignOwnedStmt(super::ReassignOwnedStmt), - - CompositeTypeStmt(super::CompositeTypeStmt), - - CreateEnumStmt(super::CreateEnumStmt), - - CreateRangeStmt(super::CreateRangeStmt), - - AlterEnumStmt(super::AlterEnumStmt), - - AlterTsdictionaryStmt(super::AlterTsDictionaryStmt), - - AlterTsconfigurationStmt(super::AlterTsConfigurationStmt), - - CreateFdwStmt(super::CreateFdwStmt), - - AlterFdwStmt(super::AlterFdwStmt), - - CreateForeignServerStmt(super::CreateForeignServerStmt), - - AlterForeignServerStmt(super::AlterForeignServerStmt), - - CreateUserMappingStmt(super::CreateUserMappingStmt), - - AlterUserMappingStmt(super::AlterUserMappingStmt), - - DropUserMappingStmt(super::DropUserMappingStmt), - - AlterTableSpaceOptionsStmt(super::AlterTableSpaceOptionsStmt), - - AlterTableMoveAllStmt(super::AlterTableMoveAllStmt), - - SecLabelStmt(Box), - - CreateForeignTableStmt(super::CreateForeignTableStmt), - - ImportForeignSchemaStmt(super::ImportForeignSchemaStmt), - - CreateExtensionStmt(super::CreateExtensionStmt), - - AlterExtensionStmt(super::AlterExtensionStmt), - - AlterExtensionContentsStmt(Box), - - CreateEventTrigStmt(super::CreateEventTrigStmt), - - AlterEventTrigStmt(super::AlterEventTrigStmt), - - RefreshMatViewStmt(super::RefreshMatViewStmt), - - ReplicaIdentityStmt(super::ReplicaIdentityStmt), - - AlterSystemStmt(super::AlterSystemStmt), - - CreatePolicyStmt(Box), - - AlterPolicyStmt(Box), - - CreateTransformStmt(super::CreateTransformStmt), - - CreateAmStmt(super::CreateAmStmt), - - CreatePublicationStmt(super::CreatePublicationStmt), - - AlterPublicationStmt(super::AlterPublicationStmt), - - CreateSubscriptionStmt(super::CreateSubscriptionStmt), - - AlterSubscriptionStmt(super::AlterSubscriptionStmt), - - DropSubscriptionStmt(super::DropSubscriptionStmt), - - CreateStatsStmt(super::CreateStatsStmt), - - AlterCollationStmt(super::AlterCollationStmt), - - CallStmt(Box), - - AlterStatsStmt(super::AlterStatsStmt), - - AExpr(Box), - - ColumnRef(super::ColumnRef), - - ParamRef(super::ParamRef), - - AConst(Box), - - FuncCall(Box), - - AStar(super::AStar), - - AIndices(Box), - - AIndirection(Box), - - AArrayExpr(super::AArrayExpr), - - ResTarget(Box), - - MultiAssignRef(Box), - - TypeCast(Box), - - CollateClause(Box), - - SortBy(Box), - - WindowDef(Box), - - RangeSubselect(Box), - - RangeFunction(super::RangeFunction), - - RangeTableSample(Box), - - RangeTableFunc(Box), - - RangeTableFuncCol(Box), - - TypeName(super::TypeName), - - ColumnDef(Box), - - IndexElem(Box), - - Constraint(Box), - - DefElem(Box), - - RangeTblEntry(Box), - - RangeTblFunction(Box), - - TableSampleClause(Box), - - WithCheckOption(Box), - - SortGroupClause(super::SortGroupClause), - - GroupingSet(super::GroupingSet), - - WindowClause(Box), - - ObjectWithArgs(super::ObjectWithArgs), - - AccessPriv(super::AccessPriv), - - CreateOpClassItem(super::CreateOpClassItem), - - TableLikeClause(super::TableLikeClause), - - FunctionParameter(Box), - - LockingClause(super::LockingClause), - - RowMarkClause(super::RowMarkClause), - - XmlSerialize(Box), - - WithClause(super::WithClause), - - InferClause(Box), - - OnConflictClause(Box), - - CommonTableExpr(Box), - - RoleSpec(super::RoleSpec), - - TriggerTransition(super::TriggerTransition), - - PartitionElem(Box), - - PartitionSpec(super::PartitionSpec), - - PartitionBoundSpec(super::PartitionBoundSpec), - - PartitionRangeDatum(Box), - - PartitionCmd(super::PartitionCmd), - - VacuumRelation(super::VacuumRelation), - - InlineCodeBlock(super::InlineCodeBlock), - - CallContext(super::CallContext), - - Integer(super::Integer), - - Float(super::Float), - - String(super::String2), - - BitString(super::BitString), - - Null(super::Null), - - List(super::List), - - IntList(super::IntList), - - OidList(super::OidList), - } -} -#[derive(Clone, PartialEq)] -pub struct Integer { - /// machine integer - pub ival: i32, -} -#[derive(Clone, PartialEq)] -pub struct Float { - /// string - pub str: String, -} -#[derive(Clone, PartialEq)] -pub struct String2 { - /// string - pub str: String, -} -#[derive(Clone, PartialEq)] -pub struct BitString { - /// string - pub str: String, -} -/// intentionally empty -#[derive(Clone, PartialEq)] -pub struct Null {} -#[derive(Clone, PartialEq)] -pub struct List { - pub items: Vec, -} -#[derive(Clone, PartialEq)] -pub struct OidList { - pub items: Vec, -} -#[derive(Clone, PartialEq)] -pub struct IntList { - pub items: Vec, -} -#[derive(Clone, PartialEq)] -pub struct Alias { - pub aliasname: String, - - pub colnames: Vec, -} -#[derive(Clone, PartialEq)] -pub struct RangeVar { - pub catalogname: String, - - pub schemaname: String, - - pub relname: String, - - pub inh: bool, - - pub relpersistence: String, - - pub alias: ::core::option::Option, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct TableFunc { - pub ns_uris: Vec, - - pub ns_names: Vec, - - pub docexpr: ::core::option::Option>, - - pub rowexpr: ::core::option::Option>, - - pub colnames: Vec, - - pub coltypes: Vec, - - pub coltypmods: Vec, - - pub colcollations: Vec, - - pub colexprs: Vec, - - pub coldefexprs: Vec, - - pub notnulls: Vec, - - pub ordinalitycol: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct Expr {} -#[derive(Clone, PartialEq)] -pub struct Var { - pub xpr: ::core::option::Option>, - - pub varno: u32, - - pub varattno: i32, - - pub vartype: u32, - - pub vartypmod: i32, - - pub varcollid: u32, - - pub varlevelsup: u32, - - pub varnosyn: u32, - - pub varattnosyn: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct Param { - pub xpr: ::core::option::Option>, - - pub paramkind: i32, - - pub paramid: i32, - - pub paramtype: u32, - - pub paramtypmod: i32, - - pub paramcollid: u32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct Aggref { - pub xpr: ::core::option::Option>, - - pub aggfnoid: u32, - - pub aggtype: u32, - - pub aggcollid: u32, - - pub inputcollid: u32, - - pub aggtranstype: u32, - - pub aggargtypes: Vec, - - pub aggdirectargs: Vec, - - pub args: Vec, - - pub aggorder: Vec, - - pub aggdistinct: Vec, - - pub aggfilter: ::core::option::Option>, - - pub aggstar: bool, - - pub aggvariadic: bool, - - pub aggkind: String, - - pub agglevelsup: u32, - - pub aggsplit: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct GroupingFunc { - pub xpr: ::core::option::Option>, - - pub args: Vec, - - pub refs: Vec, - - pub cols: Vec, - - pub agglevelsup: u32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct WindowFunc { - pub xpr: ::core::option::Option>, - - pub winfnoid: u32, - - pub wintype: u32, - - pub wincollid: u32, - - pub inputcollid: u32, - - pub args: Vec, - - pub aggfilter: ::core::option::Option>, - - pub winref: u32, - - pub winstar: bool, - - pub winagg: bool, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct SubscriptingRef { - pub xpr: ::core::option::Option>, - - pub refcontainertype: u32, - - pub refelemtype: u32, - - pub reftypmod: i32, - - pub refcollid: u32, - - pub refupperindexpr: Vec, - - pub reflowerindexpr: Vec, - - pub refexpr: ::core::option::Option>, - - pub refassgnexpr: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct FuncExpr { - pub xpr: ::core::option::Option>, - - pub funcid: u32, - - pub funcresulttype: u32, - - pub funcretset: bool, - - pub funcvariadic: bool, - - pub funcformat: i32, - - pub funccollid: u32, - - pub inputcollid: u32, - - pub args: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct NamedArgExpr { - pub xpr: ::core::option::Option>, - - pub arg: ::core::option::Option>, - - pub name: String, - - pub argnumber: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct OpExpr { - pub xpr: ::core::option::Option>, - - pub opno: u32, - - pub opfuncid: u32, - - pub opresulttype: u32, - - pub opretset: bool, - - pub opcollid: u32, - - pub inputcollid: u32, - - pub args: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct DistinctExpr { - pub xpr: ::core::option::Option>, - - pub opno: u32, - - pub opfuncid: u32, - - pub opresulttype: u32, - - pub opretset: bool, - - pub opcollid: u32, - - pub inputcollid: u32, - - pub args: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct NullIfExpr { - pub xpr: ::core::option::Option>, - - pub opno: u32, - - pub opfuncid: u32, - - pub opresulttype: u32, - - pub opretset: bool, - - pub opcollid: u32, - - pub inputcollid: u32, - - pub args: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct ScalarArrayOpExpr { - pub xpr: ::core::option::Option>, - - pub opno: u32, - - pub opfuncid: u32, - - pub use_or: bool, - - pub inputcollid: u32, - - pub args: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct BoolExpr { - pub xpr: ::core::option::Option>, - - pub boolop: i32, - - pub args: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct SubLink { - pub xpr: ::core::option::Option>, - - pub sub_link_type: i32, - - pub sub_link_id: i32, - - pub testexpr: ::core::option::Option>, - - pub oper_name: Vec, - - pub subselect: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct SubPlan { - pub xpr: ::core::option::Option>, - - pub sub_link_type: i32, - - pub testexpr: ::core::option::Option>, - - pub param_ids: Vec, - - pub plan_id: i32, - - pub plan_name: String, - - pub first_col_type: u32, - - pub first_col_typmod: i32, - - pub first_col_collation: u32, - - pub use_hash_table: bool, - - pub unknown_eq_false: bool, - - pub parallel_safe: bool, - - pub set_param: Vec, - - pub par_param: Vec, - - pub args: Vec, - - pub startup_cost: f64, - - pub per_call_cost: f64, -} -#[derive(Clone, PartialEq)] -pub struct AlternativeSubPlan { - pub xpr: ::core::option::Option>, - - pub subplans: Vec, -} -#[derive(Clone, PartialEq)] -pub struct FieldSelect { - pub xpr: ::core::option::Option>, - - pub arg: ::core::option::Option>, - - pub fieldnum: i32, - - pub resulttype: u32, - - pub resulttypmod: i32, - - pub resultcollid: u32, -} -#[derive(Clone, PartialEq)] -pub struct FieldStore { - pub xpr: ::core::option::Option>, - - pub arg: ::core::option::Option>, - - pub newvals: Vec, - - pub fieldnums: Vec, - - pub resulttype: u32, -} -#[derive(Clone, PartialEq)] -pub struct RelabelType { - pub xpr: ::core::option::Option>, - - pub arg: ::core::option::Option>, - - pub resulttype: u32, - - pub resulttypmod: i32, - - pub resultcollid: u32, - - pub relabelformat: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct CoerceViaIo { - pub xpr: ::core::option::Option>, - - pub arg: ::core::option::Option>, - - pub resulttype: u32, - - pub resultcollid: u32, - - pub coerceformat: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct ArrayCoerceExpr { - pub xpr: ::core::option::Option>, - - pub arg: ::core::option::Option>, - - pub elemexpr: ::core::option::Option>, - - pub resulttype: u32, - - pub resulttypmod: i32, - - pub resultcollid: u32, - - pub coerceformat: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct ConvertRowtypeExpr { - pub xpr: ::core::option::Option>, - - pub arg: ::core::option::Option>, - - pub resulttype: u32, - - pub convertformat: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct CollateExpr { - pub xpr: ::core::option::Option>, - - pub arg: ::core::option::Option>, - - pub coll_oid: u32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct CaseExpr { - pub xpr: ::core::option::Option>, - - pub casetype: u32, - - pub casecollid: u32, - - pub arg: ::core::option::Option>, - - pub args: Vec, - - pub defresult: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct CaseWhen { - pub xpr: ::core::option::Option>, - - pub expr: ::core::option::Option>, - - pub result: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct CaseTestExpr { - pub xpr: ::core::option::Option>, - - pub type_id: u32, - - pub type_mod: i32, - - pub collation: u32, -} -#[derive(Clone, PartialEq)] -pub struct ArrayExpr { - pub xpr: ::core::option::Option>, - - pub array_typeid: u32, - - pub array_collid: u32, - - pub element_typeid: u32, - - pub elements: Vec, - - pub multidims: bool, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct RowExpr { - pub xpr: ::core::option::Option>, - - pub args: Vec, - - pub row_typeid: u32, - - pub row_format: i32, - - pub colnames: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct RowCompareExpr { - pub xpr: ::core::option::Option>, - - pub rctype: i32, - - pub opnos: Vec, - - pub opfamilies: Vec, - - pub inputcollids: Vec, - - pub largs: Vec, - - pub rargs: Vec, -} -#[derive(Clone, PartialEq)] -pub struct CoalesceExpr { - pub xpr: ::core::option::Option>, - - pub coalescetype: u32, - - pub coalescecollid: u32, - - pub args: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct MinMaxExpr { - pub xpr: ::core::option::Option>, - - pub minmaxtype: u32, - - pub minmaxcollid: u32, - - pub inputcollid: u32, - - pub op: i32, - - pub args: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct SqlValueFunction { - pub xpr: ::core::option::Option>, - - pub op: i32, - - pub r#type: u32, - - pub typmod: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct XmlExpr { - pub xpr: ::core::option::Option>, - - pub op: i32, - - pub name: String, - - pub named_args: Vec, - - pub arg_names: Vec, - - pub args: Vec, - - pub xmloption: i32, - - pub r#type: u32, - - pub typmod: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct NullTest { - pub xpr: ::core::option::Option>, - - pub arg: ::core::option::Option>, - - pub nulltesttype: i32, - - pub argisrow: bool, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct BooleanTest { - pub xpr: ::core::option::Option>, - - pub arg: ::core::option::Option>, - - pub booltesttype: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct CoerceToDomain { - pub xpr: ::core::option::Option>, - - pub arg: ::core::option::Option>, - - pub resulttype: u32, - - pub resulttypmod: i32, - - pub resultcollid: u32, - - pub coercionformat: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct CoerceToDomainValue { - pub xpr: ::core::option::Option>, - - pub type_id: u32, - - pub type_mod: i32, - - pub collation: u32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct SetToDefault { - pub xpr: ::core::option::Option>, - - pub type_id: u32, - - pub type_mod: i32, - - pub collation: u32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct CurrentOfExpr { - pub xpr: ::core::option::Option>, - - pub cvarno: u32, - - pub cursor_name: String, - - pub cursor_param: i32, -} -#[derive(Clone, PartialEq)] -pub struct NextValueExpr { - pub xpr: ::core::option::Option>, - - pub seqid: u32, - - pub type_id: u32, -} -#[derive(Clone, PartialEq)] -pub struct InferenceElem { - pub xpr: ::core::option::Option>, - - pub expr: ::core::option::Option>, - - pub infercollid: u32, - - pub inferopclass: u32, -} -#[derive(Clone, PartialEq)] -pub struct TargetEntry { - pub xpr: ::core::option::Option>, - - pub expr: ::core::option::Option>, - - pub resno: i32, - - pub resname: String, - - pub ressortgroupref: u32, - - pub resorigtbl: u32, - - pub resorigcol: i32, - - pub resjunk: bool, -} -#[derive(Clone, PartialEq)] -pub struct RangeTblRef { - pub rtindex: i32, -} -#[derive(Clone, PartialEq)] -pub struct JoinExpr { - pub jointype: i32, - - pub is_natural: bool, - - pub larg: ::core::option::Option>, - - pub rarg: ::core::option::Option>, - - pub using_clause: Vec, - - pub quals: ::core::option::Option>, - - pub alias: ::core::option::Option, - - pub rtindex: i32, -} -#[derive(Clone, PartialEq)] -pub struct FromExpr { - pub fromlist: Vec, - - pub quals: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct OnConflictExpr { - pub action: i32, - - pub arbiter_elems: Vec, - - pub arbiter_where: ::core::option::Option>, - - pub constraint: u32, - - pub on_conflict_set: Vec, - - pub on_conflict_where: ::core::option::Option>, - - pub excl_rel_index: i32, - - pub excl_rel_tlist: Vec, -} -#[derive(Clone, PartialEq)] -pub struct IntoClause { - pub rel: ::core::option::Option, - - pub col_names: Vec, - - pub access_method: String, - - pub options: Vec, - - pub on_commit: i32, - - pub table_space_name: String, - - pub view_query: ::core::option::Option>, - - pub skip_data: bool, -} -#[derive(Clone, PartialEq)] -pub struct RawStmt { - pub stmt: ::core::option::Option>, - - pub stmt_location: i32, - - pub stmt_len: i32, -} -#[derive(Clone, PartialEq)] -pub struct Query { - pub command_type: i32, - - pub query_source: i32, - - pub can_set_tag: bool, - - pub utility_stmt: ::core::option::Option>, - - pub result_relation: i32, - - pub has_aggs: bool, - - pub has_window_funcs: bool, - - pub has_target_srfs: bool, - - pub has_sub_links: bool, - - pub has_distinct_on: bool, - - pub has_recursive: bool, - - pub has_modifying_cte: bool, - - pub has_for_update: bool, - - pub has_row_security: bool, - - pub cte_list: Vec, - - pub rtable: Vec, - - pub jointree: ::core::option::Option>, - - pub target_list: Vec, - - pub r#override: i32, - - pub on_conflict: ::core::option::Option>, - - pub returning_list: Vec, - - pub group_clause: Vec, - - pub grouping_sets: Vec, - - pub having_qual: ::core::option::Option>, - - pub window_clause: Vec, - - pub distinct_clause: Vec, - - pub sort_clause: Vec, - - pub limit_offset: ::core::option::Option>, - - pub limit_count: ::core::option::Option>, - - pub limit_option: i32, - - pub row_marks: Vec, - - pub set_operations: ::core::option::Option>, - - pub constraint_deps: Vec, - - pub with_check_options: Vec, - - pub stmt_location: i32, - - pub stmt_len: i32, -} -#[derive(Clone, PartialEq)] -pub struct InsertStmt { - pub relation: ::core::option::Option, - - pub cols: Vec, - - pub select_stmt: ::core::option::Option>, - - pub on_conflict_clause: ::core::option::Option>, - - pub returning_list: Vec, - - pub with_clause: ::core::option::Option, - - pub r#override: i32, -} -#[derive(Clone, PartialEq)] -pub struct DeleteStmt { - pub relation: ::core::option::Option, - - pub using_clause: Vec, - - pub where_clause: ::core::option::Option>, - - pub returning_list: Vec, - - pub with_clause: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct UpdateStmt { - pub relation: ::core::option::Option, - - pub target_list: Vec, - - pub where_clause: ::core::option::Option>, - - pub from_clause: Vec, - - pub returning_list: Vec, - - pub with_clause: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct SelectStmt { - pub distinct_clause: Vec, - - pub into_clause: ::core::option::Option>, - - pub target_list: Vec, - - pub from_clause: Vec, - - pub where_clause: ::core::option::Option>, - - pub group_clause: Vec, - - pub having_clause: ::core::option::Option>, - - pub window_clause: Vec, - - pub values_lists: Vec, - - pub sort_clause: Vec, - - pub limit_offset: ::core::option::Option>, - - pub limit_count: ::core::option::Option>, - - pub limit_option: i32, - - pub locking_clause: Vec, - - pub with_clause: ::core::option::Option, - - pub op: i32, - - pub all: bool, - - pub larg: ::core::option::Option>, - - pub rarg: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct AlterTableStmt { - pub relation: ::core::option::Option, - - pub cmds: Vec, - - pub relkind: i32, - - pub missing_ok: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterTableCmd { - pub subtype: i32, - - pub name: String, - - pub num: i32, - - pub newowner: ::core::option::Option, - - pub def: ::core::option::Option>, - - pub behavior: i32, - - pub missing_ok: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterDomainStmt { - pub subtype: String, - - pub type_name: Vec, - - pub name: String, - - pub def: ::core::option::Option>, - - pub behavior: i32, - - pub missing_ok: bool, -} -#[derive(Clone, PartialEq)] -pub struct SetOperationStmt { - pub op: i32, - - pub all: bool, - - pub larg: ::core::option::Option>, - - pub rarg: ::core::option::Option>, - - pub col_types: Vec, - - pub col_typmods: Vec, - - pub col_collations: Vec, - - pub group_clauses: Vec, -} -#[derive(Clone, PartialEq)] -pub struct GrantStmt { - pub is_grant: bool, - - pub targtype: i32, - - pub objtype: i32, - - pub objects: Vec, - - pub privileges: Vec, - - pub grantees: Vec, - - pub grant_option: bool, - - pub behavior: i32, -} -#[derive(Clone, PartialEq)] -pub struct GrantRoleStmt { - pub granted_roles: Vec, - - pub grantee_roles: Vec, - - pub is_grant: bool, - - pub admin_opt: bool, - - pub grantor: ::core::option::Option, - - pub behavior: i32, -} -#[derive(Clone, PartialEq)] -pub struct AlterDefaultPrivilegesStmt { - pub options: Vec, - - pub action: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct ClosePortalStmt { - pub portalname: String, -} -#[derive(Clone, PartialEq)] -pub struct ClusterStmt { - pub relation: ::core::option::Option, - - pub indexname: String, - - pub options: i32, -} -#[derive(Clone, PartialEq)] -pub struct CopyStmt { - pub relation: ::core::option::Option, - - pub query: ::core::option::Option>, - - pub attlist: Vec, - - pub is_from: bool, - - pub is_program: bool, - - pub filename: String, - - pub options: Vec, - - pub where_clause: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct CreateStmt { - pub relation: ::core::option::Option, - - pub table_elts: Vec, - - pub inh_relations: Vec, - - pub partbound: ::core::option::Option, - - pub partspec: ::core::option::Option, - - pub of_typename: ::core::option::Option, - - pub constraints: Vec, - - pub options: Vec, - - pub oncommit: i32, - - pub tablespacename: String, - - pub access_method: String, - - pub if_not_exists: bool, -} -#[derive(Clone, PartialEq)] -pub struct DefineStmt { - pub kind: i32, - - pub oldstyle: bool, - - pub defnames: Vec, - - pub args: Vec, - - pub definition: Vec, - - pub if_not_exists: bool, - - pub replace: bool, -} -#[derive(Clone, PartialEq)] -pub struct DropStmt { - pub objects: Vec, - - pub remove_type: i32, - - pub behavior: i32, - - pub missing_ok: bool, - - pub concurrent: bool, -} -#[derive(Clone, PartialEq)] -pub struct TruncateStmt { - pub relations: Vec, - - pub restart_seqs: bool, - - pub behavior: i32, -} -#[derive(Clone, PartialEq)] -pub struct CommentStmt { - pub objtype: i32, - - pub object: ::core::option::Option>, - - pub comment: String, -} -#[derive(Clone, PartialEq)] -pub struct FetchStmt { - pub direction: i32, - - pub how_many: i64, - - pub portalname: String, - - pub ismove: bool, -} -#[derive(Clone, PartialEq)] -pub struct IndexStmt { - pub idxname: String, - - pub relation: ::core::option::Option, - - pub access_method: String, - - pub table_space: String, - - pub index_params: Vec, - - pub index_including_params: Vec, - - pub options: Vec, - - pub where_clause: ::core::option::Option>, - - pub exclude_op_names: Vec, - - pub idxcomment: String, - - pub index_oid: u32, - - pub old_node: u32, - - pub old_create_subid: u32, - - pub old_first_relfilenode_subid: u32, - - pub unique: bool, - - pub primary: bool, - - pub isconstraint: bool, - - pub deferrable: bool, - - pub initdeferred: bool, - - pub transformed: bool, - - pub concurrent: bool, - - pub if_not_exists: bool, - - pub reset_default_tblspc: bool, -} -#[derive(Clone, PartialEq)] -pub struct CreateFunctionStmt { - pub is_procedure: bool, - - pub replace: bool, - - pub funcname: Vec, - - pub parameters: Vec, - - pub return_type: ::core::option::Option, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterFunctionStmt { - pub objtype: i32, - - pub func: ::core::option::Option, - - pub actions: Vec, -} -#[derive(Clone, PartialEq)] -pub struct DoStmt { - pub args: Vec, -} -#[derive(Clone, PartialEq)] -pub struct RenameStmt { - pub rename_type: i32, - - pub relation_type: i32, - - pub relation: ::core::option::Option, - - pub object: ::core::option::Option>, - - pub subname: String, - - pub newname: String, - - pub behavior: i32, - - pub missing_ok: bool, -} -#[derive(Clone, PartialEq)] -pub struct RuleStmt { - pub relation: ::core::option::Option, - - pub rulename: String, - - pub where_clause: ::core::option::Option>, - - pub event: i32, - - pub instead: bool, - - pub actions: Vec, - - pub replace: bool, -} -#[derive(Clone, PartialEq)] -pub struct NotifyStmt { - pub conditionname: String, - - pub payload: String, -} -#[derive(Clone, PartialEq)] -pub struct ListenStmt { - pub conditionname: String, -} -#[derive(Clone, PartialEq)] -pub struct UnlistenStmt { - pub conditionname: String, -} -#[derive(Clone, PartialEq)] -pub struct TransactionStmt { - pub kind: i32, - - pub options: Vec, - - pub savepoint_name: String, - - pub gid: String, - - pub chain: bool, -} -#[derive(Clone, PartialEq)] -pub struct ViewStmt { - pub view: ::core::option::Option, - - pub aliases: Vec, - - pub query: ::core::option::Option>, - - pub replace: bool, - - pub options: Vec, - - pub with_check_option: i32, -} -#[derive(Clone, PartialEq)] -pub struct LoadStmt { - pub filename: String, -} -#[derive(Clone, PartialEq)] -pub struct CreateDomainStmt { - pub domainname: Vec, - - pub type_name: ::core::option::Option, - - pub coll_clause: ::core::option::Option>, - - pub constraints: Vec, -} -#[derive(Clone, PartialEq)] -pub struct CreatedbStmt { - pub dbname: String, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct DropdbStmt { - pub dbname: String, - - pub missing_ok: bool, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct VacuumStmt { - pub options: Vec, - - pub rels: Vec, - - pub is_vacuumcmd: bool, -} -#[derive(Clone, PartialEq)] -pub struct ExplainStmt { - pub query: ::core::option::Option>, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct CreateTableAsStmt { - pub query: ::core::option::Option>, - - pub into: ::core::option::Option>, - - pub relkind: i32, - - pub is_select_into: bool, - - pub if_not_exists: bool, -} -#[derive(Clone, PartialEq)] -pub struct CreateSeqStmt { - pub sequence: ::core::option::Option, - - pub options: Vec, - - pub owner_id: u32, - - pub for_identity: bool, - - pub if_not_exists: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterSeqStmt { - pub sequence: ::core::option::Option, - - pub options: Vec, - - pub for_identity: bool, - - pub missing_ok: bool, -} -#[derive(Clone, PartialEq)] -pub struct VariableSetStmt { - pub kind: i32, - - pub name: String, - - pub args: Vec, - - pub is_local: bool, -} -#[derive(Clone, PartialEq)] -pub struct VariableShowStmt { - pub name: String, -} -#[derive(Clone, PartialEq)] -pub struct DiscardStmt { - pub target: i32, -} -#[derive(Clone, PartialEq)] -pub struct CreateTrigStmt { - pub trigname: String, - - pub relation: ::core::option::Option, - - pub funcname: Vec, - - pub args: Vec, - - pub row: bool, - - pub timing: i32, - - pub events: i32, - - pub columns: Vec, - - pub when_clause: ::core::option::Option>, - - pub isconstraint: bool, - - pub transition_rels: Vec, - - pub deferrable: bool, - - pub initdeferred: bool, - - pub constrrel: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct CreatePLangStmt { - pub replace: bool, - - pub plname: String, - - pub plhandler: Vec, - - pub plinline: Vec, - - pub plvalidator: Vec, - - pub pltrusted: bool, -} -#[derive(Clone, PartialEq)] -pub struct CreateRoleStmt { - pub stmt_type: i32, - - pub role: String, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterRoleStmt { - pub role: ::core::option::Option, - - pub options: Vec, - - pub action: i32, -} -#[derive(Clone, PartialEq)] -pub struct DropRoleStmt { - pub roles: Vec, - - pub missing_ok: bool, -} -#[derive(Clone, PartialEq)] -pub struct LockStmt { - pub relations: Vec, - - pub mode: i32, - - pub nowait: bool, -} -#[derive(Clone, PartialEq)] -pub struct ConstraintsSetStmt { - pub constraints: Vec, - - pub deferred: bool, -} -#[derive(Clone, PartialEq)] -pub struct ReindexStmt { - pub kind: i32, - - pub relation: ::core::option::Option, - - pub name: String, - - pub options: i32, - - pub concurrent: bool, -} -#[derive(Clone, PartialEq)] -pub struct CheckPointStmt {} -#[derive(Clone, PartialEq)] -pub struct CreateSchemaStmt { - pub schemaname: String, - - pub authrole: ::core::option::Option, - - pub schema_elts: Vec, - - pub if_not_exists: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterDatabaseStmt { - pub dbname: String, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterDatabaseSetStmt { - pub dbname: String, - - pub setstmt: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct AlterRoleSetStmt { - pub role: ::core::option::Option, - - pub database: String, - - pub setstmt: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct CreateConversionStmt { - pub conversion_name: Vec, - - pub for_encoding_name: String, - - pub to_encoding_name: String, - - pub func_name: Vec, - - pub def: bool, -} -#[derive(Clone, PartialEq)] -pub struct CreateCastStmt { - pub sourcetype: ::core::option::Option, - - pub targettype: ::core::option::Option, - - pub func: ::core::option::Option, - - pub context: i32, - - pub inout: bool, -} -#[derive(Clone, PartialEq)] -pub struct CreateOpClassStmt { - pub opclassname: Vec, - - pub opfamilyname: Vec, - - pub amname: String, - - pub datatype: ::core::option::Option, - - pub items: Vec, - - pub is_default: bool, -} -#[derive(Clone, PartialEq)] -pub struct CreateOpFamilyStmt { - pub opfamilyname: Vec, - - pub amname: String, -} -#[derive(Clone, PartialEq)] -pub struct AlterOpFamilyStmt { - pub opfamilyname: Vec, - - pub amname: String, - - pub is_drop: bool, - - pub items: Vec, -} -#[derive(Clone, PartialEq)] -pub struct PrepareStmt { - pub name: String, - - pub argtypes: Vec, - - pub query: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct ExecuteStmt { - pub name: String, - - pub params: Vec, -} -#[derive(Clone, PartialEq)] -pub struct DeallocateStmt { - pub name: String, -} -#[derive(Clone, PartialEq)] -pub struct DeclareCursorStmt { - pub portalname: String, - - pub options: i32, - - pub query: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct CreateTableSpaceStmt { - pub tablespacename: String, - - pub owner: ::core::option::Option, - - pub location: String, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct DropTableSpaceStmt { - pub tablespacename: String, - - pub missing_ok: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterObjectDependsStmt { - pub object_type: i32, - - pub relation: ::core::option::Option, - - pub object: ::core::option::Option>, - - pub extname: ::core::option::Option>, - - pub remove: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterObjectSchemaStmt { - pub object_type: i32, - - pub relation: ::core::option::Option, - - pub object: ::core::option::Option>, - - pub newschema: String, - - pub missing_ok: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterOwnerStmt { - pub object_type: i32, - - pub relation: ::core::option::Option, - - pub object: ::core::option::Option>, - - pub newowner: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct AlterOperatorStmt { - pub opername: ::core::option::Option, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterTypeStmt { - pub type_name: Vec, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct DropOwnedStmt { - pub roles: Vec, - - pub behavior: i32, -} -#[derive(Clone, PartialEq)] -pub struct ReassignOwnedStmt { - pub roles: Vec, - - pub newrole: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct CompositeTypeStmt { - pub typevar: ::core::option::Option, - - pub coldeflist: Vec, -} -#[derive(Clone, PartialEq)] -pub struct CreateEnumStmt { - pub type_name: Vec, - - pub vals: Vec, -} -#[derive(Clone, PartialEq)] -pub struct CreateRangeStmt { - pub type_name: Vec, - - pub params: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterEnumStmt { - pub type_name: Vec, - - pub old_val: String, - - pub new_val: String, - - pub new_val_neighbor: String, - - pub new_val_is_after: bool, - - pub skip_if_new_val_exists: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterTsDictionaryStmt { - pub dictname: Vec, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterTsConfigurationStmt { - pub kind: i32, - - pub cfgname: Vec, - - pub tokentype: Vec, - - pub dicts: Vec, - - pub r#override: bool, - - pub replace: bool, - - pub missing_ok: bool, -} -#[derive(Clone, PartialEq)] -pub struct CreateFdwStmt { - pub fdwname: String, - - pub func_options: Vec, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterFdwStmt { - pub fdwname: String, - - pub func_options: Vec, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct CreateForeignServerStmt { - pub servername: String, - - pub servertype: String, - - pub version: String, - - pub fdwname: String, - - pub if_not_exists: bool, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterForeignServerStmt { - pub servername: String, - - pub version: String, - - pub options: Vec, - - pub has_version: bool, -} -#[derive(Clone, PartialEq)] -pub struct CreateUserMappingStmt { - pub user: ::core::option::Option, - - pub servername: String, - - pub if_not_exists: bool, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterUserMappingStmt { - pub user: ::core::option::Option, - - pub servername: String, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct DropUserMappingStmt { - pub user: ::core::option::Option, - - pub servername: String, - - pub missing_ok: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterTableSpaceOptionsStmt { - pub tablespacename: String, - - pub options: Vec, - - pub is_reset: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterTableMoveAllStmt { - pub orig_tablespacename: String, - - pub objtype: i32, - - pub roles: Vec, - - pub new_tablespacename: String, - - pub nowait: bool, -} -#[derive(Clone, PartialEq)] -pub struct SecLabelStmt { - pub objtype: i32, - - pub object: ::core::option::Option>, - - pub provider: String, - - pub label: String, -} -#[derive(Clone, PartialEq)] -pub struct CreateForeignTableStmt { - pub base_stmt: ::core::option::Option, - - pub servername: String, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct ImportForeignSchemaStmt { - pub server_name: String, - - pub remote_schema: String, - - pub local_schema: String, - - pub list_type: i32, - - pub table_list: Vec, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct CreateExtensionStmt { - pub extname: String, - - pub if_not_exists: bool, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterExtensionStmt { - pub extname: String, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterExtensionContentsStmt { - pub extname: String, - - pub action: i32, - - pub objtype: i32, - - pub object: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct CreateEventTrigStmt { - pub trigname: String, - - pub eventname: String, - - pub whenclause: Vec, - - pub funcname: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterEventTrigStmt { - pub trigname: String, - - pub tgenabled: String, -} -#[derive(Clone, PartialEq)] -pub struct RefreshMatViewStmt { - pub concurrent: bool, - - pub skip_data: bool, - - pub relation: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct ReplicaIdentityStmt { - pub identity_type: String, - - pub name: String, -} -#[derive(Clone, PartialEq)] -pub struct AlterSystemStmt { - pub setstmt: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct CreatePolicyStmt { - pub policy_name: String, - - pub table: ::core::option::Option, - - pub cmd_name: String, - - pub permissive: bool, - - pub roles: Vec, - - pub qual: ::core::option::Option>, - - pub with_check: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct AlterPolicyStmt { - pub policy_name: String, - - pub table: ::core::option::Option, - - pub roles: Vec, - - pub qual: ::core::option::Option>, - - pub with_check: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct CreateTransformStmt { - pub replace: bool, - - pub type_name: ::core::option::Option, - - pub lang: String, - - pub fromsql: ::core::option::Option, - - pub tosql: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct CreateAmStmt { - pub amname: String, - - pub handler_name: Vec, - - pub amtype: String, -} -#[derive(Clone, PartialEq)] -pub struct CreatePublicationStmt { - pub pubname: String, - - pub options: Vec, - - pub tables: Vec, - - pub for_all_tables: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterPublicationStmt { - pub pubname: String, - - pub options: Vec, - - pub tables: Vec, - - pub for_all_tables: bool, - - pub table_action: i32, -} -#[derive(Clone, PartialEq)] -pub struct CreateSubscriptionStmt { - pub subname: String, - - pub conninfo: String, - - pub publication: Vec, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterSubscriptionStmt { - pub kind: i32, - - pub subname: String, - - pub conninfo: String, - - pub publication: Vec, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct DropSubscriptionStmt { - pub subname: String, - - pub missing_ok: bool, - - pub behavior: i32, -} -#[derive(Clone, PartialEq)] -pub struct CreateStatsStmt { - pub defnames: Vec, - - pub stat_types: Vec, - - pub exprs: Vec, - - pub relations: Vec, - - pub stxcomment: String, - - pub if_not_exists: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterCollationStmt { - pub collname: Vec, -} -#[derive(Clone, PartialEq)] -pub struct CallStmt { - pub funccall: ::core::option::Option>, - - pub funcexpr: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct AlterStatsStmt { - pub defnames: Vec, - - pub stxstattarget: i32, - - pub missing_ok: bool, -} -#[derive(Clone, PartialEq)] -pub struct AExpr { - pub kind: i32, - - pub name: Vec, - - pub lexpr: ::core::option::Option>, - - pub rexpr: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct ColumnRef { - pub fields: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct ParamRef { - pub number: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct AConst { - pub val: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct FuncCall { - pub funcname: Vec, - - pub args: Vec, - - pub agg_order: Vec, - - pub agg_filter: ::core::option::Option>, - - pub agg_within_group: bool, - - pub agg_star: bool, - - pub agg_distinct: bool, - - pub func_variadic: bool, - - pub over: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct AStar {} -#[derive(Clone, PartialEq)] -pub struct AIndices { - pub is_slice: bool, - - pub lidx: ::core::option::Option>, - - pub uidx: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct AIndirection { - pub arg: ::core::option::Option>, - - pub indirection: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AArrayExpr { - pub elements: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct ResTarget { - pub name: String, - - pub indirection: Vec, - - pub val: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct MultiAssignRef { - pub source: ::core::option::Option>, - - pub colno: i32, - - pub ncolumns: i32, -} -#[derive(Clone, PartialEq)] -pub struct TypeCast { - pub arg: ::core::option::Option>, - - pub type_name: ::core::option::Option, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct CollateClause { - pub arg: ::core::option::Option>, - - pub collname: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct SortBy { - pub node: ::core::option::Option>, - - pub sortby_dir: i32, - - pub sortby_nulls: i32, - - pub use_op: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct WindowDef { - pub name: String, - - pub refname: String, - - pub partition_clause: Vec, - - pub order_clause: Vec, - - pub frame_options: i32, - - pub start_offset: ::core::option::Option>, - - pub end_offset: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct RangeSubselect { - pub lateral: bool, - - pub subquery: ::core::option::Option>, - - pub alias: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct RangeFunction { - pub lateral: bool, - - pub ordinality: bool, - - pub is_rowsfrom: bool, - - pub functions: Vec, - - pub alias: ::core::option::Option, - - pub coldeflist: Vec, -} -#[derive(Clone, PartialEq)] -pub struct RangeTableSample { - pub relation: ::core::option::Option>, - - pub method: Vec, - - pub args: Vec, - - pub repeatable: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct RangeTableFunc { - pub lateral: bool, - - pub docexpr: ::core::option::Option>, - - pub rowexpr: ::core::option::Option>, - - pub namespaces: Vec, - - pub columns: Vec, - - pub alias: ::core::option::Option, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct RangeTableFuncCol { - pub colname: String, - - pub type_name: ::core::option::Option, - - pub for_ordinality: bool, - - pub is_not_null: bool, - - pub colexpr: ::core::option::Option>, - - pub coldefexpr: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct TypeName { - pub names: Vec, - - pub type_oid: u32, - - pub setof: bool, - - pub pct_type: bool, - - pub typmods: Vec, - - pub typemod: i32, - - pub array_bounds: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct ColumnDef { - pub colname: String, - - pub type_name: ::core::option::Option, - - pub inhcount: i32, - - pub is_local: bool, - - pub is_not_null: bool, - - pub is_from_type: bool, - - pub storage: String, - - pub raw_default: ::core::option::Option>, - - pub cooked_default: ::core::option::Option>, - - pub identity: String, - - pub identity_sequence: ::core::option::Option, - - pub generated: String, - - pub coll_clause: ::core::option::Option>, - - pub coll_oid: u32, - - pub constraints: Vec, - - pub fdwoptions: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct IndexElem { - pub name: String, - - pub expr: ::core::option::Option>, - - pub indexcolname: String, - - pub collation: Vec, - - pub opclass: Vec, - - pub opclassopts: Vec, - - pub ordering: i32, - - pub nulls_ordering: i32, -} -#[derive(Clone, PartialEq)] -pub struct Constraint { - pub contype: i32, - - pub conname: String, - - pub deferrable: bool, - - pub initdeferred: bool, - - pub location: i32, - - pub is_no_inherit: bool, - - pub raw_expr: ::core::option::Option>, - - pub cooked_expr: String, - - pub generated_when: String, - - pub keys: Vec, - - pub including: Vec, - - pub exclusions: Vec, - - pub options: Vec, - - pub indexname: String, - - pub indexspace: String, - - pub reset_default_tblspc: bool, - - pub access_method: String, - - pub where_clause: ::core::option::Option>, - - pub pktable: ::core::option::Option, - - pub fk_attrs: Vec, - - pub pk_attrs: Vec, - - pub fk_matchtype: String, - - pub fk_upd_action: String, - - pub fk_del_action: String, - - pub old_conpfeqop: Vec, - - pub old_pktable_oid: u32, - - pub skip_validation: bool, - - pub initially_valid: bool, -} -#[derive(Clone, PartialEq)] -pub struct DefElem { - pub defnamespace: String, - - pub defname: String, - - pub arg: ::core::option::Option>, - - pub defaction: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct RangeTblEntry { - pub rtekind: i32, - - pub relid: u32, - - pub relkind: String, - - pub rellockmode: i32, - - pub tablesample: ::core::option::Option>, - - pub subquery: ::core::option::Option>, - - pub security_barrier: bool, - - pub jointype: i32, - - pub joinmergedcols: i32, - - pub joinaliasvars: Vec, - - pub joinleftcols: Vec, - - pub joinrightcols: Vec, - - pub functions: Vec, - - pub funcordinality: bool, - - pub tablefunc: ::core::option::Option>, - - pub values_lists: Vec, - - pub ctename: String, - - pub ctelevelsup: u32, - - pub self_reference: bool, - - pub coltypes: Vec, - - pub coltypmods: Vec, - - pub colcollations: Vec, - - pub enrname: String, - - pub enrtuples: f64, - - pub alias: ::core::option::Option, - - pub eref: ::core::option::Option, - - pub lateral: bool, - - pub inh: bool, - - pub in_from_cl: bool, - - pub required_perms: u32, - - pub check_as_user: u32, - - pub selected_cols: Vec, - - pub inserted_cols: Vec, - - pub updated_cols: Vec, - - pub extra_updated_cols: Vec, - - pub security_quals: Vec, -} -#[derive(Clone, PartialEq)] -pub struct RangeTblFunction { - pub funcexpr: ::core::option::Option>, - - pub funccolcount: i32, - - pub funccolnames: Vec, - - pub funccoltypes: Vec, - - pub funccoltypmods: Vec, - - pub funccolcollations: Vec, - - pub funcparams: Vec, -} -#[derive(Clone, PartialEq)] -pub struct TableSampleClause { - pub tsmhandler: u32, - - pub args: Vec, - - pub repeatable: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct WithCheckOption { - pub kind: i32, - - pub relname: String, - - pub polname: String, - - pub qual: ::core::option::Option>, - - pub cascaded: bool, -} -#[derive(Clone, PartialEq)] -pub struct SortGroupClause { - pub tle_sort_group_ref: u32, - - pub eqop: u32, - - pub sortop: u32, - - pub nulls_first: bool, - - pub hashable: bool, -} -#[derive(Clone, PartialEq)] -pub struct GroupingSet { - pub kind: i32, - - pub content: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct WindowClause { - pub name: String, - - pub refname: String, - - pub partition_clause: Vec, - - pub order_clause: Vec, - - pub frame_options: i32, - - pub start_offset: ::core::option::Option>, - - pub end_offset: ::core::option::Option>, - - pub start_in_range_func: u32, - - pub end_in_range_func: u32, - - pub in_range_coll: u32, - - pub in_range_asc: bool, - - pub in_range_nulls_first: bool, - - pub winref: u32, - - pub copied_order: bool, -} -#[derive(Clone, PartialEq)] -pub struct ObjectWithArgs { - pub objname: Vec, - - pub objargs: Vec, - - pub args_unspecified: bool, -} -#[derive(Clone, PartialEq)] -pub struct AccessPriv { - pub priv_name: String, - - pub cols: Vec, -} -#[derive(Clone, PartialEq)] -pub struct CreateOpClassItem { - pub itemtype: i32, - - pub name: ::core::option::Option, - - pub number: i32, - - pub order_family: Vec, - - pub class_args: Vec, - - pub storedtype: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct TableLikeClause { - pub relation: ::core::option::Option, - - pub options: u32, - - pub relation_oid: u32, -} -#[derive(Clone, PartialEq)] -pub struct FunctionParameter { - pub name: String, - - pub arg_type: ::core::option::Option, - - pub mode: i32, - - pub defexpr: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct LockingClause { - pub locked_rels: Vec, - - pub strength: i32, - - pub wait_policy: i32, -} -#[derive(Clone, PartialEq)] -pub struct RowMarkClause { - pub rti: u32, - - pub strength: i32, - - pub wait_policy: i32, - - pub pushed_down: bool, -} -#[derive(Clone, PartialEq)] -pub struct XmlSerialize { - pub xmloption: i32, - - pub expr: ::core::option::Option>, - - pub type_name: ::core::option::Option, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct WithClause { - pub ctes: Vec, - - pub recursive: bool, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct InferClause { - pub index_elems: Vec, - - pub where_clause: ::core::option::Option>, - - pub conname: String, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct OnConflictClause { - pub action: i32, - - pub infer: ::core::option::Option>, - - pub target_list: Vec, - - pub where_clause: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct CommonTableExpr { - pub ctename: String, - - pub aliascolnames: Vec, - - pub ctematerialized: i32, - - pub ctequery: ::core::option::Option>, - - pub location: i32, - - pub cterecursive: bool, - - pub cterefcount: i32, - - pub ctecolnames: Vec, - - pub ctecoltypes: Vec, - - pub ctecoltypmods: Vec, - - pub ctecolcollations: Vec, -} -#[derive(Clone, PartialEq)] -pub struct RoleSpec { - pub roletype: i32, - - pub rolename: String, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct TriggerTransition { - pub name: String, - - pub is_new: bool, - - pub is_table: bool, -} -#[derive(Clone, PartialEq)] -pub struct PartitionElem { - pub name: String, - - pub expr: ::core::option::Option>, - - pub collation: Vec, - - pub opclass: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct PartitionSpec { - pub strategy: String, - - pub part_params: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct PartitionBoundSpec { - pub strategy: String, - - pub is_default: bool, - - pub modulus: i32, - - pub remainder: i32, - - pub listdatums: Vec, - - pub lowerdatums: Vec, - - pub upperdatums: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct PartitionRangeDatum { - pub kind: i32, - - pub value: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct PartitionCmd { - pub name: ::core::option::Option, - - pub bound: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct VacuumRelation { - pub relation: ::core::option::Option, - - pub oid: u32, - - pub va_cols: Vec, -} -#[derive(Clone, PartialEq)] -pub struct InlineCodeBlock { - pub source_text: String, - - pub lang_oid: u32, - - pub lang_is_trusted: bool, - - pub atomic: bool, -} -#[derive(Clone, PartialEq)] -pub struct CallContext { - pub atomic: bool, -} -#[derive(Clone, PartialEq)] -pub struct ScanToken { - pub start: i32, - - pub end: i32, - - pub token: i32, - - pub keyword_kind: i32, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum OverridingKind { - Undefined = 0, - OverridingNotSet = 1, - OverridingUserValue = 2, - OverridingSystemValue = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum QuerySource { - Undefined = 0, - QsrcOriginal = 1, - QsrcParser = 2, - QsrcInsteadRule = 3, - QsrcQualInsteadRule = 4, - QsrcNonInsteadRule = 5, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum SortByDir { - Undefined = 0, - SortbyDefault = 1, - SortbyAsc = 2, - SortbyDesc = 3, - SortbyUsing = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum SortByNulls { - Undefined = 0, - SortbyNullsDefault = 1, - SortbyNullsFirst = 2, - SortbyNullsLast = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum AExprKind { - Undefined = 0, - AexprOp = 1, - AexprOpAny = 2, - AexprOpAll = 3, - AexprDistinct = 4, - AexprNotDistinct = 5, - AexprNullif = 6, - AexprOf = 7, - AexprIn = 8, - AexprLike = 9, - AexprIlike = 10, - AexprSimilar = 11, - AexprBetween = 12, - AexprNotBetween = 13, - AexprBetweenSym = 14, - AexprNotBetweenSym = 15, - AexprParen = 16, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum RoleSpecType { - Undefined = 0, - RolespecCstring = 1, - RolespecCurrentUser = 2, - RolespecSessionUser = 3, - RolespecPublic = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum TableLikeOption { - Undefined = 0, - CreateTableLikeComments = 1, - CreateTableLikeConstraints = 2, - CreateTableLikeDefaults = 3, - CreateTableLikeGenerated = 4, - CreateTableLikeIdentity = 5, - CreateTableLikeIndexes = 6, - CreateTableLikeStatistics = 7, - CreateTableLikeStorage = 8, - CreateTableLikeAll = 9, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum DefElemAction { - Undefined = 0, - DefelemUnspec = 1, - DefelemSet = 2, - DefelemAdd = 3, - DefelemDrop = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum PartitionRangeDatumKind { - Undefined = 0, - PartitionRangeDatumMinvalue = 1, - PartitionRangeDatumValue = 2, - PartitionRangeDatumMaxvalue = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum RteKind { - RtekindUndefined = 0, - RteRelation = 1, - RteSubquery = 2, - RteJoin = 3, - RteFunction = 4, - RteTablefunc = 5, - RteValues = 6, - RteCte = 7, - RteNamedtuplestore = 8, - RteResult = 9, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum WcoKind { - WcokindUndefined = 0, - WcoViewCheck = 1, - WcoRlsInsertCheck = 2, - WcoRlsUpdateCheck = 3, - WcoRlsConflictCheck = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum GroupingSetKind { - Undefined = 0, - GroupingSetEmpty = 1, - GroupingSetSimple = 2, - GroupingSetRollup = 3, - GroupingSetCube = 4, - GroupingSetSets = 5, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum CteMaterialize { - CtematerializeUndefined = 0, - Default = 1, - Always = 2, - Never = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum SetOperation { - Undefined = 0, - SetopNone = 1, - SetopUnion = 2, - SetopIntersect = 3, - SetopExcept = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum ObjectType { - Undefined = 0, - ObjectAccessMethod = 1, - ObjectAggregate = 2, - ObjectAmop = 3, - ObjectAmproc = 4, - ObjectAttribute = 5, - ObjectCast = 6, - ObjectColumn = 7, - ObjectCollation = 8, - ObjectConversion = 9, - ObjectDatabase = 10, - ObjectDefault = 11, - ObjectDefacl = 12, - ObjectDomain = 13, - ObjectDomconstraint = 14, - ObjectEventTrigger = 15, - ObjectExtension = 16, - ObjectFdw = 17, - ObjectForeignServer = 18, - ObjectForeignTable = 19, - ObjectFunction = 20, - ObjectIndex = 21, - ObjectLanguage = 22, - ObjectLargeobject = 23, - ObjectMatview = 24, - ObjectOpclass = 25, - ObjectOperator = 26, - ObjectOpfamily = 27, - ObjectPolicy = 28, - ObjectProcedure = 29, - ObjectPublication = 30, - ObjectPublicationRel = 31, - ObjectRole = 32, - ObjectRoutine = 33, - ObjectRule = 34, - ObjectSchema = 35, - ObjectSequence = 36, - ObjectSubscription = 37, - ObjectStatisticExt = 38, - ObjectTabconstraint = 39, - ObjectTable = 40, - ObjectTablespace = 41, - ObjectTransform = 42, - ObjectTrigger = 43, - ObjectTsconfiguration = 44, - ObjectTsdictionary = 45, - ObjectTsparser = 46, - ObjectTstemplate = 47, - ObjectType = 48, - ObjectUserMapping = 49, - ObjectView = 50, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum DropBehavior { - Undefined = 0, - DropRestrict = 1, - DropCascade = 2, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum AlterTableType { - Undefined = 0, - AtAddColumn = 1, - AtAddColumnRecurse = 2, - AtAddColumnToView = 3, - AtColumnDefault = 4, - AtCookedColumnDefault = 5, - AtDropNotNull = 6, - AtSetNotNull = 7, - AtDropExpression = 8, - AtCheckNotNull = 9, - AtSetStatistics = 10, - AtSetOptions = 11, - AtResetOptions = 12, - AtSetStorage = 13, - AtDropColumn = 14, - AtDropColumnRecurse = 15, - AtAddIndex = 16, - AtReAddIndex = 17, - AtAddConstraint = 18, - AtAddConstraintRecurse = 19, - AtReAddConstraint = 20, - AtReAddDomainConstraint = 21, - AtAlterConstraint = 22, - AtValidateConstraint = 23, - AtValidateConstraintRecurse = 24, - AtAddIndexConstraint = 25, - AtDropConstraint = 26, - AtDropConstraintRecurse = 27, - AtReAddComment = 28, - AtAlterColumnType = 29, - AtAlterColumnGenericOptions = 30, - AtChangeOwner = 31, - AtClusterOn = 32, - AtDropCluster = 33, - AtSetLogged = 34, - AtSetUnLogged = 35, - AtDropOids = 36, - AtSetTableSpace = 37, - AtSetRelOptions = 38, - AtResetRelOptions = 39, - AtReplaceRelOptions = 40, - AtEnableTrig = 41, - AtEnableAlwaysTrig = 42, - AtEnableReplicaTrig = 43, - AtDisableTrig = 44, - AtEnableTrigAll = 45, - AtDisableTrigAll = 46, - AtEnableTrigUser = 47, - AtDisableTrigUser = 48, - AtEnableRule = 49, - AtEnableAlwaysRule = 50, - AtEnableReplicaRule = 51, - AtDisableRule = 52, - AtAddInherit = 53, - AtDropInherit = 54, - AtAddOf = 55, - AtDropOf = 56, - AtReplicaIdentity = 57, - AtEnableRowSecurity = 58, - AtDisableRowSecurity = 59, - AtForceRowSecurity = 60, - AtNoForceRowSecurity = 61, - AtGenericOptions = 62, - AtAttachPartition = 63, - AtDetachPartition = 64, - AtAddIdentity = 65, - AtSetIdentity = 66, - AtDropIdentity = 67, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum GrantTargetType { - Undefined = 0, - AclTargetObject = 1, - AclTargetAllInSchema = 2, - AclTargetDefaults = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum VariableSetKind { - Undefined = 0, - VarSetValue = 1, - VarSetDefault = 2, - VarSetCurrent = 3, - VarSetMulti = 4, - VarReset = 5, - VarResetAll = 6, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum ConstrType { - Undefined = 0, - ConstrNull = 1, - ConstrNotnull = 2, - ConstrDefault = 3, - ConstrIdentity = 4, - ConstrGenerated = 5, - ConstrCheck = 6, - ConstrPrimary = 7, - ConstrUnique = 8, - ConstrExclusion = 9, - ConstrForeign = 10, - ConstrAttrDeferrable = 11, - ConstrAttrNotDeferrable = 12, - ConstrAttrDeferred = 13, - ConstrAttrImmediate = 14, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum ImportForeignSchemaType { - Undefined = 0, - FdwImportSchemaAll = 1, - FdwImportSchemaLimitTo = 2, - FdwImportSchemaExcept = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum RoleStmtType { - Undefined = 0, - RolestmtRole = 1, - RolestmtUser = 2, - RolestmtGroup = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum FetchDirection { - Undefined = 0, - FetchForward = 1, - FetchBackward = 2, - FetchAbsolute = 3, - FetchRelative = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum FunctionParameterMode { - Undefined = 0, - FuncParamIn = 1, - FuncParamOut = 2, - FuncParamInout = 3, - FuncParamVariadic = 4, - FuncParamTable = 5, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum TransactionStmtKind { - Undefined = 0, - TransStmtBegin = 1, - TransStmtStart = 2, - TransStmtCommit = 3, - TransStmtRollback = 4, - TransStmtSavepoint = 5, - TransStmtRelease = 6, - TransStmtRollbackTo = 7, - TransStmtPrepare = 8, - TransStmtCommitPrepared = 9, - TransStmtRollbackPrepared = 10, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum ViewCheckOption { - Undefined = 0, - NoCheckOption = 1, - LocalCheckOption = 2, - CascadedCheckOption = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum ClusterOption { - Undefined = 0, - CluoptRecheck = 1, - CluoptVerbose = 2, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum DiscardMode { - Undefined = 0, - DiscardAll = 1, - DiscardPlans = 2, - DiscardSequences = 3, - DiscardTemp = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum ReindexObjectType { - Undefined = 0, - ReindexObjectIndex = 1, - ReindexObjectTable = 2, - ReindexObjectSchema = 3, - ReindexObjectSystem = 4, - ReindexObjectDatabase = 5, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum AlterTsConfigType { - AlterTsconfigTypeUndefined = 0, - AlterTsconfigAddMapping = 1, - AlterTsconfigAlterMappingForToken = 2, - AlterTsconfigReplaceDict = 3, - AlterTsconfigReplaceDictForToken = 4, - AlterTsconfigDropMapping = 5, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum AlterSubscriptionType { - Undefined = 0, - AlterSubscriptionOptions = 1, - AlterSubscriptionConnection = 2, - AlterSubscriptionPublication = 3, - AlterSubscriptionRefresh = 4, - AlterSubscriptionEnabled = 5, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum OnCommitAction { - Undefined = 0, - OncommitNoop = 1, - OncommitPreserveRows = 2, - OncommitDeleteRows = 3, - OncommitDrop = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum ParamKind { - Undefined = 0, - ParamExtern = 1, - ParamExec = 2, - ParamSublink = 3, - ParamMultiexpr = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum CoercionContext { - Undefined = 0, - CoercionImplicit = 1, - CoercionAssignment = 2, - CoercionExplicit = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum CoercionForm { - Undefined = 0, - CoerceExplicitCall = 1, - CoerceExplicitCast = 2, - CoerceImplicitCast = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum BoolExprType { - Undefined = 0, - AndExpr = 1, - OrExpr = 2, - NotExpr = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum SubLinkType { - Undefined = 0, - ExistsSublink = 1, - AllSublink = 2, - AnySublink = 3, - RowcompareSublink = 4, - ExprSublink = 5, - MultiexprSublink = 6, - ArraySublink = 7, - CteSublink = 8, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum RowCompareType { - Undefined = 0, - RowcompareLt = 1, - RowcompareLe = 2, - RowcompareEq = 3, - RowcompareGe = 4, - RowcompareGt = 5, - RowcompareNe = 6, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum MinMaxOp { - Undefined = 0, - IsGreatest = 1, - IsLeast = 2, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum SqlValueFunctionOp { - SqlvalueFunctionOpUndefined = 0, - SvfopCurrentDate = 1, - SvfopCurrentTime = 2, - SvfopCurrentTimeN = 3, - SvfopCurrentTimestamp = 4, - SvfopCurrentTimestampN = 5, - SvfopLocaltime = 6, - SvfopLocaltimeN = 7, - SvfopLocaltimestamp = 8, - SvfopLocaltimestampN = 9, - SvfopCurrentRole = 10, - SvfopCurrentUser = 11, - SvfopUser = 12, - SvfopSessionUser = 13, - SvfopCurrentCatalog = 14, - SvfopCurrentSchema = 15, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum XmlExprOp { - Undefined = 0, - IsXmlconcat = 1, - IsXmlelement = 2, - IsXmlforest = 3, - IsXmlparse = 4, - IsXmlpi = 5, - IsXmlroot = 6, - IsXmlserialize = 7, - IsDocument = 8, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum XmlOptionType { - Undefined = 0, - XmloptionDocument = 1, - XmloptionContent = 2, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum NullTestType { - Undefined = 0, - IsNull = 1, - IsNotNull = 2, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum BoolTestType { - Undefined = 0, - IsTrue = 1, - IsNotTrue = 2, - IsFalse = 3, - IsNotFalse = 4, - IsUnknown = 5, - IsNotUnknown = 6, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum CmdType { - Undefined = 0, - CmdUnknown = 1, - CmdSelect = 2, - CmdUpdate = 3, - CmdInsert = 4, - CmdDelete = 5, - CmdUtility = 6, - CmdNothing = 7, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum JoinType { - Undefined = 0, - JoinInner = 1, - JoinLeft = 2, - JoinFull = 3, - JoinRight = 4, - JoinSemi = 5, - JoinAnti = 6, - JoinUniqueOuter = 7, - JoinUniqueInner = 8, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum AggStrategy { - Undefined = 0, - AggPlain = 1, - AggSorted = 2, - AggHashed = 3, - AggMixed = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum AggSplit { - Undefined = 0, - AggsplitSimple = 1, - AggsplitInitialSerial = 2, - AggsplitFinalDeserial = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum SetOpCmd { - Undefined = 0, - SetopcmdIntersect = 1, - SetopcmdIntersectAll = 2, - SetopcmdExcept = 3, - SetopcmdExceptAll = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum SetOpStrategy { - Undefined = 0, - SetopSorted = 1, - SetopHashed = 2, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum OnConflictAction { - Undefined = 0, - OnconflictNone = 1, - OnconflictNothing = 2, - OnconflictUpdate = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum LimitOption { - Undefined = 0, - Default = 1, - Count = 2, - WithTies = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum LockClauseStrength { - Undefined = 0, - LcsNone = 1, - LcsForkeyshare = 2, - LcsForshare = 3, - LcsFornokeyupdate = 4, - LcsForupdate = 5, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum LockWaitPolicy { - Undefined = 0, - LockWaitBlock = 1, - LockWaitSkip = 2, - LockWaitError = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum LockTupleMode { - Undefined = 0, - LockTupleKeyShare = 1, - LockTupleShare = 2, - LockTupleNoKeyExclusive = 3, - LockTupleExclusive = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum KeywordKind { - NoKeyword = 0, - UnreservedKeyword = 1, - ColNameKeyword = 2, - TypeFuncNameKeyword = 3, - ReservedKeyword = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum Token { - Nul = 0, - /// Single-character tokens that are returned 1:1 (identical with "self" list in scan.l) - /// Either supporting syntax, or single-character operators (some can be both) - /// Also see - /// - /// "%" - Ascii37 = 37, - /// "(" - Ascii40 = 40, - /// ")" - Ascii41 = 41, - /// "*" - Ascii42 = 42, - /// "+" - Ascii43 = 43, - /// "," - Ascii44 = 44, - /// "-" - Ascii45 = 45, - /// "." - Ascii46 = 46, - /// "/" - Ascii47 = 47, - /// ":" - Ascii58 = 58, - /// ";" - Ascii59 = 59, - /// "<" - Ascii60 = 60, - /// "=" - Ascii61 = 61, - /// ">" - Ascii62 = 62, - /// "?" - Ascii63 = 63, - /// "[" - Ascii91 = 91, - /// "\" - Ascii92 = 92, - /// "]" - Ascii93 = 93, - /// "^" - Ascii94 = 94, - /// Named tokens in scan.l - Ident = 258, - Uident = 259, - Fconst = 260, - Sconst = 261, - Usconst = 262, - Bconst = 263, - Xconst = 264, - Op = 265, - Iconst = 266, - Param = 267, - Typecast = 268, - DotDot = 269, - ColonEquals = 270, - EqualsGreater = 271, - LessEquals = 272, - GreaterEquals = 273, - NotEquals = 274, - SqlComment = 275, - CComment = 276, - AbortP = 277, - AbsoluteP = 278, - Access = 279, - Action = 280, - AddP = 281, - Admin = 282, - After = 283, - Aggregate = 284, - All = 285, - Also = 286, - Alter = 287, - Always = 288, - Analyse = 289, - Analyze = 290, - And = 291, - Any = 292, - Array = 293, - As = 294, - Asc = 295, - Assertion = 296, - Assignment = 297, - Asymmetric = 298, - At = 299, - Attach = 300, - Attribute = 301, - Authorization = 302, - Backward = 303, - Before = 304, - BeginP = 305, - Between = 306, - Bigint = 307, - Binary = 308, - Bit = 309, - BooleanP = 310, - Both = 311, - By = 312, - Cache = 313, - Call = 314, - Called = 315, - Cascade = 316, - Cascaded = 317, - Case = 318, - Cast = 319, - CatalogP = 320, - Chain = 321, - CharP = 322, - Character = 323, - Characteristics = 324, - Check = 325, - Checkpoint = 326, - Class = 327, - Close = 328, - Cluster = 329, - Coalesce = 330, - Collate = 331, - Collation = 332, - Column = 333, - Columns = 334, - Comment = 335, - Comments = 336, - Commit = 337, - Committed = 338, - Concurrently = 339, - Configuration = 340, - Conflict = 341, - Connection = 342, - Constraint = 343, - Constraints = 344, - ContentP = 345, - ContinueP = 346, - ConversionP = 347, - Copy = 348, - Cost = 349, - Create = 350, - Cross = 351, - Csv = 352, - Cube = 353, - CurrentP = 354, - CurrentCatalog = 355, - CurrentDate = 356, - CurrentRole = 357, - CurrentSchema = 358, - CurrentTime = 359, - CurrentTimestamp = 360, - CurrentUser = 361, - Cursor = 362, - Cycle = 363, - DataP = 364, - Database = 365, - DayP = 366, - Deallocate = 367, - Dec = 368, - DecimalP = 369, - Declare = 370, - Default = 371, - Defaults = 372, - Deferrable = 373, - Deferred = 374, - Definer = 375, - DeleteP = 376, - Delimiter = 377, - Delimiters = 378, - Depends = 379, - Desc = 380, - Detach = 381, - Dictionary = 382, - DisableP = 383, - Discard = 384, - Distinct = 385, - Do = 386, - DocumentP = 387, - DomainP = 388, - DoubleP = 389, - Drop = 390, - Each = 391, - Else = 392, - EnableP = 393, - Encoding = 394, - Encrypted = 395, - EndP = 396, - EnumP = 397, - Escape = 398, - Event = 399, - Except = 400, - Exclude = 401, - Excluding = 402, - Exclusive = 403, - Execute = 404, - Exists = 405, - Explain = 406, - Expression = 407, - Extension = 408, - External = 409, - Extract = 410, - FalseP = 411, - Family = 412, - Fetch = 413, - Filter = 414, - FirstP = 415, - FloatP = 416, - Following = 417, - For = 418, - Force = 419, - Foreign = 420, - Forward = 421, - Freeze = 422, - From = 423, - Full = 424, - Function = 425, - Functions = 426, - Generated = 427, - Global = 428, - Grant = 429, - Granted = 430, - Greatest = 431, - GroupP = 432, - Grouping = 433, - Groups = 434, - Handler = 435, - Having = 436, - HeaderP = 437, - Hold = 438, - HourP = 439, - IdentityP = 440, - IfP = 441, - Ilike = 442, - Immediate = 443, - Immutable = 444, - ImplicitP = 445, - ImportP = 446, - InP = 447, - Include = 448, - Including = 449, - Increment = 450, - Index = 451, - Indexes = 452, - Inherit = 453, - Inherits = 454, - Initially = 455, - InlineP = 456, - InnerP = 457, - Inout = 458, - InputP = 459, - Insensitive = 460, - Insert = 461, - Instead = 462, - IntP = 463, - Integer = 464, - Intersect = 465, - Interval = 466, - Into = 467, - Invoker = 468, - Is = 469, - Isnull = 470, - Isolation = 471, - Join = 472, - Key = 473, - Label = 474, - Language = 475, - LargeP = 476, - LastP = 477, - LateralP = 478, - Leading = 479, - Leakproof = 480, - Least = 481, - Left = 482, - Level = 483, - Like = 484, - Limit = 485, - Listen = 486, - Load = 487, - Local = 488, - Localtime = 489, - Localtimestamp = 490, - Location = 491, - LockP = 492, - Locked = 493, - Logged = 494, - Mapping = 495, - Match = 496, - Materialized = 497, - Maxvalue = 498, - Method = 499, - MinuteP = 500, - Minvalue = 501, - Mode = 502, - MonthP = 503, - Move = 504, - NameP = 505, - Names = 506, - National = 507, - Natural = 508, - Nchar = 509, - New = 510, - Next = 511, - Nfc = 512, - Nfd = 513, - Nfkc = 514, - Nfkd = 515, - No = 516, - None = 517, - Normalize = 518, - Normalized = 519, - Not = 520, - Nothing = 521, - Notify = 522, - Notnull = 523, - Nowait = 524, - NullP = 525, - Nullif = 526, - NullsP = 527, - Numeric = 528, - ObjectP = 529, - Of = 530, - Off = 531, - Offset = 532, - Oids = 533, - Old = 534, - On = 535, - Only = 536, - Operator = 537, - Option = 538, - Options = 539, - Or = 540, - Order = 541, - Ordinality = 542, - Others = 543, - OutP = 544, - OuterP = 545, - Over = 546, - Overlaps = 547, - Overlay = 548, - Overriding = 549, - Owned = 550, - Owner = 551, - Parallel = 552, - Parser = 553, - Partial = 554, - Partition = 555, - Passing = 556, - Password = 557, - Placing = 558, - Plans = 559, - Policy = 560, - Position = 561, - Preceding = 562, - Precision = 563, - Preserve = 564, - Prepare = 565, - Prepared = 566, - Primary = 567, - Prior = 568, - Privileges = 569, - Procedural = 570, - Procedure = 571, - Procedures = 572, - Program = 573, - Publication = 574, - Quote = 575, - Range = 576, - Read = 577, - Real = 578, - Reassign = 579, - Recheck = 580, - Recursive = 581, - Ref = 582, - References = 583, - Referencing = 584, - Refresh = 585, - Reindex = 586, - RelativeP = 587, - Release = 588, - Rename = 589, - Repeatable = 590, - Replace = 591, - Replica = 592, - Reset = 593, - Restart = 594, - Restrict = 595, - Returning = 596, - Returns = 597, - Revoke = 598, - Right = 599, - Role = 600, - Rollback = 601, - Rollup = 602, - Routine = 603, - Routines = 604, - Row = 605, - Rows = 606, - Rule = 607, - Savepoint = 608, - Schema = 609, - Schemas = 610, - Scroll = 611, - Search = 612, - SecondP = 613, - Security = 614, - Select = 615, - Sequence = 616, - Sequences = 617, - Serializable = 618, - Server = 619, - Session = 620, - SessionUser = 621, - Set = 622, - Sets = 623, - Setof = 624, - Share = 625, - Show = 626, - Similar = 627, - Simple = 628, - Skip = 629, - Smallint = 630, - Snapshot = 631, - Some = 632, - SqlP = 633, - Stable = 634, - StandaloneP = 635, - Start = 636, - Statement = 637, - Statistics = 638, - Stdin = 639, - Stdout = 640, - Storage = 641, - Stored = 642, - StrictP = 643, - StripP = 644, - Subscription = 645, - Substring = 646, - Support = 647, - Symmetric = 648, - Sysid = 649, - SystemP = 650, - Table = 651, - Tables = 652, - Tablesample = 653, - Tablespace = 654, - Temp = 655, - Template = 656, - Temporary = 657, - TextP = 658, - Then = 659, - Ties = 660, - Time = 661, - Timestamp = 662, - To = 663, - Trailing = 664, - Transaction = 665, - Transform = 666, - Treat = 667, - Trigger = 668, - Trim = 669, - TrueP = 670, - Truncate = 671, - Trusted = 672, - TypeP = 673, - TypesP = 674, - Uescape = 675, - Unbounded = 676, - Uncommitted = 677, - Unencrypted = 678, - Union = 679, - Unique = 680, - Unknown = 681, - Unlisten = 682, - Unlogged = 683, - Until = 684, - Update = 685, - User = 686, - Using = 687, - Vacuum = 688, - Valid = 689, - Validate = 690, - Validator = 691, - ValueP = 692, - Values = 693, - Varchar = 694, - Variadic = 695, - Varying = 696, - Verbose = 697, - VersionP = 698, - View = 699, - Views = 700, - Volatile = 701, - When = 702, - Where = 703, - WhitespaceP = 704, - Window = 705, - With = 706, - Within = 707, - Without = 708, - Work = 709, - Wrapper = 710, - Write = 711, - XmlP = 712, - Xmlattributes = 713, - Xmlconcat = 714, - Xmlelement = 715, - Xmlexists = 716, - Xmlforest = 717, - Xmlnamespaces = 718, - Xmlparse = 719, - Xmlpi = 720, - Xmlroot = 721, - Xmlserialize = 722, - Xmltable = 723, - YearP = 724, - YesP = 725, - Zone = 726, - NotLa = 727, - NullsLa = 728, - WithLa = 729, - Postfixop = 730, - Uminus = 731, -} From a18709156818a1205e9b5f89192808b651c6c59d Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Wed, 22 Jun 2022 13:40:10 -0400 Subject: [PATCH 208/401] Add idempotency test for issue 5399 --- tests/target/issue_5399.rs | 48 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 tests/target/issue_5399.rs diff --git a/tests/target/issue_5399.rs b/tests/target/issue_5399.rs new file mode 100644 index 000000000000..17364c38919a --- /dev/null +++ b/tests/target/issue_5399.rs @@ -0,0 +1,48 @@ +// rustfmt-max_width: 140 + +impl NotificationRepository { + fn set_status_changed( + &self, + repo_tx_conn: &RepoTxConn, + rid: &RoutableId, + changed_at: NaiveDateTime, + ) -> NukeResult> { + repo_tx_conn.run(move |conn| { + let res = diesel::update(client_notification::table) + .filter( + client_notification::routable_id.eq(DieselRoutableId(rid.clone())).and( + client_notification::changed_at + .lt(changed_at) + .or(client_notification::changed_at.is_null()), + ), + ) + .set(client_notification::changed_at.eq(changed_at)) + .returning(( + client_notification::id, + client_notification::changed_at, + client_notification::polled_at, + client_notification::notified_at, + )) + .get_result::<(Uuid, Option, Option, Option)>(conn) + .optional()?; + + match res { + Some(row) => { + let client_id = client_contract::table + .inner_join(client_notification::table) + .filter(client_notification::id.eq(row.0)) + .select(client_contract::client_id) + .get_result::(conn)?; + + Ok(Some(NotificationStatus { + client_id: client_id.into(), + changed_at: row.1, + polled_at: row.2, + notified_at: row.3, + })) + } + None => Ok(None), + } + }) + } +} From 08105e80b7c48782cfd7acbf3695c7f14c839cfd Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Tue, 21 Jun 2022 18:55:24 -0400 Subject: [PATCH 209/401] Fix issue where `cargo fmt --version` would not display version info Fixes 5395 In PR 5239 we switched from using `structopt` to `clap`. It seems that the default behavior for `clap` is to override the `--version` flag, which prevented our custom version display code from running. The fix as outlined in https://github.com/clap-rs/clap/issues/3405 was to set `#[clap(global_setting(AppSettings::NoAutoVersion))]` to prevent clap from setting its own default behavior for the `--version` flag. --- src/cargo-fmt/main.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cargo-fmt/main.rs b/src/cargo-fmt/main.rs index 55fd75f6de9b..9031d29b45f7 100644 --- a/src/cargo-fmt/main.rs +++ b/src/cargo-fmt/main.rs @@ -14,7 +14,7 @@ use std::path::{Path, PathBuf}; use std::process::Command; use std::str; -use clap::{CommandFactory, Parser}; +use clap::{AppSettings, CommandFactory, Parser}; #[path = "test/mod.rs"] #[cfg(test)] @@ -22,6 +22,7 @@ mod cargo_fmt_tests; #[derive(Parser)] #[clap( + global_setting(AppSettings::NoAutoVersion), bin_name = "cargo fmt", about = "This utility formats all bin and lib files of \ the current crate using rustfmt." From 778e1b1a76d4c6f6d7b56d4613db4a94e2bc434d Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Wed, 22 Jun 2022 19:29:38 -0500 Subject: [PATCH 210/401] Revert "Memoize format_expr" This reverts commit a37d3ab0e1c7c05f1a6410fb7ddf5539f0d030f8. --- src/expr.rs | 51 +- src/formatting.rs | 2 - src/rewrite.rs | 13 - src/shape.rs | 4 +- src/visitor.rs | 7 +- tests/source/performance/issue-4476.rs | 638 --- tests/source/performance/issue-5128.rs | 5127 ------------------------ tests/target/performance/issue-4476.rs | 705 ---- tests/target/performance/issue-4867.rs | 13 - tests/target/performance/issue-5128.rs | 4898 ---------------------- 10 files changed, 4 insertions(+), 11454 deletions(-) delete mode 100644 tests/source/performance/issue-4476.rs delete mode 100644 tests/source/performance/issue-5128.rs delete mode 100644 tests/target/performance/issue-4476.rs delete mode 100644 tests/target/performance/issue-4867.rs delete mode 100644 tests/target/performance/issue-5128.rs diff --git a/src/expr.rs b/src/expr.rs index 4ccf1ca70c9d..e4cc93026f10 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -1,6 +1,5 @@ use std::borrow::Cow; use std::cmp::min; -use std::collections::HashMap; use itertools::Itertools; use rustc_ast::token::{Delimiter, LitKind}; @@ -23,7 +22,7 @@ use crate::macros::{rewrite_macro, MacroPosition}; use crate::matches::rewrite_match; use crate::overflow::{self, IntoOverflowableItem, OverflowableItem}; use crate::pairs::{rewrite_all_pairs, rewrite_pair, PairParts}; -use crate::rewrite::{QueryId, Rewrite, RewriteContext}; +use crate::rewrite::{Rewrite, RewriteContext}; use crate::shape::{Indent, Shape}; use crate::source_map::{LineRangeUtils, SpanUtils}; use crate::spanned::Spanned; @@ -54,54 +53,6 @@ pub(crate) fn format_expr( expr_type: ExprType, context: &RewriteContext<'_>, shape: Shape, -) -> Option { - // when max_width is tight, we should check all possible formattings, in order to find - // if we can fit expression in the limit. Doing it recursively takes exponential time - // relative to input size, and people hit it with rustfmt takes minutes in #4476 #4867 #5128 - // By memoization of format_expr function, we format each pair of expression and shape - // only once, so worst case execution time becomes O(n*max_width^3). - if context.inside_macro() || context.is_macro_def { - // span ids are not unique in macros, so we don't memoize result of them. - return format_expr_inner(expr, expr_type, context, shape); - } - let clean; - let query_id = QueryId { - shape, - span: expr.span, - }; - if let Some(map) = context.memoize.take() { - if let Some(r) = map.get(&query_id) { - let r = r.clone(); - context.memoize.set(Some(map)); // restore map in the memoize cell for other users - return r; - } - context.memoize.set(Some(map)); - clean = false; - } else { - context.memoize.set(Some(HashMap::default())); - clean = true; // We got None, so we are the top level called function. When - // this function finishes, no one is interested in what is in the map, because - // all of them are sub expressions of this top level expression, and this is - // done. So we should clean up memoize map to save some memory. - } - - let r = format_expr_inner(expr, expr_type, context, shape); - if clean { - context.memoize.set(None); - } else { - if let Some(mut map) = context.memoize.take() { - map.insert(query_id, r.clone()); // insert the result in the memoize map - context.memoize.set(Some(map)); // so it won't be computed again - } - } - r -} - -fn format_expr_inner( - expr: &ast::Expr, - expr_type: ExprType, - context: &RewriteContext<'_>, - shape: Shape, ) -> Option { skip_out_of_file_lines_range!(context, expr.span); diff --git a/src/formatting.rs b/src/formatting.rs index e644ea50effd..1dfd8a514f0b 100644 --- a/src/formatting.rs +++ b/src/formatting.rs @@ -2,7 +2,6 @@ use std::collections::HashMap; use std::io::{self, Write}; -use std::rc::Rc; use std::time::{Duration, Instant}; use rustc_ast::ast; @@ -203,7 +202,6 @@ impl<'a, T: FormatHandler + 'a> FormatContext<'a, T> { self.config, &snippet_provider, self.report.clone(), - Rc::default(), ); visitor.skip_context.update_with_attrs(&self.krate.attrs); visitor.is_macro_def = is_macro_def; diff --git a/src/rewrite.rs b/src/rewrite.rs index f97df70cc6a7..4a3bd129d16f 100644 --- a/src/rewrite.rs +++ b/src/rewrite.rs @@ -12,7 +12,6 @@ use crate::shape::Shape; use crate::skip::SkipContext; use crate::visitor::SnippetProvider; use crate::FormatReport; -use rustc_data_structures::stable_map::FxHashMap; pub(crate) trait Rewrite { /// Rewrite self into shape. @@ -25,22 +24,10 @@ impl Rewrite for ptr::P { } } -#[derive(Clone, PartialEq, Eq, Hash)] -pub(crate) struct QueryId { - pub(crate) shape: Shape, - pub(crate) span: Span, -} - -// We use Option instead of HashMap, because in case of `None` -// the function clean the memoize map, but it doesn't clean when -// there is `Some(empty)`, so they are different. -pub(crate) type Memoize = Rc>>>>; - #[derive(Clone)] pub(crate) struct RewriteContext<'a> { pub(crate) parse_sess: &'a ParseSess, pub(crate) config: &'a Config, - pub(crate) memoize: Memoize, pub(crate) inside_macro: Rc>, // Force block indent style even if we are using visual indent style. pub(crate) use_block: Cell, diff --git a/src/shape.rs b/src/shape.rs index b3f785a9470e..4376fd12b526 100644 --- a/src/shape.rs +++ b/src/shape.rs @@ -4,7 +4,7 @@ use std::ops::{Add, Sub}; use crate::Config; -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, Debug)] pub(crate) struct Indent { // Width of the block indent, in characters. Must be a multiple of // Config::tab_spaces. @@ -139,7 +139,7 @@ impl Sub for Indent { // 8096 is close enough to infinite for rustfmt. const INFINITE_SHAPE_WIDTH: usize = 8096; -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, Debug)] pub(crate) struct Shape { pub(crate) width: usize, // The current indentation of code. diff --git a/src/visitor.rs b/src/visitor.rs index 3ff56d52f92d..9a0e0752c12f 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -17,7 +17,7 @@ use crate::items::{ use crate::macros::{macro_style, rewrite_macro, rewrite_macro_def, MacroPosition}; use crate::modules::Module; use crate::parse::session::ParseSess; -use crate::rewrite::{Memoize, Rewrite, RewriteContext}; +use crate::rewrite::{Rewrite, RewriteContext}; use crate::shape::{Indent, Shape}; use crate::skip::{is_skip_attr, SkipContext}; use crate::source_map::{LineRangeUtils, SpanUtils}; @@ -71,7 +71,6 @@ impl SnippetProvider { pub(crate) struct FmtVisitor<'a> { parent_context: Option<&'a RewriteContext<'a>>, - pub(crate) memoize: Memoize, pub(crate) parse_sess: &'a ParseSess, pub(crate) buffer: String, pub(crate) last_pos: BytePos, @@ -759,7 +758,6 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { ctx.config, ctx.snippet_provider, ctx.report.clone(), - ctx.memoize.clone(), ); visitor.skip_context.update(ctx.skip_context.clone()); visitor.set_parent_context(ctx); @@ -771,12 +769,10 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { config: &'a Config, snippet_provider: &'a SnippetProvider, report: FormatReport, - memoize: Memoize, ) -> FmtVisitor<'a> { FmtVisitor { parent_context: None, parse_sess: parse_session, - memoize, buffer: String::with_capacity(snippet_provider.big_snippet.len() * 2), last_pos: BytePos(0), block_indent: Indent::empty(), @@ -999,7 +995,6 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { RewriteContext { parse_sess: self.parse_sess, config: self.config, - memoize: self.memoize.clone(), inside_macro: Rc::new(Cell::new(false)), use_block: Cell::new(false), is_if_else_block: Cell::new(false), diff --git a/tests/source/performance/issue-4476.rs b/tests/source/performance/issue-4476.rs deleted file mode 100644 index 8da3f19b62d6..000000000000 --- a/tests/source/performance/issue-4476.rs +++ /dev/null @@ -1,638 +0,0 @@ -use super::SemverParser; - -#[allow(dead_code, non_camel_case_types)] -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub enum Rule { - EOI, - range_set, - logical_or, - range, - empty, - hyphen, - simple, - primitive, - primitive_op, - partial, - xr, - xr_op, - nr, - tilde, - caret, - qualifier, - parts, - part, - space, -} -#[allow(clippy::all)] -impl ::pest::Parser for SemverParser { - fn parse<'i>( - rule: Rule, - input: &'i str, - ) -> ::std::result::Result<::pest::iterators::Pairs<'i, Rule>, ::pest::error::Error> { - mod rules { - pub mod hidden { - use super::super::Rule; - #[inline] - #[allow(dead_code, non_snake_case, unused_variables)] - pub fn skip( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - Ok(state) - } - } - pub mod visible { - use super::super::Rule; - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn range_set( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::range_set, |state| { - state.sequence(|state| { - self::SOI(state) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - self::space(state).and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state).and_then(|state| self::space(state)) - }) - }) - }) - }) - }) - }) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::range(state)) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - state - .sequence(|state| { - self::logical_or(state) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::range(state)) - }) - .and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state).and_then(|state| { - state.sequence(|state| { - self::logical_or(state) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::range(state)) - }) - }) - }) - }) - }) - }) - }) - }) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - self::space(state).and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state).and_then(|state| self::space(state)) - }) - }) - }) - }) - }) - }) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::EOI(state)) - }) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn logical_or( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::logical_or, |state| { - state.sequence(|state| { - state - .sequence(|state| { - state.optional(|state| { - self::space(state).and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state).and_then(|state| self::space(state)) - }) - }) - }) - }) - }) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| state.match_string("||")) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - self::space(state).and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state).and_then(|state| self::space(state)) - }) - }) - }) - }) - }) - }) - }) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn range( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::range, |state| { - self::hyphen(state) - .or_else(|state| { - state.sequence(|state| { - self::simple(state) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - state - .sequence(|state| { - state - .optional(|state| state.match_string(",")) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - self::space(state) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - self::space(state).and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state) - .and_then(|state| self::space(state)) - }) - }) - }) - }) - }) - }) - }) - }) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::simple(state)) - }) - .and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state).and_then(|state| { - state.sequence(|state| { - state - .optional(|state| state.match_string(",")) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - self::space(state) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - self::space(state).and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state) - .and_then(|state| self::space(state)) - }) - }) - }) - }) - }) - }) - }) - }) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::simple(state)) - }) - }) - }) - }) - }) - }) - }) - }) - }) - }) - .or_else(|state| self::empty(state)) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn empty( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::empty, |state| state.match_string("")) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn hyphen( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::hyphen, |state| { - state.sequence(|state| { - self::partial(state) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - self::space(state) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - self::space(state).and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state).and_then(|state| self::space(state)) - }) - }) - }) - }) - }) - }) - }) - }) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| state.match_string("-")) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - self::space(state) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - self::space(state).and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state).and_then(|state| self::space(state)) - }) - }) - }) - }) - }) - }) - }) - }) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::partial(state)) - }) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn simple( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::simple, |state| { - self::primitive(state) - .or_else(|state| self::partial(state)) - .or_else(|state| self::tilde(state)) - .or_else(|state| self::caret(state)) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn primitive( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::primitive, |state| { - state.sequence(|state| { - self::primitive_op(state) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - self::space(state).and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state).and_then(|state| self::space(state)) - }) - }) - }) - }) - }) - }) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::partial(state)) - }) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn primitive_op( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::primitive_op, |state| { - state - .match_string("<=") - .or_else(|state| state.match_string(">=")) - .or_else(|state| state.match_string(">")) - .or_else(|state| state.match_string("<")) - .or_else(|state| state.match_string("=")) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn partial( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::partial, |state| { - state.sequence(|state| { - self::xr(state) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.optional(|state| { - state.sequence(|state| { - state - .match_string(".") - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::xr(state)) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.optional(|state| { - state.sequence(|state| { - state - .match_string(".") - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::xr(state)) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| state.optional(|state| self::qualifier(state))) - }) - }) - }) - }) - }) - }) - }) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn xr( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::xr, |state| { - self::xr_op(state).or_else(|state| self::nr(state)) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn xr_op( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::xr_op, |state| { - state - .match_string("x") - .or_else(|state| state.match_string("X")) - .or_else(|state| state.match_string("*")) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn nr( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::nr, |state| { - state.match_string("0").or_else(|state| { - state.sequence(|state| { - state - .match_range('1'..'9') - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - state.match_range('0'..'9').and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state) - .and_then(|state| state.match_range('0'..'9')) - }) - }) - }) - }) - }) - }) - }) - }) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn tilde( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::tilde, |state| { - state.sequence(|state| { - state - .match_string("~>") - .or_else(|state| state.match_string("~")) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - self::space(state).and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state).and_then(|state| self::space(state)) - }) - }) - }) - }) - }) - }) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::partial(state)) - }) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn caret( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::caret, |state| { - state.sequence(|state| { - state - .match_string("^") - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - self::space(state).and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state).and_then(|state| self::space(state)) - }) - }) - }) - }) - }) - }) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::partial(state)) - }) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn qualifier( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::qualifier, |state| { - state.sequence(|state| { - state - .match_string("-") - .or_else(|state| state.match_string("+")) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::parts(state)) - }) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn parts( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::parts, |state| { - state.sequence(|state| { - self::part(state) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - state - .sequence(|state| { - state - .match_string(".") - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::part(state)) - }) - .and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state).and_then(|state| { - state.sequence(|state| { - state - .match_string(".") - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::part(state)) - }) - }) - }) - }) - }) - }) - }) - }) - }) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn part( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::part, |state| { - self::nr(state).or_else(|state| { - state.sequence(|state| { - state - .match_string("-") - .or_else(|state| state.match_range('0'..'9')) - .or_else(|state| state.match_range('A'..'Z')) - .or_else(|state| state.match_range('a'..'z')) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - state - .match_string("-") - .or_else(|state| state.match_range('0'..'9')) - .or_else(|state| state.match_range('A'..'Z')) - .or_else(|state| state.match_range('a'..'z')) - .and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state).and_then(|state| { - state - .match_string("-") - .or_else(|state| state.match_range('0'..'9')) - .or_else(|state| state.match_range('A'..'Z')) - .or_else(|state| state.match_range('a'..'z')) - }) - }) - }) - }) - }) - }) - }) - }) - }) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn space( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state - .match_string(" ") - .or_else(|state| state.match_string("\t")) - } - #[inline] - #[allow(dead_code, non_snake_case, unused_variables)] - pub fn EOI( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::EOI, |state| state.end_of_input()) - } - #[inline] - #[allow(dead_code, non_snake_case, unused_variables)] - pub fn SOI( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.start_of_input() - } - } - pub use self::visible::*; - } - ::pest::state(input, |state| match rule { - Rule::range_set => rules::range_set(state), - Rule::logical_or => rules::logical_or(state), - Rule::range => rules::range(state), - Rule::empty => rules::empty(state), - Rule::hyphen => rules::hyphen(state), - Rule::simple => rules::simple(state), - Rule::primitive => rules::primitive(state), - Rule::primitive_op => rules::primitive_op(state), - Rule::partial => rules::partial(state), - Rule::xr => rules::xr(state), - Rule::xr_op => rules::xr_op(state), - Rule::nr => rules::nr(state), - Rule::tilde => rules::tilde(state), - Rule::caret => rules::caret(state), - Rule::qualifier => rules::qualifier(state), - Rule::parts => rules::parts(state), - Rule::part => rules::part(state), - Rule::space => rules::space(state), - Rule::EOI => rules::EOI(state), - }) - } -} \ No newline at end of file diff --git a/tests/source/performance/issue-5128.rs b/tests/source/performance/issue-5128.rs deleted file mode 100644 index 3adce49601c0..000000000000 --- a/tests/source/performance/issue-5128.rs +++ /dev/null @@ -1,5127 +0,0 @@ - -fn takes_a_long_time_to_rustfmt() { - let inner_cte = vec![Node { - node: Some(node::Node::CommonTableExpr(Box::new(CommonTableExpr { - ctename: String::from("ranked_by_age_within_key"), - aliascolnames: vec![], - ctematerialized: CteMaterialize::Default as i32, - ctequery: Some(Box::new(Node { - node: Some(node::Node::SelectStmt(Box::new(SelectStmt { - distinct_clause: vec![], - into_clause: None, - target_list: vec![ - Node { - node: Some(node::Node::ResTarget(Box::new(ResTarget { - name: String::from(""), - indirection: vec![], - val: Some(Box::new(Node { - node: Some(node::Node::ColumnRef(ColumnRef { - fields: vec![Node { - node: Some(node::Node::AStar(AStar{})) - }], - location: 80 - })) - })), - location: 80 - }))) - }, - Node { - node: Some(node::Node::ResTarget(Box::new(ResTarget { - name: String::from("rank_in_key"), - indirection: vec![], - val: Some(Box::new(Node { - node: Some(node::Node::FuncCall(Box::new(FuncCall { - funcname: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("row_number") - })) - }], - args: vec![], - agg_order: vec![], - agg_filter: None, - agg_within_group: false, - agg_star: false, - agg_distinct: false, - func_variadic: false, - over: Some(Box::new(WindowDef { - name: String::from(""), - refname: String::from(""), - partition_clause: vec![ - Node { - node: Some(node::Node::ColumnRef(ColumnRef { - fields: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("synthetic_key") - })) - }], location: 123 - })) - }], order_clause: vec![Node { - node: Some(node::Node::SortBy(Box::new(SortBy { - node: Some(Box::new(Node { - node: Some(node::Node::ColumnRef(ColumnRef { - fields: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("logical_timestamp") - })) - }], location: 156 - })) - })), - sortby_dir: SortByDir::SortbyDesc as i32, - sortby_nulls: SortByNulls::SortbyNullsDefault as i32, - use_op: vec![], - location: -1 - }))) - }], frame_options: 1058, start_offset: None, end_offset: None, location: 109 - })), - location: 91 - }))) - })), - location: 91 - }))) - }], - from_clause: vec![Node { - node: Some(node::Node::RangeVar(RangeVar { - catalogname: String::from(""), schemaname: String::from("_supertables"), relname: String::from("9999-9999-9999"), inh: true, relpersistence: String::from("p"), alias: None, location: 206 - })) - }], - where_clause: Some(Box::new(Node { - node: Some(node::Node::AExpr(Box::new(AExpr { - kind: AExprKind::AexprOp as i32, - name: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("<=") - })) - }], - lexpr: Some(Box::new(Node { - node: Some(node::Node::ColumnRef(ColumnRef { - fields: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("logical_timestamp") - })) - }], - location: 250 - })) - })), - rexpr: Some(Box::new(Node { - node: Some(node::Node::AConst(Box::new(AConst { - val: Some(Box::new(Node { - node: Some(node::Node::Integer(Integer { - ival: 9000 - })) - })), - location: 271 - }))) - })), - location: 268 - }))) - })), - group_clause: vec![], - having_clause: None, - window_clause: vec![], - values_lists: vec![], - sort_clause: vec![], - limit_offset: None, - limit_count: None, - limit_option: LimitOption::Default as i32, - locking_clause: vec![], - with_clause: None, - op: SetOperation::SetopNone as i32, - all: false, - larg: None, - rarg: None - }))), - })), - location: 29, - cterecursive: false, - cterefcount: 0, - ctecolnames: vec![], - ctecoltypes: vec![], - ctecoltypmods: vec![], - ctecolcollations: vec![], - }))), - }]; - let outer_cte = vec![Node { - node: Some(node::Node::CommonTableExpr(Box::new(CommonTableExpr { - ctename: String::from("table_name"), - aliascolnames: vec![], - ctematerialized: CteMaterialize::Default as i32, - ctequery: Some(Box::new(Node { - node: Some(node::Node::SelectStmt(Box::new(SelectStmt { - distinct_clause: vec![], - into_clause: None, - target_list: vec![ - Node { - node: Some(node::Node::ResTarget(Box::new(ResTarget { - name: String::from("column1"), - indirection: vec![], - val: Some(Box::new(Node { - node: Some(node::Node::ColumnRef(ColumnRef { - fields: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("c1"), - })), - }], - location: 301, - })), - })), - location: 301, - }))), - }, - Node { - node: Some(node::Node::ResTarget(Box::new(ResTarget { - name: String::from("column2"), - indirection: vec![], - val: Some(Box::new(Node { - node: Some(node::Node::ColumnRef(ColumnRef { - fields: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("c2"), - })), - }], - location: 324, - })), - })), - location: 324, - }))), - }, - ], - from_clause: vec![Node { - node: Some(node::Node::RangeVar(RangeVar { - catalogname: String::from(""), - schemaname: String::from(""), - relname: String::from("ranked_by_age_within_key"), - inh: true, - relpersistence: String::from("p"), - alias: None, - location: 347, - })), - }], - where_clause: Some(Box::new(Node { - node: Some(node::Node::BoolExpr(Box::new(BoolExpr { - xpr: None, - boolop: BoolExprType::AndExpr as i32, - args: vec![ - Node { - node: Some(node::Node::AExpr(Box::new(AExpr { - kind: AExprKind::AexprOp as i32, - name: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("="), - })), - }], - lexpr: Some(Box::new(Node { - node: Some(node::Node::ColumnRef(ColumnRef { - fields: vec![Node { - node: Some(node::Node::String( - String2 { - str: String::from("rank_in_key"), - }, - )), - }], - location: 382, - })), - })), - rexpr: Some(Box::new(Node { - node: Some(node::Node::AConst(Box::new(AConst { - val: Some(Box::new(Node { - node: Some(node::Node::Integer( - Integer { ival: 1 }, - )), - })), - location: 396, - }))), - })), - location: 394, - }))), - }, - Node { - node: Some(node::Node::AExpr(Box::new(AExpr { - kind: AExprKind::AexprOp as i32, - name: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("="), - })), - }], - lexpr: Some(Box::new(Node { - node: Some(node::Node::ColumnRef(ColumnRef { - fields: vec![Node { - node: Some(node::Node::String( - String2 { - str: String::from("is_deleted"), - }, - )), - }], - location: 402, - })), - })), - rexpr: Some(Box::new(Node { - node: Some(node::Node::TypeCast(Box::new( - TypeCast { - arg: Some(Box::new(Node { - node: Some(node::Node::AConst( - Box::new(AConst { - val: Some(Box::new(Node { - node: Some( - node::Node::String( - String2 { - str: - String::from( - "f", - ), - }, - ), - ), - })), - location: 415, - }), - )), - })), - type_name: Some(TypeName { - names: vec![ - Node { - node: Some(node::Node::String( - String2 { - str: String::from( - "pg_catalog", - ), - }, - )), - }, - Node { - node: Some(node::Node::String( - String2 { - str: String::from( - "bool", - ), - }, - )), - }, - ], - type_oid: 0, - setof: false, - pct_type: false, - typmods: vec![], - typemod: -1, - array_bounds: vec![], - location: -1, - }), - location: -1, - }, - ))), - })), - location: 413, - }))), - }, - ], - location: 398, - }))), - })), - group_clause: vec![], - having_clause: None, - window_clause: vec![], - values_lists: vec![], - sort_clause: vec![], - limit_offset: None, - limit_count: None, - limit_option: LimitOption::Default as i32, - locking_clause: vec![], - with_clause: Some(WithClause { - ctes: inner_cte, - recursive: false, - location: 24, - }), - op: SetOperation::SetopNone as i32, - all: false, - larg: None, - rarg: None, - }))), - })), - location: 5, - cterecursive: false, - cterefcount: 0, - ctecolnames: vec![], - ctecoltypes: vec![], - ctecoltypmods: vec![], - ctecolcollations: vec![], - }))), - }]; - let expected_result = ParseResult { - version: 130003, - stmts: vec![RawStmt { - stmt: Some(Box::new(Node { - node: Some(node::Node::SelectStmt(Box::new(SelectStmt { - distinct_clause: vec![], - into_clause: None, - - target_list: vec![Node { - node: Some(node::Node::ResTarget(Box::new(ResTarget { - name: String::from(""), - indirection: vec![], - val: Some(Box::new(Node { - node: Some(node::Node::ColumnRef(ColumnRef { - fields: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("column1"), - })), - }], - location: 430, - })), - })), - location: 430, - }))), - }], - from_clause: vec![Node { - node: Some(node::Node::RangeVar(RangeVar { - catalogname: String::from(""), - schemaname: String::from(""), - relname: String::from("table_name"), - inh: true, - relpersistence: String::from("p"), - alias: None, - location: 443, - })), - }], - where_clause: Some(Box::new(Node { - node: Some(node::Node::AExpr(Box::new(AExpr { - kind: AExprKind::AexprOp as i32, - name: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from(">"), - })), - }], - lexpr: Some(Box::new(Node { - node: Some(node::Node::ColumnRef(ColumnRef { - fields: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("column2"), - })), - }], - location: 460, - })), - })), - rexpr: Some(Box::new(Node { - node: Some(node::Node::AConst(Box::new(AConst { - val: Some(Box::new(Node { - node: Some(node::Node::Integer(Integer { - ival: 9000, - })), - })), - location: 470, - }))), - })), - location: 468, - }))), - })), - group_clause: vec![], - having_clause: None, - window_clause: vec![], - values_lists: vec![], - sort_clause: vec![], - limit_offset: None, - limit_count: None, - limit_option: LimitOption::Default as i32, - locking_clause: vec![], - with_clause: Some(WithClause { - ctes: outer_cte, - recursive: false, - location: 0, - }), - op: SetOperation::SetopNone as i32, - all: false, - larg: None, - rarg: None, - }))), - })), - stmt_location: 0, - stmt_len: 0, - }], - }; - -} -#[derive(Clone, PartialEq)] -pub struct ParseResult { - - pub version: i32, - - pub stmts: Vec, -} -#[derive(Clone, PartialEq)] -pub struct ScanResult { - - pub version: i32, - - pub tokens: Vec, -} -#[derive(Clone, PartialEq)] -pub struct Node { - pub node: ::core::option::Option, -} -/// Nested message and enum types in `Node`. -pub mod node { - #[derive(Clone, PartialEq)] - pub enum Node { - - Alias(super::Alias), - - RangeVar(super::RangeVar), - - TableFunc(Box), - - Expr(super::Expr), - - Var(Box), - - Param(Box), - - Aggref(Box), - - GroupingFunc(Box), - - WindowFunc(Box), - - SubscriptingRef(Box), - - FuncExpr(Box), - - NamedArgExpr(Box), - - OpExpr(Box), - - DistinctExpr(Box), - - NullIfExpr(Box), - - ScalarArrayOpExpr(Box), - - BoolExpr(Box), - - SubLink(Box), - - SubPlan(Box), - - AlternativeSubPlan(Box), - - FieldSelect(Box), - - FieldStore(Box), - - RelabelType(Box), - - CoerceViaIo(Box), - - ArrayCoerceExpr(Box), - - ConvertRowtypeExpr(Box), - - CollateExpr(Box), - - CaseExpr(Box), - - CaseWhen(Box), - - CaseTestExpr(Box), - - ArrayExpr(Box), - - RowExpr(Box), - - RowCompareExpr(Box), - - CoalesceExpr(Box), - - MinMaxExpr(Box), - - SqlvalueFunction(Box), - - XmlExpr(Box), - - NullTest(Box), - - BooleanTest(Box), - - CoerceToDomain(Box), - - CoerceToDomainValue(Box), - - SetToDefault(Box), - - CurrentOfExpr(Box), - - NextValueExpr(Box), - - InferenceElem(Box), - - TargetEntry(Box), - - RangeTblRef(super::RangeTblRef), - - JoinExpr(Box), - - FromExpr(Box), - - OnConflictExpr(Box), - - IntoClause(Box), - - RawStmt(Box), - - Query(Box), - - InsertStmt(Box), - - DeleteStmt(Box), - - UpdateStmt(Box), - - SelectStmt(Box), - - AlterTableStmt(super::AlterTableStmt), - - AlterTableCmd(Box), - - AlterDomainStmt(Box), - - SetOperationStmt(Box), - - GrantStmt(super::GrantStmt), - - GrantRoleStmt(super::GrantRoleStmt), - - AlterDefaultPrivilegesStmt(super::AlterDefaultPrivilegesStmt), - - ClosePortalStmt(super::ClosePortalStmt), - - ClusterStmt(super::ClusterStmt), - - CopyStmt(Box), - - CreateStmt(super::CreateStmt), - - DefineStmt(super::DefineStmt), - - DropStmt(super::DropStmt), - - TruncateStmt(super::TruncateStmt), - - CommentStmt(Box), - - FetchStmt(super::FetchStmt), - - IndexStmt(Box), - - CreateFunctionStmt(super::CreateFunctionStmt), - - AlterFunctionStmt(super::AlterFunctionStmt), - - DoStmt(super::DoStmt), - - RenameStmt(Box), - - RuleStmt(Box), - - NotifyStmt(super::NotifyStmt), - - ListenStmt(super::ListenStmt), - - UnlistenStmt(super::UnlistenStmt), - - TransactionStmt(super::TransactionStmt), - - ViewStmt(Box), - - LoadStmt(super::LoadStmt), - - CreateDomainStmt(Box), - - CreatedbStmt(super::CreatedbStmt), - - DropdbStmt(super::DropdbStmt), - - VacuumStmt(super::VacuumStmt), - - ExplainStmt(Box), - - CreateTableAsStmt(Box), - - CreateSeqStmt(super::CreateSeqStmt), - - AlterSeqStmt(super::AlterSeqStmt), - - VariableSetStmt(super::VariableSetStmt), - - VariableShowStmt(super::VariableShowStmt), - - DiscardStmt(super::DiscardStmt), - - CreateTrigStmt(Box), - - CreatePlangStmt(super::CreatePLangStmt), - - CreateRoleStmt(super::CreateRoleStmt), - - AlterRoleStmt(super::AlterRoleStmt), - - DropRoleStmt(super::DropRoleStmt), - - LockStmt(super::LockStmt), - - ConstraintsSetStmt(super::ConstraintsSetStmt), - - ReindexStmt(super::ReindexStmt), - - CheckPointStmt(super::CheckPointStmt), - - CreateSchemaStmt(super::CreateSchemaStmt), - - AlterDatabaseStmt(super::AlterDatabaseStmt), - - AlterDatabaseSetStmt(super::AlterDatabaseSetStmt), - - AlterRoleSetStmt(super::AlterRoleSetStmt), - - CreateConversionStmt(super::CreateConversionStmt), - - CreateCastStmt(super::CreateCastStmt), - - CreateOpClassStmt(super::CreateOpClassStmt), - - CreateOpFamilyStmt(super::CreateOpFamilyStmt), - - AlterOpFamilyStmt(super::AlterOpFamilyStmt), - - PrepareStmt(Box), - - ExecuteStmt(super::ExecuteStmt), - - DeallocateStmt(super::DeallocateStmt), - - DeclareCursorStmt(Box), - - CreateTableSpaceStmt(super::CreateTableSpaceStmt), - - DropTableSpaceStmt(super::DropTableSpaceStmt), - - AlterObjectDependsStmt(Box), - - AlterObjectSchemaStmt(Box), - - AlterOwnerStmt(Box), - - AlterOperatorStmt(super::AlterOperatorStmt), - - AlterTypeStmt(super::AlterTypeStmt), - - DropOwnedStmt(super::DropOwnedStmt), - - ReassignOwnedStmt(super::ReassignOwnedStmt), - - CompositeTypeStmt(super::CompositeTypeStmt), - - CreateEnumStmt(super::CreateEnumStmt), - - CreateRangeStmt(super::CreateRangeStmt), - - AlterEnumStmt(super::AlterEnumStmt), - - AlterTsdictionaryStmt(super::AlterTsDictionaryStmt), - - AlterTsconfigurationStmt(super::AlterTsConfigurationStmt), - - CreateFdwStmt(super::CreateFdwStmt), - - AlterFdwStmt(super::AlterFdwStmt), - - CreateForeignServerStmt(super::CreateForeignServerStmt), - - AlterForeignServerStmt(super::AlterForeignServerStmt), - - CreateUserMappingStmt(super::CreateUserMappingStmt), - - AlterUserMappingStmt(super::AlterUserMappingStmt), - - DropUserMappingStmt(super::DropUserMappingStmt), - - AlterTableSpaceOptionsStmt(super::AlterTableSpaceOptionsStmt), - - AlterTableMoveAllStmt(super::AlterTableMoveAllStmt), - - SecLabelStmt(Box), - - CreateForeignTableStmt(super::CreateForeignTableStmt), - - ImportForeignSchemaStmt(super::ImportForeignSchemaStmt), - - CreateExtensionStmt(super::CreateExtensionStmt), - - AlterExtensionStmt(super::AlterExtensionStmt), - - AlterExtensionContentsStmt(Box), - - CreateEventTrigStmt(super::CreateEventTrigStmt), - - AlterEventTrigStmt(super::AlterEventTrigStmt), - - RefreshMatViewStmt(super::RefreshMatViewStmt), - - ReplicaIdentityStmt(super::ReplicaIdentityStmt), - - AlterSystemStmt(super::AlterSystemStmt), - - CreatePolicyStmt(Box), - - AlterPolicyStmt(Box), - - CreateTransformStmt(super::CreateTransformStmt), - - CreateAmStmt(super::CreateAmStmt), - - CreatePublicationStmt(super::CreatePublicationStmt), - - AlterPublicationStmt(super::AlterPublicationStmt), - - CreateSubscriptionStmt(super::CreateSubscriptionStmt), - - AlterSubscriptionStmt(super::AlterSubscriptionStmt), - - DropSubscriptionStmt(super::DropSubscriptionStmt), - - CreateStatsStmt(super::CreateStatsStmt), - - AlterCollationStmt(super::AlterCollationStmt), - - CallStmt(Box), - - AlterStatsStmt(super::AlterStatsStmt), - - AExpr(Box), - - ColumnRef(super::ColumnRef), - - ParamRef(super::ParamRef), - - AConst(Box), - - FuncCall(Box), - - AStar(super::AStar), - - AIndices(Box), - - AIndirection(Box), - - AArrayExpr(super::AArrayExpr), - - ResTarget(Box), - - MultiAssignRef(Box), - - TypeCast(Box), - - CollateClause(Box), - - SortBy(Box), - - WindowDef(Box), - - RangeSubselect(Box), - - RangeFunction(super::RangeFunction), - - RangeTableSample(Box), - - RangeTableFunc(Box), - - RangeTableFuncCol(Box), - - TypeName(super::TypeName), - - ColumnDef(Box), - - IndexElem(Box), - - Constraint(Box), - - DefElem(Box), - - RangeTblEntry(Box), - - RangeTblFunction(Box), - - TableSampleClause(Box), - - WithCheckOption(Box), - - SortGroupClause(super::SortGroupClause), - - GroupingSet(super::GroupingSet), - - WindowClause(Box), - - ObjectWithArgs(super::ObjectWithArgs), - - AccessPriv(super::AccessPriv), - - CreateOpClassItem(super::CreateOpClassItem), - - TableLikeClause(super::TableLikeClause), - - FunctionParameter(Box), - - LockingClause(super::LockingClause), - - RowMarkClause(super::RowMarkClause), - - XmlSerialize(Box), - - WithClause(super::WithClause), - - InferClause(Box), - - OnConflictClause(Box), - - CommonTableExpr(Box), - - RoleSpec(super::RoleSpec), - - TriggerTransition(super::TriggerTransition), - - PartitionElem(Box), - - PartitionSpec(super::PartitionSpec), - - PartitionBoundSpec(super::PartitionBoundSpec), - - PartitionRangeDatum(Box), - - PartitionCmd(super::PartitionCmd), - - VacuumRelation(super::VacuumRelation), - - InlineCodeBlock(super::InlineCodeBlock), - - CallContext(super::CallContext), - - Integer(super::Integer), - - Float(super::Float), - - String(super::String2), - - BitString(super::BitString), - - Null(super::Null), - - List(super::List), - - IntList(super::IntList), - - OidList(super::OidList), - } -} -#[derive(Clone, PartialEq)] -pub struct Integer { - /// machine integer - - pub ival: i32, -} -#[derive(Clone, PartialEq)] -pub struct Float { - /// string - - pub str: String, -} -#[derive(Clone, PartialEq)] -pub struct String2 { - /// string - - pub str: String, -} -#[derive(Clone, PartialEq)] -pub struct BitString { - /// string - - pub str: String, -} -/// intentionally empty -#[derive(Clone, PartialEq)] -pub struct Null {} -#[derive(Clone, PartialEq)] -pub struct List { - - pub items: Vec, -} -#[derive(Clone, PartialEq)] -pub struct OidList { - - pub items: Vec, -} -#[derive(Clone, PartialEq)] -pub struct IntList { - - pub items: Vec, -} -#[derive(Clone, PartialEq)] -pub struct Alias { - - pub aliasname: String, - - pub colnames: Vec, -} -#[derive(Clone, PartialEq)] -pub struct RangeVar { - - pub catalogname: String, - - pub schemaname: String, - - pub relname: String, - - pub inh: bool, - - pub relpersistence: String, - - pub alias: ::core::option::Option, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct TableFunc { - - pub ns_uris: Vec, - - pub ns_names: Vec, - - pub docexpr: ::core::option::Option>, - - pub rowexpr: ::core::option::Option>, - - pub colnames: Vec, - - pub coltypes: Vec, - - pub coltypmods: Vec, - - pub colcollations: Vec, - - pub colexprs: Vec, - - pub coldefexprs: Vec, - - pub notnulls: Vec, - - pub ordinalitycol: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct Expr {} -#[derive(Clone, PartialEq)] -pub struct Var { - - pub xpr: ::core::option::Option>, - - pub varno: u32, - - pub varattno: i32, - - pub vartype: u32, - - pub vartypmod: i32, - - pub varcollid: u32, - - pub varlevelsup: u32, - - pub varnosyn: u32, - - pub varattnosyn: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct Param { - - pub xpr: ::core::option::Option>, - - pub paramkind: i32, - - pub paramid: i32, - - pub paramtype: u32, - - pub paramtypmod: i32, - - pub paramcollid: u32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct Aggref { - - pub xpr: ::core::option::Option>, - - pub aggfnoid: u32, - - pub aggtype: u32, - - pub aggcollid: u32, - - pub inputcollid: u32, - - pub aggtranstype: u32, - - pub aggargtypes: Vec, - - pub aggdirectargs: Vec, - - pub args: Vec, - - pub aggorder: Vec, - - pub aggdistinct: Vec, - - pub aggfilter: ::core::option::Option>, - - pub aggstar: bool, - - pub aggvariadic: bool, - - pub aggkind: String, - - pub agglevelsup: u32, - - pub aggsplit: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct GroupingFunc { - - pub xpr: ::core::option::Option>, - - pub args: Vec, - - pub refs: Vec, - - pub cols: Vec, - - pub agglevelsup: u32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct WindowFunc { - - pub xpr: ::core::option::Option>, - - pub winfnoid: u32, - - pub wintype: u32, - - pub wincollid: u32, - - pub inputcollid: u32, - - pub args: Vec, - - pub aggfilter: ::core::option::Option>, - - pub winref: u32, - - pub winstar: bool, - - pub winagg: bool, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct SubscriptingRef { - - pub xpr: ::core::option::Option>, - - pub refcontainertype: u32, - - pub refelemtype: u32, - - pub reftypmod: i32, - - pub refcollid: u32, - - pub refupperindexpr: Vec, - - pub reflowerindexpr: Vec, - - pub refexpr: ::core::option::Option>, - - pub refassgnexpr: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct FuncExpr { - - pub xpr: ::core::option::Option>, - - pub funcid: u32, - - pub funcresulttype: u32, - - pub funcretset: bool, - - pub funcvariadic: bool, - - pub funcformat: i32, - - pub funccollid: u32, - - pub inputcollid: u32, - - pub args: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct NamedArgExpr { - - pub xpr: ::core::option::Option>, - - pub arg: ::core::option::Option>, - - pub name: String, - - pub argnumber: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct OpExpr { - - pub xpr: ::core::option::Option>, - - pub opno: u32, - - pub opfuncid: u32, - - pub opresulttype: u32, - - pub opretset: bool, - - pub opcollid: u32, - - pub inputcollid: u32, - - pub args: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct DistinctExpr { - - pub xpr: ::core::option::Option>, - - pub opno: u32, - - pub opfuncid: u32, - - pub opresulttype: u32, - - pub opretset: bool, - - pub opcollid: u32, - - pub inputcollid: u32, - - pub args: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct NullIfExpr { - - pub xpr: ::core::option::Option>, - - pub opno: u32, - - pub opfuncid: u32, - - pub opresulttype: u32, - - pub opretset: bool, - - pub opcollid: u32, - - pub inputcollid: u32, - - pub args: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct ScalarArrayOpExpr { - - pub xpr: ::core::option::Option>, - - pub opno: u32, - - pub opfuncid: u32, - - pub use_or: bool, - - pub inputcollid: u32, - - pub args: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct BoolExpr { - - pub xpr: ::core::option::Option>, - - pub boolop: i32, - - pub args: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct SubLink { - - pub xpr: ::core::option::Option>, - - pub sub_link_type: i32, - - pub sub_link_id: i32, - - pub testexpr: ::core::option::Option>, - - pub oper_name: Vec, - - pub subselect: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct SubPlan { - - pub xpr: ::core::option::Option>, - - pub sub_link_type: i32, - - pub testexpr: ::core::option::Option>, - - pub param_ids: Vec, - - pub plan_id: i32, - - pub plan_name: String, - - pub first_col_type: u32, - - pub first_col_typmod: i32, - - pub first_col_collation: u32, - - pub use_hash_table: bool, - - pub unknown_eq_false: bool, - - pub parallel_safe: bool, - - pub set_param: Vec, - - pub par_param: Vec, - - pub args: Vec, - - pub startup_cost: f64, - - pub per_call_cost: f64, -} -#[derive(Clone, PartialEq)] -pub struct AlternativeSubPlan { - - pub xpr: ::core::option::Option>, - - pub subplans: Vec, -} -#[derive(Clone, PartialEq)] -pub struct FieldSelect { - - pub xpr: ::core::option::Option>, - - pub arg: ::core::option::Option>, - - pub fieldnum: i32, - - pub resulttype: u32, - - pub resulttypmod: i32, - - pub resultcollid: u32, -} -#[derive(Clone, PartialEq)] -pub struct FieldStore { - - pub xpr: ::core::option::Option>, - - pub arg: ::core::option::Option>, - - pub newvals: Vec, - - pub fieldnums: Vec, - - pub resulttype: u32, -} -#[derive(Clone, PartialEq)] -pub struct RelabelType { - - pub xpr: ::core::option::Option>, - - pub arg: ::core::option::Option>, - - pub resulttype: u32, - - pub resulttypmod: i32, - - pub resultcollid: u32, - - pub relabelformat: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct CoerceViaIo { - - pub xpr: ::core::option::Option>, - - pub arg: ::core::option::Option>, - - pub resulttype: u32, - - pub resultcollid: u32, - - pub coerceformat: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct ArrayCoerceExpr { - - pub xpr: ::core::option::Option>, - - pub arg: ::core::option::Option>, - - pub elemexpr: ::core::option::Option>, - - pub resulttype: u32, - - pub resulttypmod: i32, - - pub resultcollid: u32, - - pub coerceformat: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct ConvertRowtypeExpr { - - pub xpr: ::core::option::Option>, - - pub arg: ::core::option::Option>, - - pub resulttype: u32, - - pub convertformat: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct CollateExpr { - - pub xpr: ::core::option::Option>, - - pub arg: ::core::option::Option>, - - pub coll_oid: u32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct CaseExpr { - - pub xpr: ::core::option::Option>, - - pub casetype: u32, - - pub casecollid: u32, - - pub arg: ::core::option::Option>, - - pub args: Vec, - - pub defresult: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct CaseWhen { - - pub xpr: ::core::option::Option>, - - pub expr: ::core::option::Option>, - - pub result: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct CaseTestExpr { - - pub xpr: ::core::option::Option>, - - pub type_id: u32, - - pub type_mod: i32, - - pub collation: u32, -} -#[derive(Clone, PartialEq)] -pub struct ArrayExpr { - - pub xpr: ::core::option::Option>, - - pub array_typeid: u32, - - pub array_collid: u32, - - pub element_typeid: u32, - - pub elements: Vec, - - pub multidims: bool, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct RowExpr { - - pub xpr: ::core::option::Option>, - - pub args: Vec, - - pub row_typeid: u32, - - pub row_format: i32, - - pub colnames: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct RowCompareExpr { - - pub xpr: ::core::option::Option>, - - pub rctype: i32, - - pub opnos: Vec, - - pub opfamilies: Vec, - - pub inputcollids: Vec, - - pub largs: Vec, - - pub rargs: Vec, -} -#[derive(Clone, PartialEq)] -pub struct CoalesceExpr { - - pub xpr: ::core::option::Option>, - - pub coalescetype: u32, - - pub coalescecollid: u32, - - pub args: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct MinMaxExpr { - - pub xpr: ::core::option::Option>, - - pub minmaxtype: u32, - - pub minmaxcollid: u32, - - pub inputcollid: u32, - - pub op: i32, - - pub args: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct SqlValueFunction { - - pub xpr: ::core::option::Option>, - - pub op: i32, - - pub r#type: u32, - - pub typmod: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct XmlExpr { - - pub xpr: ::core::option::Option>, - - pub op: i32, - - pub name: String, - - pub named_args: Vec, - - pub arg_names: Vec, - - pub args: Vec, - - pub xmloption: i32, - - pub r#type: u32, - - pub typmod: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct NullTest { - - pub xpr: ::core::option::Option>, - - pub arg: ::core::option::Option>, - - pub nulltesttype: i32, - - pub argisrow: bool, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct BooleanTest { - - pub xpr: ::core::option::Option>, - - pub arg: ::core::option::Option>, - - pub booltesttype: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct CoerceToDomain { - - pub xpr: ::core::option::Option>, - - pub arg: ::core::option::Option>, - - pub resulttype: u32, - - pub resulttypmod: i32, - - pub resultcollid: u32, - - pub coercionformat: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct CoerceToDomainValue { - - pub xpr: ::core::option::Option>, - - pub type_id: u32, - - pub type_mod: i32, - - pub collation: u32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct SetToDefault { - - pub xpr: ::core::option::Option>, - - pub type_id: u32, - - pub type_mod: i32, - - pub collation: u32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct CurrentOfExpr { - - pub xpr: ::core::option::Option>, - - pub cvarno: u32, - - pub cursor_name: String, - - pub cursor_param: i32, -} -#[derive(Clone, PartialEq)] -pub struct NextValueExpr { - - pub xpr: ::core::option::Option>, - - pub seqid: u32, - - pub type_id: u32, -} -#[derive(Clone, PartialEq)] -pub struct InferenceElem { - - pub xpr: ::core::option::Option>, - - pub expr: ::core::option::Option>, - - pub infercollid: u32, - - pub inferopclass: u32, -} -#[derive(Clone, PartialEq)] -pub struct TargetEntry { - - pub xpr: ::core::option::Option>, - - pub expr: ::core::option::Option>, - - pub resno: i32, - - pub resname: String, - - pub ressortgroupref: u32, - - pub resorigtbl: u32, - - pub resorigcol: i32, - - pub resjunk: bool, -} -#[derive(Clone, PartialEq)] -pub struct RangeTblRef { - - pub rtindex: i32, -} -#[derive(Clone, PartialEq)] -pub struct JoinExpr { - - pub jointype: i32, - - pub is_natural: bool, - - pub larg: ::core::option::Option>, - - pub rarg: ::core::option::Option>, - - pub using_clause: Vec, - - pub quals: ::core::option::Option>, - - pub alias: ::core::option::Option, - - pub rtindex: i32, -} -#[derive(Clone, PartialEq)] -pub struct FromExpr { - - pub fromlist: Vec, - - pub quals: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct OnConflictExpr { - - pub action: i32, - - pub arbiter_elems: Vec, - - pub arbiter_where: ::core::option::Option>, - - pub constraint: u32, - - pub on_conflict_set: Vec, - - pub on_conflict_where: ::core::option::Option>, - - pub excl_rel_index: i32, - - pub excl_rel_tlist: Vec, -} -#[derive(Clone, PartialEq)] -pub struct IntoClause { - - pub rel: ::core::option::Option, - - pub col_names: Vec, - - pub access_method: String, - - pub options: Vec, - - pub on_commit: i32, - - pub table_space_name: String, - - pub view_query: ::core::option::Option>, - - pub skip_data: bool, -} -#[derive(Clone, PartialEq)] -pub struct RawStmt { - - pub stmt: ::core::option::Option>, - - pub stmt_location: i32, - - pub stmt_len: i32, -} -#[derive(Clone, PartialEq)] -pub struct Query { - - pub command_type: i32, - - pub query_source: i32, - - pub can_set_tag: bool, - - pub utility_stmt: ::core::option::Option>, - - pub result_relation: i32, - - pub has_aggs: bool, - - pub has_window_funcs: bool, - - pub has_target_srfs: bool, - - pub has_sub_links: bool, - - pub has_distinct_on: bool, - - pub has_recursive: bool, - - pub has_modifying_cte: bool, - - pub has_for_update: bool, - - pub has_row_security: bool, - - pub cte_list: Vec, - - pub rtable: Vec, - - pub jointree: ::core::option::Option>, - - pub target_list: Vec, - - pub r#override: i32, - - pub on_conflict: ::core::option::Option>, - - pub returning_list: Vec, - - pub group_clause: Vec, - - pub grouping_sets: Vec, - - pub having_qual: ::core::option::Option>, - - pub window_clause: Vec, - - pub distinct_clause: Vec, - - pub sort_clause: Vec, - - pub limit_offset: ::core::option::Option>, - - pub limit_count: ::core::option::Option>, - - pub limit_option: i32, - - pub row_marks: Vec, - - pub set_operations: ::core::option::Option>, - - pub constraint_deps: Vec, - - pub with_check_options: Vec, - - pub stmt_location: i32, - - pub stmt_len: i32, -} -#[derive(Clone, PartialEq)] -pub struct InsertStmt { - - pub relation: ::core::option::Option, - - pub cols: Vec, - - pub select_stmt: ::core::option::Option>, - - pub on_conflict_clause: ::core::option::Option>, - - pub returning_list: Vec, - - pub with_clause: ::core::option::Option, - - pub r#override: i32, -} -#[derive(Clone, PartialEq)] -pub struct DeleteStmt { - - pub relation: ::core::option::Option, - - pub using_clause: Vec, - - pub where_clause: ::core::option::Option>, - - pub returning_list: Vec, - - pub with_clause: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct UpdateStmt { - - pub relation: ::core::option::Option, - - pub target_list: Vec, - - pub where_clause: ::core::option::Option>, - - pub from_clause: Vec, - - pub returning_list: Vec, - - pub with_clause: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct SelectStmt { - - pub distinct_clause: Vec, - - pub into_clause: ::core::option::Option>, - - pub target_list: Vec, - - pub from_clause: Vec, - - pub where_clause: ::core::option::Option>, - - pub group_clause: Vec, - - pub having_clause: ::core::option::Option>, - - pub window_clause: Vec, - - pub values_lists: Vec, - - pub sort_clause: Vec, - - pub limit_offset: ::core::option::Option>, - - pub limit_count: ::core::option::Option>, - - pub limit_option: i32, - - pub locking_clause: Vec, - - pub with_clause: ::core::option::Option, - - pub op: i32, - - pub all: bool, - - pub larg: ::core::option::Option>, - - pub rarg: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct AlterTableStmt { - - pub relation: ::core::option::Option, - - pub cmds: Vec, - - pub relkind: i32, - - pub missing_ok: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterTableCmd { - - pub subtype: i32, - - pub name: String, - - pub num: i32, - - pub newowner: ::core::option::Option, - - pub def: ::core::option::Option>, - - pub behavior: i32, - - pub missing_ok: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterDomainStmt { - - pub subtype: String, - - pub type_name: Vec, - - pub name: String, - - pub def: ::core::option::Option>, - - pub behavior: i32, - - pub missing_ok: bool, -} -#[derive(Clone, PartialEq)] -pub struct SetOperationStmt { - - pub op: i32, - - pub all: bool, - - pub larg: ::core::option::Option>, - - pub rarg: ::core::option::Option>, - - pub col_types: Vec, - - pub col_typmods: Vec, - - pub col_collations: Vec, - - pub group_clauses: Vec, -} -#[derive(Clone, PartialEq)] -pub struct GrantStmt { - - pub is_grant: bool, - - pub targtype: i32, - - pub objtype: i32, - - pub objects: Vec, - - pub privileges: Vec, - - pub grantees: Vec, - - pub grant_option: bool, - - pub behavior: i32, -} -#[derive(Clone, PartialEq)] -pub struct GrantRoleStmt { - - pub granted_roles: Vec, - - pub grantee_roles: Vec, - - pub is_grant: bool, - - pub admin_opt: bool, - - pub grantor: ::core::option::Option, - - pub behavior: i32, -} -#[derive(Clone, PartialEq)] -pub struct AlterDefaultPrivilegesStmt { - - pub options: Vec, - - pub action: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct ClosePortalStmt { - - pub portalname: String, -} -#[derive(Clone, PartialEq)] -pub struct ClusterStmt { - - pub relation: ::core::option::Option, - - pub indexname: String, - - pub options: i32, -} -#[derive(Clone, PartialEq)] -pub struct CopyStmt { - - pub relation: ::core::option::Option, - - pub query: ::core::option::Option>, - - pub attlist: Vec, - - pub is_from: bool, - - pub is_program: bool, - - pub filename: String, - - pub options: Vec, - - pub where_clause: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct CreateStmt { - - pub relation: ::core::option::Option, - - pub table_elts: Vec, - - pub inh_relations: Vec, - - pub partbound: ::core::option::Option, - - pub partspec: ::core::option::Option, - - pub of_typename: ::core::option::Option, - - pub constraints: Vec, - - pub options: Vec, - - pub oncommit: i32, - - pub tablespacename: String, - - pub access_method: String, - - pub if_not_exists: bool, -} -#[derive(Clone, PartialEq)] -pub struct DefineStmt { - - pub kind: i32, - - pub oldstyle: bool, - - pub defnames: Vec, - - pub args: Vec, - - pub definition: Vec, - - pub if_not_exists: bool, - - pub replace: bool, -} -#[derive(Clone, PartialEq)] -pub struct DropStmt { - - pub objects: Vec, - - pub remove_type: i32, - - pub behavior: i32, - - pub missing_ok: bool, - - pub concurrent: bool, -} -#[derive(Clone, PartialEq)] -pub struct TruncateStmt { - - pub relations: Vec, - - pub restart_seqs: bool, - - pub behavior: i32, -} -#[derive(Clone, PartialEq)] -pub struct CommentStmt { - - pub objtype: i32, - - pub object: ::core::option::Option>, - - pub comment: String, -} -#[derive(Clone, PartialEq)] -pub struct FetchStmt { - - pub direction: i32, - - pub how_many: i64, - - pub portalname: String, - - pub ismove: bool, -} -#[derive(Clone, PartialEq)] -pub struct IndexStmt { - - pub idxname: String, - - pub relation: ::core::option::Option, - - pub access_method: String, - - pub table_space: String, - - pub index_params: Vec, - - pub index_including_params: Vec, - - pub options: Vec, - - pub where_clause: ::core::option::Option>, - - pub exclude_op_names: Vec, - - pub idxcomment: String, - - pub index_oid: u32, - - pub old_node: u32, - - pub old_create_subid: u32, - - pub old_first_relfilenode_subid: u32, - - pub unique: bool, - - pub primary: bool, - - pub isconstraint: bool, - - pub deferrable: bool, - - pub initdeferred: bool, - - pub transformed: bool, - - pub concurrent: bool, - - pub if_not_exists: bool, - - pub reset_default_tblspc: bool, -} -#[derive(Clone, PartialEq)] -pub struct CreateFunctionStmt { - - pub is_procedure: bool, - - pub replace: bool, - - pub funcname: Vec, - - pub parameters: Vec, - - pub return_type: ::core::option::Option, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterFunctionStmt { - - pub objtype: i32, - - pub func: ::core::option::Option, - - pub actions: Vec, -} -#[derive(Clone, PartialEq)] -pub struct DoStmt { - - pub args: Vec, -} -#[derive(Clone, PartialEq)] -pub struct RenameStmt { - - pub rename_type: i32, - - pub relation_type: i32, - - pub relation: ::core::option::Option, - - pub object: ::core::option::Option>, - - pub subname: String, - - pub newname: String, - - pub behavior: i32, - - pub missing_ok: bool, -} -#[derive(Clone, PartialEq)] -pub struct RuleStmt { - - pub relation: ::core::option::Option, - - pub rulename: String, - - pub where_clause: ::core::option::Option>, - - pub event: i32, - - pub instead: bool, - - pub actions: Vec, - - pub replace: bool, -} -#[derive(Clone, PartialEq)] -pub struct NotifyStmt { - - pub conditionname: String, - - pub payload: String, -} -#[derive(Clone, PartialEq)] -pub struct ListenStmt { - - pub conditionname: String, -} -#[derive(Clone, PartialEq)] -pub struct UnlistenStmt { - - pub conditionname: String, -} -#[derive(Clone, PartialEq)] -pub struct TransactionStmt { - - pub kind: i32, - - pub options: Vec, - - pub savepoint_name: String, - - pub gid: String, - - pub chain: bool, -} -#[derive(Clone, PartialEq)] -pub struct ViewStmt { - - pub view: ::core::option::Option, - - pub aliases: Vec, - - pub query: ::core::option::Option>, - - pub replace: bool, - - pub options: Vec, - - pub with_check_option: i32, -} -#[derive(Clone, PartialEq)] -pub struct LoadStmt { - - pub filename: String, -} -#[derive(Clone, PartialEq)] -pub struct CreateDomainStmt { - - pub domainname: Vec, - - pub type_name: ::core::option::Option, - - pub coll_clause: ::core::option::Option>, - - pub constraints: Vec, -} -#[derive(Clone, PartialEq)] -pub struct CreatedbStmt { - - pub dbname: String, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct DropdbStmt { - - pub dbname: String, - - pub missing_ok: bool, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct VacuumStmt { - - pub options: Vec, - - pub rels: Vec, - - pub is_vacuumcmd: bool, -} -#[derive(Clone, PartialEq)] -pub struct ExplainStmt { - - pub query: ::core::option::Option>, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct CreateTableAsStmt { - - pub query: ::core::option::Option>, - - pub into: ::core::option::Option>, - - pub relkind: i32, - - pub is_select_into: bool, - - pub if_not_exists: bool, -} -#[derive(Clone, PartialEq)] -pub struct CreateSeqStmt { - - pub sequence: ::core::option::Option, - - pub options: Vec, - - pub owner_id: u32, - - pub for_identity: bool, - - pub if_not_exists: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterSeqStmt { - - pub sequence: ::core::option::Option, - - pub options: Vec, - - pub for_identity: bool, - - pub missing_ok: bool, -} -#[derive(Clone, PartialEq)] -pub struct VariableSetStmt { - - pub kind: i32, - - pub name: String, - - pub args: Vec, - - pub is_local: bool, -} -#[derive(Clone, PartialEq)] -pub struct VariableShowStmt { - - pub name: String, -} -#[derive(Clone, PartialEq)] -pub struct DiscardStmt { - - pub target: i32, -} -#[derive(Clone, PartialEq)] -pub struct CreateTrigStmt { - - pub trigname: String, - - pub relation: ::core::option::Option, - - pub funcname: Vec, - - pub args: Vec, - - pub row: bool, - - pub timing: i32, - - pub events: i32, - - pub columns: Vec, - - pub when_clause: ::core::option::Option>, - - pub isconstraint: bool, - - pub transition_rels: Vec, - - pub deferrable: bool, - - pub initdeferred: bool, - - pub constrrel: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct CreatePLangStmt { - - pub replace: bool, - - pub plname: String, - - pub plhandler: Vec, - - pub plinline: Vec, - - pub plvalidator: Vec, - - pub pltrusted: bool, -} -#[derive(Clone, PartialEq)] -pub struct CreateRoleStmt { - - pub stmt_type: i32, - - pub role: String, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterRoleStmt { - - pub role: ::core::option::Option, - - pub options: Vec, - - pub action: i32, -} -#[derive(Clone, PartialEq)] -pub struct DropRoleStmt { - - pub roles: Vec, - - pub missing_ok: bool, -} -#[derive(Clone, PartialEq)] -pub struct LockStmt { - - pub relations: Vec, - - pub mode: i32, - - pub nowait: bool, -} -#[derive(Clone, PartialEq)] -pub struct ConstraintsSetStmt { - - pub constraints: Vec, - - pub deferred: bool, -} -#[derive(Clone, PartialEq)] -pub struct ReindexStmt { - - pub kind: i32, - - pub relation: ::core::option::Option, - - pub name: String, - - pub options: i32, - - pub concurrent: bool, -} -#[derive(Clone, PartialEq)] -pub struct CheckPointStmt {} -#[derive(Clone, PartialEq)] -pub struct CreateSchemaStmt { - - pub schemaname: String, - - pub authrole: ::core::option::Option, - - pub schema_elts: Vec, - - pub if_not_exists: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterDatabaseStmt { - - pub dbname: String, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterDatabaseSetStmt { - - pub dbname: String, - - pub setstmt: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct AlterRoleSetStmt { - - pub role: ::core::option::Option, - - pub database: String, - - pub setstmt: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct CreateConversionStmt { - - pub conversion_name: Vec, - - pub for_encoding_name: String, - - pub to_encoding_name: String, - - pub func_name: Vec, - - pub def: bool, -} -#[derive(Clone, PartialEq)] -pub struct CreateCastStmt { - - pub sourcetype: ::core::option::Option, - - pub targettype: ::core::option::Option, - - pub func: ::core::option::Option, - - pub context: i32, - - pub inout: bool, -} -#[derive(Clone, PartialEq)] -pub struct CreateOpClassStmt { - - pub opclassname: Vec, - - pub opfamilyname: Vec, - - pub amname: String, - - pub datatype: ::core::option::Option, - - pub items: Vec, - - pub is_default: bool, -} -#[derive(Clone, PartialEq)] -pub struct CreateOpFamilyStmt { - - pub opfamilyname: Vec, - - pub amname: String, -} -#[derive(Clone, PartialEq)] -pub struct AlterOpFamilyStmt { - - pub opfamilyname: Vec, - - pub amname: String, - - pub is_drop: bool, - - pub items: Vec, -} -#[derive(Clone, PartialEq)] -pub struct PrepareStmt { - - pub name: String, - - pub argtypes: Vec, - - pub query: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct ExecuteStmt { - - pub name: String, - - pub params: Vec, -} -#[derive(Clone, PartialEq)] -pub struct DeallocateStmt { - - pub name: String, -} -#[derive(Clone, PartialEq)] -pub struct DeclareCursorStmt { - - pub portalname: String, - - pub options: i32, - - pub query: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct CreateTableSpaceStmt { - - pub tablespacename: String, - - pub owner: ::core::option::Option, - - pub location: String, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct DropTableSpaceStmt { - - pub tablespacename: String, - - pub missing_ok: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterObjectDependsStmt { - - pub object_type: i32, - - pub relation: ::core::option::Option, - - pub object: ::core::option::Option>, - - pub extname: ::core::option::Option>, - - pub remove: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterObjectSchemaStmt { - - pub object_type: i32, - - pub relation: ::core::option::Option, - - pub object: ::core::option::Option>, - - pub newschema: String, - - pub missing_ok: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterOwnerStmt { - - pub object_type: i32, - - pub relation: ::core::option::Option, - - pub object: ::core::option::Option>, - - pub newowner: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct AlterOperatorStmt { - - pub opername: ::core::option::Option, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterTypeStmt { - - pub type_name: Vec, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct DropOwnedStmt { - - pub roles: Vec, - - pub behavior: i32, -} -#[derive(Clone, PartialEq)] -pub struct ReassignOwnedStmt { - - pub roles: Vec, - - pub newrole: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct CompositeTypeStmt { - - pub typevar: ::core::option::Option, - - pub coldeflist: Vec, -} -#[derive(Clone, PartialEq)] -pub struct CreateEnumStmt { - - pub type_name: Vec, - - pub vals: Vec, -} -#[derive(Clone, PartialEq)] -pub struct CreateRangeStmt { - - pub type_name: Vec, - - pub params: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterEnumStmt { - - pub type_name: Vec, - - pub old_val: String, - - pub new_val: String, - - pub new_val_neighbor: String, - - pub new_val_is_after: bool, - - pub skip_if_new_val_exists: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterTsDictionaryStmt { - - pub dictname: Vec, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterTsConfigurationStmt { - - pub kind: i32, - - pub cfgname: Vec, - - pub tokentype: Vec, - - pub dicts: Vec, - - pub r#override: bool, - - pub replace: bool, - - pub missing_ok: bool, -} -#[derive(Clone, PartialEq)] -pub struct CreateFdwStmt { - - pub fdwname: String, - - pub func_options: Vec, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterFdwStmt { - - pub fdwname: String, - - pub func_options: Vec, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct CreateForeignServerStmt { - - pub servername: String, - - pub servertype: String, - - pub version: String, - - pub fdwname: String, - - pub if_not_exists: bool, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterForeignServerStmt { - - pub servername: String, - - pub version: String, - - pub options: Vec, - - pub has_version: bool, -} -#[derive(Clone, PartialEq)] -pub struct CreateUserMappingStmt { - - pub user: ::core::option::Option, - - pub servername: String, - - pub if_not_exists: bool, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterUserMappingStmt { - - pub user: ::core::option::Option, - - pub servername: String, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct DropUserMappingStmt { - - pub user: ::core::option::Option, - - pub servername: String, - - pub missing_ok: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterTableSpaceOptionsStmt { - - pub tablespacename: String, - - pub options: Vec, - - pub is_reset: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterTableMoveAllStmt { - - pub orig_tablespacename: String, - - pub objtype: i32, - - pub roles: Vec, - - pub new_tablespacename: String, - - pub nowait: bool, -} -#[derive(Clone, PartialEq)] -pub struct SecLabelStmt { - - pub objtype: i32, - - pub object: ::core::option::Option>, - - pub provider: String, - - pub label: String, -} -#[derive(Clone, PartialEq)] -pub struct CreateForeignTableStmt { - - pub base_stmt: ::core::option::Option, - - pub servername: String, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct ImportForeignSchemaStmt { - - pub server_name: String, - - pub remote_schema: String, - - pub local_schema: String, - - pub list_type: i32, - - pub table_list: Vec, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct CreateExtensionStmt { - - pub extname: String, - - pub if_not_exists: bool, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterExtensionStmt { - - pub extname: String, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterExtensionContentsStmt { - - pub extname: String, - - pub action: i32, - - pub objtype: i32, - - pub object: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct CreateEventTrigStmt { - - pub trigname: String, - - pub eventname: String, - - pub whenclause: Vec, - - pub funcname: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterEventTrigStmt { - - pub trigname: String, - - pub tgenabled: String, -} -#[derive(Clone, PartialEq)] -pub struct RefreshMatViewStmt { - - pub concurrent: bool, - - pub skip_data: bool, - - pub relation: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct ReplicaIdentityStmt { - - pub identity_type: String, - - pub name: String, -} -#[derive(Clone, PartialEq)] -pub struct AlterSystemStmt { - - pub setstmt: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct CreatePolicyStmt { - - pub policy_name: String, - - pub table: ::core::option::Option, - - pub cmd_name: String, - - pub permissive: bool, - - pub roles: Vec, - - pub qual: ::core::option::Option>, - - pub with_check: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct AlterPolicyStmt { - - pub policy_name: String, - - pub table: ::core::option::Option, - - pub roles: Vec, - - pub qual: ::core::option::Option>, - - pub with_check: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct CreateTransformStmt { - - pub replace: bool, - - pub type_name: ::core::option::Option, - - pub lang: String, - - pub fromsql: ::core::option::Option, - - pub tosql: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct CreateAmStmt { - - pub amname: String, - - pub handler_name: Vec, - - pub amtype: String, -} -#[derive(Clone, PartialEq)] -pub struct CreatePublicationStmt { - - pub pubname: String, - - pub options: Vec, - - pub tables: Vec, - - pub for_all_tables: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterPublicationStmt { - - pub pubname: String, - - pub options: Vec, - - pub tables: Vec, - - pub for_all_tables: bool, - - pub table_action: i32, -} -#[derive(Clone, PartialEq)] -pub struct CreateSubscriptionStmt { - - pub subname: String, - - pub conninfo: String, - - pub publication: Vec, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterSubscriptionStmt { - - pub kind: i32, - - pub subname: String, - - pub conninfo: String, - - pub publication: Vec, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct DropSubscriptionStmt { - - pub subname: String, - - pub missing_ok: bool, - - pub behavior: i32, -} -#[derive(Clone, PartialEq)] -pub struct CreateStatsStmt { - - pub defnames: Vec, - - pub stat_types: Vec, - - pub exprs: Vec, - - pub relations: Vec, - - pub stxcomment: String, - - pub if_not_exists: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterCollationStmt { - - pub collname: Vec, -} -#[derive(Clone, PartialEq)] -pub struct CallStmt { - - pub funccall: ::core::option::Option>, - - pub funcexpr: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct AlterStatsStmt { - - pub defnames: Vec, - - pub stxstattarget: i32, - - pub missing_ok: bool, -} -#[derive(Clone, PartialEq)] -pub struct AExpr { - - pub kind: i32, - - pub name: Vec, - - pub lexpr: ::core::option::Option>, - - pub rexpr: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct ColumnRef { - - pub fields: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct ParamRef { - - pub number: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct AConst { - - pub val: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct FuncCall { - - pub funcname: Vec, - - pub args: Vec, - - pub agg_order: Vec, - - pub agg_filter: ::core::option::Option>, - - pub agg_within_group: bool, - - pub agg_star: bool, - - pub agg_distinct: bool, - - pub func_variadic: bool, - - pub over: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct AStar {} -#[derive(Clone, PartialEq)] -pub struct AIndices { - - pub is_slice: bool, - - pub lidx: ::core::option::Option>, - - pub uidx: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct AIndirection { - - pub arg: ::core::option::Option>, - - pub indirection: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AArrayExpr { - - pub elements: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct ResTarget { - - pub name: String, - - pub indirection: Vec, - - pub val: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct MultiAssignRef { - - pub source: ::core::option::Option>, - - pub colno: i32, - - pub ncolumns: i32, -} -#[derive(Clone, PartialEq)] -pub struct TypeCast { - - pub arg: ::core::option::Option>, - - pub type_name: ::core::option::Option, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct CollateClause { - - pub arg: ::core::option::Option>, - - pub collname: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct SortBy { - - pub node: ::core::option::Option>, - - pub sortby_dir: i32, - - pub sortby_nulls: i32, - - pub use_op: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct WindowDef { - - pub name: String, - - pub refname: String, - - pub partition_clause: Vec, - - pub order_clause: Vec, - - pub frame_options: i32, - - pub start_offset: ::core::option::Option>, - - pub end_offset: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct RangeSubselect { - - pub lateral: bool, - - pub subquery: ::core::option::Option>, - - pub alias: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct RangeFunction { - - pub lateral: bool, - - pub ordinality: bool, - - pub is_rowsfrom: bool, - - pub functions: Vec, - - pub alias: ::core::option::Option, - - pub coldeflist: Vec, -} -#[derive(Clone, PartialEq)] -pub struct RangeTableSample { - - pub relation: ::core::option::Option>, - - pub method: Vec, - - pub args: Vec, - - pub repeatable: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct RangeTableFunc { - - pub lateral: bool, - - pub docexpr: ::core::option::Option>, - - pub rowexpr: ::core::option::Option>, - - pub namespaces: Vec, - - pub columns: Vec, - - pub alias: ::core::option::Option, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct RangeTableFuncCol { - - pub colname: String, - - pub type_name: ::core::option::Option, - - pub for_ordinality: bool, - - pub is_not_null: bool, - - pub colexpr: ::core::option::Option>, - - pub coldefexpr: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct TypeName { - - pub names: Vec, - - pub type_oid: u32, - - pub setof: bool, - - pub pct_type: bool, - - pub typmods: Vec, - - pub typemod: i32, - - pub array_bounds: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct ColumnDef { - - pub colname: String, - - pub type_name: ::core::option::Option, - - pub inhcount: i32, - - pub is_local: bool, - - pub is_not_null: bool, - - pub is_from_type: bool, - - pub storage: String, - - pub raw_default: ::core::option::Option>, - - pub cooked_default: ::core::option::Option>, - - pub identity: String, - - pub identity_sequence: ::core::option::Option, - - pub generated: String, - - pub coll_clause: ::core::option::Option>, - - pub coll_oid: u32, - - pub constraints: Vec, - - pub fdwoptions: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct IndexElem { - - pub name: String, - - pub expr: ::core::option::Option>, - - pub indexcolname: String, - - pub collation: Vec, - - pub opclass: Vec, - - pub opclassopts: Vec, - - pub ordering: i32, - - pub nulls_ordering: i32, -} -#[derive(Clone, PartialEq)] -pub struct Constraint { - - pub contype: i32, - - pub conname: String, - - pub deferrable: bool, - - pub initdeferred: bool, - - pub location: i32, - - pub is_no_inherit: bool, - - pub raw_expr: ::core::option::Option>, - - pub cooked_expr: String, - - pub generated_when: String, - - pub keys: Vec, - - pub including: Vec, - - pub exclusions: Vec, - - pub options: Vec, - - pub indexname: String, - - pub indexspace: String, - - pub reset_default_tblspc: bool, - - pub access_method: String, - - pub where_clause: ::core::option::Option>, - - pub pktable: ::core::option::Option, - - pub fk_attrs: Vec, - - pub pk_attrs: Vec, - - pub fk_matchtype: String, - - pub fk_upd_action: String, - - pub fk_del_action: String, - - pub old_conpfeqop: Vec, - - pub old_pktable_oid: u32, - - pub skip_validation: bool, - - pub initially_valid: bool, -} -#[derive(Clone, PartialEq)] -pub struct DefElem { - - pub defnamespace: String, - - pub defname: String, - - pub arg: ::core::option::Option>, - - pub defaction: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct RangeTblEntry { - - pub rtekind: i32, - - pub relid: u32, - - pub relkind: String, - - pub rellockmode: i32, - - pub tablesample: ::core::option::Option>, - - pub subquery: ::core::option::Option>, - - pub security_barrier: bool, - - pub jointype: i32, - - pub joinmergedcols: i32, - - pub joinaliasvars: Vec, - - pub joinleftcols: Vec, - - pub joinrightcols: Vec, - - pub functions: Vec, - - pub funcordinality: bool, - - pub tablefunc: ::core::option::Option>, - - pub values_lists: Vec, - - pub ctename: String, - - pub ctelevelsup: u32, - - pub self_reference: bool, - - pub coltypes: Vec, - - pub coltypmods: Vec, - - pub colcollations: Vec, - - pub enrname: String, - - pub enrtuples: f64, - - pub alias: ::core::option::Option, - - pub eref: ::core::option::Option, - - pub lateral: bool, - - pub inh: bool, - - pub in_from_cl: bool, - - pub required_perms: u32, - - pub check_as_user: u32, - - pub selected_cols: Vec, - - pub inserted_cols: Vec, - - pub updated_cols: Vec, - - pub extra_updated_cols: Vec, - - pub security_quals: Vec, -} -#[derive(Clone, PartialEq)] -pub struct RangeTblFunction { - - pub funcexpr: ::core::option::Option>, - - pub funccolcount: i32, - - pub funccolnames: Vec, - - pub funccoltypes: Vec, - - pub funccoltypmods: Vec, - - pub funccolcollations: Vec, - - pub funcparams: Vec, -} -#[derive(Clone, PartialEq)] -pub struct TableSampleClause { - - pub tsmhandler: u32, - - pub args: Vec, - - pub repeatable: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct WithCheckOption { - - pub kind: i32, - - pub relname: String, - - pub polname: String, - - pub qual: ::core::option::Option>, - - pub cascaded: bool, -} -#[derive(Clone, PartialEq)] -pub struct SortGroupClause { - - pub tle_sort_group_ref: u32, - - pub eqop: u32, - - pub sortop: u32, - - pub nulls_first: bool, - - pub hashable: bool, -} -#[derive(Clone, PartialEq)] -pub struct GroupingSet { - - pub kind: i32, - - pub content: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct WindowClause { - - pub name: String, - - pub refname: String, - - pub partition_clause: Vec, - - pub order_clause: Vec, - - pub frame_options: i32, - - pub start_offset: ::core::option::Option>, - - pub end_offset: ::core::option::Option>, - - pub start_in_range_func: u32, - - pub end_in_range_func: u32, - - pub in_range_coll: u32, - - pub in_range_asc: bool, - - pub in_range_nulls_first: bool, - - pub winref: u32, - - pub copied_order: bool, -} -#[derive(Clone, PartialEq)] -pub struct ObjectWithArgs { - - pub objname: Vec, - - pub objargs: Vec, - - pub args_unspecified: bool, -} -#[derive(Clone, PartialEq)] -pub struct AccessPriv { - - pub priv_name: String, - - pub cols: Vec, -} -#[derive(Clone, PartialEq)] -pub struct CreateOpClassItem { - - pub itemtype: i32, - - pub name: ::core::option::Option, - - pub number: i32, - - pub order_family: Vec, - - pub class_args: Vec, - - pub storedtype: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct TableLikeClause { - - pub relation: ::core::option::Option, - - pub options: u32, - - pub relation_oid: u32, -} -#[derive(Clone, PartialEq)] -pub struct FunctionParameter { - - pub name: String, - - pub arg_type: ::core::option::Option, - - pub mode: i32, - - pub defexpr: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct LockingClause { - - pub locked_rels: Vec, - - pub strength: i32, - - pub wait_policy: i32, -} -#[derive(Clone, PartialEq)] -pub struct RowMarkClause { - - pub rti: u32, - - pub strength: i32, - - pub wait_policy: i32, - - pub pushed_down: bool, -} -#[derive(Clone, PartialEq)] -pub struct XmlSerialize { - - pub xmloption: i32, - - pub expr: ::core::option::Option>, - - pub type_name: ::core::option::Option, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct WithClause { - - pub ctes: Vec, - - pub recursive: bool, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct InferClause { - - pub index_elems: Vec, - - pub where_clause: ::core::option::Option>, - - pub conname: String, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct OnConflictClause { - - pub action: i32, - - pub infer: ::core::option::Option>, - - pub target_list: Vec, - - pub where_clause: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct CommonTableExpr { - - pub ctename: String, - - pub aliascolnames: Vec, - - pub ctematerialized: i32, - - pub ctequery: ::core::option::Option>, - - pub location: i32, - - pub cterecursive: bool, - - pub cterefcount: i32, - - pub ctecolnames: Vec, - - pub ctecoltypes: Vec, - - pub ctecoltypmods: Vec, - - pub ctecolcollations: Vec, -} -#[derive(Clone, PartialEq)] -pub struct RoleSpec { - - pub roletype: i32, - - pub rolename: String, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct TriggerTransition { - - pub name: String, - - pub is_new: bool, - - pub is_table: bool, -} -#[derive(Clone, PartialEq)] -pub struct PartitionElem { - - pub name: String, - - pub expr: ::core::option::Option>, - - pub collation: Vec, - - pub opclass: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct PartitionSpec { - - pub strategy: String, - - pub part_params: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct PartitionBoundSpec { - - pub strategy: String, - - pub is_default: bool, - - pub modulus: i32, - - pub remainder: i32, - - pub listdatums: Vec, - - pub lowerdatums: Vec, - - pub upperdatums: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct PartitionRangeDatum { - - pub kind: i32, - - pub value: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct PartitionCmd { - - pub name: ::core::option::Option, - - pub bound: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct VacuumRelation { - - pub relation: ::core::option::Option, - - pub oid: u32, - - pub va_cols: Vec, -} -#[derive(Clone, PartialEq)] -pub struct InlineCodeBlock { - - pub source_text: String, - - pub lang_oid: u32, - - pub lang_is_trusted: bool, - - pub atomic: bool, -} -#[derive(Clone, PartialEq)] -pub struct CallContext { - - pub atomic: bool, -} -#[derive(Clone, PartialEq)] -pub struct ScanToken { - - pub start: i32, - - pub end: i32, - - pub token: i32, - - pub keyword_kind: i32, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum OverridingKind { - Undefined = 0, - OverridingNotSet = 1, - OverridingUserValue = 2, - OverridingSystemValue = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum QuerySource { - Undefined = 0, - QsrcOriginal = 1, - QsrcParser = 2, - QsrcInsteadRule = 3, - QsrcQualInsteadRule = 4, - QsrcNonInsteadRule = 5, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum SortByDir { - Undefined = 0, - SortbyDefault = 1, - SortbyAsc = 2, - SortbyDesc = 3, - SortbyUsing = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum SortByNulls { - Undefined = 0, - SortbyNullsDefault = 1, - SortbyNullsFirst = 2, - SortbyNullsLast = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum AExprKind { - Undefined = 0, - AexprOp = 1, - AexprOpAny = 2, - AexprOpAll = 3, - AexprDistinct = 4, - AexprNotDistinct = 5, - AexprNullif = 6, - AexprOf = 7, - AexprIn = 8, - AexprLike = 9, - AexprIlike = 10, - AexprSimilar = 11, - AexprBetween = 12, - AexprNotBetween = 13, - AexprBetweenSym = 14, - AexprNotBetweenSym = 15, - AexprParen = 16, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum RoleSpecType { - Undefined = 0, - RolespecCstring = 1, - RolespecCurrentUser = 2, - RolespecSessionUser = 3, - RolespecPublic = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum TableLikeOption { - Undefined = 0, - CreateTableLikeComments = 1, - CreateTableLikeConstraints = 2, - CreateTableLikeDefaults = 3, - CreateTableLikeGenerated = 4, - CreateTableLikeIdentity = 5, - CreateTableLikeIndexes = 6, - CreateTableLikeStatistics = 7, - CreateTableLikeStorage = 8, - CreateTableLikeAll = 9, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum DefElemAction { - Undefined = 0, - DefelemUnspec = 1, - DefelemSet = 2, - DefelemAdd = 3, - DefelemDrop = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum PartitionRangeDatumKind { - Undefined = 0, - PartitionRangeDatumMinvalue = 1, - PartitionRangeDatumValue = 2, - PartitionRangeDatumMaxvalue = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum RteKind { - RtekindUndefined = 0, - RteRelation = 1, - RteSubquery = 2, - RteJoin = 3, - RteFunction = 4, - RteTablefunc = 5, - RteValues = 6, - RteCte = 7, - RteNamedtuplestore = 8, - RteResult = 9, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum WcoKind { - WcokindUndefined = 0, - WcoViewCheck = 1, - WcoRlsInsertCheck = 2, - WcoRlsUpdateCheck = 3, - WcoRlsConflictCheck = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum GroupingSetKind { - Undefined = 0, - GroupingSetEmpty = 1, - GroupingSetSimple = 2, - GroupingSetRollup = 3, - GroupingSetCube = 4, - GroupingSetSets = 5, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum CteMaterialize { - CtematerializeUndefined = 0, - Default = 1, - Always = 2, - Never = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum SetOperation { - Undefined = 0, - SetopNone = 1, - SetopUnion = 2, - SetopIntersect = 3, - SetopExcept = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum ObjectType { - Undefined = 0, - ObjectAccessMethod = 1, - ObjectAggregate = 2, - ObjectAmop = 3, - ObjectAmproc = 4, - ObjectAttribute = 5, - ObjectCast = 6, - ObjectColumn = 7, - ObjectCollation = 8, - ObjectConversion = 9, - ObjectDatabase = 10, - ObjectDefault = 11, - ObjectDefacl = 12, - ObjectDomain = 13, - ObjectDomconstraint = 14, - ObjectEventTrigger = 15, - ObjectExtension = 16, - ObjectFdw = 17, - ObjectForeignServer = 18, - ObjectForeignTable = 19, - ObjectFunction = 20, - ObjectIndex = 21, - ObjectLanguage = 22, - ObjectLargeobject = 23, - ObjectMatview = 24, - ObjectOpclass = 25, - ObjectOperator = 26, - ObjectOpfamily = 27, - ObjectPolicy = 28, - ObjectProcedure = 29, - ObjectPublication = 30, - ObjectPublicationRel = 31, - ObjectRole = 32, - ObjectRoutine = 33, - ObjectRule = 34, - ObjectSchema = 35, - ObjectSequence = 36, - ObjectSubscription = 37, - ObjectStatisticExt = 38, - ObjectTabconstraint = 39, - ObjectTable = 40, - ObjectTablespace = 41, - ObjectTransform = 42, - ObjectTrigger = 43, - ObjectTsconfiguration = 44, - ObjectTsdictionary = 45, - ObjectTsparser = 46, - ObjectTstemplate = 47, - ObjectType = 48, - ObjectUserMapping = 49, - ObjectView = 50, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum DropBehavior { - Undefined = 0, - DropRestrict = 1, - DropCascade = 2, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum AlterTableType { - Undefined = 0, - AtAddColumn = 1, - AtAddColumnRecurse = 2, - AtAddColumnToView = 3, - AtColumnDefault = 4, - AtCookedColumnDefault = 5, - AtDropNotNull = 6, - AtSetNotNull = 7, - AtDropExpression = 8, - AtCheckNotNull = 9, - AtSetStatistics = 10, - AtSetOptions = 11, - AtResetOptions = 12, - AtSetStorage = 13, - AtDropColumn = 14, - AtDropColumnRecurse = 15, - AtAddIndex = 16, - AtReAddIndex = 17, - AtAddConstraint = 18, - AtAddConstraintRecurse = 19, - AtReAddConstraint = 20, - AtReAddDomainConstraint = 21, - AtAlterConstraint = 22, - AtValidateConstraint = 23, - AtValidateConstraintRecurse = 24, - AtAddIndexConstraint = 25, - AtDropConstraint = 26, - AtDropConstraintRecurse = 27, - AtReAddComment = 28, - AtAlterColumnType = 29, - AtAlterColumnGenericOptions = 30, - AtChangeOwner = 31, - AtClusterOn = 32, - AtDropCluster = 33, - AtSetLogged = 34, - AtSetUnLogged = 35, - AtDropOids = 36, - AtSetTableSpace = 37, - AtSetRelOptions = 38, - AtResetRelOptions = 39, - AtReplaceRelOptions = 40, - AtEnableTrig = 41, - AtEnableAlwaysTrig = 42, - AtEnableReplicaTrig = 43, - AtDisableTrig = 44, - AtEnableTrigAll = 45, - AtDisableTrigAll = 46, - AtEnableTrigUser = 47, - AtDisableTrigUser = 48, - AtEnableRule = 49, - AtEnableAlwaysRule = 50, - AtEnableReplicaRule = 51, - AtDisableRule = 52, - AtAddInherit = 53, - AtDropInherit = 54, - AtAddOf = 55, - AtDropOf = 56, - AtReplicaIdentity = 57, - AtEnableRowSecurity = 58, - AtDisableRowSecurity = 59, - AtForceRowSecurity = 60, - AtNoForceRowSecurity = 61, - AtGenericOptions = 62, - AtAttachPartition = 63, - AtDetachPartition = 64, - AtAddIdentity = 65, - AtSetIdentity = 66, - AtDropIdentity = 67, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum GrantTargetType { - Undefined = 0, - AclTargetObject = 1, - AclTargetAllInSchema = 2, - AclTargetDefaults = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum VariableSetKind { - Undefined = 0, - VarSetValue = 1, - VarSetDefault = 2, - VarSetCurrent = 3, - VarSetMulti = 4, - VarReset = 5, - VarResetAll = 6, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum ConstrType { - Undefined = 0, - ConstrNull = 1, - ConstrNotnull = 2, - ConstrDefault = 3, - ConstrIdentity = 4, - ConstrGenerated = 5, - ConstrCheck = 6, - ConstrPrimary = 7, - ConstrUnique = 8, - ConstrExclusion = 9, - ConstrForeign = 10, - ConstrAttrDeferrable = 11, - ConstrAttrNotDeferrable = 12, - ConstrAttrDeferred = 13, - ConstrAttrImmediate = 14, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum ImportForeignSchemaType { - Undefined = 0, - FdwImportSchemaAll = 1, - FdwImportSchemaLimitTo = 2, - FdwImportSchemaExcept = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum RoleStmtType { - Undefined = 0, - RolestmtRole = 1, - RolestmtUser = 2, - RolestmtGroup = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum FetchDirection { - Undefined = 0, - FetchForward = 1, - FetchBackward = 2, - FetchAbsolute = 3, - FetchRelative = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum FunctionParameterMode { - Undefined = 0, - FuncParamIn = 1, - FuncParamOut = 2, - FuncParamInout = 3, - FuncParamVariadic = 4, - FuncParamTable = 5, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum TransactionStmtKind { - Undefined = 0, - TransStmtBegin = 1, - TransStmtStart = 2, - TransStmtCommit = 3, - TransStmtRollback = 4, - TransStmtSavepoint = 5, - TransStmtRelease = 6, - TransStmtRollbackTo = 7, - TransStmtPrepare = 8, - TransStmtCommitPrepared = 9, - TransStmtRollbackPrepared = 10, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum ViewCheckOption { - Undefined = 0, - NoCheckOption = 1, - LocalCheckOption = 2, - CascadedCheckOption = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum ClusterOption { - Undefined = 0, - CluoptRecheck = 1, - CluoptVerbose = 2, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum DiscardMode { - Undefined = 0, - DiscardAll = 1, - DiscardPlans = 2, - DiscardSequences = 3, - DiscardTemp = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum ReindexObjectType { - Undefined = 0, - ReindexObjectIndex = 1, - ReindexObjectTable = 2, - ReindexObjectSchema = 3, - ReindexObjectSystem = 4, - ReindexObjectDatabase = 5, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum AlterTsConfigType { - AlterTsconfigTypeUndefined = 0, - AlterTsconfigAddMapping = 1, - AlterTsconfigAlterMappingForToken = 2, - AlterTsconfigReplaceDict = 3, - AlterTsconfigReplaceDictForToken = 4, - AlterTsconfigDropMapping = 5, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum AlterSubscriptionType { - Undefined = 0, - AlterSubscriptionOptions = 1, - AlterSubscriptionConnection = 2, - AlterSubscriptionPublication = 3, - AlterSubscriptionRefresh = 4, - AlterSubscriptionEnabled = 5, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum OnCommitAction { - Undefined = 0, - OncommitNoop = 1, - OncommitPreserveRows = 2, - OncommitDeleteRows = 3, - OncommitDrop = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum ParamKind { - Undefined = 0, - ParamExtern = 1, - ParamExec = 2, - ParamSublink = 3, - ParamMultiexpr = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum CoercionContext { - Undefined = 0, - CoercionImplicit = 1, - CoercionAssignment = 2, - CoercionExplicit = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum CoercionForm { - Undefined = 0, - CoerceExplicitCall = 1, - CoerceExplicitCast = 2, - CoerceImplicitCast = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum BoolExprType { - Undefined = 0, - AndExpr = 1, - OrExpr = 2, - NotExpr = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum SubLinkType { - Undefined = 0, - ExistsSublink = 1, - AllSublink = 2, - AnySublink = 3, - RowcompareSublink = 4, - ExprSublink = 5, - MultiexprSublink = 6, - ArraySublink = 7, - CteSublink = 8, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum RowCompareType { - Undefined = 0, - RowcompareLt = 1, - RowcompareLe = 2, - RowcompareEq = 3, - RowcompareGe = 4, - RowcompareGt = 5, - RowcompareNe = 6, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum MinMaxOp { - Undefined = 0, - IsGreatest = 1, - IsLeast = 2, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum SqlValueFunctionOp { - SqlvalueFunctionOpUndefined = 0, - SvfopCurrentDate = 1, - SvfopCurrentTime = 2, - SvfopCurrentTimeN = 3, - SvfopCurrentTimestamp = 4, - SvfopCurrentTimestampN = 5, - SvfopLocaltime = 6, - SvfopLocaltimeN = 7, - SvfopLocaltimestamp = 8, - SvfopLocaltimestampN = 9, - SvfopCurrentRole = 10, - SvfopCurrentUser = 11, - SvfopUser = 12, - SvfopSessionUser = 13, - SvfopCurrentCatalog = 14, - SvfopCurrentSchema = 15, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum XmlExprOp { - Undefined = 0, - IsXmlconcat = 1, - IsXmlelement = 2, - IsXmlforest = 3, - IsXmlparse = 4, - IsXmlpi = 5, - IsXmlroot = 6, - IsXmlserialize = 7, - IsDocument = 8, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum XmlOptionType { - Undefined = 0, - XmloptionDocument = 1, - XmloptionContent = 2, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum NullTestType { - Undefined = 0, - IsNull = 1, - IsNotNull = 2, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum BoolTestType { - Undefined = 0, - IsTrue = 1, - IsNotTrue = 2, - IsFalse = 3, - IsNotFalse = 4, - IsUnknown = 5, - IsNotUnknown = 6, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum CmdType { - Undefined = 0, - CmdUnknown = 1, - CmdSelect = 2, - CmdUpdate = 3, - CmdInsert = 4, - CmdDelete = 5, - CmdUtility = 6, - CmdNothing = 7, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum JoinType { - Undefined = 0, - JoinInner = 1, - JoinLeft = 2, - JoinFull = 3, - JoinRight = 4, - JoinSemi = 5, - JoinAnti = 6, - JoinUniqueOuter = 7, - JoinUniqueInner = 8, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum AggStrategy { - Undefined = 0, - AggPlain = 1, - AggSorted = 2, - AggHashed = 3, - AggMixed = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum AggSplit { - Undefined = 0, - AggsplitSimple = 1, - AggsplitInitialSerial = 2, - AggsplitFinalDeserial = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum SetOpCmd { - Undefined = 0, - SetopcmdIntersect = 1, - SetopcmdIntersectAll = 2, - SetopcmdExcept = 3, - SetopcmdExceptAll = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum SetOpStrategy { - Undefined = 0, - SetopSorted = 1, - SetopHashed = 2, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum OnConflictAction { - Undefined = 0, - OnconflictNone = 1, - OnconflictNothing = 2, - OnconflictUpdate = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum LimitOption { - Undefined = 0, - Default = 1, - Count = 2, - WithTies = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum LockClauseStrength { - Undefined = 0, - LcsNone = 1, - LcsForkeyshare = 2, - LcsForshare = 3, - LcsFornokeyupdate = 4, - LcsForupdate = 5, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum LockWaitPolicy { - Undefined = 0, - LockWaitBlock = 1, - LockWaitSkip = 2, - LockWaitError = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum LockTupleMode { - Undefined = 0, - LockTupleKeyShare = 1, - LockTupleShare = 2, - LockTupleNoKeyExclusive = 3, - LockTupleExclusive = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum KeywordKind { - NoKeyword = 0, - UnreservedKeyword = 1, - ColNameKeyword = 2, - TypeFuncNameKeyword = 3, - ReservedKeyword = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum Token { - Nul = 0, - /// Single-character tokens that are returned 1:1 (identical with "self" list in scan.l) - /// Either supporting syntax, or single-character operators (some can be both) - /// Also see - /// - /// "%" - Ascii37 = 37, - /// "(" - Ascii40 = 40, - /// ")" - Ascii41 = 41, - /// "*" - Ascii42 = 42, - /// "+" - Ascii43 = 43, - /// "," - Ascii44 = 44, - /// "-" - Ascii45 = 45, - /// "." - Ascii46 = 46, - /// "/" - Ascii47 = 47, - /// ":" - Ascii58 = 58, - /// ";" - Ascii59 = 59, - /// "<" - Ascii60 = 60, - /// "=" - Ascii61 = 61, - /// ">" - Ascii62 = 62, - /// "?" - Ascii63 = 63, - /// "[" - Ascii91 = 91, - /// "\" - Ascii92 = 92, - /// "]" - Ascii93 = 93, - /// "^" - Ascii94 = 94, - /// Named tokens in scan.l - Ident = 258, - Uident = 259, - Fconst = 260, - Sconst = 261, - Usconst = 262, - Bconst = 263, - Xconst = 264, - Op = 265, - Iconst = 266, - Param = 267, - Typecast = 268, - DotDot = 269, - ColonEquals = 270, - EqualsGreater = 271, - LessEquals = 272, - GreaterEquals = 273, - NotEquals = 274, - SqlComment = 275, - CComment = 276, - AbortP = 277, - AbsoluteP = 278, - Access = 279, - Action = 280, - AddP = 281, - Admin = 282, - After = 283, - Aggregate = 284, - All = 285, - Also = 286, - Alter = 287, - Always = 288, - Analyse = 289, - Analyze = 290, - And = 291, - Any = 292, - Array = 293, - As = 294, - Asc = 295, - Assertion = 296, - Assignment = 297, - Asymmetric = 298, - At = 299, - Attach = 300, - Attribute = 301, - Authorization = 302, - Backward = 303, - Before = 304, - BeginP = 305, - Between = 306, - Bigint = 307, - Binary = 308, - Bit = 309, - BooleanP = 310, - Both = 311, - By = 312, - Cache = 313, - Call = 314, - Called = 315, - Cascade = 316, - Cascaded = 317, - Case = 318, - Cast = 319, - CatalogP = 320, - Chain = 321, - CharP = 322, - Character = 323, - Characteristics = 324, - Check = 325, - Checkpoint = 326, - Class = 327, - Close = 328, - Cluster = 329, - Coalesce = 330, - Collate = 331, - Collation = 332, - Column = 333, - Columns = 334, - Comment = 335, - Comments = 336, - Commit = 337, - Committed = 338, - Concurrently = 339, - Configuration = 340, - Conflict = 341, - Connection = 342, - Constraint = 343, - Constraints = 344, - ContentP = 345, - ContinueP = 346, - ConversionP = 347, - Copy = 348, - Cost = 349, - Create = 350, - Cross = 351, - Csv = 352, - Cube = 353, - CurrentP = 354, - CurrentCatalog = 355, - CurrentDate = 356, - CurrentRole = 357, - CurrentSchema = 358, - CurrentTime = 359, - CurrentTimestamp = 360, - CurrentUser = 361, - Cursor = 362, - Cycle = 363, - DataP = 364, - Database = 365, - DayP = 366, - Deallocate = 367, - Dec = 368, - DecimalP = 369, - Declare = 370, - Default = 371, - Defaults = 372, - Deferrable = 373, - Deferred = 374, - Definer = 375, - DeleteP = 376, - Delimiter = 377, - Delimiters = 378, - Depends = 379, - Desc = 380, - Detach = 381, - Dictionary = 382, - DisableP = 383, - Discard = 384, - Distinct = 385, - Do = 386, - DocumentP = 387, - DomainP = 388, - DoubleP = 389, - Drop = 390, - Each = 391, - Else = 392, - EnableP = 393, - Encoding = 394, - Encrypted = 395, - EndP = 396, - EnumP = 397, - Escape = 398, - Event = 399, - Except = 400, - Exclude = 401, - Excluding = 402, - Exclusive = 403, - Execute = 404, - Exists = 405, - Explain = 406, - Expression = 407, - Extension = 408, - External = 409, - Extract = 410, - FalseP = 411, - Family = 412, - Fetch = 413, - Filter = 414, - FirstP = 415, - FloatP = 416, - Following = 417, - For = 418, - Force = 419, - Foreign = 420, - Forward = 421, - Freeze = 422, - From = 423, - Full = 424, - Function = 425, - Functions = 426, - Generated = 427, - Global = 428, - Grant = 429, - Granted = 430, - Greatest = 431, - GroupP = 432, - Grouping = 433, - Groups = 434, - Handler = 435, - Having = 436, - HeaderP = 437, - Hold = 438, - HourP = 439, - IdentityP = 440, - IfP = 441, - Ilike = 442, - Immediate = 443, - Immutable = 444, - ImplicitP = 445, - ImportP = 446, - InP = 447, - Include = 448, - Including = 449, - Increment = 450, - Index = 451, - Indexes = 452, - Inherit = 453, - Inherits = 454, - Initially = 455, - InlineP = 456, - InnerP = 457, - Inout = 458, - InputP = 459, - Insensitive = 460, - Insert = 461, - Instead = 462, - IntP = 463, - Integer = 464, - Intersect = 465, - Interval = 466, - Into = 467, - Invoker = 468, - Is = 469, - Isnull = 470, - Isolation = 471, - Join = 472, - Key = 473, - Label = 474, - Language = 475, - LargeP = 476, - LastP = 477, - LateralP = 478, - Leading = 479, - Leakproof = 480, - Least = 481, - Left = 482, - Level = 483, - Like = 484, - Limit = 485, - Listen = 486, - Load = 487, - Local = 488, - Localtime = 489, - Localtimestamp = 490, - Location = 491, - LockP = 492, - Locked = 493, - Logged = 494, - Mapping = 495, - Match = 496, - Materialized = 497, - Maxvalue = 498, - Method = 499, - MinuteP = 500, - Minvalue = 501, - Mode = 502, - MonthP = 503, - Move = 504, - NameP = 505, - Names = 506, - National = 507, - Natural = 508, - Nchar = 509, - New = 510, - Next = 511, - Nfc = 512, - Nfd = 513, - Nfkc = 514, - Nfkd = 515, - No = 516, - None = 517, - Normalize = 518, - Normalized = 519, - Not = 520, - Nothing = 521, - Notify = 522, - Notnull = 523, - Nowait = 524, - NullP = 525, - Nullif = 526, - NullsP = 527, - Numeric = 528, - ObjectP = 529, - Of = 530, - Off = 531, - Offset = 532, - Oids = 533, - Old = 534, - On = 535, - Only = 536, - Operator = 537, - Option = 538, - Options = 539, - Or = 540, - Order = 541, - Ordinality = 542, - Others = 543, - OutP = 544, - OuterP = 545, - Over = 546, - Overlaps = 547, - Overlay = 548, - Overriding = 549, - Owned = 550, - Owner = 551, - Parallel = 552, - Parser = 553, - Partial = 554, - Partition = 555, - Passing = 556, - Password = 557, - Placing = 558, - Plans = 559, - Policy = 560, - Position = 561, - Preceding = 562, - Precision = 563, - Preserve = 564, - Prepare = 565, - Prepared = 566, - Primary = 567, - Prior = 568, - Privileges = 569, - Procedural = 570, - Procedure = 571, - Procedures = 572, - Program = 573, - Publication = 574, - Quote = 575, - Range = 576, - Read = 577, - Real = 578, - Reassign = 579, - Recheck = 580, - Recursive = 581, - Ref = 582, - References = 583, - Referencing = 584, - Refresh = 585, - Reindex = 586, - RelativeP = 587, - Release = 588, - Rename = 589, - Repeatable = 590, - Replace = 591, - Replica = 592, - Reset = 593, - Restart = 594, - Restrict = 595, - Returning = 596, - Returns = 597, - Revoke = 598, - Right = 599, - Role = 600, - Rollback = 601, - Rollup = 602, - Routine = 603, - Routines = 604, - Row = 605, - Rows = 606, - Rule = 607, - Savepoint = 608, - Schema = 609, - Schemas = 610, - Scroll = 611, - Search = 612, - SecondP = 613, - Security = 614, - Select = 615, - Sequence = 616, - Sequences = 617, - Serializable = 618, - Server = 619, - Session = 620, - SessionUser = 621, - Set = 622, - Sets = 623, - Setof = 624, - Share = 625, - Show = 626, - Similar = 627, - Simple = 628, - Skip = 629, - Smallint = 630, - Snapshot = 631, - Some = 632, - SqlP = 633, - Stable = 634, - StandaloneP = 635, - Start = 636, - Statement = 637, - Statistics = 638, - Stdin = 639, - Stdout = 640, - Storage = 641, - Stored = 642, - StrictP = 643, - StripP = 644, - Subscription = 645, - Substring = 646, - Support = 647, - Symmetric = 648, - Sysid = 649, - SystemP = 650, - Table = 651, - Tables = 652, - Tablesample = 653, - Tablespace = 654, - Temp = 655, - Template = 656, - Temporary = 657, - TextP = 658, - Then = 659, - Ties = 660, - Time = 661, - Timestamp = 662, - To = 663, - Trailing = 664, - Transaction = 665, - Transform = 666, - Treat = 667, - Trigger = 668, - Trim = 669, - TrueP = 670, - Truncate = 671, - Trusted = 672, - TypeP = 673, - TypesP = 674, - Uescape = 675, - Unbounded = 676, - Uncommitted = 677, - Unencrypted = 678, - Union = 679, - Unique = 680, - Unknown = 681, - Unlisten = 682, - Unlogged = 683, - Until = 684, - Update = 685, - User = 686, - Using = 687, - Vacuum = 688, - Valid = 689, - Validate = 690, - Validator = 691, - ValueP = 692, - Values = 693, - Varchar = 694, - Variadic = 695, - Varying = 696, - Verbose = 697, - VersionP = 698, - View = 699, - Views = 700, - Volatile = 701, - When = 702, - Where = 703, - WhitespaceP = 704, - Window = 705, - With = 706, - Within = 707, - Without = 708, - Work = 709, - Wrapper = 710, - Write = 711, - XmlP = 712, - Xmlattributes = 713, - Xmlconcat = 714, - Xmlelement = 715, - Xmlexists = 716, - Xmlforest = 717, - Xmlnamespaces = 718, - Xmlparse = 719, - Xmlpi = 720, - Xmlroot = 721, - Xmlserialize = 722, - Xmltable = 723, - YearP = 724, - YesP = 725, - Zone = 726, - NotLa = 727, - NullsLa = 728, - WithLa = 729, - Postfixop = 730, - Uminus = 731, -} diff --git a/tests/target/performance/issue-4476.rs b/tests/target/performance/issue-4476.rs deleted file mode 100644 index 30567f2644b7..000000000000 --- a/tests/target/performance/issue-4476.rs +++ /dev/null @@ -1,705 +0,0 @@ -use super::SemverParser; - -#[allow(dead_code, non_camel_case_types)] -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub enum Rule { - EOI, - range_set, - logical_or, - range, - empty, - hyphen, - simple, - primitive, - primitive_op, - partial, - xr, - xr_op, - nr, - tilde, - caret, - qualifier, - parts, - part, - space, -} -#[allow(clippy::all)] -impl ::pest::Parser for SemverParser { - fn parse<'i>( - rule: Rule, - input: &'i str, - ) -> ::std::result::Result<::pest::iterators::Pairs<'i, Rule>, ::pest::error::Error> { - mod rules { - pub mod hidden { - use super::super::Rule; - #[inline] - #[allow(dead_code, non_snake_case, unused_variables)] - pub fn skip( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - Ok(state) - } - } - pub mod visible { - use super::super::Rule; - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn range_set( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::range_set, |state| { - state.sequence(|state| { - self::SOI(state) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - self::space(state).and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state) - .and_then(|state| self::space(state)) - }) - }) - }) - }) - }) - }) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::range(state)) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - state - .sequence(|state| { - self::logical_or(state) - .and_then(|state| { - super::hidden::skip(state) - }) - .and_then(|state| self::range(state)) - }) - .and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state).and_then( - |state| { - state.sequence(|state| { - self::logical_or(state) - .and_then(|state| { - super::hidden::skip( - state, - ) - }) - .and_then(|state| { - self::range(state) - }) - }) - }, - ) - }) - }) - }) - }) - }) - }) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - self::space(state).and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state) - .and_then(|state| self::space(state)) - }) - }) - }) - }) - }) - }) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::EOI(state)) - }) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn logical_or( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::logical_or, |state| { - state.sequence(|state| { - state - .sequence(|state| { - state.optional(|state| { - self::space(state).and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state) - .and_then(|state| self::space(state)) - }) - }) - }) - }) - }) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| state.match_string("||")) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - self::space(state).and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state) - .and_then(|state| self::space(state)) - }) - }) - }) - }) - }) - }) - }) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn range( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::range, |state| { - self::hyphen(state) - .or_else(|state| { - state.sequence(|state| { - self::simple(state) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - state - .sequence(|state| { - state - .optional(|state| { - state.match_string(",") - }) - .and_then(|state| { - super::hidden::skip(state) - }) - .and_then(|state| { - state.sequence(|state| { - self::space(state) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - self::space(state).and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state) - .and_then(|state| self::space(state)) - }) - }) - }) - }) - }) - }) - }) - }) - .and_then(|state| { - super::hidden::skip(state) - }) - .and_then(|state| { - self::simple(state) - }) - }) - .and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state) - .and_then(|state| { - state.sequence( - |state| { - state - .optional(|state| state.match_string(",")) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - self::space(state) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - self::space(state).and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state) - .and_then(|state| self::space(state)) - }) - }) - }) - }) - }) - }) - }) - }) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::simple(state)) - }, - ) - }) - }) - }) - }) - }) - }) - }) - }) - }) - .or_else(|state| self::empty(state)) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn empty( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::empty, |state| state.match_string("")) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn hyphen( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::hyphen, |state| { - state.sequence(|state| { - self::partial(state) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - self::space(state) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - self::space(state).and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state) - .and_then(|state| { - self::space(state) - }) - }) - }) - }) - }) - }) - }) - }) - }) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| state.match_string("-")) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - self::space(state) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - self::space(state).and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state) - .and_then(|state| { - self::space(state) - }) - }) - }) - }) - }) - }) - }) - }) - }) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::partial(state)) - }) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn simple( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::simple, |state| { - self::primitive(state) - .or_else(|state| self::partial(state)) - .or_else(|state| self::tilde(state)) - .or_else(|state| self::caret(state)) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn primitive( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::primitive, |state| { - state.sequence(|state| { - self::primitive_op(state) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - self::space(state).and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state) - .and_then(|state| self::space(state)) - }) - }) - }) - }) - }) - }) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::partial(state)) - }) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn primitive_op( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::primitive_op, |state| { - state - .match_string("<=") - .or_else(|state| state.match_string(">=")) - .or_else(|state| state.match_string(">")) - .or_else(|state| state.match_string("<")) - .or_else(|state| state.match_string("=")) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn partial( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::partial, |state| { - state.sequence(|state| { - self::xr(state) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.optional(|state| { - state.sequence(|state| { - state - .match_string(".") - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::xr(state)) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.optional(|state| { - state.sequence(|state| { - state - .match_string(".") - .and_then(|state| { - super::hidden::skip(state) - }) - .and_then(|state| self::xr(state)) - .and_then(|state| { - super::hidden::skip(state) - }) - .and_then(|state| { - state.optional(|state| { - self::qualifier(state) - }) - }) - }) - }) - }) - }) - }) - }) - }) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn xr( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::xr, |state| { - self::xr_op(state).or_else(|state| self::nr(state)) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn xr_op( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::xr_op, |state| { - state - .match_string("x") - .or_else(|state| state.match_string("X")) - .or_else(|state| state.match_string("*")) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn nr( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::nr, |state| { - state.match_string("0").or_else(|state| { - state.sequence(|state| { - state - .match_range('1'..'9') - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - state.match_range('0'..'9').and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state).and_then( - |state| state.match_range('0'..'9'), - ) - }) - }) - }) - }) - }) - }) - }) - }) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn tilde( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::tilde, |state| { - state.sequence(|state| { - state - .match_string("~>") - .or_else(|state| state.match_string("~")) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - self::space(state).and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state) - .and_then(|state| self::space(state)) - }) - }) - }) - }) - }) - }) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::partial(state)) - }) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn caret( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::caret, |state| { - state.sequence(|state| { - state - .match_string("^") - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - self::space(state).and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state) - .and_then(|state| self::space(state)) - }) - }) - }) - }) - }) - }) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::partial(state)) - }) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn qualifier( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::qualifier, |state| { - state.sequence(|state| { - state - .match_string("-") - .or_else(|state| state.match_string("+")) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| self::parts(state)) - }) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn parts( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::parts, |state| { - state.sequence(|state| { - self::part(state) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - state - .sequence(|state| { - state - .match_string(".") - .and_then(|state| { - super::hidden::skip(state) - }) - .and_then(|state| self::part(state)) - }) - .and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state).and_then( - |state| { - state.sequence(|state| { - state - .match_string(".") - .and_then(|state| { - super::hidden::skip( - state, - ) - }) - .and_then(|state| { - self::part(state) - }) - }) - }, - ) - }) - }) - }) - }) - }) - }) - }) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn part( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::part, |state| { - self::nr(state).or_else(|state| { - state.sequence(|state| { - state - .match_string("-") - .or_else(|state| state.match_range('0'..'9')) - .or_else(|state| state.match_range('A'..'Z')) - .or_else(|state| state.match_range('a'..'z')) - .and_then(|state| super::hidden::skip(state)) - .and_then(|state| { - state.sequence(|state| { - state.optional(|state| { - state - .match_string("-") - .or_else(|state| state.match_range('0'..'9')) - .or_else(|state| state.match_range('A'..'Z')) - .or_else(|state| state.match_range('a'..'z')) - .and_then(|state| { - state.repeat(|state| { - state.sequence(|state| { - super::hidden::skip(state).and_then( - |state| { - state - .match_string("-") - .or_else(|state| { - state.match_range( - '0'..'9', - ) - }) - .or_else(|state| { - state.match_range( - 'A'..'Z', - ) - }) - .or_else(|state| { - state.match_range( - 'a'..'z', - ) - }) - }, - ) - }) - }) - }) - }) - }) - }) - }) - }) - }) - } - #[inline] - #[allow(non_snake_case, unused_variables)] - pub fn space( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state - .match_string(" ") - .or_else(|state| state.match_string("\t")) - } - #[inline] - #[allow(dead_code, non_snake_case, unused_variables)] - pub fn EOI( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.rule(Rule::EOI, |state| state.end_of_input()) - } - #[inline] - #[allow(dead_code, non_snake_case, unused_variables)] - pub fn SOI( - state: Box<::pest::ParserState>, - ) -> ::pest::ParseResult>> { - state.start_of_input() - } - } - pub use self::visible::*; - } - ::pest::state(input, |state| match rule { - Rule::range_set => rules::range_set(state), - Rule::logical_or => rules::logical_or(state), - Rule::range => rules::range(state), - Rule::empty => rules::empty(state), - Rule::hyphen => rules::hyphen(state), - Rule::simple => rules::simple(state), - Rule::primitive => rules::primitive(state), - Rule::primitive_op => rules::primitive_op(state), - Rule::partial => rules::partial(state), - Rule::xr => rules::xr(state), - Rule::xr_op => rules::xr_op(state), - Rule::nr => rules::nr(state), - Rule::tilde => rules::tilde(state), - Rule::caret => rules::caret(state), - Rule::qualifier => rules::qualifier(state), - Rule::parts => rules::parts(state), - Rule::part => rules::part(state), - Rule::space => rules::space(state), - Rule::EOI => rules::EOI(state), - }) - } -} diff --git a/tests/target/performance/issue-4867.rs b/tests/target/performance/issue-4867.rs deleted file mode 100644 index 336dae1b64ab..000000000000 --- a/tests/target/performance/issue-4867.rs +++ /dev/null @@ -1,13 +0,0 @@ -mod modA { - mod modB { - mod modC { - mod modD { - mod modE { - fn func() { - state . rule (Rule :: myrule , | state | { state . sequence (| state | { state . sequence (| state | { state . match_string ("abc") . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { state . match_string ("def") }) }) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { state . sequence (| state | { state . optional (| state | { state . sequence (| state | { state . match_string ("abc") . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { state . match_string ("def") }) }) . and_then (| state | { state . repeat (| state | { state . sequence (| state | { super :: hidden :: skip (state) . and_then (| state | { state . sequence (| state | { state . match_string ("abc") . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { state . match_string ("def") }) }) }) }) }) }) }) }) }) }) }); - } - } - } - } - } -} diff --git a/tests/target/performance/issue-5128.rs b/tests/target/performance/issue-5128.rs deleted file mode 100644 index ba9ebfc6243f..000000000000 --- a/tests/target/performance/issue-5128.rs +++ /dev/null @@ -1,4898 +0,0 @@ -fn takes_a_long_time_to_rustfmt() { - let inner_cte = vec![Node { - node: Some(node::Node::CommonTableExpr(Box::new(CommonTableExpr { - ctename: String::from("ranked_by_age_within_key"), - aliascolnames: vec![], - ctematerialized: CteMaterialize::Default as i32, - ctequery: Some(Box::new(Node { - node: Some(node::Node::SelectStmt(Box::new(SelectStmt { - distinct_clause: vec![], - into_clause: None, - target_list: vec![ - Node { - node: Some(node::Node::ResTarget(Box::new(ResTarget { - name: String::from(""), - indirection: vec![], - val: Some(Box::new(Node { - node: Some(node::Node::ColumnRef(ColumnRef { - fields: vec![Node { - node: Some(node::Node::AStar(AStar {})), - }], - location: 80, - })), - })), - location: 80, - }))), - }, - Node { - node: Some(node::Node::ResTarget(Box::new(ResTarget { - name: String::from("rank_in_key"), - indirection: vec![], - val: Some(Box::new(Node { - node: Some(node::Node::FuncCall(Box::new(FuncCall { - funcname: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("row_number"), - })), - }], - args: vec![], - agg_order: vec![], - agg_filter: None, - agg_within_group: false, - agg_star: false, - agg_distinct: false, - func_variadic: false, - over: Some(Box::new(WindowDef { - name: String::from(""), - refname: String::from(""), - partition_clause: vec![Node { - node: Some(node::Node::ColumnRef(ColumnRef { - fields: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("synthetic_key"), - })), - }], - location: 123, - })), - }], - order_clause: vec![Node { - node: Some(node::Node::SortBy(Box::new(SortBy { - node: Some(Box::new(Node { - node: Some(node::Node::ColumnRef( - ColumnRef { - fields: vec![Node { - node: Some(node::Node::String( - String2 { - str: String::from( - "logical_timestamp", - ), - }, - )), - }], - location: 156, - }, - )), - })), - sortby_dir: SortByDir::SortbyDesc as i32, - sortby_nulls: SortByNulls::SortbyNullsDefault - as i32, - use_op: vec![], - location: -1, - }))), - }], - frame_options: 1058, - start_offset: None, - end_offset: None, - location: 109, - })), - location: 91, - }))), - })), - location: 91, - }))), - }, - ], - from_clause: vec![Node { - node: Some(node::Node::RangeVar(RangeVar { - catalogname: String::from(""), - schemaname: String::from("_supertables"), - relname: String::from("9999-9999-9999"), - inh: true, - relpersistence: String::from("p"), - alias: None, - location: 206, - })), - }], - where_clause: Some(Box::new(Node { - node: Some(node::Node::AExpr(Box::new(AExpr { - kind: AExprKind::AexprOp as i32, - name: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("<="), - })), - }], - lexpr: Some(Box::new(Node { - node: Some(node::Node::ColumnRef(ColumnRef { - fields: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("logical_timestamp"), - })), - }], - location: 250, - })), - })), - rexpr: Some(Box::new(Node { - node: Some(node::Node::AConst(Box::new(AConst { - val: Some(Box::new(Node { - node: Some(node::Node::Integer(Integer { ival: 9000 })), - })), - location: 271, - }))), - })), - location: 268, - }))), - })), - group_clause: vec![], - having_clause: None, - window_clause: vec![], - values_lists: vec![], - sort_clause: vec![], - limit_offset: None, - limit_count: None, - limit_option: LimitOption::Default as i32, - locking_clause: vec![], - with_clause: None, - op: SetOperation::SetopNone as i32, - all: false, - larg: None, - rarg: None, - }))), - })), - location: 29, - cterecursive: false, - cterefcount: 0, - ctecolnames: vec![], - ctecoltypes: vec![], - ctecoltypmods: vec![], - ctecolcollations: vec![], - }))), - }]; - let outer_cte = vec![Node { - node: Some(node::Node::CommonTableExpr(Box::new(CommonTableExpr { - ctename: String::from("table_name"), - aliascolnames: vec![], - ctematerialized: CteMaterialize::Default as i32, - ctequery: Some(Box::new(Node { - node: Some(node::Node::SelectStmt(Box::new(SelectStmt { - distinct_clause: vec![], - into_clause: None, - target_list: vec![ - Node { - node: Some(node::Node::ResTarget(Box::new(ResTarget { - name: String::from("column1"), - indirection: vec![], - val: Some(Box::new(Node { - node: Some(node::Node::ColumnRef(ColumnRef { - fields: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("c1"), - })), - }], - location: 301, - })), - })), - location: 301, - }))), - }, - Node { - node: Some(node::Node::ResTarget(Box::new(ResTarget { - name: String::from("column2"), - indirection: vec![], - val: Some(Box::new(Node { - node: Some(node::Node::ColumnRef(ColumnRef { - fields: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("c2"), - })), - }], - location: 324, - })), - })), - location: 324, - }))), - }, - ], - from_clause: vec![Node { - node: Some(node::Node::RangeVar(RangeVar { - catalogname: String::from(""), - schemaname: String::from(""), - relname: String::from("ranked_by_age_within_key"), - inh: true, - relpersistence: String::from("p"), - alias: None, - location: 347, - })), - }], - where_clause: Some(Box::new(Node { - node: Some(node::Node::BoolExpr(Box::new(BoolExpr { - xpr: None, - boolop: BoolExprType::AndExpr as i32, - args: vec![ - Node { - node: Some(node::Node::AExpr(Box::new(AExpr { - kind: AExprKind::AexprOp as i32, - name: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("="), - })), - }], - lexpr: Some(Box::new(Node { - node: Some(node::Node::ColumnRef(ColumnRef { - fields: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("rank_in_key"), - })), - }], - location: 382, - })), - })), - rexpr: Some(Box::new(Node { - node: Some(node::Node::AConst(Box::new(AConst { - val: Some(Box::new(Node { - node: Some(node::Node::Integer(Integer { - ival: 1, - })), - })), - location: 396, - }))), - })), - location: 394, - }))), - }, - Node { - node: Some(node::Node::AExpr(Box::new(AExpr { - kind: AExprKind::AexprOp as i32, - name: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("="), - })), - }], - lexpr: Some(Box::new(Node { - node: Some(node::Node::ColumnRef(ColumnRef { - fields: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("is_deleted"), - })), - }], - location: 402, - })), - })), - rexpr: Some(Box::new(Node { - node: Some(node::Node::TypeCast(Box::new(TypeCast { - arg: Some(Box::new(Node { - node: Some(node::Node::AConst(Box::new( - AConst { - val: Some(Box::new(Node { - node: Some(node::Node::String( - String2 { - str: String::from("f"), - }, - )), - })), - location: 415, - }, - ))), - })), - type_name: Some(TypeName { - names: vec![ - Node { - node: Some(node::Node::String( - String2 { - str: String::from("pg_catalog"), - }, - )), - }, - Node { - node: Some(node::Node::String( - String2 { - str: String::from("bool"), - }, - )), - }, - ], - type_oid: 0, - setof: false, - pct_type: false, - typmods: vec![], - typemod: -1, - array_bounds: vec![], - location: -1, - }), - location: -1, - }))), - })), - location: 413, - }))), - }, - ], - location: 398, - }))), - })), - group_clause: vec![], - having_clause: None, - window_clause: vec![], - values_lists: vec![], - sort_clause: vec![], - limit_offset: None, - limit_count: None, - limit_option: LimitOption::Default as i32, - locking_clause: vec![], - with_clause: Some(WithClause { - ctes: inner_cte, - recursive: false, - location: 24, - }), - op: SetOperation::SetopNone as i32, - all: false, - larg: None, - rarg: None, - }))), - })), - location: 5, - cterecursive: false, - cterefcount: 0, - ctecolnames: vec![], - ctecoltypes: vec![], - ctecoltypmods: vec![], - ctecolcollations: vec![], - }))), - }]; - let expected_result = ParseResult { - version: 130003, - stmts: vec![RawStmt { - stmt: Some(Box::new(Node { - node: Some(node::Node::SelectStmt(Box::new(SelectStmt { - distinct_clause: vec![], - into_clause: None, - - target_list: vec![Node { - node: Some(node::Node::ResTarget(Box::new(ResTarget { - name: String::from(""), - indirection: vec![], - val: Some(Box::new(Node { - node: Some(node::Node::ColumnRef(ColumnRef { - fields: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("column1"), - })), - }], - location: 430, - })), - })), - location: 430, - }))), - }], - from_clause: vec![Node { - node: Some(node::Node::RangeVar(RangeVar { - catalogname: String::from(""), - schemaname: String::from(""), - relname: String::from("table_name"), - inh: true, - relpersistence: String::from("p"), - alias: None, - location: 443, - })), - }], - where_clause: Some(Box::new(Node { - node: Some(node::Node::AExpr(Box::new(AExpr { - kind: AExprKind::AexprOp as i32, - name: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from(">"), - })), - }], - lexpr: Some(Box::new(Node { - node: Some(node::Node::ColumnRef(ColumnRef { - fields: vec![Node { - node: Some(node::Node::String(String2 { - str: String::from("column2"), - })), - }], - location: 460, - })), - })), - rexpr: Some(Box::new(Node { - node: Some(node::Node::AConst(Box::new(AConst { - val: Some(Box::new(Node { - node: Some(node::Node::Integer(Integer { ival: 9000 })), - })), - location: 470, - }))), - })), - location: 468, - }))), - })), - group_clause: vec![], - having_clause: None, - window_clause: vec![], - values_lists: vec![], - sort_clause: vec![], - limit_offset: None, - limit_count: None, - limit_option: LimitOption::Default as i32, - locking_clause: vec![], - with_clause: Some(WithClause { - ctes: outer_cte, - recursive: false, - location: 0, - }), - op: SetOperation::SetopNone as i32, - all: false, - larg: None, - rarg: None, - }))), - })), - stmt_location: 0, - stmt_len: 0, - }], - }; -} -#[derive(Clone, PartialEq)] -pub struct ParseResult { - pub version: i32, - - pub stmts: Vec, -} -#[derive(Clone, PartialEq)] -pub struct ScanResult { - pub version: i32, - - pub tokens: Vec, -} -#[derive(Clone, PartialEq)] -pub struct Node { - pub node: ::core::option::Option, -} -/// Nested message and enum types in `Node`. -pub mod node { - #[derive(Clone, PartialEq)] - pub enum Node { - Alias(super::Alias), - - RangeVar(super::RangeVar), - - TableFunc(Box), - - Expr(super::Expr), - - Var(Box), - - Param(Box), - - Aggref(Box), - - GroupingFunc(Box), - - WindowFunc(Box), - - SubscriptingRef(Box), - - FuncExpr(Box), - - NamedArgExpr(Box), - - OpExpr(Box), - - DistinctExpr(Box), - - NullIfExpr(Box), - - ScalarArrayOpExpr(Box), - - BoolExpr(Box), - - SubLink(Box), - - SubPlan(Box), - - AlternativeSubPlan(Box), - - FieldSelect(Box), - - FieldStore(Box), - - RelabelType(Box), - - CoerceViaIo(Box), - - ArrayCoerceExpr(Box), - - ConvertRowtypeExpr(Box), - - CollateExpr(Box), - - CaseExpr(Box), - - CaseWhen(Box), - - CaseTestExpr(Box), - - ArrayExpr(Box), - - RowExpr(Box), - - RowCompareExpr(Box), - - CoalesceExpr(Box), - - MinMaxExpr(Box), - - SqlvalueFunction(Box), - - XmlExpr(Box), - - NullTest(Box), - - BooleanTest(Box), - - CoerceToDomain(Box), - - CoerceToDomainValue(Box), - - SetToDefault(Box), - - CurrentOfExpr(Box), - - NextValueExpr(Box), - - InferenceElem(Box), - - TargetEntry(Box), - - RangeTblRef(super::RangeTblRef), - - JoinExpr(Box), - - FromExpr(Box), - - OnConflictExpr(Box), - - IntoClause(Box), - - RawStmt(Box), - - Query(Box), - - InsertStmt(Box), - - DeleteStmt(Box), - - UpdateStmt(Box), - - SelectStmt(Box), - - AlterTableStmt(super::AlterTableStmt), - - AlterTableCmd(Box), - - AlterDomainStmt(Box), - - SetOperationStmt(Box), - - GrantStmt(super::GrantStmt), - - GrantRoleStmt(super::GrantRoleStmt), - - AlterDefaultPrivilegesStmt(super::AlterDefaultPrivilegesStmt), - - ClosePortalStmt(super::ClosePortalStmt), - - ClusterStmt(super::ClusterStmt), - - CopyStmt(Box), - - CreateStmt(super::CreateStmt), - - DefineStmt(super::DefineStmt), - - DropStmt(super::DropStmt), - - TruncateStmt(super::TruncateStmt), - - CommentStmt(Box), - - FetchStmt(super::FetchStmt), - - IndexStmt(Box), - - CreateFunctionStmt(super::CreateFunctionStmt), - - AlterFunctionStmt(super::AlterFunctionStmt), - - DoStmt(super::DoStmt), - - RenameStmt(Box), - - RuleStmt(Box), - - NotifyStmt(super::NotifyStmt), - - ListenStmt(super::ListenStmt), - - UnlistenStmt(super::UnlistenStmt), - - TransactionStmt(super::TransactionStmt), - - ViewStmt(Box), - - LoadStmt(super::LoadStmt), - - CreateDomainStmt(Box), - - CreatedbStmt(super::CreatedbStmt), - - DropdbStmt(super::DropdbStmt), - - VacuumStmt(super::VacuumStmt), - - ExplainStmt(Box), - - CreateTableAsStmt(Box), - - CreateSeqStmt(super::CreateSeqStmt), - - AlterSeqStmt(super::AlterSeqStmt), - - VariableSetStmt(super::VariableSetStmt), - - VariableShowStmt(super::VariableShowStmt), - - DiscardStmt(super::DiscardStmt), - - CreateTrigStmt(Box), - - CreatePlangStmt(super::CreatePLangStmt), - - CreateRoleStmt(super::CreateRoleStmt), - - AlterRoleStmt(super::AlterRoleStmt), - - DropRoleStmt(super::DropRoleStmt), - - LockStmt(super::LockStmt), - - ConstraintsSetStmt(super::ConstraintsSetStmt), - - ReindexStmt(super::ReindexStmt), - - CheckPointStmt(super::CheckPointStmt), - - CreateSchemaStmt(super::CreateSchemaStmt), - - AlterDatabaseStmt(super::AlterDatabaseStmt), - - AlterDatabaseSetStmt(super::AlterDatabaseSetStmt), - - AlterRoleSetStmt(super::AlterRoleSetStmt), - - CreateConversionStmt(super::CreateConversionStmt), - - CreateCastStmt(super::CreateCastStmt), - - CreateOpClassStmt(super::CreateOpClassStmt), - - CreateOpFamilyStmt(super::CreateOpFamilyStmt), - - AlterOpFamilyStmt(super::AlterOpFamilyStmt), - - PrepareStmt(Box), - - ExecuteStmt(super::ExecuteStmt), - - DeallocateStmt(super::DeallocateStmt), - - DeclareCursorStmt(Box), - - CreateTableSpaceStmt(super::CreateTableSpaceStmt), - - DropTableSpaceStmt(super::DropTableSpaceStmt), - - AlterObjectDependsStmt(Box), - - AlterObjectSchemaStmt(Box), - - AlterOwnerStmt(Box), - - AlterOperatorStmt(super::AlterOperatorStmt), - - AlterTypeStmt(super::AlterTypeStmt), - - DropOwnedStmt(super::DropOwnedStmt), - - ReassignOwnedStmt(super::ReassignOwnedStmt), - - CompositeTypeStmt(super::CompositeTypeStmt), - - CreateEnumStmt(super::CreateEnumStmt), - - CreateRangeStmt(super::CreateRangeStmt), - - AlterEnumStmt(super::AlterEnumStmt), - - AlterTsdictionaryStmt(super::AlterTsDictionaryStmt), - - AlterTsconfigurationStmt(super::AlterTsConfigurationStmt), - - CreateFdwStmt(super::CreateFdwStmt), - - AlterFdwStmt(super::AlterFdwStmt), - - CreateForeignServerStmt(super::CreateForeignServerStmt), - - AlterForeignServerStmt(super::AlterForeignServerStmt), - - CreateUserMappingStmt(super::CreateUserMappingStmt), - - AlterUserMappingStmt(super::AlterUserMappingStmt), - - DropUserMappingStmt(super::DropUserMappingStmt), - - AlterTableSpaceOptionsStmt(super::AlterTableSpaceOptionsStmt), - - AlterTableMoveAllStmt(super::AlterTableMoveAllStmt), - - SecLabelStmt(Box), - - CreateForeignTableStmt(super::CreateForeignTableStmt), - - ImportForeignSchemaStmt(super::ImportForeignSchemaStmt), - - CreateExtensionStmt(super::CreateExtensionStmt), - - AlterExtensionStmt(super::AlterExtensionStmt), - - AlterExtensionContentsStmt(Box), - - CreateEventTrigStmt(super::CreateEventTrigStmt), - - AlterEventTrigStmt(super::AlterEventTrigStmt), - - RefreshMatViewStmt(super::RefreshMatViewStmt), - - ReplicaIdentityStmt(super::ReplicaIdentityStmt), - - AlterSystemStmt(super::AlterSystemStmt), - - CreatePolicyStmt(Box), - - AlterPolicyStmt(Box), - - CreateTransformStmt(super::CreateTransformStmt), - - CreateAmStmt(super::CreateAmStmt), - - CreatePublicationStmt(super::CreatePublicationStmt), - - AlterPublicationStmt(super::AlterPublicationStmt), - - CreateSubscriptionStmt(super::CreateSubscriptionStmt), - - AlterSubscriptionStmt(super::AlterSubscriptionStmt), - - DropSubscriptionStmt(super::DropSubscriptionStmt), - - CreateStatsStmt(super::CreateStatsStmt), - - AlterCollationStmt(super::AlterCollationStmt), - - CallStmt(Box), - - AlterStatsStmt(super::AlterStatsStmt), - - AExpr(Box), - - ColumnRef(super::ColumnRef), - - ParamRef(super::ParamRef), - - AConst(Box), - - FuncCall(Box), - - AStar(super::AStar), - - AIndices(Box), - - AIndirection(Box), - - AArrayExpr(super::AArrayExpr), - - ResTarget(Box), - - MultiAssignRef(Box), - - TypeCast(Box), - - CollateClause(Box), - - SortBy(Box), - - WindowDef(Box), - - RangeSubselect(Box), - - RangeFunction(super::RangeFunction), - - RangeTableSample(Box), - - RangeTableFunc(Box), - - RangeTableFuncCol(Box), - - TypeName(super::TypeName), - - ColumnDef(Box), - - IndexElem(Box), - - Constraint(Box), - - DefElem(Box), - - RangeTblEntry(Box), - - RangeTblFunction(Box), - - TableSampleClause(Box), - - WithCheckOption(Box), - - SortGroupClause(super::SortGroupClause), - - GroupingSet(super::GroupingSet), - - WindowClause(Box), - - ObjectWithArgs(super::ObjectWithArgs), - - AccessPriv(super::AccessPriv), - - CreateOpClassItem(super::CreateOpClassItem), - - TableLikeClause(super::TableLikeClause), - - FunctionParameter(Box), - - LockingClause(super::LockingClause), - - RowMarkClause(super::RowMarkClause), - - XmlSerialize(Box), - - WithClause(super::WithClause), - - InferClause(Box), - - OnConflictClause(Box), - - CommonTableExpr(Box), - - RoleSpec(super::RoleSpec), - - TriggerTransition(super::TriggerTransition), - - PartitionElem(Box), - - PartitionSpec(super::PartitionSpec), - - PartitionBoundSpec(super::PartitionBoundSpec), - - PartitionRangeDatum(Box), - - PartitionCmd(super::PartitionCmd), - - VacuumRelation(super::VacuumRelation), - - InlineCodeBlock(super::InlineCodeBlock), - - CallContext(super::CallContext), - - Integer(super::Integer), - - Float(super::Float), - - String(super::String2), - - BitString(super::BitString), - - Null(super::Null), - - List(super::List), - - IntList(super::IntList), - - OidList(super::OidList), - } -} -#[derive(Clone, PartialEq)] -pub struct Integer { - /// machine integer - pub ival: i32, -} -#[derive(Clone, PartialEq)] -pub struct Float { - /// string - pub str: String, -} -#[derive(Clone, PartialEq)] -pub struct String2 { - /// string - pub str: String, -} -#[derive(Clone, PartialEq)] -pub struct BitString { - /// string - pub str: String, -} -/// intentionally empty -#[derive(Clone, PartialEq)] -pub struct Null {} -#[derive(Clone, PartialEq)] -pub struct List { - pub items: Vec, -} -#[derive(Clone, PartialEq)] -pub struct OidList { - pub items: Vec, -} -#[derive(Clone, PartialEq)] -pub struct IntList { - pub items: Vec, -} -#[derive(Clone, PartialEq)] -pub struct Alias { - pub aliasname: String, - - pub colnames: Vec, -} -#[derive(Clone, PartialEq)] -pub struct RangeVar { - pub catalogname: String, - - pub schemaname: String, - - pub relname: String, - - pub inh: bool, - - pub relpersistence: String, - - pub alias: ::core::option::Option, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct TableFunc { - pub ns_uris: Vec, - - pub ns_names: Vec, - - pub docexpr: ::core::option::Option>, - - pub rowexpr: ::core::option::Option>, - - pub colnames: Vec, - - pub coltypes: Vec, - - pub coltypmods: Vec, - - pub colcollations: Vec, - - pub colexprs: Vec, - - pub coldefexprs: Vec, - - pub notnulls: Vec, - - pub ordinalitycol: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct Expr {} -#[derive(Clone, PartialEq)] -pub struct Var { - pub xpr: ::core::option::Option>, - - pub varno: u32, - - pub varattno: i32, - - pub vartype: u32, - - pub vartypmod: i32, - - pub varcollid: u32, - - pub varlevelsup: u32, - - pub varnosyn: u32, - - pub varattnosyn: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct Param { - pub xpr: ::core::option::Option>, - - pub paramkind: i32, - - pub paramid: i32, - - pub paramtype: u32, - - pub paramtypmod: i32, - - pub paramcollid: u32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct Aggref { - pub xpr: ::core::option::Option>, - - pub aggfnoid: u32, - - pub aggtype: u32, - - pub aggcollid: u32, - - pub inputcollid: u32, - - pub aggtranstype: u32, - - pub aggargtypes: Vec, - - pub aggdirectargs: Vec, - - pub args: Vec, - - pub aggorder: Vec, - - pub aggdistinct: Vec, - - pub aggfilter: ::core::option::Option>, - - pub aggstar: bool, - - pub aggvariadic: bool, - - pub aggkind: String, - - pub agglevelsup: u32, - - pub aggsplit: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct GroupingFunc { - pub xpr: ::core::option::Option>, - - pub args: Vec, - - pub refs: Vec, - - pub cols: Vec, - - pub agglevelsup: u32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct WindowFunc { - pub xpr: ::core::option::Option>, - - pub winfnoid: u32, - - pub wintype: u32, - - pub wincollid: u32, - - pub inputcollid: u32, - - pub args: Vec, - - pub aggfilter: ::core::option::Option>, - - pub winref: u32, - - pub winstar: bool, - - pub winagg: bool, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct SubscriptingRef { - pub xpr: ::core::option::Option>, - - pub refcontainertype: u32, - - pub refelemtype: u32, - - pub reftypmod: i32, - - pub refcollid: u32, - - pub refupperindexpr: Vec, - - pub reflowerindexpr: Vec, - - pub refexpr: ::core::option::Option>, - - pub refassgnexpr: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct FuncExpr { - pub xpr: ::core::option::Option>, - - pub funcid: u32, - - pub funcresulttype: u32, - - pub funcretset: bool, - - pub funcvariadic: bool, - - pub funcformat: i32, - - pub funccollid: u32, - - pub inputcollid: u32, - - pub args: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct NamedArgExpr { - pub xpr: ::core::option::Option>, - - pub arg: ::core::option::Option>, - - pub name: String, - - pub argnumber: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct OpExpr { - pub xpr: ::core::option::Option>, - - pub opno: u32, - - pub opfuncid: u32, - - pub opresulttype: u32, - - pub opretset: bool, - - pub opcollid: u32, - - pub inputcollid: u32, - - pub args: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct DistinctExpr { - pub xpr: ::core::option::Option>, - - pub opno: u32, - - pub opfuncid: u32, - - pub opresulttype: u32, - - pub opretset: bool, - - pub opcollid: u32, - - pub inputcollid: u32, - - pub args: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct NullIfExpr { - pub xpr: ::core::option::Option>, - - pub opno: u32, - - pub opfuncid: u32, - - pub opresulttype: u32, - - pub opretset: bool, - - pub opcollid: u32, - - pub inputcollid: u32, - - pub args: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct ScalarArrayOpExpr { - pub xpr: ::core::option::Option>, - - pub opno: u32, - - pub opfuncid: u32, - - pub use_or: bool, - - pub inputcollid: u32, - - pub args: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct BoolExpr { - pub xpr: ::core::option::Option>, - - pub boolop: i32, - - pub args: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct SubLink { - pub xpr: ::core::option::Option>, - - pub sub_link_type: i32, - - pub sub_link_id: i32, - - pub testexpr: ::core::option::Option>, - - pub oper_name: Vec, - - pub subselect: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct SubPlan { - pub xpr: ::core::option::Option>, - - pub sub_link_type: i32, - - pub testexpr: ::core::option::Option>, - - pub param_ids: Vec, - - pub plan_id: i32, - - pub plan_name: String, - - pub first_col_type: u32, - - pub first_col_typmod: i32, - - pub first_col_collation: u32, - - pub use_hash_table: bool, - - pub unknown_eq_false: bool, - - pub parallel_safe: bool, - - pub set_param: Vec, - - pub par_param: Vec, - - pub args: Vec, - - pub startup_cost: f64, - - pub per_call_cost: f64, -} -#[derive(Clone, PartialEq)] -pub struct AlternativeSubPlan { - pub xpr: ::core::option::Option>, - - pub subplans: Vec, -} -#[derive(Clone, PartialEq)] -pub struct FieldSelect { - pub xpr: ::core::option::Option>, - - pub arg: ::core::option::Option>, - - pub fieldnum: i32, - - pub resulttype: u32, - - pub resulttypmod: i32, - - pub resultcollid: u32, -} -#[derive(Clone, PartialEq)] -pub struct FieldStore { - pub xpr: ::core::option::Option>, - - pub arg: ::core::option::Option>, - - pub newvals: Vec, - - pub fieldnums: Vec, - - pub resulttype: u32, -} -#[derive(Clone, PartialEq)] -pub struct RelabelType { - pub xpr: ::core::option::Option>, - - pub arg: ::core::option::Option>, - - pub resulttype: u32, - - pub resulttypmod: i32, - - pub resultcollid: u32, - - pub relabelformat: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct CoerceViaIo { - pub xpr: ::core::option::Option>, - - pub arg: ::core::option::Option>, - - pub resulttype: u32, - - pub resultcollid: u32, - - pub coerceformat: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct ArrayCoerceExpr { - pub xpr: ::core::option::Option>, - - pub arg: ::core::option::Option>, - - pub elemexpr: ::core::option::Option>, - - pub resulttype: u32, - - pub resulttypmod: i32, - - pub resultcollid: u32, - - pub coerceformat: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct ConvertRowtypeExpr { - pub xpr: ::core::option::Option>, - - pub arg: ::core::option::Option>, - - pub resulttype: u32, - - pub convertformat: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct CollateExpr { - pub xpr: ::core::option::Option>, - - pub arg: ::core::option::Option>, - - pub coll_oid: u32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct CaseExpr { - pub xpr: ::core::option::Option>, - - pub casetype: u32, - - pub casecollid: u32, - - pub arg: ::core::option::Option>, - - pub args: Vec, - - pub defresult: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct CaseWhen { - pub xpr: ::core::option::Option>, - - pub expr: ::core::option::Option>, - - pub result: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct CaseTestExpr { - pub xpr: ::core::option::Option>, - - pub type_id: u32, - - pub type_mod: i32, - - pub collation: u32, -} -#[derive(Clone, PartialEq)] -pub struct ArrayExpr { - pub xpr: ::core::option::Option>, - - pub array_typeid: u32, - - pub array_collid: u32, - - pub element_typeid: u32, - - pub elements: Vec, - - pub multidims: bool, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct RowExpr { - pub xpr: ::core::option::Option>, - - pub args: Vec, - - pub row_typeid: u32, - - pub row_format: i32, - - pub colnames: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct RowCompareExpr { - pub xpr: ::core::option::Option>, - - pub rctype: i32, - - pub opnos: Vec, - - pub opfamilies: Vec, - - pub inputcollids: Vec, - - pub largs: Vec, - - pub rargs: Vec, -} -#[derive(Clone, PartialEq)] -pub struct CoalesceExpr { - pub xpr: ::core::option::Option>, - - pub coalescetype: u32, - - pub coalescecollid: u32, - - pub args: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct MinMaxExpr { - pub xpr: ::core::option::Option>, - - pub minmaxtype: u32, - - pub minmaxcollid: u32, - - pub inputcollid: u32, - - pub op: i32, - - pub args: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct SqlValueFunction { - pub xpr: ::core::option::Option>, - - pub op: i32, - - pub r#type: u32, - - pub typmod: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct XmlExpr { - pub xpr: ::core::option::Option>, - - pub op: i32, - - pub name: String, - - pub named_args: Vec, - - pub arg_names: Vec, - - pub args: Vec, - - pub xmloption: i32, - - pub r#type: u32, - - pub typmod: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct NullTest { - pub xpr: ::core::option::Option>, - - pub arg: ::core::option::Option>, - - pub nulltesttype: i32, - - pub argisrow: bool, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct BooleanTest { - pub xpr: ::core::option::Option>, - - pub arg: ::core::option::Option>, - - pub booltesttype: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct CoerceToDomain { - pub xpr: ::core::option::Option>, - - pub arg: ::core::option::Option>, - - pub resulttype: u32, - - pub resulttypmod: i32, - - pub resultcollid: u32, - - pub coercionformat: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct CoerceToDomainValue { - pub xpr: ::core::option::Option>, - - pub type_id: u32, - - pub type_mod: i32, - - pub collation: u32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct SetToDefault { - pub xpr: ::core::option::Option>, - - pub type_id: u32, - - pub type_mod: i32, - - pub collation: u32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct CurrentOfExpr { - pub xpr: ::core::option::Option>, - - pub cvarno: u32, - - pub cursor_name: String, - - pub cursor_param: i32, -} -#[derive(Clone, PartialEq)] -pub struct NextValueExpr { - pub xpr: ::core::option::Option>, - - pub seqid: u32, - - pub type_id: u32, -} -#[derive(Clone, PartialEq)] -pub struct InferenceElem { - pub xpr: ::core::option::Option>, - - pub expr: ::core::option::Option>, - - pub infercollid: u32, - - pub inferopclass: u32, -} -#[derive(Clone, PartialEq)] -pub struct TargetEntry { - pub xpr: ::core::option::Option>, - - pub expr: ::core::option::Option>, - - pub resno: i32, - - pub resname: String, - - pub ressortgroupref: u32, - - pub resorigtbl: u32, - - pub resorigcol: i32, - - pub resjunk: bool, -} -#[derive(Clone, PartialEq)] -pub struct RangeTblRef { - pub rtindex: i32, -} -#[derive(Clone, PartialEq)] -pub struct JoinExpr { - pub jointype: i32, - - pub is_natural: bool, - - pub larg: ::core::option::Option>, - - pub rarg: ::core::option::Option>, - - pub using_clause: Vec, - - pub quals: ::core::option::Option>, - - pub alias: ::core::option::Option, - - pub rtindex: i32, -} -#[derive(Clone, PartialEq)] -pub struct FromExpr { - pub fromlist: Vec, - - pub quals: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct OnConflictExpr { - pub action: i32, - - pub arbiter_elems: Vec, - - pub arbiter_where: ::core::option::Option>, - - pub constraint: u32, - - pub on_conflict_set: Vec, - - pub on_conflict_where: ::core::option::Option>, - - pub excl_rel_index: i32, - - pub excl_rel_tlist: Vec, -} -#[derive(Clone, PartialEq)] -pub struct IntoClause { - pub rel: ::core::option::Option, - - pub col_names: Vec, - - pub access_method: String, - - pub options: Vec, - - pub on_commit: i32, - - pub table_space_name: String, - - pub view_query: ::core::option::Option>, - - pub skip_data: bool, -} -#[derive(Clone, PartialEq)] -pub struct RawStmt { - pub stmt: ::core::option::Option>, - - pub stmt_location: i32, - - pub stmt_len: i32, -} -#[derive(Clone, PartialEq)] -pub struct Query { - pub command_type: i32, - - pub query_source: i32, - - pub can_set_tag: bool, - - pub utility_stmt: ::core::option::Option>, - - pub result_relation: i32, - - pub has_aggs: bool, - - pub has_window_funcs: bool, - - pub has_target_srfs: bool, - - pub has_sub_links: bool, - - pub has_distinct_on: bool, - - pub has_recursive: bool, - - pub has_modifying_cte: bool, - - pub has_for_update: bool, - - pub has_row_security: bool, - - pub cte_list: Vec, - - pub rtable: Vec, - - pub jointree: ::core::option::Option>, - - pub target_list: Vec, - - pub r#override: i32, - - pub on_conflict: ::core::option::Option>, - - pub returning_list: Vec, - - pub group_clause: Vec, - - pub grouping_sets: Vec, - - pub having_qual: ::core::option::Option>, - - pub window_clause: Vec, - - pub distinct_clause: Vec, - - pub sort_clause: Vec, - - pub limit_offset: ::core::option::Option>, - - pub limit_count: ::core::option::Option>, - - pub limit_option: i32, - - pub row_marks: Vec, - - pub set_operations: ::core::option::Option>, - - pub constraint_deps: Vec, - - pub with_check_options: Vec, - - pub stmt_location: i32, - - pub stmt_len: i32, -} -#[derive(Clone, PartialEq)] -pub struct InsertStmt { - pub relation: ::core::option::Option, - - pub cols: Vec, - - pub select_stmt: ::core::option::Option>, - - pub on_conflict_clause: ::core::option::Option>, - - pub returning_list: Vec, - - pub with_clause: ::core::option::Option, - - pub r#override: i32, -} -#[derive(Clone, PartialEq)] -pub struct DeleteStmt { - pub relation: ::core::option::Option, - - pub using_clause: Vec, - - pub where_clause: ::core::option::Option>, - - pub returning_list: Vec, - - pub with_clause: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct UpdateStmt { - pub relation: ::core::option::Option, - - pub target_list: Vec, - - pub where_clause: ::core::option::Option>, - - pub from_clause: Vec, - - pub returning_list: Vec, - - pub with_clause: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct SelectStmt { - pub distinct_clause: Vec, - - pub into_clause: ::core::option::Option>, - - pub target_list: Vec, - - pub from_clause: Vec, - - pub where_clause: ::core::option::Option>, - - pub group_clause: Vec, - - pub having_clause: ::core::option::Option>, - - pub window_clause: Vec, - - pub values_lists: Vec, - - pub sort_clause: Vec, - - pub limit_offset: ::core::option::Option>, - - pub limit_count: ::core::option::Option>, - - pub limit_option: i32, - - pub locking_clause: Vec, - - pub with_clause: ::core::option::Option, - - pub op: i32, - - pub all: bool, - - pub larg: ::core::option::Option>, - - pub rarg: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct AlterTableStmt { - pub relation: ::core::option::Option, - - pub cmds: Vec, - - pub relkind: i32, - - pub missing_ok: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterTableCmd { - pub subtype: i32, - - pub name: String, - - pub num: i32, - - pub newowner: ::core::option::Option, - - pub def: ::core::option::Option>, - - pub behavior: i32, - - pub missing_ok: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterDomainStmt { - pub subtype: String, - - pub type_name: Vec, - - pub name: String, - - pub def: ::core::option::Option>, - - pub behavior: i32, - - pub missing_ok: bool, -} -#[derive(Clone, PartialEq)] -pub struct SetOperationStmt { - pub op: i32, - - pub all: bool, - - pub larg: ::core::option::Option>, - - pub rarg: ::core::option::Option>, - - pub col_types: Vec, - - pub col_typmods: Vec, - - pub col_collations: Vec, - - pub group_clauses: Vec, -} -#[derive(Clone, PartialEq)] -pub struct GrantStmt { - pub is_grant: bool, - - pub targtype: i32, - - pub objtype: i32, - - pub objects: Vec, - - pub privileges: Vec, - - pub grantees: Vec, - - pub grant_option: bool, - - pub behavior: i32, -} -#[derive(Clone, PartialEq)] -pub struct GrantRoleStmt { - pub granted_roles: Vec, - - pub grantee_roles: Vec, - - pub is_grant: bool, - - pub admin_opt: bool, - - pub grantor: ::core::option::Option, - - pub behavior: i32, -} -#[derive(Clone, PartialEq)] -pub struct AlterDefaultPrivilegesStmt { - pub options: Vec, - - pub action: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct ClosePortalStmt { - pub portalname: String, -} -#[derive(Clone, PartialEq)] -pub struct ClusterStmt { - pub relation: ::core::option::Option, - - pub indexname: String, - - pub options: i32, -} -#[derive(Clone, PartialEq)] -pub struct CopyStmt { - pub relation: ::core::option::Option, - - pub query: ::core::option::Option>, - - pub attlist: Vec, - - pub is_from: bool, - - pub is_program: bool, - - pub filename: String, - - pub options: Vec, - - pub where_clause: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct CreateStmt { - pub relation: ::core::option::Option, - - pub table_elts: Vec, - - pub inh_relations: Vec, - - pub partbound: ::core::option::Option, - - pub partspec: ::core::option::Option, - - pub of_typename: ::core::option::Option, - - pub constraints: Vec, - - pub options: Vec, - - pub oncommit: i32, - - pub tablespacename: String, - - pub access_method: String, - - pub if_not_exists: bool, -} -#[derive(Clone, PartialEq)] -pub struct DefineStmt { - pub kind: i32, - - pub oldstyle: bool, - - pub defnames: Vec, - - pub args: Vec, - - pub definition: Vec, - - pub if_not_exists: bool, - - pub replace: bool, -} -#[derive(Clone, PartialEq)] -pub struct DropStmt { - pub objects: Vec, - - pub remove_type: i32, - - pub behavior: i32, - - pub missing_ok: bool, - - pub concurrent: bool, -} -#[derive(Clone, PartialEq)] -pub struct TruncateStmt { - pub relations: Vec, - - pub restart_seqs: bool, - - pub behavior: i32, -} -#[derive(Clone, PartialEq)] -pub struct CommentStmt { - pub objtype: i32, - - pub object: ::core::option::Option>, - - pub comment: String, -} -#[derive(Clone, PartialEq)] -pub struct FetchStmt { - pub direction: i32, - - pub how_many: i64, - - pub portalname: String, - - pub ismove: bool, -} -#[derive(Clone, PartialEq)] -pub struct IndexStmt { - pub idxname: String, - - pub relation: ::core::option::Option, - - pub access_method: String, - - pub table_space: String, - - pub index_params: Vec, - - pub index_including_params: Vec, - - pub options: Vec, - - pub where_clause: ::core::option::Option>, - - pub exclude_op_names: Vec, - - pub idxcomment: String, - - pub index_oid: u32, - - pub old_node: u32, - - pub old_create_subid: u32, - - pub old_first_relfilenode_subid: u32, - - pub unique: bool, - - pub primary: bool, - - pub isconstraint: bool, - - pub deferrable: bool, - - pub initdeferred: bool, - - pub transformed: bool, - - pub concurrent: bool, - - pub if_not_exists: bool, - - pub reset_default_tblspc: bool, -} -#[derive(Clone, PartialEq)] -pub struct CreateFunctionStmt { - pub is_procedure: bool, - - pub replace: bool, - - pub funcname: Vec, - - pub parameters: Vec, - - pub return_type: ::core::option::Option, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterFunctionStmt { - pub objtype: i32, - - pub func: ::core::option::Option, - - pub actions: Vec, -} -#[derive(Clone, PartialEq)] -pub struct DoStmt { - pub args: Vec, -} -#[derive(Clone, PartialEq)] -pub struct RenameStmt { - pub rename_type: i32, - - pub relation_type: i32, - - pub relation: ::core::option::Option, - - pub object: ::core::option::Option>, - - pub subname: String, - - pub newname: String, - - pub behavior: i32, - - pub missing_ok: bool, -} -#[derive(Clone, PartialEq)] -pub struct RuleStmt { - pub relation: ::core::option::Option, - - pub rulename: String, - - pub where_clause: ::core::option::Option>, - - pub event: i32, - - pub instead: bool, - - pub actions: Vec, - - pub replace: bool, -} -#[derive(Clone, PartialEq)] -pub struct NotifyStmt { - pub conditionname: String, - - pub payload: String, -} -#[derive(Clone, PartialEq)] -pub struct ListenStmt { - pub conditionname: String, -} -#[derive(Clone, PartialEq)] -pub struct UnlistenStmt { - pub conditionname: String, -} -#[derive(Clone, PartialEq)] -pub struct TransactionStmt { - pub kind: i32, - - pub options: Vec, - - pub savepoint_name: String, - - pub gid: String, - - pub chain: bool, -} -#[derive(Clone, PartialEq)] -pub struct ViewStmt { - pub view: ::core::option::Option, - - pub aliases: Vec, - - pub query: ::core::option::Option>, - - pub replace: bool, - - pub options: Vec, - - pub with_check_option: i32, -} -#[derive(Clone, PartialEq)] -pub struct LoadStmt { - pub filename: String, -} -#[derive(Clone, PartialEq)] -pub struct CreateDomainStmt { - pub domainname: Vec, - - pub type_name: ::core::option::Option, - - pub coll_clause: ::core::option::Option>, - - pub constraints: Vec, -} -#[derive(Clone, PartialEq)] -pub struct CreatedbStmt { - pub dbname: String, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct DropdbStmt { - pub dbname: String, - - pub missing_ok: bool, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct VacuumStmt { - pub options: Vec, - - pub rels: Vec, - - pub is_vacuumcmd: bool, -} -#[derive(Clone, PartialEq)] -pub struct ExplainStmt { - pub query: ::core::option::Option>, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct CreateTableAsStmt { - pub query: ::core::option::Option>, - - pub into: ::core::option::Option>, - - pub relkind: i32, - - pub is_select_into: bool, - - pub if_not_exists: bool, -} -#[derive(Clone, PartialEq)] -pub struct CreateSeqStmt { - pub sequence: ::core::option::Option, - - pub options: Vec, - - pub owner_id: u32, - - pub for_identity: bool, - - pub if_not_exists: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterSeqStmt { - pub sequence: ::core::option::Option, - - pub options: Vec, - - pub for_identity: bool, - - pub missing_ok: bool, -} -#[derive(Clone, PartialEq)] -pub struct VariableSetStmt { - pub kind: i32, - - pub name: String, - - pub args: Vec, - - pub is_local: bool, -} -#[derive(Clone, PartialEq)] -pub struct VariableShowStmt { - pub name: String, -} -#[derive(Clone, PartialEq)] -pub struct DiscardStmt { - pub target: i32, -} -#[derive(Clone, PartialEq)] -pub struct CreateTrigStmt { - pub trigname: String, - - pub relation: ::core::option::Option, - - pub funcname: Vec, - - pub args: Vec, - - pub row: bool, - - pub timing: i32, - - pub events: i32, - - pub columns: Vec, - - pub when_clause: ::core::option::Option>, - - pub isconstraint: bool, - - pub transition_rels: Vec, - - pub deferrable: bool, - - pub initdeferred: bool, - - pub constrrel: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct CreatePLangStmt { - pub replace: bool, - - pub plname: String, - - pub plhandler: Vec, - - pub plinline: Vec, - - pub plvalidator: Vec, - - pub pltrusted: bool, -} -#[derive(Clone, PartialEq)] -pub struct CreateRoleStmt { - pub stmt_type: i32, - - pub role: String, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterRoleStmt { - pub role: ::core::option::Option, - - pub options: Vec, - - pub action: i32, -} -#[derive(Clone, PartialEq)] -pub struct DropRoleStmt { - pub roles: Vec, - - pub missing_ok: bool, -} -#[derive(Clone, PartialEq)] -pub struct LockStmt { - pub relations: Vec, - - pub mode: i32, - - pub nowait: bool, -} -#[derive(Clone, PartialEq)] -pub struct ConstraintsSetStmt { - pub constraints: Vec, - - pub deferred: bool, -} -#[derive(Clone, PartialEq)] -pub struct ReindexStmt { - pub kind: i32, - - pub relation: ::core::option::Option, - - pub name: String, - - pub options: i32, - - pub concurrent: bool, -} -#[derive(Clone, PartialEq)] -pub struct CheckPointStmt {} -#[derive(Clone, PartialEq)] -pub struct CreateSchemaStmt { - pub schemaname: String, - - pub authrole: ::core::option::Option, - - pub schema_elts: Vec, - - pub if_not_exists: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterDatabaseStmt { - pub dbname: String, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterDatabaseSetStmt { - pub dbname: String, - - pub setstmt: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct AlterRoleSetStmt { - pub role: ::core::option::Option, - - pub database: String, - - pub setstmt: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct CreateConversionStmt { - pub conversion_name: Vec, - - pub for_encoding_name: String, - - pub to_encoding_name: String, - - pub func_name: Vec, - - pub def: bool, -} -#[derive(Clone, PartialEq)] -pub struct CreateCastStmt { - pub sourcetype: ::core::option::Option, - - pub targettype: ::core::option::Option, - - pub func: ::core::option::Option, - - pub context: i32, - - pub inout: bool, -} -#[derive(Clone, PartialEq)] -pub struct CreateOpClassStmt { - pub opclassname: Vec, - - pub opfamilyname: Vec, - - pub amname: String, - - pub datatype: ::core::option::Option, - - pub items: Vec, - - pub is_default: bool, -} -#[derive(Clone, PartialEq)] -pub struct CreateOpFamilyStmt { - pub opfamilyname: Vec, - - pub amname: String, -} -#[derive(Clone, PartialEq)] -pub struct AlterOpFamilyStmt { - pub opfamilyname: Vec, - - pub amname: String, - - pub is_drop: bool, - - pub items: Vec, -} -#[derive(Clone, PartialEq)] -pub struct PrepareStmt { - pub name: String, - - pub argtypes: Vec, - - pub query: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct ExecuteStmt { - pub name: String, - - pub params: Vec, -} -#[derive(Clone, PartialEq)] -pub struct DeallocateStmt { - pub name: String, -} -#[derive(Clone, PartialEq)] -pub struct DeclareCursorStmt { - pub portalname: String, - - pub options: i32, - - pub query: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct CreateTableSpaceStmt { - pub tablespacename: String, - - pub owner: ::core::option::Option, - - pub location: String, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct DropTableSpaceStmt { - pub tablespacename: String, - - pub missing_ok: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterObjectDependsStmt { - pub object_type: i32, - - pub relation: ::core::option::Option, - - pub object: ::core::option::Option>, - - pub extname: ::core::option::Option>, - - pub remove: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterObjectSchemaStmt { - pub object_type: i32, - - pub relation: ::core::option::Option, - - pub object: ::core::option::Option>, - - pub newschema: String, - - pub missing_ok: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterOwnerStmt { - pub object_type: i32, - - pub relation: ::core::option::Option, - - pub object: ::core::option::Option>, - - pub newowner: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct AlterOperatorStmt { - pub opername: ::core::option::Option, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterTypeStmt { - pub type_name: Vec, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct DropOwnedStmt { - pub roles: Vec, - - pub behavior: i32, -} -#[derive(Clone, PartialEq)] -pub struct ReassignOwnedStmt { - pub roles: Vec, - - pub newrole: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct CompositeTypeStmt { - pub typevar: ::core::option::Option, - - pub coldeflist: Vec, -} -#[derive(Clone, PartialEq)] -pub struct CreateEnumStmt { - pub type_name: Vec, - - pub vals: Vec, -} -#[derive(Clone, PartialEq)] -pub struct CreateRangeStmt { - pub type_name: Vec, - - pub params: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterEnumStmt { - pub type_name: Vec, - - pub old_val: String, - - pub new_val: String, - - pub new_val_neighbor: String, - - pub new_val_is_after: bool, - - pub skip_if_new_val_exists: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterTsDictionaryStmt { - pub dictname: Vec, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterTsConfigurationStmt { - pub kind: i32, - - pub cfgname: Vec, - - pub tokentype: Vec, - - pub dicts: Vec, - - pub r#override: bool, - - pub replace: bool, - - pub missing_ok: bool, -} -#[derive(Clone, PartialEq)] -pub struct CreateFdwStmt { - pub fdwname: String, - - pub func_options: Vec, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterFdwStmt { - pub fdwname: String, - - pub func_options: Vec, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct CreateForeignServerStmt { - pub servername: String, - - pub servertype: String, - - pub version: String, - - pub fdwname: String, - - pub if_not_exists: bool, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterForeignServerStmt { - pub servername: String, - - pub version: String, - - pub options: Vec, - - pub has_version: bool, -} -#[derive(Clone, PartialEq)] -pub struct CreateUserMappingStmt { - pub user: ::core::option::Option, - - pub servername: String, - - pub if_not_exists: bool, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterUserMappingStmt { - pub user: ::core::option::Option, - - pub servername: String, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct DropUserMappingStmt { - pub user: ::core::option::Option, - - pub servername: String, - - pub missing_ok: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterTableSpaceOptionsStmt { - pub tablespacename: String, - - pub options: Vec, - - pub is_reset: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterTableMoveAllStmt { - pub orig_tablespacename: String, - - pub objtype: i32, - - pub roles: Vec, - - pub new_tablespacename: String, - - pub nowait: bool, -} -#[derive(Clone, PartialEq)] -pub struct SecLabelStmt { - pub objtype: i32, - - pub object: ::core::option::Option>, - - pub provider: String, - - pub label: String, -} -#[derive(Clone, PartialEq)] -pub struct CreateForeignTableStmt { - pub base_stmt: ::core::option::Option, - - pub servername: String, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct ImportForeignSchemaStmt { - pub server_name: String, - - pub remote_schema: String, - - pub local_schema: String, - - pub list_type: i32, - - pub table_list: Vec, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct CreateExtensionStmt { - pub extname: String, - - pub if_not_exists: bool, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterExtensionStmt { - pub extname: String, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterExtensionContentsStmt { - pub extname: String, - - pub action: i32, - - pub objtype: i32, - - pub object: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct CreateEventTrigStmt { - pub trigname: String, - - pub eventname: String, - - pub whenclause: Vec, - - pub funcname: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterEventTrigStmt { - pub trigname: String, - - pub tgenabled: String, -} -#[derive(Clone, PartialEq)] -pub struct RefreshMatViewStmt { - pub concurrent: bool, - - pub skip_data: bool, - - pub relation: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct ReplicaIdentityStmt { - pub identity_type: String, - - pub name: String, -} -#[derive(Clone, PartialEq)] -pub struct AlterSystemStmt { - pub setstmt: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct CreatePolicyStmt { - pub policy_name: String, - - pub table: ::core::option::Option, - - pub cmd_name: String, - - pub permissive: bool, - - pub roles: Vec, - - pub qual: ::core::option::Option>, - - pub with_check: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct AlterPolicyStmt { - pub policy_name: String, - - pub table: ::core::option::Option, - - pub roles: Vec, - - pub qual: ::core::option::Option>, - - pub with_check: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct CreateTransformStmt { - pub replace: bool, - - pub type_name: ::core::option::Option, - - pub lang: String, - - pub fromsql: ::core::option::Option, - - pub tosql: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct CreateAmStmt { - pub amname: String, - - pub handler_name: Vec, - - pub amtype: String, -} -#[derive(Clone, PartialEq)] -pub struct CreatePublicationStmt { - pub pubname: String, - - pub options: Vec, - - pub tables: Vec, - - pub for_all_tables: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterPublicationStmt { - pub pubname: String, - - pub options: Vec, - - pub tables: Vec, - - pub for_all_tables: bool, - - pub table_action: i32, -} -#[derive(Clone, PartialEq)] -pub struct CreateSubscriptionStmt { - pub subname: String, - - pub conninfo: String, - - pub publication: Vec, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AlterSubscriptionStmt { - pub kind: i32, - - pub subname: String, - - pub conninfo: String, - - pub publication: Vec, - - pub options: Vec, -} -#[derive(Clone, PartialEq)] -pub struct DropSubscriptionStmt { - pub subname: String, - - pub missing_ok: bool, - - pub behavior: i32, -} -#[derive(Clone, PartialEq)] -pub struct CreateStatsStmt { - pub defnames: Vec, - - pub stat_types: Vec, - - pub exprs: Vec, - - pub relations: Vec, - - pub stxcomment: String, - - pub if_not_exists: bool, -} -#[derive(Clone, PartialEq)] -pub struct AlterCollationStmt { - pub collname: Vec, -} -#[derive(Clone, PartialEq)] -pub struct CallStmt { - pub funccall: ::core::option::Option>, - - pub funcexpr: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct AlterStatsStmt { - pub defnames: Vec, - - pub stxstattarget: i32, - - pub missing_ok: bool, -} -#[derive(Clone, PartialEq)] -pub struct AExpr { - pub kind: i32, - - pub name: Vec, - - pub lexpr: ::core::option::Option>, - - pub rexpr: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct ColumnRef { - pub fields: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct ParamRef { - pub number: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct AConst { - pub val: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct FuncCall { - pub funcname: Vec, - - pub args: Vec, - - pub agg_order: Vec, - - pub agg_filter: ::core::option::Option>, - - pub agg_within_group: bool, - - pub agg_star: bool, - - pub agg_distinct: bool, - - pub func_variadic: bool, - - pub over: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct AStar {} -#[derive(Clone, PartialEq)] -pub struct AIndices { - pub is_slice: bool, - - pub lidx: ::core::option::Option>, - - pub uidx: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct AIndirection { - pub arg: ::core::option::Option>, - - pub indirection: Vec, -} -#[derive(Clone, PartialEq)] -pub struct AArrayExpr { - pub elements: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct ResTarget { - pub name: String, - - pub indirection: Vec, - - pub val: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct MultiAssignRef { - pub source: ::core::option::Option>, - - pub colno: i32, - - pub ncolumns: i32, -} -#[derive(Clone, PartialEq)] -pub struct TypeCast { - pub arg: ::core::option::Option>, - - pub type_name: ::core::option::Option, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct CollateClause { - pub arg: ::core::option::Option>, - - pub collname: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct SortBy { - pub node: ::core::option::Option>, - - pub sortby_dir: i32, - - pub sortby_nulls: i32, - - pub use_op: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct WindowDef { - pub name: String, - - pub refname: String, - - pub partition_clause: Vec, - - pub order_clause: Vec, - - pub frame_options: i32, - - pub start_offset: ::core::option::Option>, - - pub end_offset: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct RangeSubselect { - pub lateral: bool, - - pub subquery: ::core::option::Option>, - - pub alias: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct RangeFunction { - pub lateral: bool, - - pub ordinality: bool, - - pub is_rowsfrom: bool, - - pub functions: Vec, - - pub alias: ::core::option::Option, - - pub coldeflist: Vec, -} -#[derive(Clone, PartialEq)] -pub struct RangeTableSample { - pub relation: ::core::option::Option>, - - pub method: Vec, - - pub args: Vec, - - pub repeatable: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct RangeTableFunc { - pub lateral: bool, - - pub docexpr: ::core::option::Option>, - - pub rowexpr: ::core::option::Option>, - - pub namespaces: Vec, - - pub columns: Vec, - - pub alias: ::core::option::Option, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct RangeTableFuncCol { - pub colname: String, - - pub type_name: ::core::option::Option, - - pub for_ordinality: bool, - - pub is_not_null: bool, - - pub colexpr: ::core::option::Option>, - - pub coldefexpr: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct TypeName { - pub names: Vec, - - pub type_oid: u32, - - pub setof: bool, - - pub pct_type: bool, - - pub typmods: Vec, - - pub typemod: i32, - - pub array_bounds: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct ColumnDef { - pub colname: String, - - pub type_name: ::core::option::Option, - - pub inhcount: i32, - - pub is_local: bool, - - pub is_not_null: bool, - - pub is_from_type: bool, - - pub storage: String, - - pub raw_default: ::core::option::Option>, - - pub cooked_default: ::core::option::Option>, - - pub identity: String, - - pub identity_sequence: ::core::option::Option, - - pub generated: String, - - pub coll_clause: ::core::option::Option>, - - pub coll_oid: u32, - - pub constraints: Vec, - - pub fdwoptions: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct IndexElem { - pub name: String, - - pub expr: ::core::option::Option>, - - pub indexcolname: String, - - pub collation: Vec, - - pub opclass: Vec, - - pub opclassopts: Vec, - - pub ordering: i32, - - pub nulls_ordering: i32, -} -#[derive(Clone, PartialEq)] -pub struct Constraint { - pub contype: i32, - - pub conname: String, - - pub deferrable: bool, - - pub initdeferred: bool, - - pub location: i32, - - pub is_no_inherit: bool, - - pub raw_expr: ::core::option::Option>, - - pub cooked_expr: String, - - pub generated_when: String, - - pub keys: Vec, - - pub including: Vec, - - pub exclusions: Vec, - - pub options: Vec, - - pub indexname: String, - - pub indexspace: String, - - pub reset_default_tblspc: bool, - - pub access_method: String, - - pub where_clause: ::core::option::Option>, - - pub pktable: ::core::option::Option, - - pub fk_attrs: Vec, - - pub pk_attrs: Vec, - - pub fk_matchtype: String, - - pub fk_upd_action: String, - - pub fk_del_action: String, - - pub old_conpfeqop: Vec, - - pub old_pktable_oid: u32, - - pub skip_validation: bool, - - pub initially_valid: bool, -} -#[derive(Clone, PartialEq)] -pub struct DefElem { - pub defnamespace: String, - - pub defname: String, - - pub arg: ::core::option::Option>, - - pub defaction: i32, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct RangeTblEntry { - pub rtekind: i32, - - pub relid: u32, - - pub relkind: String, - - pub rellockmode: i32, - - pub tablesample: ::core::option::Option>, - - pub subquery: ::core::option::Option>, - - pub security_barrier: bool, - - pub jointype: i32, - - pub joinmergedcols: i32, - - pub joinaliasvars: Vec, - - pub joinleftcols: Vec, - - pub joinrightcols: Vec, - - pub functions: Vec, - - pub funcordinality: bool, - - pub tablefunc: ::core::option::Option>, - - pub values_lists: Vec, - - pub ctename: String, - - pub ctelevelsup: u32, - - pub self_reference: bool, - - pub coltypes: Vec, - - pub coltypmods: Vec, - - pub colcollations: Vec, - - pub enrname: String, - - pub enrtuples: f64, - - pub alias: ::core::option::Option, - - pub eref: ::core::option::Option, - - pub lateral: bool, - - pub inh: bool, - - pub in_from_cl: bool, - - pub required_perms: u32, - - pub check_as_user: u32, - - pub selected_cols: Vec, - - pub inserted_cols: Vec, - - pub updated_cols: Vec, - - pub extra_updated_cols: Vec, - - pub security_quals: Vec, -} -#[derive(Clone, PartialEq)] -pub struct RangeTblFunction { - pub funcexpr: ::core::option::Option>, - - pub funccolcount: i32, - - pub funccolnames: Vec, - - pub funccoltypes: Vec, - - pub funccoltypmods: Vec, - - pub funccolcollations: Vec, - - pub funcparams: Vec, -} -#[derive(Clone, PartialEq)] -pub struct TableSampleClause { - pub tsmhandler: u32, - - pub args: Vec, - - pub repeatable: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct WithCheckOption { - pub kind: i32, - - pub relname: String, - - pub polname: String, - - pub qual: ::core::option::Option>, - - pub cascaded: bool, -} -#[derive(Clone, PartialEq)] -pub struct SortGroupClause { - pub tle_sort_group_ref: u32, - - pub eqop: u32, - - pub sortop: u32, - - pub nulls_first: bool, - - pub hashable: bool, -} -#[derive(Clone, PartialEq)] -pub struct GroupingSet { - pub kind: i32, - - pub content: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct WindowClause { - pub name: String, - - pub refname: String, - - pub partition_clause: Vec, - - pub order_clause: Vec, - - pub frame_options: i32, - - pub start_offset: ::core::option::Option>, - - pub end_offset: ::core::option::Option>, - - pub start_in_range_func: u32, - - pub end_in_range_func: u32, - - pub in_range_coll: u32, - - pub in_range_asc: bool, - - pub in_range_nulls_first: bool, - - pub winref: u32, - - pub copied_order: bool, -} -#[derive(Clone, PartialEq)] -pub struct ObjectWithArgs { - pub objname: Vec, - - pub objargs: Vec, - - pub args_unspecified: bool, -} -#[derive(Clone, PartialEq)] -pub struct AccessPriv { - pub priv_name: String, - - pub cols: Vec, -} -#[derive(Clone, PartialEq)] -pub struct CreateOpClassItem { - pub itemtype: i32, - - pub name: ::core::option::Option, - - pub number: i32, - - pub order_family: Vec, - - pub class_args: Vec, - - pub storedtype: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct TableLikeClause { - pub relation: ::core::option::Option, - - pub options: u32, - - pub relation_oid: u32, -} -#[derive(Clone, PartialEq)] -pub struct FunctionParameter { - pub name: String, - - pub arg_type: ::core::option::Option, - - pub mode: i32, - - pub defexpr: ::core::option::Option>, -} -#[derive(Clone, PartialEq)] -pub struct LockingClause { - pub locked_rels: Vec, - - pub strength: i32, - - pub wait_policy: i32, -} -#[derive(Clone, PartialEq)] -pub struct RowMarkClause { - pub rti: u32, - - pub strength: i32, - - pub wait_policy: i32, - - pub pushed_down: bool, -} -#[derive(Clone, PartialEq)] -pub struct XmlSerialize { - pub xmloption: i32, - - pub expr: ::core::option::Option>, - - pub type_name: ::core::option::Option, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct WithClause { - pub ctes: Vec, - - pub recursive: bool, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct InferClause { - pub index_elems: Vec, - - pub where_clause: ::core::option::Option>, - - pub conname: String, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct OnConflictClause { - pub action: i32, - - pub infer: ::core::option::Option>, - - pub target_list: Vec, - - pub where_clause: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct CommonTableExpr { - pub ctename: String, - - pub aliascolnames: Vec, - - pub ctematerialized: i32, - - pub ctequery: ::core::option::Option>, - - pub location: i32, - - pub cterecursive: bool, - - pub cterefcount: i32, - - pub ctecolnames: Vec, - - pub ctecoltypes: Vec, - - pub ctecoltypmods: Vec, - - pub ctecolcollations: Vec, -} -#[derive(Clone, PartialEq)] -pub struct RoleSpec { - pub roletype: i32, - - pub rolename: String, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct TriggerTransition { - pub name: String, - - pub is_new: bool, - - pub is_table: bool, -} -#[derive(Clone, PartialEq)] -pub struct PartitionElem { - pub name: String, - - pub expr: ::core::option::Option>, - - pub collation: Vec, - - pub opclass: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct PartitionSpec { - pub strategy: String, - - pub part_params: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct PartitionBoundSpec { - pub strategy: String, - - pub is_default: bool, - - pub modulus: i32, - - pub remainder: i32, - - pub listdatums: Vec, - - pub lowerdatums: Vec, - - pub upperdatums: Vec, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct PartitionRangeDatum { - pub kind: i32, - - pub value: ::core::option::Option>, - - pub location: i32, -} -#[derive(Clone, PartialEq)] -pub struct PartitionCmd { - pub name: ::core::option::Option, - - pub bound: ::core::option::Option, -} -#[derive(Clone, PartialEq)] -pub struct VacuumRelation { - pub relation: ::core::option::Option, - - pub oid: u32, - - pub va_cols: Vec, -} -#[derive(Clone, PartialEq)] -pub struct InlineCodeBlock { - pub source_text: String, - - pub lang_oid: u32, - - pub lang_is_trusted: bool, - - pub atomic: bool, -} -#[derive(Clone, PartialEq)] -pub struct CallContext { - pub atomic: bool, -} -#[derive(Clone, PartialEq)] -pub struct ScanToken { - pub start: i32, - - pub end: i32, - - pub token: i32, - - pub keyword_kind: i32, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum OverridingKind { - Undefined = 0, - OverridingNotSet = 1, - OverridingUserValue = 2, - OverridingSystemValue = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum QuerySource { - Undefined = 0, - QsrcOriginal = 1, - QsrcParser = 2, - QsrcInsteadRule = 3, - QsrcQualInsteadRule = 4, - QsrcNonInsteadRule = 5, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum SortByDir { - Undefined = 0, - SortbyDefault = 1, - SortbyAsc = 2, - SortbyDesc = 3, - SortbyUsing = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum SortByNulls { - Undefined = 0, - SortbyNullsDefault = 1, - SortbyNullsFirst = 2, - SortbyNullsLast = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum AExprKind { - Undefined = 0, - AexprOp = 1, - AexprOpAny = 2, - AexprOpAll = 3, - AexprDistinct = 4, - AexprNotDistinct = 5, - AexprNullif = 6, - AexprOf = 7, - AexprIn = 8, - AexprLike = 9, - AexprIlike = 10, - AexprSimilar = 11, - AexprBetween = 12, - AexprNotBetween = 13, - AexprBetweenSym = 14, - AexprNotBetweenSym = 15, - AexprParen = 16, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum RoleSpecType { - Undefined = 0, - RolespecCstring = 1, - RolespecCurrentUser = 2, - RolespecSessionUser = 3, - RolespecPublic = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum TableLikeOption { - Undefined = 0, - CreateTableLikeComments = 1, - CreateTableLikeConstraints = 2, - CreateTableLikeDefaults = 3, - CreateTableLikeGenerated = 4, - CreateTableLikeIdentity = 5, - CreateTableLikeIndexes = 6, - CreateTableLikeStatistics = 7, - CreateTableLikeStorage = 8, - CreateTableLikeAll = 9, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum DefElemAction { - Undefined = 0, - DefelemUnspec = 1, - DefelemSet = 2, - DefelemAdd = 3, - DefelemDrop = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum PartitionRangeDatumKind { - Undefined = 0, - PartitionRangeDatumMinvalue = 1, - PartitionRangeDatumValue = 2, - PartitionRangeDatumMaxvalue = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum RteKind { - RtekindUndefined = 0, - RteRelation = 1, - RteSubquery = 2, - RteJoin = 3, - RteFunction = 4, - RteTablefunc = 5, - RteValues = 6, - RteCte = 7, - RteNamedtuplestore = 8, - RteResult = 9, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum WcoKind { - WcokindUndefined = 0, - WcoViewCheck = 1, - WcoRlsInsertCheck = 2, - WcoRlsUpdateCheck = 3, - WcoRlsConflictCheck = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum GroupingSetKind { - Undefined = 0, - GroupingSetEmpty = 1, - GroupingSetSimple = 2, - GroupingSetRollup = 3, - GroupingSetCube = 4, - GroupingSetSets = 5, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum CteMaterialize { - CtematerializeUndefined = 0, - Default = 1, - Always = 2, - Never = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum SetOperation { - Undefined = 0, - SetopNone = 1, - SetopUnion = 2, - SetopIntersect = 3, - SetopExcept = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum ObjectType { - Undefined = 0, - ObjectAccessMethod = 1, - ObjectAggregate = 2, - ObjectAmop = 3, - ObjectAmproc = 4, - ObjectAttribute = 5, - ObjectCast = 6, - ObjectColumn = 7, - ObjectCollation = 8, - ObjectConversion = 9, - ObjectDatabase = 10, - ObjectDefault = 11, - ObjectDefacl = 12, - ObjectDomain = 13, - ObjectDomconstraint = 14, - ObjectEventTrigger = 15, - ObjectExtension = 16, - ObjectFdw = 17, - ObjectForeignServer = 18, - ObjectForeignTable = 19, - ObjectFunction = 20, - ObjectIndex = 21, - ObjectLanguage = 22, - ObjectLargeobject = 23, - ObjectMatview = 24, - ObjectOpclass = 25, - ObjectOperator = 26, - ObjectOpfamily = 27, - ObjectPolicy = 28, - ObjectProcedure = 29, - ObjectPublication = 30, - ObjectPublicationRel = 31, - ObjectRole = 32, - ObjectRoutine = 33, - ObjectRule = 34, - ObjectSchema = 35, - ObjectSequence = 36, - ObjectSubscription = 37, - ObjectStatisticExt = 38, - ObjectTabconstraint = 39, - ObjectTable = 40, - ObjectTablespace = 41, - ObjectTransform = 42, - ObjectTrigger = 43, - ObjectTsconfiguration = 44, - ObjectTsdictionary = 45, - ObjectTsparser = 46, - ObjectTstemplate = 47, - ObjectType = 48, - ObjectUserMapping = 49, - ObjectView = 50, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum DropBehavior { - Undefined = 0, - DropRestrict = 1, - DropCascade = 2, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum AlterTableType { - Undefined = 0, - AtAddColumn = 1, - AtAddColumnRecurse = 2, - AtAddColumnToView = 3, - AtColumnDefault = 4, - AtCookedColumnDefault = 5, - AtDropNotNull = 6, - AtSetNotNull = 7, - AtDropExpression = 8, - AtCheckNotNull = 9, - AtSetStatistics = 10, - AtSetOptions = 11, - AtResetOptions = 12, - AtSetStorage = 13, - AtDropColumn = 14, - AtDropColumnRecurse = 15, - AtAddIndex = 16, - AtReAddIndex = 17, - AtAddConstraint = 18, - AtAddConstraintRecurse = 19, - AtReAddConstraint = 20, - AtReAddDomainConstraint = 21, - AtAlterConstraint = 22, - AtValidateConstraint = 23, - AtValidateConstraintRecurse = 24, - AtAddIndexConstraint = 25, - AtDropConstraint = 26, - AtDropConstraintRecurse = 27, - AtReAddComment = 28, - AtAlterColumnType = 29, - AtAlterColumnGenericOptions = 30, - AtChangeOwner = 31, - AtClusterOn = 32, - AtDropCluster = 33, - AtSetLogged = 34, - AtSetUnLogged = 35, - AtDropOids = 36, - AtSetTableSpace = 37, - AtSetRelOptions = 38, - AtResetRelOptions = 39, - AtReplaceRelOptions = 40, - AtEnableTrig = 41, - AtEnableAlwaysTrig = 42, - AtEnableReplicaTrig = 43, - AtDisableTrig = 44, - AtEnableTrigAll = 45, - AtDisableTrigAll = 46, - AtEnableTrigUser = 47, - AtDisableTrigUser = 48, - AtEnableRule = 49, - AtEnableAlwaysRule = 50, - AtEnableReplicaRule = 51, - AtDisableRule = 52, - AtAddInherit = 53, - AtDropInherit = 54, - AtAddOf = 55, - AtDropOf = 56, - AtReplicaIdentity = 57, - AtEnableRowSecurity = 58, - AtDisableRowSecurity = 59, - AtForceRowSecurity = 60, - AtNoForceRowSecurity = 61, - AtGenericOptions = 62, - AtAttachPartition = 63, - AtDetachPartition = 64, - AtAddIdentity = 65, - AtSetIdentity = 66, - AtDropIdentity = 67, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum GrantTargetType { - Undefined = 0, - AclTargetObject = 1, - AclTargetAllInSchema = 2, - AclTargetDefaults = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum VariableSetKind { - Undefined = 0, - VarSetValue = 1, - VarSetDefault = 2, - VarSetCurrent = 3, - VarSetMulti = 4, - VarReset = 5, - VarResetAll = 6, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum ConstrType { - Undefined = 0, - ConstrNull = 1, - ConstrNotnull = 2, - ConstrDefault = 3, - ConstrIdentity = 4, - ConstrGenerated = 5, - ConstrCheck = 6, - ConstrPrimary = 7, - ConstrUnique = 8, - ConstrExclusion = 9, - ConstrForeign = 10, - ConstrAttrDeferrable = 11, - ConstrAttrNotDeferrable = 12, - ConstrAttrDeferred = 13, - ConstrAttrImmediate = 14, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum ImportForeignSchemaType { - Undefined = 0, - FdwImportSchemaAll = 1, - FdwImportSchemaLimitTo = 2, - FdwImportSchemaExcept = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum RoleStmtType { - Undefined = 0, - RolestmtRole = 1, - RolestmtUser = 2, - RolestmtGroup = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum FetchDirection { - Undefined = 0, - FetchForward = 1, - FetchBackward = 2, - FetchAbsolute = 3, - FetchRelative = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum FunctionParameterMode { - Undefined = 0, - FuncParamIn = 1, - FuncParamOut = 2, - FuncParamInout = 3, - FuncParamVariadic = 4, - FuncParamTable = 5, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum TransactionStmtKind { - Undefined = 0, - TransStmtBegin = 1, - TransStmtStart = 2, - TransStmtCommit = 3, - TransStmtRollback = 4, - TransStmtSavepoint = 5, - TransStmtRelease = 6, - TransStmtRollbackTo = 7, - TransStmtPrepare = 8, - TransStmtCommitPrepared = 9, - TransStmtRollbackPrepared = 10, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum ViewCheckOption { - Undefined = 0, - NoCheckOption = 1, - LocalCheckOption = 2, - CascadedCheckOption = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum ClusterOption { - Undefined = 0, - CluoptRecheck = 1, - CluoptVerbose = 2, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum DiscardMode { - Undefined = 0, - DiscardAll = 1, - DiscardPlans = 2, - DiscardSequences = 3, - DiscardTemp = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum ReindexObjectType { - Undefined = 0, - ReindexObjectIndex = 1, - ReindexObjectTable = 2, - ReindexObjectSchema = 3, - ReindexObjectSystem = 4, - ReindexObjectDatabase = 5, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum AlterTsConfigType { - AlterTsconfigTypeUndefined = 0, - AlterTsconfigAddMapping = 1, - AlterTsconfigAlterMappingForToken = 2, - AlterTsconfigReplaceDict = 3, - AlterTsconfigReplaceDictForToken = 4, - AlterTsconfigDropMapping = 5, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum AlterSubscriptionType { - Undefined = 0, - AlterSubscriptionOptions = 1, - AlterSubscriptionConnection = 2, - AlterSubscriptionPublication = 3, - AlterSubscriptionRefresh = 4, - AlterSubscriptionEnabled = 5, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum OnCommitAction { - Undefined = 0, - OncommitNoop = 1, - OncommitPreserveRows = 2, - OncommitDeleteRows = 3, - OncommitDrop = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum ParamKind { - Undefined = 0, - ParamExtern = 1, - ParamExec = 2, - ParamSublink = 3, - ParamMultiexpr = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum CoercionContext { - Undefined = 0, - CoercionImplicit = 1, - CoercionAssignment = 2, - CoercionExplicit = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum CoercionForm { - Undefined = 0, - CoerceExplicitCall = 1, - CoerceExplicitCast = 2, - CoerceImplicitCast = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum BoolExprType { - Undefined = 0, - AndExpr = 1, - OrExpr = 2, - NotExpr = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum SubLinkType { - Undefined = 0, - ExistsSublink = 1, - AllSublink = 2, - AnySublink = 3, - RowcompareSublink = 4, - ExprSublink = 5, - MultiexprSublink = 6, - ArraySublink = 7, - CteSublink = 8, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum RowCompareType { - Undefined = 0, - RowcompareLt = 1, - RowcompareLe = 2, - RowcompareEq = 3, - RowcompareGe = 4, - RowcompareGt = 5, - RowcompareNe = 6, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum MinMaxOp { - Undefined = 0, - IsGreatest = 1, - IsLeast = 2, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum SqlValueFunctionOp { - SqlvalueFunctionOpUndefined = 0, - SvfopCurrentDate = 1, - SvfopCurrentTime = 2, - SvfopCurrentTimeN = 3, - SvfopCurrentTimestamp = 4, - SvfopCurrentTimestampN = 5, - SvfopLocaltime = 6, - SvfopLocaltimeN = 7, - SvfopLocaltimestamp = 8, - SvfopLocaltimestampN = 9, - SvfopCurrentRole = 10, - SvfopCurrentUser = 11, - SvfopUser = 12, - SvfopSessionUser = 13, - SvfopCurrentCatalog = 14, - SvfopCurrentSchema = 15, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum XmlExprOp { - Undefined = 0, - IsXmlconcat = 1, - IsXmlelement = 2, - IsXmlforest = 3, - IsXmlparse = 4, - IsXmlpi = 5, - IsXmlroot = 6, - IsXmlserialize = 7, - IsDocument = 8, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum XmlOptionType { - Undefined = 0, - XmloptionDocument = 1, - XmloptionContent = 2, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum NullTestType { - Undefined = 0, - IsNull = 1, - IsNotNull = 2, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum BoolTestType { - Undefined = 0, - IsTrue = 1, - IsNotTrue = 2, - IsFalse = 3, - IsNotFalse = 4, - IsUnknown = 5, - IsNotUnknown = 6, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum CmdType { - Undefined = 0, - CmdUnknown = 1, - CmdSelect = 2, - CmdUpdate = 3, - CmdInsert = 4, - CmdDelete = 5, - CmdUtility = 6, - CmdNothing = 7, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum JoinType { - Undefined = 0, - JoinInner = 1, - JoinLeft = 2, - JoinFull = 3, - JoinRight = 4, - JoinSemi = 5, - JoinAnti = 6, - JoinUniqueOuter = 7, - JoinUniqueInner = 8, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum AggStrategy { - Undefined = 0, - AggPlain = 1, - AggSorted = 2, - AggHashed = 3, - AggMixed = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum AggSplit { - Undefined = 0, - AggsplitSimple = 1, - AggsplitInitialSerial = 2, - AggsplitFinalDeserial = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum SetOpCmd { - Undefined = 0, - SetopcmdIntersect = 1, - SetopcmdIntersectAll = 2, - SetopcmdExcept = 3, - SetopcmdExceptAll = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum SetOpStrategy { - Undefined = 0, - SetopSorted = 1, - SetopHashed = 2, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum OnConflictAction { - Undefined = 0, - OnconflictNone = 1, - OnconflictNothing = 2, - OnconflictUpdate = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum LimitOption { - Undefined = 0, - Default = 1, - Count = 2, - WithTies = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum LockClauseStrength { - Undefined = 0, - LcsNone = 1, - LcsForkeyshare = 2, - LcsForshare = 3, - LcsFornokeyupdate = 4, - LcsForupdate = 5, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum LockWaitPolicy { - Undefined = 0, - LockWaitBlock = 1, - LockWaitSkip = 2, - LockWaitError = 3, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum LockTupleMode { - Undefined = 0, - LockTupleKeyShare = 1, - LockTupleShare = 2, - LockTupleNoKeyExclusive = 3, - LockTupleExclusive = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum KeywordKind { - NoKeyword = 0, - UnreservedKeyword = 1, - ColNameKeyword = 2, - TypeFuncNameKeyword = 3, - ReservedKeyword = 4, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[repr(i32)] -pub enum Token { - Nul = 0, - /// Single-character tokens that are returned 1:1 (identical with "self" list in scan.l) - /// Either supporting syntax, or single-character operators (some can be both) - /// Also see - /// - /// "%" - Ascii37 = 37, - /// "(" - Ascii40 = 40, - /// ")" - Ascii41 = 41, - /// "*" - Ascii42 = 42, - /// "+" - Ascii43 = 43, - /// "," - Ascii44 = 44, - /// "-" - Ascii45 = 45, - /// "." - Ascii46 = 46, - /// "/" - Ascii47 = 47, - /// ":" - Ascii58 = 58, - /// ";" - Ascii59 = 59, - /// "<" - Ascii60 = 60, - /// "=" - Ascii61 = 61, - /// ">" - Ascii62 = 62, - /// "?" - Ascii63 = 63, - /// "[" - Ascii91 = 91, - /// "\" - Ascii92 = 92, - /// "]" - Ascii93 = 93, - /// "^" - Ascii94 = 94, - /// Named tokens in scan.l - Ident = 258, - Uident = 259, - Fconst = 260, - Sconst = 261, - Usconst = 262, - Bconst = 263, - Xconst = 264, - Op = 265, - Iconst = 266, - Param = 267, - Typecast = 268, - DotDot = 269, - ColonEquals = 270, - EqualsGreater = 271, - LessEquals = 272, - GreaterEquals = 273, - NotEquals = 274, - SqlComment = 275, - CComment = 276, - AbortP = 277, - AbsoluteP = 278, - Access = 279, - Action = 280, - AddP = 281, - Admin = 282, - After = 283, - Aggregate = 284, - All = 285, - Also = 286, - Alter = 287, - Always = 288, - Analyse = 289, - Analyze = 290, - And = 291, - Any = 292, - Array = 293, - As = 294, - Asc = 295, - Assertion = 296, - Assignment = 297, - Asymmetric = 298, - At = 299, - Attach = 300, - Attribute = 301, - Authorization = 302, - Backward = 303, - Before = 304, - BeginP = 305, - Between = 306, - Bigint = 307, - Binary = 308, - Bit = 309, - BooleanP = 310, - Both = 311, - By = 312, - Cache = 313, - Call = 314, - Called = 315, - Cascade = 316, - Cascaded = 317, - Case = 318, - Cast = 319, - CatalogP = 320, - Chain = 321, - CharP = 322, - Character = 323, - Characteristics = 324, - Check = 325, - Checkpoint = 326, - Class = 327, - Close = 328, - Cluster = 329, - Coalesce = 330, - Collate = 331, - Collation = 332, - Column = 333, - Columns = 334, - Comment = 335, - Comments = 336, - Commit = 337, - Committed = 338, - Concurrently = 339, - Configuration = 340, - Conflict = 341, - Connection = 342, - Constraint = 343, - Constraints = 344, - ContentP = 345, - ContinueP = 346, - ConversionP = 347, - Copy = 348, - Cost = 349, - Create = 350, - Cross = 351, - Csv = 352, - Cube = 353, - CurrentP = 354, - CurrentCatalog = 355, - CurrentDate = 356, - CurrentRole = 357, - CurrentSchema = 358, - CurrentTime = 359, - CurrentTimestamp = 360, - CurrentUser = 361, - Cursor = 362, - Cycle = 363, - DataP = 364, - Database = 365, - DayP = 366, - Deallocate = 367, - Dec = 368, - DecimalP = 369, - Declare = 370, - Default = 371, - Defaults = 372, - Deferrable = 373, - Deferred = 374, - Definer = 375, - DeleteP = 376, - Delimiter = 377, - Delimiters = 378, - Depends = 379, - Desc = 380, - Detach = 381, - Dictionary = 382, - DisableP = 383, - Discard = 384, - Distinct = 385, - Do = 386, - DocumentP = 387, - DomainP = 388, - DoubleP = 389, - Drop = 390, - Each = 391, - Else = 392, - EnableP = 393, - Encoding = 394, - Encrypted = 395, - EndP = 396, - EnumP = 397, - Escape = 398, - Event = 399, - Except = 400, - Exclude = 401, - Excluding = 402, - Exclusive = 403, - Execute = 404, - Exists = 405, - Explain = 406, - Expression = 407, - Extension = 408, - External = 409, - Extract = 410, - FalseP = 411, - Family = 412, - Fetch = 413, - Filter = 414, - FirstP = 415, - FloatP = 416, - Following = 417, - For = 418, - Force = 419, - Foreign = 420, - Forward = 421, - Freeze = 422, - From = 423, - Full = 424, - Function = 425, - Functions = 426, - Generated = 427, - Global = 428, - Grant = 429, - Granted = 430, - Greatest = 431, - GroupP = 432, - Grouping = 433, - Groups = 434, - Handler = 435, - Having = 436, - HeaderP = 437, - Hold = 438, - HourP = 439, - IdentityP = 440, - IfP = 441, - Ilike = 442, - Immediate = 443, - Immutable = 444, - ImplicitP = 445, - ImportP = 446, - InP = 447, - Include = 448, - Including = 449, - Increment = 450, - Index = 451, - Indexes = 452, - Inherit = 453, - Inherits = 454, - Initially = 455, - InlineP = 456, - InnerP = 457, - Inout = 458, - InputP = 459, - Insensitive = 460, - Insert = 461, - Instead = 462, - IntP = 463, - Integer = 464, - Intersect = 465, - Interval = 466, - Into = 467, - Invoker = 468, - Is = 469, - Isnull = 470, - Isolation = 471, - Join = 472, - Key = 473, - Label = 474, - Language = 475, - LargeP = 476, - LastP = 477, - LateralP = 478, - Leading = 479, - Leakproof = 480, - Least = 481, - Left = 482, - Level = 483, - Like = 484, - Limit = 485, - Listen = 486, - Load = 487, - Local = 488, - Localtime = 489, - Localtimestamp = 490, - Location = 491, - LockP = 492, - Locked = 493, - Logged = 494, - Mapping = 495, - Match = 496, - Materialized = 497, - Maxvalue = 498, - Method = 499, - MinuteP = 500, - Minvalue = 501, - Mode = 502, - MonthP = 503, - Move = 504, - NameP = 505, - Names = 506, - National = 507, - Natural = 508, - Nchar = 509, - New = 510, - Next = 511, - Nfc = 512, - Nfd = 513, - Nfkc = 514, - Nfkd = 515, - No = 516, - None = 517, - Normalize = 518, - Normalized = 519, - Not = 520, - Nothing = 521, - Notify = 522, - Notnull = 523, - Nowait = 524, - NullP = 525, - Nullif = 526, - NullsP = 527, - Numeric = 528, - ObjectP = 529, - Of = 530, - Off = 531, - Offset = 532, - Oids = 533, - Old = 534, - On = 535, - Only = 536, - Operator = 537, - Option = 538, - Options = 539, - Or = 540, - Order = 541, - Ordinality = 542, - Others = 543, - OutP = 544, - OuterP = 545, - Over = 546, - Overlaps = 547, - Overlay = 548, - Overriding = 549, - Owned = 550, - Owner = 551, - Parallel = 552, - Parser = 553, - Partial = 554, - Partition = 555, - Passing = 556, - Password = 557, - Placing = 558, - Plans = 559, - Policy = 560, - Position = 561, - Preceding = 562, - Precision = 563, - Preserve = 564, - Prepare = 565, - Prepared = 566, - Primary = 567, - Prior = 568, - Privileges = 569, - Procedural = 570, - Procedure = 571, - Procedures = 572, - Program = 573, - Publication = 574, - Quote = 575, - Range = 576, - Read = 577, - Real = 578, - Reassign = 579, - Recheck = 580, - Recursive = 581, - Ref = 582, - References = 583, - Referencing = 584, - Refresh = 585, - Reindex = 586, - RelativeP = 587, - Release = 588, - Rename = 589, - Repeatable = 590, - Replace = 591, - Replica = 592, - Reset = 593, - Restart = 594, - Restrict = 595, - Returning = 596, - Returns = 597, - Revoke = 598, - Right = 599, - Role = 600, - Rollback = 601, - Rollup = 602, - Routine = 603, - Routines = 604, - Row = 605, - Rows = 606, - Rule = 607, - Savepoint = 608, - Schema = 609, - Schemas = 610, - Scroll = 611, - Search = 612, - SecondP = 613, - Security = 614, - Select = 615, - Sequence = 616, - Sequences = 617, - Serializable = 618, - Server = 619, - Session = 620, - SessionUser = 621, - Set = 622, - Sets = 623, - Setof = 624, - Share = 625, - Show = 626, - Similar = 627, - Simple = 628, - Skip = 629, - Smallint = 630, - Snapshot = 631, - Some = 632, - SqlP = 633, - Stable = 634, - StandaloneP = 635, - Start = 636, - Statement = 637, - Statistics = 638, - Stdin = 639, - Stdout = 640, - Storage = 641, - Stored = 642, - StrictP = 643, - StripP = 644, - Subscription = 645, - Substring = 646, - Support = 647, - Symmetric = 648, - Sysid = 649, - SystemP = 650, - Table = 651, - Tables = 652, - Tablesample = 653, - Tablespace = 654, - Temp = 655, - Template = 656, - Temporary = 657, - TextP = 658, - Then = 659, - Ties = 660, - Time = 661, - Timestamp = 662, - To = 663, - Trailing = 664, - Transaction = 665, - Transform = 666, - Treat = 667, - Trigger = 668, - Trim = 669, - TrueP = 670, - Truncate = 671, - Trusted = 672, - TypeP = 673, - TypesP = 674, - Uescape = 675, - Unbounded = 676, - Uncommitted = 677, - Unencrypted = 678, - Union = 679, - Unique = 680, - Unknown = 681, - Unlisten = 682, - Unlogged = 683, - Until = 684, - Update = 685, - User = 686, - Using = 687, - Vacuum = 688, - Valid = 689, - Validate = 690, - Validator = 691, - ValueP = 692, - Values = 693, - Varchar = 694, - Variadic = 695, - Varying = 696, - Verbose = 697, - VersionP = 698, - View = 699, - Views = 700, - Volatile = 701, - When = 702, - Where = 703, - WhitespaceP = 704, - Window = 705, - With = 706, - Within = 707, - Without = 708, - Work = 709, - Wrapper = 710, - Write = 711, - XmlP = 712, - Xmlattributes = 713, - Xmlconcat = 714, - Xmlelement = 715, - Xmlexists = 716, - Xmlforest = 717, - Xmlnamespaces = 718, - Xmlparse = 719, - Xmlpi = 720, - Xmlroot = 721, - Xmlserialize = 722, - Xmltable = 723, - YearP = 724, - YesP = 725, - Zone = 726, - NotLa = 727, - NullsLa = 728, - WithLa = 729, - Postfixop = 730, - Uminus = 731, -} From dc2d86dec3c55b63ab80d193fb6139a6762b2012 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Wed, 22 Jun 2022 19:36:53 -0500 Subject: [PATCH 211/401] chore: bump toolchain --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index 813e5e2c10fe..2640a9e0ecc2 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-06-06" +channel = "nightly-2022-06-21" components = ["rustc-dev"] From c4416f20dcaec5d93077f72470e83e150fb923b1 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Wed, 22 Jun 2022 21:19:00 -0500 Subject: [PATCH 212/401] chore: prep v1.5.1 release --- CHANGELOG.md | 26 ++++++++++++++++++++++++-- Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bfc155cd656a..0c1893bf8c38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,29 @@ ## [Unreleased] -## [1.5.0] 2022-06-13 +## [1.5.1] 2022-06-24 + +**N.B** A bug was introduced in v1.5.0/nightly-2022-06-15 which modified formatting. If you happened to run rustfmt over your code with one of those ~10 nightlies it's possible you may have seen formatting changes, and you may see additional changes after this fix since that bug has now been reverted. + +### Fixed + +- Correct an issue introduced in v1.5.0 where formatting changes were unintentionally introduced in a few cases with a large/long construct in a right hand side position (e.g. a large chain on the RHS of a local/assignment statement) +- `cargo fmt --version` properly displays the version value again [#5395](https://github.com/rust-lang/rustfmt/issues/5395) + +### Changed + +- Properly sort imports containing raw identifiers [#3791](https://github.com/rust-lang/rustfmt/issues/3791) (note this is change version gated, and not applied by default) + +### Added + +- Add new configuration option, `doc_comment_code_block_width`, which allows for setting a shorter width limit to use for formatting code snippets in doc comments [#5384](https://github.com/rust-lang/rustfmt/issues/5384) + +### Install/Download Options +- **rustup (nightly)** - nightly-2022-06-24 +- **GitHub Release Binaries** - [Release v1.5.1](https://github.com/rust-lang/rustfmt/releases/tag/v1.5.0) +- **Build from source** - [Tag v1.5.1](https://github.com/rust-lang/rustfmt/tree/v1.5.1), see instructions for how to [install rustfmt from source][install-from-source] + +## [1.5.0] 2022-06-14 ### Changed @@ -75,7 +97,7 @@ - Improved performance when formatting large and deeply nested expression trees, often found in generated code, which have many expressions that exceed `max_width` [#5128](https://github.com/rust-lang/rustfmt/issues/5128), [#4867](https://github.com/rust-lang/rustfmt/issues/4867), [#4476](https://github.com/rust-lang/rustfmt/issues/4476), [#5139](https://github.com/rust-lang/rustfmt/pull/5139) ### Install/Download Options -- **rustup (nightly)** - *pending* +- **rustup (nightly)** - nightly-2022-06-15 - **GitHub Release Binaries** - [Release v1.5.0](https://github.com/rust-lang/rustfmt/releases/tag/v1.5.0) - **Build from source** - [Tag v1.5.0](https://github.com/rust-lang/rustfmt/tree/v1.5.0), see instructions for how to [install rustfmt from source][install-from-source] diff --git a/Cargo.lock b/Cargo.lock index 639d35886dcd..311df226da19 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -485,7 +485,7 @@ dependencies = [ [[package]] name = "rustfmt-nightly" -version = "1.5.0" +version = "1.5.1" dependencies = [ "annotate-snippets", "anyhow", diff --git a/Cargo.toml b/Cargo.toml index 4f5127e1de2a..7a4e02d69edd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rustfmt-nightly" -version = "1.5.0" +version = "1.5.1" description = "Tool to find and fix Rust formatting issues" repository = "https://github.com/rust-lang/rustfmt" readme = "README.md" From b3d4fb448c79240785d8ccd78b73d40c59fc5071 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Tue, 21 Jun 2022 19:28:04 -0400 Subject: [PATCH 213/401] Allow `#[ignore]` tests to run in rustfmt's test suite There are some tests in the rustfmt test suite that are ignored by default. I believe these tests are ignored because they have caused issues with the the `rust-lang/rust` test suite. However, we recently experienced an issue (5395) that would have been avoided had these tests been running. With the introduction of the new `#[rustfmt_only_ci_test]` attribute macro we can run these tests when the `RUSTFMT_CI` environment variable is set, which will presumably only be set during rustfmts CI runs. When the environment variable is not set the `#[rustfmt_only_ci_test]` will be replaced with an `#[ignore]`. --- ci/build_and_test.bat | 1 + ci/build_and_test.sh | 1 + config_proc_macro/src/lib.rs | 13 +++++++++++++ tests/cargo-fmt/main.rs | 10 ++++++---- tests/rustfmt/main.rs | 6 ++++-- 5 files changed, 25 insertions(+), 6 deletions(-) diff --git a/ci/build_and_test.bat b/ci/build_and_test.bat index ef41017783fe..69dae1fff7b4 100755 --- a/ci/build_and_test.bat +++ b/ci/build_and_test.bat @@ -1,4 +1,5 @@ set "RUSTFLAGS=-D warnings" +set "RUSTFMT_CI=1" :: Print version information rustc -Vv || exit /b 1 diff --git a/ci/build_and_test.sh b/ci/build_and_test.sh index 8fa0f67b0d02..94991853263c 100755 --- a/ci/build_and_test.sh +++ b/ci/build_and_test.sh @@ -3,6 +3,7 @@ set -euo pipefail export RUSTFLAGS="-D warnings" +export RUSTFMT_CI=1 # Print version information rustc -Vv diff --git a/config_proc_macro/src/lib.rs b/config_proc_macro/src/lib.rs index e772c53f4236..0c54c132c97d 100644 --- a/config_proc_macro/src/lib.rs +++ b/config_proc_macro/src/lib.rs @@ -69,3 +69,16 @@ pub fn stable_only_test(_args: TokenStream, input: TokenStream) -> TokenStream { TokenStream::from_str("").unwrap() } } + +/// Used to conditionally output the TokenStream for tests that should be run as part of rustfmts +/// test suite, but should be ignored when running in the rust-lang/rust test suite. +#[proc_macro_attribute] +pub fn rustfmt_only_ci_test(_args: TokenStream, input: TokenStream) -> TokenStream { + if option_env!("RUSTFMT_CI").is_some() { + input + } else { + let mut token_stream = TokenStream::from_str("#[ignore]").unwrap(); + token_stream.extend(input); + token_stream + } +} diff --git a/tests/cargo-fmt/main.rs b/tests/cargo-fmt/main.rs index 348876cd264f..5aa4388518b8 100644 --- a/tests/cargo-fmt/main.rs +++ b/tests/cargo-fmt/main.rs @@ -4,6 +4,8 @@ use std::env; use std::path::Path; use std::process::Command; +use rustfmt_config_proc_macro::rustfmt_only_ci_test; + /// Run the cargo-fmt executable and return its output. fn cargo_fmt(args: &[&str]) -> (String, String) { let mut bin_dir = env::current_exe().unwrap(); @@ -47,7 +49,7 @@ macro_rules! assert_that { }; } -#[ignore] +#[rustfmt_only_ci_test] #[test] fn version() { assert_that!(&["--version"], starts_with("rustfmt ")); @@ -56,7 +58,7 @@ fn version() { assert_that!(&["--", "--version"], starts_with("rustfmt ")); } -#[ignore] +#[rustfmt_only_ci_test] #[test] fn print_config() { assert_that!( @@ -65,7 +67,7 @@ fn print_config() { ); } -#[ignore] +#[rustfmt_only_ci_test] #[test] fn rustfmt_help() { assert_that!(&["--", "--help"], contains("Format Rust code")); @@ -73,7 +75,7 @@ fn rustfmt_help() { assert_that!(&["--", "--help=config"], contains("Configuration Options:")); } -#[ignore] +#[rustfmt_only_ci_test] #[test] fn cargo_fmt_out_of_line_test_modules() { // See also https://github.com/rust-lang/rustfmt/issues/5119 diff --git a/tests/rustfmt/main.rs b/tests/rustfmt/main.rs index 4c6d52726f3f..636e3053e0f8 100644 --- a/tests/rustfmt/main.rs +++ b/tests/rustfmt/main.rs @@ -5,6 +5,8 @@ use std::fs::remove_file; use std::path::Path; use std::process::Command; +use rustfmt_config_proc_macro::rustfmt_only_ci_test; + /// Run the rustfmt executable and return its output. fn rustfmt(args: &[&str]) -> (String, String) { let mut bin_dir = env::current_exe().unwrap(); @@ -47,7 +49,7 @@ macro_rules! assert_that { }; } -#[ignore] +#[rustfmt_only_ci_test] #[test] fn print_config() { assert_that!( @@ -76,7 +78,7 @@ fn print_config() { remove_file("minimal-config").unwrap(); } -#[ignore] +#[rustfmt_only_ci_test] #[test] fn inline_config() { // single invocation From 2ae63f0018a7e18deb7ac0063379c2350a631fca Mon Sep 17 00:00:00 2001 From: Tom Milligan Date: Fri, 10 Jun 2022 12:17:29 +0100 Subject: [PATCH 214/401] config_type: add unstable_variant attribute --- Cargo.lock | 2 +- Cargo.toml | 2 +- config_proc_macro/Cargo.lock | 2 +- config_proc_macro/Cargo.toml | 2 +- config_proc_macro/src/attrs.rs | 27 +++++-- config_proc_macro/src/item_enum.rs | 40 ++++++++++- config_proc_macro/tests/smoke.rs | 1 + src/config/config_type.rs | 90 ++++++++++++++++++----- src/config/mod.rs | 112 +++++++++++++++++++++++++++-- 9 files changed, 244 insertions(+), 34 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 311df226da19..e51755289706 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -476,7 +476,7 @@ checksum = "fc71d2faa173b74b232dedc235e3ee1696581bb132fc116fa3626d6151a1a8fb" [[package]] name = "rustfmt-config_proc_macro" -version = "0.2.0" +version = "0.3.0" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 7a4e02d69edd..7438335eaa78 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,7 +57,7 @@ unicode-segmentation = "1.9" unicode-width = "0.1" unicode_categories = "0.1" -rustfmt-config_proc_macro = { version = "0.2", path = "config_proc_macro" } +rustfmt-config_proc_macro = { version = "0.3", path = "config_proc_macro" } # A noop dependency that changes in the Rust repository, it's a bit of a hack. # See the `src/tools/rustc-workspace-hack/README.md` file in `rust-lang/rust` diff --git a/config_proc_macro/Cargo.lock b/config_proc_macro/Cargo.lock index ecf561f28fb6..49f2f72a8d21 100644 --- a/config_proc_macro/Cargo.lock +++ b/config_proc_macro/Cargo.lock @@ -22,7 +22,7 @@ dependencies = [ [[package]] name = "rustfmt-config_proc_macro" -version = "0.2.0" +version = "0.3.0" dependencies = [ "proc-macro2", "quote", diff --git a/config_proc_macro/Cargo.toml b/config_proc_macro/Cargo.toml index a41b3a5e6bf8..d10d0469cc40 100644 --- a/config_proc_macro/Cargo.toml +++ b/config_proc_macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustfmt-config_proc_macro" -version = "0.2.0" +version = "0.3.0" edition = "2018" description = "A collection of procedural macros for rustfmt" license = "Apache-2.0/MIT" diff --git a/config_proc_macro/src/attrs.rs b/config_proc_macro/src/attrs.rs index 0baba046f9e9..dd18ff572cb1 100644 --- a/config_proc_macro/src/attrs.rs +++ b/config_proc_macro/src/attrs.rs @@ -1,8 +1,10 @@ //! This module provides utilities for handling attributes on variants -//! of `config_type` enum. Currently there are two types of attributes -//! that could appear on the variants of `config_type` enum: `doc_hint` -//! and `value`. Both comes in the form of name-value pair whose value -//! is string literal. +//! of `config_type` enum. Currently there are the following attributes +//! that could appear on the variants of `config_type` enum: +//! +//! - `doc_hint`: name-value pair whose value is string literal +//! - `value`: name-value pair whose value is string literal +//! - `unstable_variant`: name only /// Returns the value of the first `doc_hint` attribute in the given slice or /// `None` if `doc_hint` attribute is not available. @@ -27,6 +29,11 @@ pub fn find_config_value(attrs: &[syn::Attribute]) -> Option { attrs.iter().filter_map(config_value).next() } +/// Returns `true` if the there is at least one `unstable` attribute in the given slice. +pub fn any_unstable_variant(attrs: &[syn::Attribute]) -> bool { + attrs.iter().any(is_unstable_variant) +} + /// Returns a string literal value if the given attribute is `value` /// attribute or `None` otherwise. pub fn config_value(attr: &syn::Attribute) -> Option { @@ -38,6 +45,11 @@ pub fn is_config_value(attr: &syn::Attribute) -> bool { is_attr_name_value(attr, "value") } +/// Returns `true` if the given attribute is an `unstable` attribute. +pub fn is_unstable_variant(attr: &syn::Attribute) -> bool { + is_attr_path(attr, "unstable_variant") +} + fn is_attr_name_value(attr: &syn::Attribute, name: &str) -> bool { attr.parse_meta().ok().map_or(false, |meta| match meta { syn::Meta::NameValue(syn::MetaNameValue { ref path, .. }) if path.is_ident(name) => true, @@ -45,6 +57,13 @@ fn is_attr_name_value(attr: &syn::Attribute, name: &str) -> bool { }) } +fn is_attr_path(attr: &syn::Attribute, name: &str) -> bool { + attr.parse_meta().ok().map_or(false, |meta| match meta { + syn::Meta::Path(path) if path.is_ident(name) => true, + _ => false, + }) +} + fn get_name_value_str_lit(attr: &syn::Attribute, name: &str) -> Option { attr.parse_meta().ok().and_then(|meta| match meta { syn::Meta::NameValue(syn::MetaNameValue { diff --git a/config_proc_macro/src/item_enum.rs b/config_proc_macro/src/item_enum.rs index dcee77a8549c..731a7ea06077 100644 --- a/config_proc_macro/src/item_enum.rs +++ b/config_proc_macro/src/item_enum.rs @@ -1,5 +1,6 @@ use proc_macro2::TokenStream; -use quote::quote; +use quote::{quote, quote_spanned}; +use syn::spanned::Spanned; use crate::attrs::*; use crate::utils::*; @@ -47,12 +48,23 @@ fn process_variant(variant: &syn::Variant) -> TokenStream { let metas = variant .attrs .iter() - .filter(|attr| !is_doc_hint(attr) && !is_config_value(attr)); + .filter(|attr| !is_doc_hint(attr) && !is_config_value(attr) && !is_unstable_variant(attr)); let attrs = fold_quote(metas, |meta| quote!(#meta)); let syn::Variant { ident, fields, .. } = variant; quote!(#attrs #ident #fields) } +/// Return the correct syntax to pattern match on the enum variant, discarding all +/// internal field data. +fn fields_in_variant(variant: &syn::Variant) -> TokenStream { + // With thanks to https://stackoverflow.com/a/65182902 + match &variant.fields { + syn::Fields::Unnamed(_) => quote_spanned! { variant.span() => (..) }, + syn::Fields::Unit => quote_spanned! { variant.span() => }, + syn::Fields::Named(_) => quote_spanned! { variant.span() => {..} }, + } +} + fn impl_doc_hint(ident: &syn::Ident, variants: &Variants) -> TokenStream { let doc_hint = variants .iter() @@ -60,12 +72,26 @@ fn impl_doc_hint(ident: &syn::Ident, variants: &Variants) -> TokenStream { .collect::>() .join("|"); let doc_hint = format!("[{}]", doc_hint); + + let variant_stables = variants + .iter() + .map(|v| (&v.ident, fields_in_variant(&v), !unstable_of_variant(v))); + let match_patterns = fold_quote(variant_stables, |(v, fields, stable)| { + quote! { + #ident::#v #fields => #stable, + } + }); quote! { use crate::config::ConfigType; impl ConfigType for #ident { fn doc_hint() -> String { #doc_hint.to_owned() } + fn stable_variant(&self) -> bool { + match self { + #match_patterns + } + } } } } @@ -123,13 +149,21 @@ fn impl_from_str(ident: &syn::Ident, variants: &Variants) -> TokenStream { } fn doc_hint_of_variant(variant: &syn::Variant) -> String { - find_doc_hint(&variant.attrs).unwrap_or(variant.ident.to_string()) + let mut text = find_doc_hint(&variant.attrs).unwrap_or(variant.ident.to_string()); + if unstable_of_variant(&variant) { + text.push_str(" (unstable)") + }; + text } fn config_value_of_variant(variant: &syn::Variant) -> String { find_config_value(&variant.attrs).unwrap_or(variant.ident.to_string()) } +fn unstable_of_variant(variant: &syn::Variant) -> bool { + any_unstable_variant(&variant.attrs) +} + fn impl_serde(ident: &syn::Ident, variants: &Variants) -> TokenStream { let arms = fold_quote(variants.iter(), |v| { let v_ident = &v.ident; diff --git a/config_proc_macro/tests/smoke.rs b/config_proc_macro/tests/smoke.rs index 940a8a0c251e..c8a83e39c9ef 100644 --- a/config_proc_macro/tests/smoke.rs +++ b/config_proc_macro/tests/smoke.rs @@ -1,6 +1,7 @@ pub mod config { pub trait ConfigType: Sized { fn doc_hint() -> String; + fn stable_variant(&self) -> bool; } } diff --git a/src/config/config_type.rs b/src/config/config_type.rs index e37ed798cb55..26d57a13791a 100644 --- a/src/config/config_type.rs +++ b/src/config/config_type.rs @@ -6,6 +6,14 @@ pub(crate) trait ConfigType: Sized { /// Returns hint text for use in `Config::print_docs()`. For enum types, this is a /// pipe-separated list of variants; for other types it returns "". fn doc_hint() -> String; + + /// Return `true` if the variant (i.e. value of this type) is stable. + /// + /// By default, return true for all values. Enums annotated with `#[config_type]` + /// are automatically implemented, based on the `#[unstable_variant]` annotation. + fn stable_variant(&self) -> bool { + true + } } impl ConfigType for bool { @@ -51,6 +59,13 @@ impl ConfigType for IgnoreList { } macro_rules! create_config { + // Options passed in to the macro. + // + // - $i: the ident name of the option + // - $ty: the type of the option value + // - $def: the default value of the option + // - $stb: true if the option is stable + // - $dstring: description of the option ($($i:ident: $ty:ty, $def:expr, $stb:expr, $( $dstring:expr ),+ );+ $(;)*) => ( #[cfg(test)] use std::collections::HashSet; @@ -61,9 +76,12 @@ macro_rules! create_config { #[derive(Clone)] #[allow(unreachable_pub)] pub struct Config { - // For each config item, we store a bool indicating whether it has - // been accessed and the value, and a bool whether the option was - // manually initialised, or taken from the default, + // For each config item, we store: + // + // - 0: true if the value has been access + // - 1: true if the option was manually initialized + // - 2: the option value + // - 3: true if the option is unstable $($i: (Cell, bool, $ty, bool)),+ } @@ -143,18 +161,13 @@ macro_rules! create_config { fn fill_from_parsed_config(mut self, parsed: PartialConfig, dir: &Path) -> Config { $( - if let Some(val) = parsed.$i { - if self.$i.3 { + if let Some(option_value) = parsed.$i { + let option_stable = self.$i.3; + if $crate::config::config_type::is_stable_option_and_value( + stringify!($i), option_stable, &option_value + ) { self.$i.1 = true; - self.$i.2 = val; - } else { - if crate::is_nightly_channel!() { - self.$i.1 = true; - self.$i.2 = val; - } else { - eprintln!("Warning: can't set `{} = {:?}`, unstable features are only \ - available in nightly channel.", stringify!($i), val); - } + self.$i.2 = option_value; } } )+ @@ -221,12 +234,22 @@ macro_rules! create_config { match key { $( stringify!($i) => { - self.$i.1 = true; - self.$i.2 = val.parse::<$ty>() + let option_value = val.parse::<$ty>() .expect(&format!("Failed to parse override for {} (\"{}\") as a {}", stringify!($i), val, stringify!($ty))); + + // Users are currently allowed to set unstable + // options/variants via the `--config` options override. + // + // There is ongoing discussion about how to move forward here: + // https://github.com/rust-lang/rustfmt/pull/5379 + // + // For now, do not validate whether the option or value is stable, + // just always set it. + self.$i.1 = true; + self.$i.2 = option_value; } )+ _ => panic!("Unknown config key in override: {}", key) @@ -424,3 +447,38 @@ macro_rules! create_config { } ) } + +pub(crate) fn is_stable_option_and_value( + option_name: &str, + option_stable: bool, + option_value: &T, +) -> bool +where + T: PartialEq + std::fmt::Debug + ConfigType, +{ + let nightly = crate::is_nightly_channel!(); + let variant_stable = option_value.stable_variant(); + match (nightly, option_stable, variant_stable) { + // Stable with an unstable option + (false, false, _) => { + eprintln!( + "Warning: can't set `{} = {:?}`, unstable features are only \ + available in nightly channel.", + option_name, option_value + ); + false + } + // Stable with a stable option, but an unstable variant + (false, true, false) => { + eprintln!( + "Warning: can't set `{} = {:?}`, unstable variants are only \ + available in nightly channel.", + option_name, option_value + ); + false + } + // Nightly: everything allowed + // Stable with stable option and variant: allowed + (true, _, _) | (false, true, true) => true, + } +} diff --git a/src/config/mod.rs b/src/config/mod.rs index f49c18d3a460..eaada8db090a 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -408,6 +408,15 @@ mod test { #[allow(dead_code)] mod mock { use super::super::*; + use rustfmt_config_proc_macro::config_type; + + #[config_type] + pub enum PartiallyUnstableOption { + V1, + V2, + #[unstable_variant] + V3, + } create_config! { // Options that are used by the generated functions @@ -451,6 +460,63 @@ mod test { // Options that are used by the tests stable_option: bool, false, true, "A stable option"; unstable_option: bool, false, false, "An unstable option"; + partially_unstable_option: PartiallyUnstableOption, PartiallyUnstableOption::V1, true, + "A partially unstable option"; + } + + #[cfg(test)] + mod partially_unstable_option { + use super::{Config, PartialConfig, PartiallyUnstableOption}; + use rustfmt_config_proc_macro::{nightly_only_test, stable_only_test}; + use std::path::Path; + + /// From the config file, we can fill with a stable variant + #[test] + fn test_from_toml_stable_value() { + let toml = r#" + partially_unstable_option = "V2" + "#; + let partial_config: PartialConfig = toml::from_str(toml).unwrap(); + let config = Config::default(); + let config = config.fill_from_parsed_config(partial_config, Path::new("")); + assert_eq!( + config.partially_unstable_option(), + PartiallyUnstableOption::V2 + ); + } + + /// From the config file, we cannot fill with an unstable variant (stable only) + #[stable_only_test] + #[test] + fn test_from_toml_unstable_value_on_stable() { + let toml = r#" + partially_unstable_option = "V3" + "#; + let partial_config: PartialConfig = toml::from_str(toml).unwrap(); + let config = Config::default(); + let config = config.fill_from_parsed_config(partial_config, Path::new("")); + assert_eq!( + config.partially_unstable_option(), + // default value from config, i.e. fill failed + PartiallyUnstableOption::V1 + ); + } + + /// From the config file, we can fill with an unstable variant (nightly only) + #[nightly_only_test] + #[test] + fn test_from_toml_unstable_value_on_nightly() { + let toml = r#" + partially_unstable_option = "V3" + "#; + let partial_config: PartialConfig = toml::from_str(toml).unwrap(); + let config = Config::default(); + let config = config.fill_from_parsed_config(partial_config, Path::new("")); + assert_eq!( + config.partially_unstable_option(), + PartiallyUnstableOption::V3 + ); + } } } @@ -489,6 +555,11 @@ mod test { assert_eq!(config.was_set().verbose(), false); } + const PRINT_DOCS_STABLE_OPTION: &str = "stable_option Default: false"; + const PRINT_DOCS_UNSTABLE_OPTION: &str = "unstable_option Default: false (unstable)"; + const PRINT_DOCS_PARTIALLY_UNSTABLE_OPTION: &str = + "partially_unstable_option [V1|V2|V3 (unstable)] Default: V1"; + #[test] fn test_print_docs_exclude_unstable() { use self::mock::Config; @@ -497,10 +568,9 @@ mod test { Config::print_docs(&mut output, false); let s = str::from_utf8(&output).unwrap(); - - assert_eq!(s.contains("stable_option"), true); - assert_eq!(s.contains("unstable_option"), false); - assert_eq!(s.contains("(unstable)"), false); + assert_eq!(s.contains(PRINT_DOCS_STABLE_OPTION), true); + assert_eq!(s.contains(PRINT_DOCS_UNSTABLE_OPTION), false); + assert_eq!(s.contains(PRINT_DOCS_PARTIALLY_UNSTABLE_OPTION), true); } #[test] @@ -511,9 +581,9 @@ mod test { Config::print_docs(&mut output, true); let s = str::from_utf8(&output).unwrap(); - assert_eq!(s.contains("stable_option"), true); - assert_eq!(s.contains("unstable_option"), true); - assert_eq!(s.contains("(unstable)"), true); + assert_eq!(s.contains(PRINT_DOCS_STABLE_OPTION), true); + assert_eq!(s.contains(PRINT_DOCS_UNSTABLE_OPTION), true); + assert_eq!(s.contains(PRINT_DOCS_PARTIALLY_UNSTABLE_OPTION), true); } #[test] @@ -921,4 +991,32 @@ make_backup = false assert_eq!(config.single_line_if_else_max_width(), 100); } } + + #[cfg(test)] + mod partially_unstable_option { + use super::mock::{Config, PartiallyUnstableOption}; + use super::*; + + /// From the command line, we can override with a stable variant. + #[test] + fn test_override_stable_value() { + let mut config = Config::default(); + config.override_value("partially_unstable_option", "V2"); + assert_eq!( + config.partially_unstable_option(), + PartiallyUnstableOption::V2 + ); + } + + /// From the command line, we can override with an unstable variant. + #[test] + fn test_override_unstable_value() { + let mut config = Config::default(); + config.override_value("partially_unstable_option", "V3"); + assert_eq!( + config.partially_unstable_option(), + PartiallyUnstableOption::V3 + ); + } + } } From 35f4c55bf476884feadf525e110453f6815f8dd8 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Sun, 3 Jul 2022 22:09:42 -0500 Subject: [PATCH 215/401] refactor: remove some unnecessary clones --- src/chains.rs | 21 ++++++++++----------- src/expr.rs | 9 ++++----- src/macros.rs | 15 +++++++-------- src/utils.rs | 5 +++-- 4 files changed, 24 insertions(+), 26 deletions(-) diff --git a/src/chains.rs b/src/chains.rs index e26e24ec55ad..5bccb22db4c1 100644 --- a/src/chains.rs +++ b/src/chains.rs @@ -70,8 +70,8 @@ use crate::rewrite::{Rewrite, RewriteContext}; use crate::shape::Shape; use crate::source_map::SpanUtils; use crate::utils::{ - self, first_line_width, last_line_extendable, last_line_width, mk_sp, rewrite_ident, - trimmed_last_line_width, wrap_str, + self, filtered_str_fits, first_line_width, last_line_extendable, last_line_width, mk_sp, + rewrite_ident, trimmed_last_line_width, wrap_str, }; pub(crate) fn rewrite_chain( @@ -810,15 +810,14 @@ impl<'a> ChainFormatter for ChainFormatterVisual<'a> { .visual_indent(self.offset) .sub_width(self.offset)?; let rewrite = item.rewrite(context, child_shape)?; - match wrap_str(rewrite, context.config.max_width(), shape) { - Some(rewrite) => root_rewrite.push_str(&rewrite), - None => { - // We couldn't fit in at the visual indent, try the last - // indent. - let rewrite = item.rewrite(context, parent_shape)?; - root_rewrite.push_str(&rewrite); - self.offset = 0; - } + if filtered_str_fits(&rewrite, context.config.max_width(), shape) { + root_rewrite.push_str(&rewrite); + } else { + // We couldn't fit in at the visual indent, try the last + // indent. + let rewrite = item.rewrite(context, parent_shape)?; + root_rewrite.push_str(&rewrite); + self.offset = 0; } self.shared.children = &self.shared.children[1..]; diff --git a/src/expr.rs b/src/expr.rs index e4cc93026f10..13d068d0c2dd 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -29,9 +29,9 @@ use crate::spanned::Spanned; use crate::string::{rewrite_string, StringFormat}; use crate::types::{rewrite_path, PathContext}; use crate::utils::{ - colon_spaces, contains_skip, count_newlines, first_line_ends_with, inner_attributes, - last_line_extendable, last_line_width, mk_sp, outer_attributes, semicolon_for_expr, - unicode_str_width, wrap_str, + colon_spaces, contains_skip, count_newlines, filtered_str_fits, first_line_ends_with, + inner_attributes, last_line_extendable, last_line_width, mk_sp, outer_attributes, + semicolon_for_expr, unicode_str_width, wrap_str, }; use crate::vertical::rewrite_with_alignment; use crate::visitor::FmtVisitor; @@ -2037,8 +2037,7 @@ fn choose_rhs( match (orig_rhs, new_rhs) { (Some(ref orig_rhs), Some(ref new_rhs)) - if wrap_str(new_rhs.clone(), context.config.max_width(), new_shape) - .is_none() => + if !filtered_str_fits(&new_rhs, context.config.max_width(), new_shape) => { Some(format!("{}{}", before_space_str, orig_rhs)) } diff --git a/src/macros.rs b/src/macros.rs index f4b2bcf28157..e78ef1f80667 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -35,8 +35,8 @@ use crate::shape::{Indent, Shape}; use crate::source_map::SpanUtils; use crate::spanned::Spanned; use crate::utils::{ - format_visibility, indent_next_line, is_empty_line, mk_sp, remove_trailing_white_spaces, - rewrite_ident, trim_left_preserve_layout, wrap_str, NodeIdExt, + filtered_str_fits, format_visibility, indent_next_line, is_empty_line, mk_sp, + remove_trailing_white_spaces, rewrite_ident, trim_left_preserve_layout, NodeIdExt, }; use crate::visitor::FmtVisitor; @@ -1241,15 +1241,14 @@ impl MacroBranch { } } }; - let new_body = wrap_str( - new_body_snippet.snippet.to_string(), - config.max_width(), - shape, - )?; + + if !filtered_str_fits(&new_body_snippet.snippet, config.max_width(), shape) { + return None; + } // Indent the body since it is in a block. let indent_str = body_indent.to_string(&config); - let mut new_body = LineClasses::new(new_body.trim_end()) + let mut new_body = LineClasses::new(new_body_snippet.snippet.trim_end()) .enumerate() .fold( (String::new(), true), diff --git a/src/utils.rs b/src/utils.rs index 58fd95c656e7..0e87d6bb50e2 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -384,14 +384,15 @@ macro_rules! skip_out_of_file_lines_range_visitor { // Wraps String in an Option. Returns Some when the string adheres to the // Rewrite constraints defined for the Rewrite trait and None otherwise. pub(crate) fn wrap_str(s: String, max_width: usize, shape: Shape) -> Option { - if is_valid_str(&filter_normal_code(&s), max_width, shape) { + if filtered_str_fits(&s, max_width, shape) { Some(s) } else { None } } -fn is_valid_str(snippet: &str, max_width: usize, shape: Shape) -> bool { +pub(crate) fn filtered_str_fits(snippet: &str, max_width: usize, shape: Shape) -> bool { + let snippet = &filter_normal_code(snippet); if !snippet.is_empty() { // First line must fits with `shape.width`. if first_line_width(snippet) > shape.width { From 2403f827bf1e427a04ce45c88a64cbebe91041d7 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Sat, 9 Jul 2022 17:54:59 -0400 Subject: [PATCH 216/401] Add test case for issue 1306 which was resolved Closes 1306 It's unclear when the issue was fixed, but it cannot be reproduced. --- tests/source/issue_1306.rs | 29 +++++++++++++++++++++++++++++ tests/target/issue_1306.rs | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 tests/source/issue_1306.rs create mode 100644 tests/target/issue_1306.rs diff --git a/tests/source/issue_1306.rs b/tests/source/issue_1306.rs new file mode 100644 index 000000000000..03b78e34108c --- /dev/null +++ b/tests/source/issue_1306.rs @@ -0,0 +1,29 @@ +// rustfmt-max_width: 160 +// rustfmt-fn_call_width: 96 +// rustfmt-fn_args_layout: Compressed +// rustfmt-trailing_comma: Always +// rustfmt-wrap_comments: true + +fn foo() { + for elem in try!(gen_epub_book::ops::parse_descriptor_file(&mut try!(File::open(&opts.source_file.1).map_err(|_| { + gen_epub_book::Error::Io { + desc: "input file", + op: "open", + more: None, + } + })), + "input file")) { + println!("{}", elem); + } +} + +fn write_content() { + io::copy(try!(File::open(in_f).map_err(|_| { + Error::Io { + desc: "Content", + op: "open", + more: None, + } + })), + w); +} diff --git a/tests/target/issue_1306.rs b/tests/target/issue_1306.rs new file mode 100644 index 000000000000..6bb514cdfe55 --- /dev/null +++ b/tests/target/issue_1306.rs @@ -0,0 +1,33 @@ +// rustfmt-max_width: 160 +// rustfmt-fn_call_width: 96 +// rustfmt-fn_args_layout: Compressed +// rustfmt-trailing_comma: Always +// rustfmt-wrap_comments: true + +fn foo() { + for elem in try!(gen_epub_book::ops::parse_descriptor_file( + &mut try!(File::open(&opts.source_file.1).map_err(|_| { + gen_epub_book::Error::Io { + desc: "input file", + op: "open", + more: None, + } + })), + "input file" + )) { + println!("{}", elem); + } +} + +fn write_content() { + io::copy( + try!(File::open(in_f).map_err(|_| { + Error::Io { + desc: "Content", + op: "open", + more: None, + } + })), + w, + ); +} From c240f3a6b3f0b7f91113dc999217f40f029a2776 Mon Sep 17 00:00:00 2001 From: Tom Milligan Date: Wed, 13 Jul 2022 01:31:19 +0100 Subject: [PATCH 217/401] feat: add skip_macro_invocations option (#5347) * feat: add skip_macro_names option * [review] update configuration documentation * [review] fix docstring * [feat] implement wildcard macro invocation skip * commit missed files * [review] test override skip macro names * [review] skip_macro_names -> skip_macro_invocations * [review] expand doc configuration * [review] add lots more tests * [review] add use alias test examples * [review] add link to standard macro behaviour --- Configurations.md | 56 +++++++++ src/config/config_type.rs | 7 ++ src/config/macro_names.rs | 118 ++++++++++++++++++ src/config/mod.rs | 20 +++ src/skip.rs | 10 +- src/test/configuration_snippet.rs | 7 +- src/visitor.rs | 13 +- tests/source/skip_macro_invocations/all.rs | 11 ++ .../skip_macro_invocations/all_and_name.rs | 11 ++ tests/source/skip_macro_invocations/empty.rs | 11 ++ tests/source/skip_macro_invocations/name.rs | 11 ++ .../skip_macro_invocations/name_unknown.rs | 6 + tests/source/skip_macro_invocations/names.rs | 16 +++ .../path_qualified_invocation_mismatch.rs | 6 + .../path_qualified_match.rs | 6 + .../path_qualified_name_mismatch.rs | 6 + .../use_alias_examples.rs | 32 +++++ tests/target/skip_macro_invocations/all.rs | 11 ++ .../skip_macro_invocations/all_and_name.rs | 11 ++ tests/target/skip_macro_invocations/empty.rs | 11 ++ tests/target/skip_macro_invocations/name.rs | 11 ++ .../skip_macro_invocations/name_unknown.rs | 6 + tests/target/skip_macro_invocations/names.rs | 16 +++ .../path_qualified_invocation_mismatch.rs | 6 + .../path_qualified_match.rs | 6 + .../path_qualified_name_mismatch.rs | 6 + .../use_alias_examples.rs | 32 +++++ 27 files changed, 459 insertions(+), 4 deletions(-) create mode 100644 src/config/macro_names.rs create mode 100644 tests/source/skip_macro_invocations/all.rs create mode 100644 tests/source/skip_macro_invocations/all_and_name.rs create mode 100644 tests/source/skip_macro_invocations/empty.rs create mode 100644 tests/source/skip_macro_invocations/name.rs create mode 100644 tests/source/skip_macro_invocations/name_unknown.rs create mode 100644 tests/source/skip_macro_invocations/names.rs create mode 100644 tests/source/skip_macro_invocations/path_qualified_invocation_mismatch.rs create mode 100644 tests/source/skip_macro_invocations/path_qualified_match.rs create mode 100644 tests/source/skip_macro_invocations/path_qualified_name_mismatch.rs create mode 100644 tests/source/skip_macro_invocations/use_alias_examples.rs create mode 100644 tests/target/skip_macro_invocations/all.rs create mode 100644 tests/target/skip_macro_invocations/all_and_name.rs create mode 100644 tests/target/skip_macro_invocations/empty.rs create mode 100644 tests/target/skip_macro_invocations/name.rs create mode 100644 tests/target/skip_macro_invocations/name_unknown.rs create mode 100644 tests/target/skip_macro_invocations/names.rs create mode 100644 tests/target/skip_macro_invocations/path_qualified_invocation_mismatch.rs create mode 100644 tests/target/skip_macro_invocations/path_qualified_match.rs create mode 100644 tests/target/skip_macro_invocations/path_qualified_name_mismatch.rs create mode 100644 tests/target/skip_macro_invocations/use_alias_examples.rs diff --git a/Configurations.md b/Configurations.md index 8b96b9d36892..5579b5095afa 100644 --- a/Configurations.md +++ b/Configurations.md @@ -1014,6 +1014,62 @@ macro_rules! foo { See also [`format_macro_matchers`](#format_macro_matchers). +## `skip_macro_invocations` + +Skip formatting the bodies of macro invocations with the following names. + +rustfmt will not format any macro invocation for macros with names set in this list. +Including the special value "*" will prevent any macro invocations from being formatted. + +Note: This option does not have any impact on how rustfmt formats macro definitions. + +- **Default value**: `[]` +- **Possible values**: a list of macro name idents, `["name_0", "name_1", ..., "*"]` +- **Stable**: No (tracking issue: [#5346](https://github.com/rust-lang/rustfmt/issues/5346)) + +#### `[]` (default): + +rustfmt will follow its standard approach to formatting macro invocations. + +No macro invocations will be skipped based on their name. More information about rustfmt's standard macro invocation formatting behavior can be found in [#5437](https://github.com/rust-lang/rustfmt/discussions/5437). + +```rust +lorem!( + const _: u8 = 0; +); + +ipsum!( + const _: u8 = 0; +); +``` + +#### `["lorem"]`: + +The named macro invocations will be skipped. + +```rust +lorem!( + const _: u8 = 0; +); + +ipsum!( + const _: u8 = 0; +); +``` + +#### `["*"]`: + +The special selector `*` will skip all macro invocations. + +```rust +lorem!( + const _: u8 = 0; +); + +ipsum!( + const _: u8 = 0; +); +``` ## `format_strings` diff --git a/src/config/config_type.rs b/src/config/config_type.rs index 26d57a13791a..48f4d9ce80e4 100644 --- a/src/config/config_type.rs +++ b/src/config/config_type.rs @@ -1,4 +1,5 @@ use crate::config::file_lines::FileLines; +use crate::config::macro_names::MacroSelectors; use crate::config::options::{IgnoreList, WidthHeuristics}; /// Trait for types that can be used in `Config`. @@ -46,6 +47,12 @@ impl ConfigType for FileLines { } } +impl ConfigType for MacroSelectors { + fn doc_hint() -> String { + String::from("[, ...]") + } +} + impl ConfigType for WidthHeuristics { fn doc_hint() -> String { String::new() diff --git a/src/config/macro_names.rs b/src/config/macro_names.rs new file mode 100644 index 000000000000..26ad78d6dcae --- /dev/null +++ b/src/config/macro_names.rs @@ -0,0 +1,118 @@ +//! This module contains types and functions to support formatting specific macros. + +use itertools::Itertools; +use std::{fmt, str}; + +use serde::{Deserialize, Serialize}; +use serde_json as json; +use thiserror::Error; + +/// Defines the name of a macro. +#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize)] +pub struct MacroName(String); + +impl MacroName { + pub fn new(other: String) -> Self { + Self(other) + } +} + +impl fmt::Display for MacroName { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl From for String { + fn from(other: MacroName) -> Self { + other.0 + } +} + +/// Defines a selector to match against a macro. +#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize)] +pub enum MacroSelector { + Name(MacroName), + All, +} + +impl fmt::Display for MacroSelector { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Name(name) => name.fmt(f), + Self::All => write!(f, "*"), + } + } +} + +impl str::FromStr for MacroSelector { + type Err = std::convert::Infallible; + + fn from_str(s: &str) -> Result { + Ok(match s { + "*" => MacroSelector::All, + name => MacroSelector::Name(MacroName(name.to_owned())), + }) + } +} + +/// A set of macro selectors. +#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)] +pub struct MacroSelectors(pub Vec); + +impl fmt::Display for MacroSelectors { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0.iter().format(", ")) + } +} + +#[derive(Error, Debug)] +pub enum MacroSelectorsError { + #[error("{0}")] + Json(json::Error), +} + +// This impl is needed for `Config::override_value` to work for use in tests. +impl str::FromStr for MacroSelectors { + type Err = MacroSelectorsError; + + fn from_str(s: &str) -> Result { + let raw: Vec<&str> = json::from_str(s).map_err(MacroSelectorsError::Json)?; + Ok(Self( + raw.into_iter() + .map(|raw| { + MacroSelector::from_str(raw).expect("MacroSelector from_str is infallible") + }) + .collect(), + )) + } +} + +#[cfg(test)] +mod test { + use super::*; + use std::str::FromStr; + + #[test] + fn macro_names_from_str() { + let macro_names = MacroSelectors::from_str(r#"["foo", "*", "bar"]"#).unwrap(); + assert_eq!( + macro_names, + MacroSelectors( + [ + MacroSelector::Name(MacroName("foo".to_owned())), + MacroSelector::All, + MacroSelector::Name(MacroName("bar".to_owned())) + ] + .into_iter() + .collect() + ) + ); + } + + #[test] + fn macro_names_display() { + let macro_names = MacroSelectors::from_str(r#"["foo", "*", "bar"]"#).unwrap(); + assert_eq!(format!("{}", macro_names), "foo, *, bar"); + } +} diff --git a/src/config/mod.rs b/src/config/mod.rs index eaada8db090a..0c6a3cbc9532 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -13,6 +13,8 @@ pub use crate::config::file_lines::{FileLines, FileName, Range}; #[allow(unreachable_pub)] pub use crate::config::lists::*; #[allow(unreachable_pub)] +pub use crate::config::macro_names::{MacroSelector, MacroSelectors}; +#[allow(unreachable_pub)] pub use crate::config::options::*; #[macro_use] @@ -22,6 +24,7 @@ pub(crate) mod options; pub(crate) mod file_lines; pub(crate) mod lists; +pub(crate) mod macro_names; // This macro defines configuration options used in rustfmt. Each option // is defined as follows: @@ -67,6 +70,8 @@ create_config! { format_macro_matchers: bool, false, false, "Format the metavariable matching patterns in macros"; format_macro_bodies: bool, true, false, "Format the bodies of macros"; + skip_macro_invocations: MacroSelectors, MacroSelectors::default(), false, + "Skip formatting the bodies of macros invoked with the following names."; hex_literal_case: HexLiteralCase, HexLiteralCase::Preserve, false, "Format hexadecimal integer literals"; @@ -403,6 +408,7 @@ mod test { use super::*; use std::str; + use crate::config::macro_names::MacroName; use rustfmt_config_proc_macro::{nightly_only_test, stable_only_test}; #[allow(dead_code)] @@ -611,6 +617,7 @@ normalize_doc_attributes = false format_strings = false format_macro_matchers = false format_macro_bodies = true +skip_macro_invocations = [] hex_literal_case = "Preserve" empty_item_single_line = true struct_lit_single_line = true @@ -1019,4 +1026,17 @@ make_backup = false ); } } + + #[test] + fn test_override_skip_macro_invocations() { + let mut config = Config::default(); + config.override_value("skip_macro_invocations", r#"["*", "println"]"#); + assert_eq!( + config.skip_macro_invocations(), + MacroSelectors(vec![ + MacroSelector::All, + MacroSelector::Name(MacroName::new("println".to_owned())) + ]) + ); + } } diff --git a/src/skip.rs b/src/skip.rs index 0fdc097efc23..8b2fd7736ae5 100644 --- a/src/skip.rs +++ b/src/skip.rs @@ -7,6 +7,7 @@ use rustc_ast_pretty::pprust; /// by other context. Query this context to know if you need skip a block. #[derive(Default, Clone)] pub(crate) struct SkipContext { + pub(crate) all_macros: bool, macros: Vec, attributes: Vec, } @@ -23,8 +24,15 @@ impl SkipContext { self.attributes.append(&mut other.attributes); } + pub(crate) fn update_macros(&mut self, other: T) + where + T: IntoIterator, + { + self.macros.extend(other.into_iter()); + } + pub(crate) fn skip_macro(&self, name: &str) -> bool { - self.macros.iter().any(|n| n == name) + self.all_macros || self.macros.iter().any(|n| n == name) } pub(crate) fn skip_attribute(&self, name: &str) -> bool { diff --git a/src/test/configuration_snippet.rs b/src/test/configuration_snippet.rs index c8fda7c8556d..c70b3c5facd5 100644 --- a/src/test/configuration_snippet.rs +++ b/src/test/configuration_snippet.rs @@ -27,8 +27,13 @@ impl ConfigurationSection { lazy_static! { static ref CONFIG_NAME_REGEX: regex::Regex = regex::Regex::new(r"^## `([^`]+)`").expect("failed creating configuration pattern"); + // Configuration values, which will be passed to `from_str`: + // + // - must be prefixed with `####` + // - must be wrapped in backticks + // - may by wrapped in double quotes (which will be stripped) static ref CONFIG_VALUE_REGEX: regex::Regex = - regex::Regex::new(r#"^#### `"?([^`"]+)"?`"#) + regex::Regex::new(r#"^#### `"?([^`]+?)"?`"#) .expect("failed creating configuration value pattern"); } diff --git a/src/visitor.rs b/src/visitor.rs index 9a0e0752c12f..b93153de154a 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -8,7 +8,7 @@ use rustc_span::{symbol, BytePos, Pos, Span}; use crate::attr::*; use crate::comment::{contains_comment, rewrite_comment, CodeCharKind, CommentCodeSlices}; use crate::config::Version; -use crate::config::{BraceStyle, Config}; +use crate::config::{BraceStyle, Config, MacroSelector}; use crate::coverage::transform_missing_snippet; use crate::items::{ format_impl, format_trait, format_trait_alias, is_mod_decl, is_use_item, rewrite_extern_crate, @@ -770,6 +770,15 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { snippet_provider: &'a SnippetProvider, report: FormatReport, ) -> FmtVisitor<'a> { + let mut skip_context = SkipContext::default(); + let mut macro_names = Vec::new(); + for macro_selector in config.skip_macro_invocations().0 { + match macro_selector { + MacroSelector::Name(name) => macro_names.push(name.to_string()), + MacroSelector::All => skip_context.all_macros = true, + } + } + skip_context.update_macros(macro_names); FmtVisitor { parent_context: None, parse_sess: parse_session, @@ -784,7 +793,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { is_macro_def: false, macro_rewrite_failure: false, report, - skip_context: Default::default(), + skip_context, } } diff --git a/tests/source/skip_macro_invocations/all.rs b/tests/source/skip_macro_invocations/all.rs new file mode 100644 index 000000000000..d0437ee10fd1 --- /dev/null +++ b/tests/source/skip_macro_invocations/all.rs @@ -0,0 +1,11 @@ +// rustfmt-skip_macro_invocations: ["*"] + +// Should skip this invocation +items!( + const _: u8 = 0; +); + +// Should skip this invocation +renamed_items!( + const _: u8 = 0; +); diff --git a/tests/source/skip_macro_invocations/all_and_name.rs b/tests/source/skip_macro_invocations/all_and_name.rs new file mode 100644 index 000000000000..1f6722344fe3 --- /dev/null +++ b/tests/source/skip_macro_invocations/all_and_name.rs @@ -0,0 +1,11 @@ +// rustfmt-skip_macro_invocations: ["*","items"] + +// Should skip this invocation +items!( + const _: u8 = 0; +); + +// Should also skip this invocation, as the wildcard covers it +renamed_items!( + const _: u8 = 0; +); diff --git a/tests/source/skip_macro_invocations/empty.rs b/tests/source/skip_macro_invocations/empty.rs new file mode 100644 index 000000000000..f3dd89dc4db3 --- /dev/null +++ b/tests/source/skip_macro_invocations/empty.rs @@ -0,0 +1,11 @@ +// rustfmt-skip_macro_invocations: [] + +// Should not skip this invocation +items!( + const _: u8 = 0; +); + +// Should not skip this invocation +renamed_items!( + const _: u8 = 0; +); diff --git a/tests/source/skip_macro_invocations/name.rs b/tests/source/skip_macro_invocations/name.rs new file mode 100644 index 000000000000..7fa5d3a6f715 --- /dev/null +++ b/tests/source/skip_macro_invocations/name.rs @@ -0,0 +1,11 @@ +// rustfmt-skip_macro_invocations: ["items"] + +// Should skip this invocation +items!( + const _: u8 = 0; +); + +// Should not skip this invocation +renamed_items!( + const _: u8 = 0; +); diff --git a/tests/source/skip_macro_invocations/name_unknown.rs b/tests/source/skip_macro_invocations/name_unknown.rs new file mode 100644 index 000000000000..d56695325240 --- /dev/null +++ b/tests/source/skip_macro_invocations/name_unknown.rs @@ -0,0 +1,6 @@ +// rustfmt-skip_macro_invocations: ["unknown"] + +// Should not skip this invocation +items!( + const _: u8 = 0; +); diff --git a/tests/source/skip_macro_invocations/names.rs b/tests/source/skip_macro_invocations/names.rs new file mode 100644 index 000000000000..a920381a4552 --- /dev/null +++ b/tests/source/skip_macro_invocations/names.rs @@ -0,0 +1,16 @@ +// rustfmt-skip_macro_invocations: ["foo","bar"] + +// Should skip this invocation +foo!( + const _: u8 = 0; +); + +// Should skip this invocation +bar!( + const _: u8 = 0; +); + +// Should not skip this invocation +baz!( + const _: u8 = 0; +); diff --git a/tests/source/skip_macro_invocations/path_qualified_invocation_mismatch.rs b/tests/source/skip_macro_invocations/path_qualified_invocation_mismatch.rs new file mode 100644 index 000000000000..61296869a506 --- /dev/null +++ b/tests/source/skip_macro_invocations/path_qualified_invocation_mismatch.rs @@ -0,0 +1,6 @@ +// rustfmt-skip_macro_invocations: ["items"] + +// Should not skip this invocation +self::items!( + const _: u8 = 0; +); diff --git a/tests/source/skip_macro_invocations/path_qualified_match.rs b/tests/source/skip_macro_invocations/path_qualified_match.rs new file mode 100644 index 000000000000..9398918a9e11 --- /dev/null +++ b/tests/source/skip_macro_invocations/path_qualified_match.rs @@ -0,0 +1,6 @@ +// rustfmt-skip_macro_invocations: ["self::items"] + +// Should skip this invocation +self::items!( + const _: u8 = 0; +); diff --git a/tests/source/skip_macro_invocations/path_qualified_name_mismatch.rs b/tests/source/skip_macro_invocations/path_qualified_name_mismatch.rs new file mode 100644 index 000000000000..4e3eb542dbea --- /dev/null +++ b/tests/source/skip_macro_invocations/path_qualified_name_mismatch.rs @@ -0,0 +1,6 @@ +// rustfmt-skip_macro_invocations: ["self::items"] + +// Should not skip this invocation +items!( + const _: u8 = 0; +); diff --git a/tests/source/skip_macro_invocations/use_alias_examples.rs b/tests/source/skip_macro_invocations/use_alias_examples.rs new file mode 100644 index 000000000000..43cb8015de58 --- /dev/null +++ b/tests/source/skip_macro_invocations/use_alias_examples.rs @@ -0,0 +1,32 @@ +// rustfmt-skip_macro_invocations: ["aaa","ccc"] + +// These tests demonstrate a realistic use case with use aliases. +// The use statements should not impact functionality in any way. + +use crate::{aaa, bbb, ddd}; + +// No use alias, invocation in list +// Should skip this invocation +aaa!( + const _: u8 = 0; +); + +// Use alias, invocation in list +// Should skip this invocation +use crate::bbb as ccc; +ccc!( + const _: u8 = 0; +); + +// Use alias, invocation not in list +// Should not skip this invocation +use crate::ddd as eee; +eee!( + const _: u8 = 0; +); + +// No use alias, invocation not in list +// Should not skip this invocation +fff!( + const _: u8 = 0; +); diff --git a/tests/target/skip_macro_invocations/all.rs b/tests/target/skip_macro_invocations/all.rs new file mode 100644 index 000000000000..d0437ee10fd1 --- /dev/null +++ b/tests/target/skip_macro_invocations/all.rs @@ -0,0 +1,11 @@ +// rustfmt-skip_macro_invocations: ["*"] + +// Should skip this invocation +items!( + const _: u8 = 0; +); + +// Should skip this invocation +renamed_items!( + const _: u8 = 0; +); diff --git a/tests/target/skip_macro_invocations/all_and_name.rs b/tests/target/skip_macro_invocations/all_and_name.rs new file mode 100644 index 000000000000..1f6722344fe3 --- /dev/null +++ b/tests/target/skip_macro_invocations/all_and_name.rs @@ -0,0 +1,11 @@ +// rustfmt-skip_macro_invocations: ["*","items"] + +// Should skip this invocation +items!( + const _: u8 = 0; +); + +// Should also skip this invocation, as the wildcard covers it +renamed_items!( + const _: u8 = 0; +); diff --git a/tests/target/skip_macro_invocations/empty.rs b/tests/target/skip_macro_invocations/empty.rs new file mode 100644 index 000000000000..4a398cc59c6e --- /dev/null +++ b/tests/target/skip_macro_invocations/empty.rs @@ -0,0 +1,11 @@ +// rustfmt-skip_macro_invocations: [] + +// Should not skip this invocation +items!( + const _: u8 = 0; +); + +// Should not skip this invocation +renamed_items!( + const _: u8 = 0; +); diff --git a/tests/target/skip_macro_invocations/name.rs b/tests/target/skip_macro_invocations/name.rs new file mode 100644 index 000000000000..c4d577269c6f --- /dev/null +++ b/tests/target/skip_macro_invocations/name.rs @@ -0,0 +1,11 @@ +// rustfmt-skip_macro_invocations: ["items"] + +// Should skip this invocation +items!( + const _: u8 = 0; +); + +// Should not skip this invocation +renamed_items!( + const _: u8 = 0; +); diff --git a/tests/target/skip_macro_invocations/name_unknown.rs b/tests/target/skip_macro_invocations/name_unknown.rs new file mode 100644 index 000000000000..7ab1440395c3 --- /dev/null +++ b/tests/target/skip_macro_invocations/name_unknown.rs @@ -0,0 +1,6 @@ +// rustfmt-skip_macro_invocations: ["unknown"] + +// Should not skip this invocation +items!( + const _: u8 = 0; +); diff --git a/tests/target/skip_macro_invocations/names.rs b/tests/target/skip_macro_invocations/names.rs new file mode 100644 index 000000000000..c6b41ff93d79 --- /dev/null +++ b/tests/target/skip_macro_invocations/names.rs @@ -0,0 +1,16 @@ +// rustfmt-skip_macro_invocations: ["foo","bar"] + +// Should skip this invocation +foo!( + const _: u8 = 0; +); + +// Should skip this invocation +bar!( + const _: u8 = 0; +); + +// Should not skip this invocation +baz!( + const _: u8 = 0; +); diff --git a/tests/target/skip_macro_invocations/path_qualified_invocation_mismatch.rs b/tests/target/skip_macro_invocations/path_qualified_invocation_mismatch.rs new file mode 100644 index 000000000000..6e372c726952 --- /dev/null +++ b/tests/target/skip_macro_invocations/path_qualified_invocation_mismatch.rs @@ -0,0 +1,6 @@ +// rustfmt-skip_macro_invocations: ["items"] + +// Should not skip this invocation +self::items!( + const _: u8 = 0; +); diff --git a/tests/target/skip_macro_invocations/path_qualified_match.rs b/tests/target/skip_macro_invocations/path_qualified_match.rs new file mode 100644 index 000000000000..9398918a9e11 --- /dev/null +++ b/tests/target/skip_macro_invocations/path_qualified_match.rs @@ -0,0 +1,6 @@ +// rustfmt-skip_macro_invocations: ["self::items"] + +// Should skip this invocation +self::items!( + const _: u8 = 0; +); diff --git a/tests/target/skip_macro_invocations/path_qualified_name_mismatch.rs b/tests/target/skip_macro_invocations/path_qualified_name_mismatch.rs new file mode 100644 index 000000000000..aa57a2a655c4 --- /dev/null +++ b/tests/target/skip_macro_invocations/path_qualified_name_mismatch.rs @@ -0,0 +1,6 @@ +// rustfmt-skip_macro_invocations: ["self::items"] + +// Should not skip this invocation +items!( + const _: u8 = 0; +); diff --git a/tests/target/skip_macro_invocations/use_alias_examples.rs b/tests/target/skip_macro_invocations/use_alias_examples.rs new file mode 100644 index 000000000000..799dd8c08af1 --- /dev/null +++ b/tests/target/skip_macro_invocations/use_alias_examples.rs @@ -0,0 +1,32 @@ +// rustfmt-skip_macro_invocations: ["aaa","ccc"] + +// These tests demonstrate a realistic use case with use aliases. +// The use statements should not impact functionality in any way. + +use crate::{aaa, bbb, ddd}; + +// No use alias, invocation in list +// Should skip this invocation +aaa!( + const _: u8 = 0; +); + +// Use alias, invocation in list +// Should skip this invocation +use crate::bbb as ccc; +ccc!( + const _: u8 = 0; +); + +// Use alias, invocation not in list +// Should not skip this invocation +use crate::ddd as eee; +eee!( + const _: u8 = 0; +); + +// No use alias, invocation not in list +// Should not skip this invocation +fff!( + const _: u8 = 0; +); From 0cb294f05c421d6b6ef266e3ec01c2d8739194b3 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Wed, 15 Jun 2022 22:43:51 -0400 Subject: [PATCH 218/401] Deprecate and Rename `fn_args_layout` -> `fn_params_layout` fn_args_layout is now deprecated. This option was renamed to better communicate that it affects the layout of parameters in function signatures and not the layout of arguments in function calls. Because the `fn_args_layout` is a stable option the renamed option is also stable, however users who set `fn_args_layout` will get a warning message letting them know that the option has been renamed. --- Configurations.md | 116 +++++++++++++++++- src/config/config_type.rs | 25 +++- src/config/mod.rs | 13 +- src/items.rs | 2 +- tests/config/small_tabs.toml | 2 +- .../compressed.rs | 2 +- .../tall.rs | 2 +- .../vertical.rs | 2 +- tests/source/fn-custom-7.rs | 2 +- tests/source/fn-custom.rs | 2 +- tests/source/fn_args_layout-vertical.rs | 2 +- .../compressed.rs | 2 +- .../tall.rs | 2 +- .../vertical.rs | 2 +- tests/target/fn-custom-7.rs | 2 +- tests/target/fn-custom.rs | 2 +- tests/target/fn_args_layout-vertical.rs | 2 +- tests/target/issue-4791/issue_4928.rs | 2 +- 18 files changed, 164 insertions(+), 20 deletions(-) rename tests/source/configs/{fn_args_layout => fn_params_layout}/compressed.rs (92%) rename tests/source/configs/{fn_args_layout => fn_params_layout}/tall.rs (93%) rename tests/source/configs/{fn_args_layout => fn_params_layout}/vertical.rs (92%) rename tests/target/configs/{fn_args_layout => fn_params_layout}/compressed.rs (92%) rename tests/target/configs/{fn_args_layout => fn_params_layout}/tall.rs (94%) rename tests/target/configs/{fn_args_layout => fn_params_layout}/vertical.rs (94%) diff --git a/Configurations.md b/Configurations.md index 5579b5095afa..17eabd48759e 100644 --- a/Configurations.md +++ b/Configurations.md @@ -645,7 +645,8 @@ trailing whitespaces. ## `fn_args_layout` -Control the layout of arguments in a function +This option is deprecated and has been renamed to `fn_params_layout` to better communicate that +it affects the layout of parameters in function signatures. - **Default value**: `"Tall"` - **Possible values**: `"Compressed"`, `"Tall"`, `"Vertical"` @@ -753,6 +754,8 @@ trait Lorem { } ``` +See also [`fn_params_layout`](#fn_params_layout) + ## `fn_call_width` Maximum width of the args of a function call before falling back to vertical formatting. @@ -765,6 +768,117 @@ By default this option is set as a percentage of [`max_width`](#max_width) provi See also [`max_width`](#max_width) and [`use_small_heuristics`](#use_small_heuristics) +## `fn_params_layout` + +Control the layout of parameters in function signatures. + +- **Default value**: `"Tall"` +- **Possible values**: `"Compressed"`, `"Tall"`, `"Vertical"` +- **Stable**: Yes + +#### `"Tall"` (default): + +```rust +trait Lorem { + fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet); + + fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet) { + // body + } + + fn lorem( + ipsum: Ipsum, + dolor: Dolor, + sit: Sit, + amet: Amet, + consectetur: Consectetur, + adipiscing: Adipiscing, + elit: Elit, + ); + + fn lorem( + ipsum: Ipsum, + dolor: Dolor, + sit: Sit, + amet: Amet, + consectetur: Consectetur, + adipiscing: Adipiscing, + elit: Elit, + ) { + // body + } +} +``` + +#### `"Compressed"`: + +```rust +trait Lorem { + fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet); + + fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet) { + // body + } + + fn lorem( + ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: Consectetur, + adipiscing: Adipiscing, elit: Elit, + ); + + fn lorem( + ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: Consectetur, + adipiscing: Adipiscing, elit: Elit, + ) { + // body + } +} +``` + +#### `"Vertical"`: + +```rust +trait Lorem { + fn lorem( + ipsum: Ipsum, + dolor: Dolor, + sit: Sit, + amet: Amet, + ); + + fn lorem( + ipsum: Ipsum, + dolor: Dolor, + sit: Sit, + amet: Amet, + ) { + // body + } + + fn lorem( + ipsum: Ipsum, + dolor: Dolor, + sit: Sit, + amet: Amet, + consectetur: Consectetur, + adipiscing: Adipiscing, + elit: Elit, + ); + + fn lorem( + ipsum: Ipsum, + dolor: Dolor, + sit: Sit, + amet: Amet, + consectetur: Consectetur, + adipiscing: Adipiscing, + elit: Elit, + ) { + // body + } +} +``` + + ## `fn_single_line` Put single-expression functions on a single line diff --git a/src/config/config_type.rs b/src/config/config_type.rs index 48f4d9ce80e4..90a67e12c5a7 100644 --- a/src/config/config_type.rs +++ b/src/config/config_type.rs @@ -127,6 +127,7 @@ macro_rules! create_config { | "array_width" | "chain_width" => self.0.set_heuristics(), "merge_imports" => self.0.set_merge_imports(), + "fn_args_layout" => self.0.set_fn_args_layout(), &_ => (), } } @@ -181,6 +182,7 @@ macro_rules! create_config { self.set_heuristics(); self.set_ignore(dir); self.set_merge_imports(); + self.set_fn_args_layout(); self } @@ -273,14 +275,21 @@ macro_rules! create_config { | "array_width" | "chain_width" => self.set_heuristics(), "merge_imports" => self.set_merge_imports(), + "fn_args_layout" => self.set_fn_args_layout(), &_ => (), } } #[allow(unreachable_pub)] pub fn is_hidden_option(name: &str) -> bool { - const HIDE_OPTIONS: [&str; 5] = - ["verbose", "verbose_diff", "file_lines", "width_heuristics", "merge_imports"]; + const HIDE_OPTIONS: [&str; 6] = [ + "verbose", + "verbose_diff", + "file_lines", + "width_heuristics", + "merge_imports", + "fn_args_layout" + ]; HIDE_OPTIONS.contains(&name) } @@ -430,6 +439,18 @@ macro_rules! create_config { } } + fn set_fn_args_layout(&mut self) { + if self.was_set().fn_args_layout() { + eprintln!( + "Warning: the `fn_args_layout` option is deprecated. \ + Use `fn_params_layout`. instead" + ); + if !self.was_set().fn_params_layout() { + self.fn_params_layout.2 = self.fn_args_layout(); + } + } + } + #[allow(unreachable_pub)] /// Returns `true` if the config key was explicitly set and is the default value. pub fn is_default(&self, key: &str) -> bool { diff --git a/src/config/mod.rs b/src/config/mod.rs index 0c6a3cbc9532..5492519f3894 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -124,7 +124,9 @@ create_config! { force_multiline_blocks: bool, false, false, "Force multiline closure bodies and match arms to be wrapped in a block"; fn_args_layout: Density, Density::Tall, true, - "Control the layout of arguments in a function"; + "(deprecated: use fn_params_layout instead)"; + fn_params_layout: Density, Density::Tall, true, + "Control the layout of parameters in function signatures."; brace_style: BraceStyle, BraceStyle::SameLineWhere, false, "Brace style for items"; control_brace_style: ControlBraceStyle, ControlBraceStyle::AlwaysSameLine, false, "Brace style for control flow constructs"; @@ -196,6 +198,7 @@ impl PartialConfig { cloned.width_heuristics = None; cloned.print_misformatted_file_names = None; cloned.merge_imports = None; + cloned.fn_args_layout = None; ::toml::to_string(&cloned).map_err(ToTomlError) } @@ -442,6 +445,12 @@ mod test { "Merge imports"; merge_imports: bool, false, false, "(deprecated: use imports_granularity instead)"; + // fn_args_layout renamed to fn_params_layout + fn_args_layout: Density, Density::Tall, true, + "(deprecated: use fn_params_layout instead)"; + fn_params_layout: Density, Density::Tall, true, + "Control the layout of parameters in a function signatures."; + // Width Heuristics use_small_heuristics: Heuristics, Heuristics::Default, true, "Whether to use different formatting for items and \ @@ -644,7 +653,7 @@ enum_discrim_align_threshold = 0 match_arm_blocks = true match_arm_leading_pipes = "Never" force_multiline_blocks = false -fn_args_layout = "Tall" +fn_params_layout = "Tall" brace_style = "SameLineWhere" control_brace_style = "AlwaysSameLine" trailing_semicolon = true diff --git a/src/items.rs b/src/items.rs index bab881f4b4e8..6ab02e7f9239 100644 --- a/src/items.rs +++ b/src/items.rs @@ -2602,7 +2602,7 @@ fn rewrite_params( ¶m_items, context .config - .fn_args_layout() + .fn_params_layout() .to_list_tactic(param_items.len()), Separator::Comma, one_line_budget, diff --git a/tests/config/small_tabs.toml b/tests/config/small_tabs.toml index c3cfd34317a3..4c37100894f8 100644 --- a/tests/config/small_tabs.toml +++ b/tests/config/small_tabs.toml @@ -3,7 +3,7 @@ comment_width = 80 tab_spaces = 2 newline_style = "Unix" brace_style = "SameLineWhere" -fn_args_layout = "Tall" +fn_params_layout = "Tall" trailing_comma = "Vertical" indent_style = "Block" reorder_imports = false diff --git a/tests/source/configs/fn_args_layout/compressed.rs b/tests/source/configs/fn_params_layout/compressed.rs similarity index 92% rename from tests/source/configs/fn_args_layout/compressed.rs rename to tests/source/configs/fn_params_layout/compressed.rs index 66a371c259f0..eb573d3121f5 100644 --- a/tests/source/configs/fn_args_layout/compressed.rs +++ b/tests/source/configs/fn_params_layout/compressed.rs @@ -1,4 +1,4 @@ -// rustfmt-fn_args_layout: Compressed +// rustfmt-fn_params_layout: Compressed // Function arguments density trait Lorem { diff --git a/tests/source/configs/fn_args_layout/tall.rs b/tests/source/configs/fn_params_layout/tall.rs similarity index 93% rename from tests/source/configs/fn_args_layout/tall.rs rename to tests/source/configs/fn_params_layout/tall.rs index f11e86fd3139..4be34f0fe4ac 100644 --- a/tests/source/configs/fn_args_layout/tall.rs +++ b/tests/source/configs/fn_params_layout/tall.rs @@ -1,4 +1,4 @@ -// rustfmt-fn_args_layout: Tall +// rustfmt-fn_params_layout: Tall // Function arguments density trait Lorem { diff --git a/tests/source/configs/fn_args_layout/vertical.rs b/tests/source/configs/fn_params_layout/vertical.rs similarity index 92% rename from tests/source/configs/fn_args_layout/vertical.rs rename to tests/source/configs/fn_params_layout/vertical.rs index a23cc025225f..674968023f99 100644 --- a/tests/source/configs/fn_args_layout/vertical.rs +++ b/tests/source/configs/fn_params_layout/vertical.rs @@ -1,4 +1,4 @@ -// rustfmt-fn_args_layout: Vertical +// rustfmt-fn_params_layout: Vertical // Function arguments density trait Lorem { diff --git a/tests/source/fn-custom-7.rs b/tests/source/fn-custom-7.rs index d5330196bf79..3ecd8701727a 100644 --- a/tests/source/fn-custom-7.rs +++ b/tests/source/fn-custom-7.rs @@ -1,5 +1,5 @@ // rustfmt-normalize_comments: true -// rustfmt-fn_args_layout: Vertical +// rustfmt-fn_params_layout: Vertical // rustfmt-brace_style: AlwaysNextLine // Case with only one variable. diff --git a/tests/source/fn-custom.rs b/tests/source/fn-custom.rs index 77ced4c5e0e1..64ef0ecfaae4 100644 --- a/tests/source/fn-custom.rs +++ b/tests/source/fn-custom.rs @@ -1,4 +1,4 @@ -// rustfmt-fn_args_layout: Compressed +// rustfmt-fn_params_layout: Compressed // Test some of the ways function signatures can be customised. // Test compressed layout of args. diff --git a/tests/source/fn_args_layout-vertical.rs b/tests/source/fn_args_layout-vertical.rs index 759bc83d0157..fd6e3f0442ec 100644 --- a/tests/source/fn_args_layout-vertical.rs +++ b/tests/source/fn_args_layout-vertical.rs @@ -1,4 +1,4 @@ -// rustfmt-fn_args_layout: Vertical +// rustfmt-fn_params_layout: Vertical // Empty list should stay on one line. fn do_bar( diff --git a/tests/target/configs/fn_args_layout/compressed.rs b/tests/target/configs/fn_params_layout/compressed.rs similarity index 92% rename from tests/target/configs/fn_args_layout/compressed.rs rename to tests/target/configs/fn_params_layout/compressed.rs index f189446e25d4..ff32f0f1d586 100644 --- a/tests/target/configs/fn_args_layout/compressed.rs +++ b/tests/target/configs/fn_params_layout/compressed.rs @@ -1,4 +1,4 @@ -// rustfmt-fn_args_layout: Compressed +// rustfmt-fn_params_layout: Compressed // Function arguments density trait Lorem { diff --git a/tests/target/configs/fn_args_layout/tall.rs b/tests/target/configs/fn_params_layout/tall.rs similarity index 94% rename from tests/target/configs/fn_args_layout/tall.rs rename to tests/target/configs/fn_params_layout/tall.rs index 20f308973acb..25a86799af0d 100644 --- a/tests/target/configs/fn_args_layout/tall.rs +++ b/tests/target/configs/fn_params_layout/tall.rs @@ -1,4 +1,4 @@ -// rustfmt-fn_args_layout: Tall +// rustfmt-fn_params_layout: Tall // Function arguments density trait Lorem { diff --git a/tests/target/configs/fn_args_layout/vertical.rs b/tests/target/configs/fn_params_layout/vertical.rs similarity index 94% rename from tests/target/configs/fn_args_layout/vertical.rs rename to tests/target/configs/fn_params_layout/vertical.rs index 6c695a75df9a..7a0e42415f3b 100644 --- a/tests/target/configs/fn_args_layout/vertical.rs +++ b/tests/target/configs/fn_params_layout/vertical.rs @@ -1,4 +1,4 @@ -// rustfmt-fn_args_layout: Vertical +// rustfmt-fn_params_layout: Vertical // Function arguments density trait Lorem { diff --git a/tests/target/fn-custom-7.rs b/tests/target/fn-custom-7.rs index 2c20ac5a7524..f6a1a90c3fc6 100644 --- a/tests/target/fn-custom-7.rs +++ b/tests/target/fn-custom-7.rs @@ -1,5 +1,5 @@ // rustfmt-normalize_comments: true -// rustfmt-fn_args_layout: Vertical +// rustfmt-fn_params_layout: Vertical // rustfmt-brace_style: AlwaysNextLine // Case with only one variable. diff --git a/tests/target/fn-custom.rs b/tests/target/fn-custom.rs index 2eb2a973d243..506d9de34370 100644 --- a/tests/target/fn-custom.rs +++ b/tests/target/fn-custom.rs @@ -1,4 +1,4 @@ -// rustfmt-fn_args_layout: Compressed +// rustfmt-fn_params_layout: Compressed // Test some of the ways function signatures can be customised. // Test compressed layout of args. diff --git a/tests/target/fn_args_layout-vertical.rs b/tests/target/fn_args_layout-vertical.rs index da0ac981d872..bfeca15c967e 100644 --- a/tests/target/fn_args_layout-vertical.rs +++ b/tests/target/fn_args_layout-vertical.rs @@ -1,4 +1,4 @@ -// rustfmt-fn_args_layout: Vertical +// rustfmt-fn_params_layout: Vertical // Empty list should stay on one line. fn do_bar() -> u8 { diff --git a/tests/target/issue-4791/issue_4928.rs b/tests/target/issue-4791/issue_4928.rs index 588656b535fa..29f6bda90631 100644 --- a/tests/target/issue-4791/issue_4928.rs +++ b/tests/target/issue-4791/issue_4928.rs @@ -1,7 +1,7 @@ // rustfmt-brace_style: SameLineWhere // rustfmt-comment_width: 100 // rustfmt-edition: 2018 -// rustfmt-fn_args_layout: Compressed +// rustfmt-fn_params_layout: Compressed // rustfmt-hard_tabs: false // rustfmt-match_block_trailing_comma: true // rustfmt-max_width: 100 From dde28314da7fa634f89411fc974ce08ada1077a7 Mon Sep 17 00:00:00 2001 From: waynewaynetsai Date: Sat, 16 Jul 2022 12:13:19 +0800 Subject: [PATCH 219/401] docs: fix duplicated names example --- Configurations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Configurations.md b/Configurations.md index 17eabd48759e..eaff09431ace 100644 --- a/Configurations.md +++ b/Configurations.md @@ -589,7 +589,7 @@ doesn't get ignored when aligning. #### `0` (default): ```rust -enum Bar { +enum Foo { A = 0, Bb = 1, RandomLongVariantGoesHere = 10, From 85fdf8ecec5551e79dc5ac3a8babaf514a58ef86 Mon Sep 17 00:00:00 2001 From: Urgau Date: Sun, 17 Jul 2022 14:18:57 +0200 Subject: [PATCH 220/401] Remove useless conditional compilation - 2 --- src/test/mod.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/test/mod.rs b/src/test/mod.rs index 6b5bc2b30dd5..cfad4a8ed0e3 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -982,11 +982,7 @@ fn rustfmt() -> PathBuf { assert!( me.is_file() || me.with_extension("exe").is_file(), "{}", - if cfg!(release) { - "no rustfmt bin, try running `cargo build --release` before testing" - } else { - "no rustfmt bin, try running `cargo build` before testing" - } + "no rustfmt bin, try running `cargo build` or `cargo build --release` before testing" ); me } From a7bf0090347d8523dd62c0508a4c8413cdba099a Mon Sep 17 00:00:00 2001 From: Jorge Martin Juarez <50276574+jmj0502@users.noreply.github.com> Date: Sun, 17 Jul 2022 21:39:25 -0400 Subject: [PATCH 221/401] Fix/comments inside trait generics gets duplicated (#5446) * Bugfix: Now slash/start comments aren't duplicated on trait parameters. * Removing unnecesary comment. * Improvements: Improving the BytePos offset. * Improvements: Improving the description of the test cases. --- src/items.rs | 6 +++++- tests/target/issue-5358.rs | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 tests/target/issue-5358.rs diff --git a/src/items.rs b/src/items.rs index 6ab02e7f9239..a76e88b77d48 100644 --- a/src/items.rs +++ b/src/items.rs @@ -1084,7 +1084,11 @@ pub(crate) fn format_trait( let item_snippet = context.snippet(item.span); if let Some(lo) = item_snippet.find('/') { // 1 = `{` - let comment_hi = body_lo - BytePos(1); + let comment_hi = if generics.params.len() > 0 { + generics.span.lo() - BytePos(1) + } else { + body_lo - BytePos(1) + }; let comment_lo = item.span.lo() + BytePos(lo as u32); if comment_lo < comment_hi { match recover_missing_comment_in_span( diff --git a/tests/target/issue-5358.rs b/tests/target/issue-5358.rs new file mode 100644 index 000000000000..d4bf4909ad76 --- /dev/null +++ b/tests/target/issue-5358.rs @@ -0,0 +1,4 @@ +// Test /* comment */ inside trait generics does not get duplicated. +trait Test {} + +trait TestTwo {} From 05332b8c50c79c86b4715b764e4967016845273f Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Tue, 19 Jul 2022 11:48:49 -0400 Subject: [PATCH 222/401] Add test for issue 3033 Closes 3033 The issue is no longer reproducible. --- tests/target/issue_3033.rs | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 tests/target/issue_3033.rs diff --git a/tests/target/issue_3033.rs b/tests/target/issue_3033.rs new file mode 100644 index 000000000000..e12249a6da6f --- /dev/null +++ b/tests/target/issue_3033.rs @@ -0,0 +1,2 @@ +use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding::BluetoothRemoteGATTServerBinding:: + BluetoothRemoteGATTServerMethods; From f2c31ba04db6bf07c140c86bfbe2cb16f1b83a8e Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Tue, 19 Jul 2022 00:37:32 -0400 Subject: [PATCH 223/401] Add tests for issue 2534 Closes 2534 The behavior described in the original issue can no longer be reproduced. The tests show that to be the case regardless of if `format_macro_matchers` is `true` or `false`. --- tests/target/issue-2534/format_macro_matchers_false.rs | 6 ++++++ tests/target/issue-2534/format_macro_matchers_true.rs | 6 ++++++ 2 files changed, 12 insertions(+) create mode 100644 tests/target/issue-2534/format_macro_matchers_false.rs create mode 100644 tests/target/issue-2534/format_macro_matchers_true.rs diff --git a/tests/target/issue-2534/format_macro_matchers_false.rs b/tests/target/issue-2534/format_macro_matchers_false.rs new file mode 100644 index 000000000000..2038ed7f1d01 --- /dev/null +++ b/tests/target/issue-2534/format_macro_matchers_false.rs @@ -0,0 +1,6 @@ +// rustfmt-format_macro_matchers: false + +macro_rules! foo { + ($a:ident : $b:ty) => {}; + ($a:ident $b:ident $c:ident) => {}; +} diff --git a/tests/target/issue-2534/format_macro_matchers_true.rs b/tests/target/issue-2534/format_macro_matchers_true.rs new file mode 100644 index 000000000000..01d939add4dc --- /dev/null +++ b/tests/target/issue-2534/format_macro_matchers_true.rs @@ -0,0 +1,6 @@ +// rustfmt-format_macro_matchers: true + +macro_rules! foo { + ($a:ident : $b:ty) => {}; + ($a:ident $b:ident $c:ident) => {}; +} From 4c78c738ba45c4ac4bab3bb74009712e00d307c7 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Tue, 19 Jul 2022 18:47:03 -0400 Subject: [PATCH 224/401] Add tests for 3245 and 3561 Closes 3245 Closes 3561 These issues were originally linked in issue 3672 and as was mentioned in PR 3706, it's unclear which commit resolved the issue but the issue can no longer be reproduced. --- tests/source/issue_3245.rs | 4 ++++ tests/source/issue_3561.rs | 6 ++++++ tests/target/issue_3245.rs | 4 ++++ tests/target/issue_3561.rs | 7 +++++++ 4 files changed, 21 insertions(+) create mode 100644 tests/source/issue_3245.rs create mode 100644 tests/source/issue_3561.rs create mode 100644 tests/target/issue_3245.rs create mode 100644 tests/target/issue_3561.rs diff --git a/tests/source/issue_3245.rs b/tests/source/issue_3245.rs new file mode 100644 index 000000000000..0279246ed6ac --- /dev/null +++ b/tests/source/issue_3245.rs @@ -0,0 +1,4 @@ +fn main() { + let x = 1; + ;let y = 3; +} diff --git a/tests/source/issue_3561.rs b/tests/source/issue_3561.rs new file mode 100644 index 000000000000..8f6cd8f9fbce --- /dev/null +++ b/tests/source/issue_3561.rs @@ -0,0 +1,6 @@ +fn main() {;7 +} + +fn main() { + ;7 +} diff --git a/tests/target/issue_3245.rs b/tests/target/issue_3245.rs new file mode 100644 index 000000000000..8f442f1181a5 --- /dev/null +++ b/tests/target/issue_3245.rs @@ -0,0 +1,4 @@ +fn main() { + let x = 1; + let y = 3; +} diff --git a/tests/target/issue_3561.rs b/tests/target/issue_3561.rs new file mode 100644 index 000000000000..846a14d86a5f --- /dev/null +++ b/tests/target/issue_3561.rs @@ -0,0 +1,7 @@ +fn main() { + 7 +} + +fn main() { + 7 +} From c19b14539b79d44f5f433ac189d70e07fb152354 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Wed, 20 Jul 2022 09:15:51 -0400 Subject: [PATCH 225/401] test for issue 3164 Closes 3164 Show that when `error_on_line_overflow=true` is set in the rustfmt.toml that an error message is written to stderr. --- tests/cargo-fmt/main.rs | 19 +++++++++++++++++++ tests/cargo-fmt/source/issue_3164/Cargo.toml | 8 ++++++++ tests/cargo-fmt/source/issue_3164/src/main.rs | 13 +++++++++++++ tests/rustfmt/main.rs | 15 +++++++++++++++ 4 files changed, 55 insertions(+) create mode 100644 tests/cargo-fmt/source/issue_3164/Cargo.toml create mode 100644 tests/cargo-fmt/source/issue_3164/src/main.rs diff --git a/tests/cargo-fmt/main.rs b/tests/cargo-fmt/main.rs index 5aa4388518b8..701c36fadeaf 100644 --- a/tests/cargo-fmt/main.rs +++ b/tests/cargo-fmt/main.rs @@ -98,3 +98,22 @@ fn cargo_fmt_out_of_line_test_modules() { assert!(stdout.contains(&format!("Diff in {}", path.display()))) } } + +#[rustfmt_only_ci_test] +#[test] +fn cargo_fmt_emits_error_on_line_overflow_true() { + // See also https://github.com/rust-lang/rustfmt/issues/3164 + let args = [ + "--check", + "--manifest-path", + "tests/cargo-fmt/source/issue_3164/Cargo.toml", + "--", + "--config", + "error_on_line_overflow=true", + ]; + + let (_stdout, stderr) = cargo_fmt(&args); + assert!(stderr.contains( + "line formatted, but exceeded maximum width (maximum: 100 (see `max_width` option)" + )) +} diff --git a/tests/cargo-fmt/source/issue_3164/Cargo.toml b/tests/cargo-fmt/source/issue_3164/Cargo.toml new file mode 100644 index 000000000000..580ef7e6e24f --- /dev/null +++ b/tests/cargo-fmt/source/issue_3164/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "issue_3164" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/cargo-fmt/source/issue_3164/src/main.rs b/tests/cargo-fmt/source/issue_3164/src/main.rs new file mode 100644 index 000000000000..9330107ac8dc --- /dev/null +++ b/tests/cargo-fmt/source/issue_3164/src/main.rs @@ -0,0 +1,13 @@ +#[allow(unused_macros)] +macro_rules! foo { + ($id:ident) => { + macro_rules! bar { + ($id2:tt) => { + #[cfg(any(target_feature = $id2, target_feature = $id2, target_feature = $id2, target_feature = $id2, target_feature = $id2))] + fn $id() {} + }; + } + }; +} + +fn main() {} diff --git a/tests/rustfmt/main.rs b/tests/rustfmt/main.rs index 636e3053e0f8..7ff301e80195 100644 --- a/tests/rustfmt/main.rs +++ b/tests/rustfmt/main.rs @@ -159,3 +159,18 @@ fn mod_resolution_error_path_attribute_does_not_exist() { // The path attribute points to a file that does not exist assert!(stderr.contains("does_not_exist.rs does not exist")); } + +#[test] +fn rustfmt_emits_error_on_line_overflow_true() { + // See also https://github.com/rust-lang/rustfmt/issues/3164 + let args = [ + "--config", + "error_on_line_overflow=true", + "tests/cargo-fmt/source/issue_3164/src/main.rs", + ]; + + let (_stdout, stderr) = rustfmt(&args); + assert!(stderr.contains( + "line formatted, but exceeded maximum width (maximum: 100 (see `max_width` option)" + )) +} From a451a39dec2bd5d7f0822eaa092e1423b42ea1d3 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Wed, 20 Jul 2022 13:59:57 -0400 Subject: [PATCH 226/401] Add test for issue 4350 Closes 4350 Its unclear which commit resolved this, but the original issue is no longer reproducible. --- tests/target/issue_4350.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 tests/target/issue_4350.rs diff --git a/tests/target/issue_4350.rs b/tests/target/issue_4350.rs new file mode 100644 index 000000000000..a94c5c32188d --- /dev/null +++ b/tests/target/issue_4350.rs @@ -0,0 +1,13 @@ +//rustfmt-format_macro_bodies: true + +macro_rules! mto_text_left { + ($buf:ident, $n:ident, $pos:ident, $state:ident) => {{ + let cursor = loop { + state = match iter.next() { + None if $pos == DP::Start => break last_char_idx($buf), + None /*some comment */ => break 0, + }; + }; + Ok(saturate_cursor($buf, cursor)) + }}; +} From ed77962d243f93e1690ac62f0b8d733383090240 Mon Sep 17 00:00:00 2001 From: Martin Juarez Date: Tue, 26 Jul 2022 12:21:22 -0400 Subject: [PATCH 227/401] Improvements: Adding tests for the issue-4643. --- tests/source/issue-4643.rs | 23 +++++++++++++++++++++++ tests/target/issue-4643.rs | 19 +++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 tests/source/issue-4643.rs create mode 100644 tests/target/issue-4643.rs diff --git a/tests/source/issue-4643.rs b/tests/source/issue-4643.rs new file mode 100644 index 000000000000..382072d9004b --- /dev/null +++ b/tests/source/issue-4643.rs @@ -0,0 +1,23 @@ +// output doesn't get corrupted when using comments within generic type parameters of a trait + +pub trait Something< + A, + // some comment + B, + C +> { + fn a(&self, x: A) -> i32; + fn b(&self, x: B) -> i32; + fn c(&self, x: C) -> i32; +} + +pub trait SomethingElse< + A, + /* some comment */ + B, + C +> { + fn a(&self, x: A) -> i32; + fn b(&self, x: B) -> i32; + fn c(&self, x: C) -> i32; +} diff --git a/tests/target/issue-4643.rs b/tests/target/issue-4643.rs new file mode 100644 index 000000000000..ef99e4db382c --- /dev/null +++ b/tests/target/issue-4643.rs @@ -0,0 +1,19 @@ +// output doesn't get corrupted when using comments within generic type parameters of a trait + +pub trait Something< + A, + // some comment + B, + C, +> +{ + fn a(&self, x: A) -> i32; + fn b(&self, x: B) -> i32; + fn c(&self, x: C) -> i32; +} + +pub trait SomethingElse { + fn a(&self, x: A) -> i32; + fn b(&self, x: B) -> i32; + fn c(&self, x: C) -> i32; +} From 7432422008f8be71df989e357d949072c77e9060 Mon Sep 17 00:00:00 2001 From: aizpurua23a <57321880+aizpurua23a@users.noreply.github.com> Date: Wed, 27 Jul 2022 18:59:29 +0200 Subject: [PATCH 228/401] Update Configurations.md to point to dirs 4.0 docs --- Configurations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Configurations.md b/Configurations.md index eaff09431ace..e066553dc63f 100644 --- a/Configurations.md +++ b/Configurations.md @@ -1,6 +1,6 @@ # Configuring Rustfmt -Rustfmt is designed to be very configurable. You can create a TOML file called `rustfmt.toml` or `.rustfmt.toml`, place it in the project or any other parent directory and it will apply the options in that file. If none of these directories contain such a file, both your home directory and a directory called `rustfmt` in your [global config directory](https://docs.rs/dirs/1.0.4/dirs/fn.config_dir.html) (e.g. `.config/rustfmt/`) are checked as well. +Rustfmt is designed to be very configurable. You can create a TOML file called `rustfmt.toml` or `.rustfmt.toml`, place it in the project or any other parent directory and it will apply the options in that file. If none of these directories contain such a file, both your home directory and a directory called `rustfmt` in your [global config directory](https://docs.rs/dirs/4.0.0/dirs/fn.config_dir.html) (e.g. `.config/rustfmt/`) are checked as well. A possible content of `rustfmt.toml` or `.rustfmt.toml` might look like this: From a7801aac272ce50da191af86bab988284de534be Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Mon, 25 Jul 2022 21:38:09 -0400 Subject: [PATCH 229/401] Add test for issue 3987 Closes 3987 It's unclear which commit resolved this issue, but it can no longer be reproduced. --- .../issue-3987/format_macro_bodies_true.rs | 26 +++++++++++++++++++ .../issue-3987/format_macro_bodies_false.rs | 26 +++++++++++++++++++ .../issue-3987/format_macro_bodies_true.rs | 21 +++++++++++++++ 3 files changed, 73 insertions(+) create mode 100644 tests/source/issue-3987/format_macro_bodies_true.rs create mode 100644 tests/target/issue-3987/format_macro_bodies_false.rs create mode 100644 tests/target/issue-3987/format_macro_bodies_true.rs diff --git a/tests/source/issue-3987/format_macro_bodies_true.rs b/tests/source/issue-3987/format_macro_bodies_true.rs new file mode 100644 index 000000000000..9af114fbe574 --- /dev/null +++ b/tests/source/issue-3987/format_macro_bodies_true.rs @@ -0,0 +1,26 @@ +// rustfmt-format_macro_bodies: true + +// with comments +macro_rules! macros { + () => {{ + Struct { + field: ( + 42 + //comment 1 + 42 + //comment 2 + ), + }; + }}; +} + +// without comments +macro_rules! macros { + () => {{ + Struct { + field: ( + 42 + + 42 + ), + }; + }}; +} diff --git a/tests/target/issue-3987/format_macro_bodies_false.rs b/tests/target/issue-3987/format_macro_bodies_false.rs new file mode 100644 index 000000000000..1352b762e450 --- /dev/null +++ b/tests/target/issue-3987/format_macro_bodies_false.rs @@ -0,0 +1,26 @@ +// rustfmt-format_macro_bodies: false + +// with comments +macro_rules! macros { + () => {{ + Struct { + field: ( + 42 + //comment 1 + 42 + //comment 2 + ), + }; + }}; +} + +// without comments +macro_rules! macros { + () => {{ + Struct { + field: ( + 42 + + 42 + ), + }; + }}; +} diff --git a/tests/target/issue-3987/format_macro_bodies_true.rs b/tests/target/issue-3987/format_macro_bodies_true.rs new file mode 100644 index 000000000000..88d57159c859 --- /dev/null +++ b/tests/target/issue-3987/format_macro_bodies_true.rs @@ -0,0 +1,21 @@ +// rustfmt-format_macro_bodies: true + +// with comments +macro_rules! macros { + () => {{ + Struct { + field: ( + 42 + //comment 1 + 42 + //comment 2 + ), + }; + }}; +} + +// without comments +macro_rules! macros { + () => {{ + Struct { field: (42 + 42) }; + }}; +} From 7cc126180f8340071741586c9b455b88b920b116 Mon Sep 17 00:00:00 2001 From: Tom Milligan Date: Wed, 20 Jul 2022 07:46:49 +0100 Subject: [PATCH 230/401] feat: nicer skip context for macro/attribute --- src/attr.rs | 4 ++-- src/macros.rs | 3 ++- src/skip.rs | 59 +++++++++++++++++++++++++++++++++----------------- src/visitor.rs | 4 ++-- 4 files changed, 45 insertions(+), 25 deletions(-) diff --git a/src/attr.rs b/src/attr.rs index 41ba9a847e67..b1efaa21f274 100644 --- a/src/attr.rs +++ b/src/attr.rs @@ -337,7 +337,7 @@ impl Rewrite for ast::Attribute { } else { let should_skip = self .ident() - .map(|s| context.skip_context.skip_attribute(s.name.as_str())) + .map(|s| context.skip_context.attributes.skip(s.name.as_str())) .unwrap_or(false); let prefix = attr_prefix(self); @@ -391,7 +391,7 @@ impl Rewrite for [ast::Attribute] { // 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"); + let skip_derives = context.skip_context.attributes.skip("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 diff --git a/src/macros.rs b/src/macros.rs index e78ef1f80667..1ef5a095bf98 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -157,7 +157,8 @@ pub(crate) fn rewrite_macro( ) -> Option { let should_skip = context .skip_context - .skip_macro(context.snippet(mac.path.span)); + .macros + .skip(context.snippet(mac.path.span)); if should_skip { None } else { diff --git a/src/skip.rs b/src/skip.rs index 8b2fd7736ae5..4ebbee542a26 100644 --- a/src/skip.rs +++ b/src/skip.rs @@ -2,41 +2,60 @@ use rustc_ast::ast; use rustc_ast_pretty::pprust; +use std::collections::HashSet; -/// Take care of skip name stack. You can update it by attributes slice or -/// by other context. Query this context to know if you need skip a block. +/// Track which blocks of code are to be skipped when formatting. +/// +/// You can update it by: +/// +/// - attributes slice +/// - manually feeding values into the underlying contexts +/// +/// Query this context to know if you need skip a block. #[derive(Default, Clone)] pub(crate) struct SkipContext { - pub(crate) all_macros: bool, - macros: Vec, - attributes: Vec, + pub(crate) macros: SkipNameContext, + pub(crate) attributes: SkipNameContext, } impl SkipContext { pub(crate) fn update_with_attrs(&mut self, attrs: &[ast::Attribute]) { - self.macros.append(&mut get_skip_names("macros", attrs)); - self.attributes - .append(&mut get_skip_names("attributes", attrs)); + self.macros.append(get_skip_names("macros", attrs)); + self.attributes.append(get_skip_names("attributes", attrs)); } - pub(crate) fn update(&mut self, mut other: SkipContext) { - self.macros.append(&mut other.macros); - self.attributes.append(&mut other.attributes); + pub(crate) fn update(&mut self, other: SkipContext) { + let SkipContext { macros, attributes } = other; + self.macros.update(macros); + self.attributes.update(attributes); + } +} + +/// Track which names to skip. +/// +/// Query this context with a string to know whether to skip it. +#[derive(Default, Clone)] +pub(crate) struct SkipNameContext { + all: bool, + values: HashSet, +} + +impl SkipNameContext { + pub(crate) fn append(&mut self, values: Vec) { + self.values.extend(values); } - pub(crate) fn update_macros(&mut self, other: T) - where - T: IntoIterator, - { - self.macros.extend(other.into_iter()); + pub(crate) fn update(&mut self, other: Self) { + self.all = self.all || other.all; + self.values.extend(other.values); } - pub(crate) fn skip_macro(&self, name: &str) -> bool { - self.all_macros || self.macros.iter().any(|n| n == name) + pub(crate) fn skip(&self, name: &str) -> bool { + self.all || self.values.contains(name) } - pub(crate) fn skip_attribute(&self, name: &str) -> bool { - self.attributes.iter().any(|n| n == name) + pub(crate) fn set_all(&mut self, all: bool) { + self.all = all; } } diff --git a/src/visitor.rs b/src/visitor.rs index b93153de154a..4b3ee7f76e18 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -775,10 +775,10 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { for macro_selector in config.skip_macro_invocations().0 { match macro_selector { MacroSelector::Name(name) => macro_names.push(name.to_string()), - MacroSelector::All => skip_context.all_macros = true, + MacroSelector::All => skip_context.macros.set_all(true), } } - skip_context.update_macros(macro_names); + skip_context.macros.append(macro_names); FmtVisitor { parent_context: None, parse_sess: parse_session, From 3fa81c6dbf72a83e4612d9490cdceade5eb2d2ae Mon Sep 17 00:00:00 2001 From: Tom Milligan Date: Wed, 20 Jul 2022 22:24:50 +0100 Subject: [PATCH 231/401] [review] use extend trait, enum for skip context --- src/skip.rs | 56 +++++++++++++++++++++++++++++++++++--------------- src/visitor.rs | 4 ++-- 2 files changed, 42 insertions(+), 18 deletions(-) diff --git a/src/skip.rs b/src/skip.rs index 4ebbee542a26..59d6d84c9643 100644 --- a/src/skip.rs +++ b/src/skip.rs @@ -11,7 +11,7 @@ use std::collections::HashSet; /// - attributes slice /// - manually feeding values into the underlying contexts /// -/// Query this context to know if you need skip a block. +/// Query this context to know if you need to skip a block. #[derive(Default, Clone)] pub(crate) struct SkipContext { pub(crate) macros: SkipNameContext, @@ -20,8 +20,8 @@ pub(crate) struct SkipContext { impl SkipContext { pub(crate) fn update_with_attrs(&mut self, attrs: &[ast::Attribute]) { - self.macros.append(get_skip_names("macros", attrs)); - self.attributes.append(get_skip_names("attributes", attrs)); + self.macros.extend(get_skip_names("macros", attrs)); + self.attributes.extend(get_skip_names("attributes", attrs)); } pub(crate) fn update(&mut self, other: SkipContext) { @@ -34,28 +34,52 @@ impl SkipContext { /// Track which names to skip. /// /// Query this context with a string to know whether to skip it. -#[derive(Default, Clone)] -pub(crate) struct SkipNameContext { - all: bool, - values: HashSet, +#[derive(Clone)] +pub(crate) enum SkipNameContext { + All, + Values(HashSet), +} + +impl Default for SkipNameContext { + fn default() -> Self { + Self::Values(Default::default()) + } +} + +impl Extend for SkipNameContext { + fn extend>(&mut self, iter: T) { + match self { + Self::All => {} + Self::Values(values) => values.extend(iter), + } + } } impl SkipNameContext { - pub(crate) fn append(&mut self, values: Vec) { - self.values.extend(values); - } - pub(crate) fn update(&mut self, other: Self) { - self.all = self.all || other.all; - self.values.extend(other.values); + match (self, other) { + // If we're already skipping everything, nothing more can be added + (Self::All, _) => {} + // If we want to skip all, set it + (this, Self::All) => { + *this = Self::All; + } + // If we have some new values to skip, add them + (Self::Values(existing_values), Self::Values(new_values)) => { + existing_values.extend(new_values) + } + } } pub(crate) fn skip(&self, name: &str) -> bool { - self.all || self.values.contains(name) + match self { + Self::All => true, + Self::Values(values) => values.contains(name), + } } - pub(crate) fn set_all(&mut self, all: bool) { - self.all = all; + pub(crate) fn skip_all(&mut self) { + *self = Self::All; } } diff --git a/src/visitor.rs b/src/visitor.rs index 4b3ee7f76e18..c0fc37eaaa89 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -775,10 +775,10 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { for macro_selector in config.skip_macro_invocations().0 { match macro_selector { MacroSelector::Name(name) => macro_names.push(name.to_string()), - MacroSelector::All => skip_context.macros.set_all(true), + MacroSelector::All => skip_context.macros.skip_all(), } } - skip_context.macros.append(macro_names); + skip_context.macros.extend(macro_names); FmtVisitor { parent_context: None, parse_sess: parse_session, From 23ef4b7ac476714886582865b39d396d615a3901 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Sun, 31 Jul 2022 13:53:37 -0500 Subject: [PATCH 232/401] refactor(chains): encapsulate shared code, prep for overflows --- src/chains.rs | 99 +++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 77 insertions(+), 22 deletions(-) diff --git a/src/chains.rs b/src/chains.rs index 5bccb22db4c1..a2976bbe92a5 100644 --- a/src/chains.rs +++ b/src/chains.rs @@ -74,6 +74,60 @@ use crate::utils::{ rewrite_ident, trimmed_last_line_width, wrap_str, }; +/// Provides the original input contents from the span +/// of a chain element with trailing spaces trimmed. +fn format_overflow_style(span: Span, context: &RewriteContext<'_>) -> Option { + context.snippet_provider.span_to_snippet(span).map(|s| { + s.lines() + .map(|l| l.trim_end()) + .collect::>() + .join("\n") + }) +} + +fn format_chain_item( + item: &ChainItem, + context: &RewriteContext<'_>, + rewrite_shape: Shape, + allow_overflow: bool, +) -> Option { + if allow_overflow { + item.rewrite(context, rewrite_shape) + .or_else(|| format_overflow_style(item.span, context)) + } else { + item.rewrite(context, rewrite_shape) + } +} + +fn get_block_child_shape( + prev_ends_with_block: bool, + context: &RewriteContext<'_>, + shape: Shape, +) -> Shape { + if prev_ends_with_block { + shape.block_indent(0) + } else { + shape.block_indent(context.config.tab_spaces()) + } + .with_max_width(context.config) +} + +fn get_visual_style_child_shape( + context: &RewriteContext<'_>, + shape: Shape, + offset: usize, + parent_overflowing: bool, +) -> Option { + if !parent_overflowing { + shape + .with_max_width(context.config) + .offset_left(offset) + .map(|s| s.visual_indent(0)) + } else { + Some(shape.visual_indent(offset)) + } +} + pub(crate) fn rewrite_chain( expr: &ast::Expr, context: &RewriteContext<'_>, @@ -498,6 +552,8 @@ struct ChainFormatterShared<'a> { // The number of children in the chain. This is not equal to `self.children.len()` // because `self.children` will change size as we process the chain. child_count: usize, + // Whether elements are allowed to overflow past the max_width limit + allow_overflow: bool, } impl<'a> ChainFormatterShared<'a> { @@ -507,6 +563,8 @@ impl<'a> ChainFormatterShared<'a> { rewrites: Vec::with_capacity(chain.children.len() + 1), fits_single_line: false, child_count: chain.children.len(), + // TODO(calebcartwright) + allow_overflow: false, } } @@ -519,6 +577,14 @@ impl<'a> ChainFormatterShared<'a> { } } + fn format_children(&mut self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<()> { + for item in &self.children[..self.children.len() - 1] { + let rewrite = format_chain_item(item, context, child_shape, self.allow_overflow)?; + self.rewrites.push(rewrite); + } + Some(()) + } + // Rewrite the last child. The last child of a chain requires special treatment. We need to // know whether 'overflowing' the last child make a better formatting: // @@ -731,22 +797,12 @@ impl<'a> ChainFormatter for ChainFormatterBlock<'a> { } fn child_shape(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { - Some( - if self.root_ends_with_block { - shape.block_indent(0) - } else { - shape.block_indent(context.config.tab_spaces()) - } - .with_max_width(context.config), - ) + let block_end = self.root_ends_with_block; + Some(get_block_child_shape(block_end, context, shape)) } fn format_children(&mut self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<()> { - for item in &self.shared.children[..self.shared.children.len() - 1] { - let rewrite = item.rewrite(context, child_shape)?; - self.shared.rewrites.push(rewrite); - } - Some(()) + self.shared.format_children(context, child_shape) } fn format_last_child( @@ -828,18 +884,17 @@ impl<'a> ChainFormatter for ChainFormatterVisual<'a> { } fn child_shape(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { - shape - .with_max_width(context.config) - .offset_left(self.offset) - .map(|s| s.visual_indent(0)) + get_visual_style_child_shape( + context, + shape, + self.offset, + // TODO(calebcartwright): self.shared.permissibly_overflowing_parent, + false, + ) } fn format_children(&mut self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<()> { - for item in &self.shared.children[..self.shared.children.len() - 1] { - let rewrite = item.rewrite(context, child_shape)?; - self.shared.rewrites.push(rewrite); - } - Some(()) + self.shared.format_children(context, child_shape) } fn format_last_child( From 662702eb54b12f16729d6559ce0478b00ef153a8 Mon Sep 17 00:00:00 2001 From: alexey semenyuk Date: Sun, 7 Aug 2022 02:53:03 +0300 Subject: [PATCH 233/401] Fix typos (#5486) * Fix typos * Fix typos --- CHANGELOG.md | 2 +- Processes.md | 2 +- src/bin/main.rs | 2 +- src/config/mod.rs | 2 +- src/imports.rs | 4 ++-- tests/mod-resolver/issue-5198/lib/c/d/explanation.txt | 2 +- tests/mod-resolver/issue-5198/lib/explanation.txt | 2 +- tests/source/cfg_if/detect/arch/x86.rs | 2 +- tests/source/enum.rs | 2 +- tests/source/tuple.rs | 2 +- .../wrap_comments_should_not_imply_format_doc_comments.rs | 2 +- tests/target/cfg_if/detect/arch/x86.rs | 2 +- tests/target/enum.rs | 2 +- tests/target/tuple.rs | 2 +- .../wrap_comments_should_not_imply_format_doc_comments.rs | 2 +- 15 files changed, 16 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c1893bf8c38..e9ce930dabc6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -840,7 +840,7 @@ from formatting an attribute #3665 - Fix formatting of raw string literals #2983 - Handle chain with try operators with spaces #2986 - Use correct shape in Visual tuple rewriting #2987 -- Impove formatting of arguments with `visual_style = "Visual"` option #2988 +- Improve formatting of arguments with `visual_style = "Visual"` option #2988 - Change `print_diff` to output the correct line number 992b179 - Propagate errors about failing to rewrite a macro 6f318e3 - Handle formatting of long function signature #3010 diff --git a/Processes.md b/Processes.md index 9d86d52b122d..334414702b15 100644 --- a/Processes.md +++ b/Processes.md @@ -2,7 +2,7 @@ This document outlines processes regarding management of rustfmt. # Stabilising an Option -In this Section, we describe how to stabilise an option of the rustfmt's configration. +In this Section, we describe how to stabilise an option of the rustfmt's configuration. ## Conditions diff --git a/src/bin/main.rs b/src/bin/main.rs index 8e871e61f268..be64559e8774 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -136,7 +136,7 @@ fn make_opts() -> Options { "l", "files-with-diff", "Prints the names of mismatched files that were formatted. Prints the names of \ - files that would be formated when used with `--check` mode. ", + files that would be formatted when used with `--check` mode. ", ); opts.optmulti( "", diff --git a/src/config/mod.rs b/src/config/mod.rs index 5492519f3894..4ec7e924bf62 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -182,7 +182,7 @@ create_config! { make_backup: bool, false, false, "Backup changed files"; print_misformatted_file_names: bool, false, true, "Prints the names of mismatched files that were formatted. Prints the names of \ - files that would be formated when used with `--check` mode. "; + files that would be formatted when used with `--check` mode. "; } #[derive(Error, Debug)] diff --git a/src/imports.rs b/src/imports.rs index 8d41c881589e..140d8953abc9 100644 --- a/src/imports.rs +++ b/src/imports.rs @@ -251,8 +251,8 @@ fn flatten_use_trees( use_trees: Vec, import_granularity: ImportGranularity, ) -> Vec { - // Return non-sorted single occurance of the use-trees text string; - // order is by first occurance of the use-tree. + // Return non-sorted single occurrence of the use-trees text string; + // order is by first occurrence of the use-tree. use_trees .into_iter() .flat_map(|tree| tree.flatten(import_granularity)) diff --git a/tests/mod-resolver/issue-5198/lib/c/d/explanation.txt b/tests/mod-resolver/issue-5198/lib/c/d/explanation.txt index 92c9e3021431..254102ebabdc 100644 --- a/tests/mod-resolver/issue-5198/lib/c/d/explanation.txt +++ b/tests/mod-resolver/issue-5198/lib/c/d/explanation.txt @@ -9,7 +9,7 @@ The directory name './lib/c/d/' conflicts with the './lib/c/d.rs' file name. * 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 +so we should fall back to looking for './lib/c/e.rs', which correctly finds the module, 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 diff --git a/tests/mod-resolver/issue-5198/lib/explanation.txt b/tests/mod-resolver/issue-5198/lib/explanation.txt index d436a8076cd7..90464def8ebc 100644 --- a/tests/mod-resolver/issue-5198/lib/explanation.txt +++ b/tests/mod-resolver/issue-5198/lib/explanation.txt @@ -9,7 +9,7 @@ The directory name './lib' conflicts with the './lib.rs' file name. * 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 +so we should fall back to looking for './a.rs', which correctly finds the module that rustfmt should format. './lib/b.rs' and './lib/c/mod.rs' exist at the default submodule paths so we should be able diff --git a/tests/source/cfg_if/detect/arch/x86.rs b/tests/source/cfg_if/detect/arch/x86.rs index d26f4ee894fa..131cbb855f16 100644 --- a/tests/source/cfg_if/detect/arch/x86.rs +++ b/tests/source/cfg_if/detect/arch/x86.rs @@ -329,7 +329,7 @@ pub enum Feature { tbm, /// POPCNT (Population Count) popcnt, - /// FXSR (Floating-point context fast save and restor) + /// FXSR (Floating-point context fast save and restore) fxsr, /// XSAVE (Save Processor Extended States) xsave, diff --git a/tests/source/enum.rs b/tests/source/enum.rs index 0ed9651abe72..a7b9616929cb 100644 --- a/tests/source/enum.rs +++ b/tests/source/enum.rs @@ -36,7 +36,7 @@ enum StructLikeVariants { Normal(u32, String, ), StructLike { x: i32, // Test comment // Pre-comment - #[Attr50] y: SomeType, // Aanother Comment + #[Attr50] y: SomeType, // Another Comment }, SL { a: A } } diff --git a/tests/source/tuple.rs b/tests/source/tuple.rs index 9a0f979fbca3..5189a7454f3a 100644 --- a/tests/source/tuple.rs +++ b/tests/source/tuple.rs @@ -1,4 +1,4 @@ -// Test tuple litterals +// Test tuple literals fn foo() { let a = (a, a, a, a, a); diff --git a/tests/source/wrap_comments_should_not_imply_format_doc_comments.rs b/tests/source/wrap_comments_should_not_imply_format_doc_comments.rs index 78b3ce146f28..56064e4a4ccc 100644 --- a/tests/source/wrap_comments_should_not_imply_format_doc_comments.rs +++ b/tests/source/wrap_comments_should_not_imply_format_doc_comments.rs @@ -11,6 +11,6 @@ /// fn foo() {} -/// A long commment for wrapping +/// A long comment for wrapping /// This is a long long long long long long long long long long long long long long long long long long long long sentence. fn bar() {} diff --git a/tests/target/cfg_if/detect/arch/x86.rs b/tests/target/cfg_if/detect/arch/x86.rs index 02d5eed1c292..47210cae2aaa 100644 --- a/tests/target/cfg_if/detect/arch/x86.rs +++ b/tests/target/cfg_if/detect/arch/x86.rs @@ -314,7 +314,7 @@ pub enum Feature { tbm, /// POPCNT (Population Count) popcnt, - /// FXSR (Floating-point context fast save and restor) + /// FXSR (Floating-point context fast save and restore) fxsr, /// XSAVE (Save Processor Extended States) xsave, diff --git a/tests/target/enum.rs b/tests/target/enum.rs index 9a25126b44ec..70fc8ab376cc 100644 --- a/tests/target/enum.rs +++ b/tests/target/enum.rs @@ -43,7 +43,7 @@ enum StructLikeVariants { x: i32, // Test comment // Pre-comment #[Attr50] - y: SomeType, // Aanother Comment + y: SomeType, // Another Comment }, SL { a: A, diff --git a/tests/target/tuple.rs b/tests/target/tuple.rs index 68bb2f3bc285..24fcf8cfd7cf 100644 --- a/tests/target/tuple.rs +++ b/tests/target/tuple.rs @@ -1,4 +1,4 @@ -// Test tuple litterals +// Test tuple literals fn foo() { let a = (a, a, a, a, a); diff --git a/tests/target/wrap_comments_should_not_imply_format_doc_comments.rs b/tests/target/wrap_comments_should_not_imply_format_doc_comments.rs index d61d4d7c216a..6ccecc7e0bbe 100644 --- a/tests/target/wrap_comments_should_not_imply_format_doc_comments.rs +++ b/tests/target/wrap_comments_should_not_imply_format_doc_comments.rs @@ -10,7 +10,7 @@ /// ``` fn foo() {} -/// A long commment for wrapping +/// A long comment for wrapping /// This is a long long long long long long long long long long long long long /// long long long long long long long sentence. fn bar() {} From 5a1ef3c7bc1f81f4a21da493f8e352d7316c7351 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Sat, 6 Aug 2022 19:48:55 -0500 Subject: [PATCH 234/401] chore: bump toolchain --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index 2640a9e0ecc2..f8ed76d2e6f9 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-06-21" +channel = "nightly-2022-08-06" components = ["rustc-dev"] From 437de8d17fc39231cc8424069579855a94e8c245 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Sat, 6 Aug 2022 19:59:52 -0500 Subject: [PATCH 235/401] chore: disable unreachable pub lint on config items --- src/config/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/config/mod.rs b/src/config/mod.rs index 4ec7e924bf62..1fc6d033541c 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -20,9 +20,11 @@ pub use crate::config::options::*; #[macro_use] pub(crate) mod config_type; #[macro_use] +#[allow(unreachable_pub)] pub(crate) mod options; pub(crate) mod file_lines; +#[allow(unreachable_pub)] pub(crate) mod lists; pub(crate) mod macro_names; From c78ef92add17c545c387bee12428d3dd51d90003 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Sat, 6 Aug 2022 20:29:46 -0500 Subject: [PATCH 236/401] chore: fix another config unreachable-pub --- src/config/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/mod.rs b/src/config/mod.rs index 1fc6d033541c..14f27f3f8b69 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -422,7 +422,7 @@ mod test { use rustfmt_config_proc_macro::config_type; #[config_type] - pub enum PartiallyUnstableOption { + pub(crate) enum PartiallyUnstableOption { V1, V2, #[unstable_variant] From a67d909627b7ec93e315bfe1c66895904055c75c Mon Sep 17 00:00:00 2001 From: alex-semenyuk Date: Mon, 8 Aug 2022 15:23:41 +0300 Subject: [PATCH 237/401] Fix some clippy issues --- src/cargo-fmt/main.rs | 12 +++++------- src/cargo-fmt/test/mod.rs | 4 ++-- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/cargo-fmt/main.rs b/src/cargo-fmt/main.rs index 9031d29b45f7..2b714b68df00 100644 --- a/src/cargo-fmt/main.rs +++ b/src/cargo-fmt/main.rs @@ -198,12 +198,10 @@ fn convert_message_format_to_rustfmt_args( Ok(()) } "human" => Ok(()), - _ => { - return Err(format!( - "invalid --message-format value: {}. Allowed values are: short|json|human", - message_format - )); - } + _ => Err(format!( + "invalid --message-format value: {}. Allowed values are: short|json|human", + message_format + )), } } @@ -215,7 +213,7 @@ fn print_usage_to_stderr(reason: &str) { .expect("failed to write to stderr"); } -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Verbosity { Verbose, Normal, diff --git a/src/cargo-fmt/test/mod.rs b/src/cargo-fmt/test/mod.rs index 56e52fbabb68..696326e4f940 100644 --- a/src/cargo-fmt/test/mod.rs +++ b/src/cargo-fmt/test/mod.rs @@ -70,9 +70,9 @@ fn mandatory_separator() { .is_err() ); assert!( - !Opts::command() + Opts::command() .try_get_matches_from(&["test", "--", "--emit"]) - .is_err() + .is_ok() ); } From ea017d7f84e218c4a8048d2ecd9d63bbda014209 Mon Sep 17 00:00:00 2001 From: David Bar-On <61089727+davidBar-On@users.noreply.github.com> Date: Tue, 9 Aug 2022 16:30:49 +0300 Subject: [PATCH 238/401] Backport PR #4730 (#5390) * Backport PR #4730 that fix issue #4689 * Test files for each Verion One and Two * Simplify per review comment - use defer and matches! * Changes per reviewer comments for reducing indentations --- src/types.rs | 43 +++++++++- tests/source/issue-4689/one.rs | 149 ++++++++++++++++++++++++++++++++ tests/source/issue-4689/two.rs | 149 ++++++++++++++++++++++++++++++++ tests/target/issue-4689/one.rs | 150 ++++++++++++++++++++++++++++++++ tests/target/issue-4689/two.rs | 152 +++++++++++++++++++++++++++++++++ 5 files changed, 639 insertions(+), 4 deletions(-) create mode 100644 tests/source/issue-4689/one.rs create mode 100644 tests/source/issue-4689/two.rs create mode 100644 tests/target/issue-4689/one.rs create mode 100644 tests/target/issue-4689/two.rs diff --git a/src/types.rs b/src/types.rs index 2627886db109..25ad587ba856 100644 --- a/src/types.rs +++ b/src/types.rs @@ -941,6 +941,28 @@ fn join_bounds_inner( ast::GenericBound::Trait(..) => last_line_extendable(s), }; + // Whether a GenericBound item is a PathSegment segment that includes internal array + // that contains more than one item + let is_item_with_multi_items_array = |item: &ast::GenericBound| match item { + ast::GenericBound::Trait(ref poly_trait_ref, ..) => { + let segments = &poly_trait_ref.trait_ref.path.segments; + if segments.len() > 1 { + true + } else { + if let Some(args_in) = &segments[0].args { + matches!( + args_in.deref(), + ast::GenericArgs::AngleBracketed(bracket_args) + if bracket_args.args.len() > 1 + ) + } else { + false + } + } + } + _ => false, + }; + let result = items.iter().enumerate().try_fold( (String::new(), None, false), |(strs, prev_trailing_span, prev_extendable), (i, item)| { @@ -1035,10 +1057,23 @@ fn join_bounds_inner( }, )?; - if !force_newline - && items.len() > 1 - && (result.0.contains('\n') || result.0.len() > shape.width) - { + // Whether retry the function with forced newline is needed: + // Only if result is not already multiline and did not exceed line width, + // and either there is more than one item; + // or the single item is of type `Trait`, + // and any of the internal arrays contains more than one item; + let retry_with_force_newline = + if force_newline || (!result.0.contains('\n') && result.0.len() <= shape.width) { + false + } else { + if items.len() > 1 { + true + } else { + is_item_with_multi_items_array(&items[0]) + } + }; + + if retry_with_force_newline { join_bounds_inner(context, shape, items, need_indent, true) } else { Some(result.0) diff --git a/tests/source/issue-4689/one.rs b/tests/source/issue-4689/one.rs new file mode 100644 index 000000000000..d048eb10fb15 --- /dev/null +++ b/tests/source/issue-4689/one.rs @@ -0,0 +1,149 @@ +// rustfmt-version: One + +// Based on the issue description +pub trait PrettyPrinter<'tcx>: +Printer< +'tcx, +Error = fmt::Error, +Path = Self, +Region = Self, +Type = Self, +DynExistential = Self, +Const = Self, +> +{ +// +} +pub trait PrettyPrinter<'tcx>: +Printer< +'tcx, +Error = fmt::Error, +Path = Self, +Region = Self, +Type = Self, +DynExistential = Self, +Const = Self, +> + fmt::Write +{ +// +} +pub trait PrettyPrinter<'tcx>: +Printer< +'tcx, +Error = fmt::Error, +Path = Self, +Region = Self, +Type = Self, +DynExistential = Self, +Const = Self, +> + fmt::Write1 + fmt::Write2 +{ +// +} +pub trait PrettyPrinter<'tcx>: +fmt::Write + Printer< +'tcx, +Error = fmt::Error, +Path = Self, +Region = Self, +Type = Self, +DynExistential = Self, +Const = Self, +> +{ +// +} +pub trait PrettyPrinter<'tcx>: +fmt::Write + Printer1< +'tcx, +Error = fmt::Error, +Path = Self, +Region = Self, +Type = Self, +DynExistential = Self, +Const = Self, +> + Printer2< +'tcx, +Error = fmt::Error, +Path = Self, +Region = Self, +Type = Self, +DynExistential = Self, +Const = Self, +> +{ +// +} + +// Some test cases to ensure other cases formatting were not changed +fn f() -> Box< +FnMut() -> Thing< +WithType = LongItemName, +Error = LONGLONGLONGLONGLONGONGEvenLongerErrorNameLongerLonger, +>, +> { +} +fn f() -> Box< +FnMut() -> Thing< +WithType = LongItemName, +Error = LONGLONGLONGLONGLONGONGEvenLongerErrorNameLongerLonger, +> + fmt::Write1 ++ fmt::Write2, +> { +} + +fn foo(foo2: F) +where +F: Fn( +// this comment is deleted +) +{ +} +fn foo(foo2: F) +where +F: Fn( +// this comment is deleted +) + fmt::Write +{ +} + +fn elaborate_bounds(mut mk_cand: F) +where +F: for<> FnMut( +&mut ProbeContext<>, +ty::PolyTraitRefffffffffffffffffffffffffffffffff<>, +tyyyyyyyyyyyyyyyyyyyyy::AssociatedItem, +), +{ +} +fn elaborate_bounds(mut mk_cand: F) +where +F: for<> FnMut( +&mut ProbeContext<>, +ty::PolyTraitRefffffffffffffffffffffffffffffffff<>, +tyyyyyyyyyyyyyyyyyyyyy::AssociatedItem, +) + fmt::Write, +{ +} + +fn build_sorted_static_get_entry_names( +mut entries: entryyyyyyyy, +) -> ( +impl Fn( +AlphabeticalTraversal, +Seconddddddddddddddddddddddddddddddddddd +) -> Parammmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm ++ Sendddddddddddddddddddddddddddddddddddddddddddd +) { +} + +pub trait SomeTrait: +Cloneeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee ++ Eqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq +{ +} + +trait B = where +for<'b> &'b Self: Send ++ Cloneeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee ++ Copyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy; diff --git a/tests/source/issue-4689/two.rs b/tests/source/issue-4689/two.rs new file mode 100644 index 000000000000..ea7feda825d4 --- /dev/null +++ b/tests/source/issue-4689/two.rs @@ -0,0 +1,149 @@ +// rustfmt-version: Two + +// Based on the issue description +pub trait PrettyPrinter<'tcx>: +Printer< +'tcx, +Error = fmt::Error, +Path = Self, +Region = Self, +Type = Self, +DynExistential = Self, +Const = Self, +> +{ +// +} +pub trait PrettyPrinter<'tcx>: +Printer< +'tcx, +Error = fmt::Error, +Path = Self, +Region = Self, +Type = Self, +DynExistential = Self, +Const = Self, +> + fmt::Write +{ +// +} +pub trait PrettyPrinter<'tcx>: +Printer< +'tcx, +Error = fmt::Error, +Path = Self, +Region = Self, +Type = Self, +DynExistential = Self, +Const = Self, +> + fmt::Write1 + fmt::Write2 +{ +// +} +pub trait PrettyPrinter<'tcx>: +fmt::Write + Printer< +'tcx, +Error = fmt::Error, +Path = Self, +Region = Self, +Type = Self, +DynExistential = Self, +Const = Self, +> +{ +// +} +pub trait PrettyPrinter<'tcx>: +fmt::Write + Printer1< +'tcx, +Error = fmt::Error, +Path = Self, +Region = Self, +Type = Self, +DynExistential = Self, +Const = Self, +> + Printer2< +'tcx, +Error = fmt::Error, +Path = Self, +Region = Self, +Type = Self, +DynExistential = Self, +Const = Self, +> +{ +// +} + +// Some test cases to ensure other cases formatting were not changed +fn f() -> Box< +FnMut() -> Thing< +WithType = LongItemName, +Error = LONGLONGLONGLONGLONGONGEvenLongerErrorNameLongerLonger, +>, +> { +} +fn f() -> Box< +FnMut() -> Thing< +WithType = LongItemName, +Error = LONGLONGLONGLONGLONGONGEvenLongerErrorNameLongerLonger, +> + fmt::Write1 ++ fmt::Write2, +> { +} + +fn foo(foo2: F) +where +F: Fn( +// this comment is deleted +) +{ +} +fn foo(foo2: F) +where +F: Fn( +// this comment is deleted +) + fmt::Write +{ +} + +fn elaborate_bounds(mut mk_cand: F) +where +F: for<> FnMut( +&mut ProbeContext<>, +ty::PolyTraitRefffffffffffffffffffffffffffffffff<>, +tyyyyyyyyyyyyyyyyyyyyy::AssociatedItem, +), +{ +} +fn elaborate_bounds(mut mk_cand: F) +where +F: for<> FnMut( +&mut ProbeContext<>, +ty::PolyTraitRefffffffffffffffffffffffffffffffff<>, +tyyyyyyyyyyyyyyyyyyyyy::AssociatedItem, +) + fmt::Write, +{ +} + +fn build_sorted_static_get_entry_names( +mut entries: entryyyyyyyy, +) -> ( +impl Fn( +AlphabeticalTraversal, +Seconddddddddddddddddddddddddddddddddddd +) -> Parammmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm ++ Sendddddddddddddddddddddddddddddddddddddddddddd +) { +} + +pub trait SomeTrait: +Cloneeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee ++ Eqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq +{ +} + +trait B = where +for<'b> &'b Self: Send ++ Cloneeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee ++ Copyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy; diff --git a/tests/target/issue-4689/one.rs b/tests/target/issue-4689/one.rs new file mode 100644 index 000000000000..df1a507bc1da --- /dev/null +++ b/tests/target/issue-4689/one.rs @@ -0,0 +1,150 @@ +// rustfmt-version: One + +// Based on the issue description +pub trait PrettyPrinter<'tcx>: + Printer< + 'tcx, + Error = fmt::Error, + Path = Self, + Region = Self, + Type = Self, + DynExistential = Self, + Const = Self, + > +{ + // +} +pub trait PrettyPrinter<'tcx>: + Printer< + 'tcx, + Error = fmt::Error, + Path = Self, + Region = Self, + Type = Self, + DynExistential = Self, + Const = Self, + > + fmt::Write +{ + // +} +pub trait PrettyPrinter<'tcx>: + Printer< + 'tcx, + Error = fmt::Error, + Path = Self, + Region = Self, + Type = Self, + DynExistential = Self, + Const = Self, + > + fmt::Write1 + + fmt::Write2 +{ + // +} +pub trait PrettyPrinter<'tcx>: + fmt::Write + + Printer< + 'tcx, + Error = fmt::Error, + Path = Self, + Region = Self, + Type = Self, + DynExistential = Self, + Const = Self, + > +{ + // +} +pub trait PrettyPrinter<'tcx>: + fmt::Write + + Printer1< + 'tcx, + Error = fmt::Error, + Path = Self, + Region = Self, + Type = Self, + DynExistential = Self, + Const = Self, + > + Printer2< + 'tcx, + Error = fmt::Error, + Path = Self, + Region = Self, + Type = Self, + DynExistential = Self, + Const = Self, + > +{ + // +} + +// Some test cases to ensure other cases formatting were not changed +fn f() -> Box< + FnMut() -> Thing< + WithType = LongItemName, + Error = LONGLONGLONGLONGLONGONGEvenLongerErrorNameLongerLonger, + >, +> { +} +fn f() -> Box< + FnMut() -> Thing< + WithType = LongItemName, + Error = LONGLONGLONGLONGLONGONGEvenLongerErrorNameLongerLonger, + > + fmt::Write1 + + fmt::Write2, +> { +} + +fn foo(foo2: F) +where + F: Fn( + // this comment is deleted + ), +{ +} +fn foo(foo2: F) +where + F: Fn( + // this comment is deleted + ) + fmt::Write, +{ +} + +fn elaborate_bounds(mut mk_cand: F) +where + F: FnMut( + &mut ProbeContext, + ty::PolyTraitRefffffffffffffffffffffffffffffffff, + tyyyyyyyyyyyyyyyyyyyyy::AssociatedItem, + ), +{ +} +fn elaborate_bounds(mut mk_cand: F) +where + F: FnMut( + &mut ProbeContext, + ty::PolyTraitRefffffffffffffffffffffffffffffffff, + tyyyyyyyyyyyyyyyyyyyyy::AssociatedItem, + ) + fmt::Write, +{ +} + +fn build_sorted_static_get_entry_names( + mut entries: entryyyyyyyy, +) -> (impl Fn( + AlphabeticalTraversal, + Seconddddddddddddddddddddddddddddddddddd, +) -> Parammmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm + + Sendddddddddddddddddddddddddddddddddddddddddddd) { +} + +pub trait SomeTrait: + Cloneeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee + + Eqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq +{ +} + +trait B = where + for<'b> &'b Self: Send + + Cloneeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee + + Copyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy; diff --git a/tests/target/issue-4689/two.rs b/tests/target/issue-4689/two.rs new file mode 100644 index 000000000000..e3b5cd22810b --- /dev/null +++ b/tests/target/issue-4689/two.rs @@ -0,0 +1,152 @@ +// rustfmt-version: Two + +// Based on the issue description +pub trait PrettyPrinter<'tcx>: + Printer< + 'tcx, + Error = fmt::Error, + Path = Self, + Region = Self, + Type = Self, + DynExistential = Self, + Const = Self, + > +{ + // +} +pub trait PrettyPrinter<'tcx>: + Printer< + 'tcx, + Error = fmt::Error, + Path = Self, + Region = Self, + Type = Self, + DynExistential = Self, + Const = Self, + > + fmt::Write +{ + // +} +pub trait PrettyPrinter<'tcx>: + Printer< + 'tcx, + Error = fmt::Error, + Path = Self, + Region = Self, + Type = Self, + DynExistential = Self, + Const = Self, + > + fmt::Write1 + + fmt::Write2 +{ + // +} +pub trait PrettyPrinter<'tcx>: + fmt::Write + + Printer< + 'tcx, + Error = fmt::Error, + Path = Self, + Region = Self, + Type = Self, + DynExistential = Self, + Const = Self, + > +{ + // +} +pub trait PrettyPrinter<'tcx>: + fmt::Write + + Printer1< + 'tcx, + Error = fmt::Error, + Path = Self, + Region = Self, + Type = Self, + DynExistential = Self, + Const = Self, + > + Printer2< + 'tcx, + Error = fmt::Error, + Path = Self, + Region = Self, + Type = Self, + DynExistential = Self, + Const = Self, + > +{ + // +} + +// Some test cases to ensure other cases formatting were not changed +fn f() -> Box< + FnMut() -> Thing< + WithType = LongItemName, + Error = LONGLONGLONGLONGLONGONGEvenLongerErrorNameLongerLonger, + >, +> { +} +fn f() -> Box< + FnMut() -> Thing< + WithType = LongItemName, + Error = LONGLONGLONGLONGLONGONGEvenLongerErrorNameLongerLonger, + > + fmt::Write1 + + fmt::Write2, +> { +} + +fn foo(foo2: F) +where + F: Fn( + // this comment is deleted + ), +{ +} +fn foo(foo2: F) +where + F: Fn( + // this comment is deleted + ) + fmt::Write, +{ +} + +fn elaborate_bounds(mut mk_cand: F) +where + F: FnMut( + &mut ProbeContext, + ty::PolyTraitRefffffffffffffffffffffffffffffffff, + tyyyyyyyyyyyyyyyyyyyyy::AssociatedItem, + ), +{ +} +fn elaborate_bounds(mut mk_cand: F) +where + F: FnMut( + &mut ProbeContext, + ty::PolyTraitRefffffffffffffffffffffffffffffffff, + tyyyyyyyyyyyyyyyyyyyyy::AssociatedItem, + ) + fmt::Write, +{ +} + +fn build_sorted_static_get_entry_names( + mut entries: entryyyyyyyy, +) -> ( + impl Fn( + AlphabeticalTraversal, + Seconddddddddddddddddddddddddddddddddddd, + ) -> Parammmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm + + Sendddddddddddddddddddddddddddddddddddddddddddd +) { +} + +pub trait SomeTrait: + Cloneeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee + + Eqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq +{ +} + +trait B = where + for<'b> &'b Self: Send + + Cloneeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee + + Copyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy; From 949da529d7a0b348376f67e77676a8722ad21899 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Mon, 1 Aug 2022 00:29:32 -0400 Subject: [PATCH 239/401] Add GitHub Action to test master rustfmt formatting vs a feature branch This new action is intended to help us maintainers determine when feature branches cause breaking formatting changes by running rustfmt (master) and the feature branch on various rust repositories. Over time I expect the list of checked projects to increase. With this action in place we can more easily test that a new feature or bug fix doesn't introduce breaking changes. Although this action needs to be manually triggered right now, we might consider adding it to our CI runs in the future. --- .github/workflows/check_diff.yml | 33 +++++ ci/check_diff.sh | 199 +++++++++++++++++++++++++++++++ 2 files changed, 232 insertions(+) create mode 100644 .github/workflows/check_diff.yml create mode 100755 ci/check_diff.sh diff --git a/.github/workflows/check_diff.yml b/.github/workflows/check_diff.yml new file mode 100644 index 000000000000..8bfb5834519c --- /dev/null +++ b/.github/workflows/check_diff.yml @@ -0,0 +1,33 @@ +name: Diff Check +on: + workflow_dispatch: + inputs: + clone_url: + description: 'Git url of a rustfmt fork to compare against the latest master rustfmt' + required: true + branch_name: + description: 'Name of the feature branch on the forked repo' + required: true + commit_hash: + description: 'Optional commit hash from the feature branch' + required: false + rustfmt_configs: + description: 'Optional comma separated list of rustfmt config options to pass when running the feature branch' + required: false + +jobs: + diff_check: + runs-on: ubuntu-latest + + steps: + - name: checkout + uses: actions/checkout@v3 + + - name: install rustup + run: | + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > rustup-init.sh + sh rustup-init.sh -y --default-toolchain none + rustup target add x86_64-unknown-linux-gnu + + - name: check diff + run: bash ${GITHUB_WORKSPACE}/ci/check_diff.sh ${{ github.event.inputs.clone_url }} ${{ github.event.inputs.branch_name }} ${{ github.event.inputs.commit_hash }} ${{ github.event.inputs.rustfmt_configs }} diff --git a/ci/check_diff.sh b/ci/check_diff.sh new file mode 100755 index 000000000000..062c2dd8673a --- /dev/null +++ b/ci/check_diff.sh @@ -0,0 +1,199 @@ +#!/bin/bash + +function print_usage() { + echo "usage check_diff REMOTE_REPO FEATURE_BRANCH [COMMIT_HASH] [OPTIONAL_RUSTFMT_CONFIGS]" +} + +if [ $# -le 1 ]; then + print_usage + exit 1 +fi + +REMOTE_REPO=$1 +FEATURE_BRANCH=$2 +OPTIONAL_COMMIT_HASH=$3 +OPTIONAL_RUSTFMT_CONFIGS=$4 + +# OUTPUT array used to collect all the status of running diffs on various repos +STATUSES=() + +# Clone a git repository and cd into it. +# +# Parameters: +# $1: git clone url +# $2: directory where the repo should be cloned +function clone_repo() { + GIT_TERMINAL_PROMPT=0 git clone --quiet $1 --depth 1 $2 && cd $2 +} + +# Initialize Git submoduels for the repo. +# +# Parameters +# $1: list of directories to initialize +function init_submodules() { + git submodule update --init $1 +} + +# Run rusfmt with the --check flag to see if a diff is produced. +# +# Parameters: +# $1: Path to a rustfmt binary +# $2: Output file path for the diff +# $3: Any additional configuration options to pass to rustfmt +# +# Globlas: +# $OPTIONAL_RUSTFMT_CONFIGS: Optional configs passed to the script from $4 +function create_diff() { + local config; + if [ -z "$3" ]; then + config="--config=error_on_line_overflow=false,error_on_unformatted=false" + else + config="--config=error_on_line_overflow=false,error_on_unformatted=false,$OPTIONAL_RUSTFMT_CONFIGS" + fi + + for i in `find . | grep "\.rs$"` + do + $1 --unstable-features --skip-children --check --color=always $config $i >> $2 2>/dev/null + done +} + +# Run the master rustfmt binary and the feature branch binary in the current directory and compare the diffs +# +# Parameters +# $1: Name of the repository (used for logging) +# +# Globlas: +# $RUSFMT_BIN: Path to the rustfmt master binary. Created when running `compile_rustfmt` +# $FEATURE_BIN: Path to the rustfmt feature binary. Created when running `compile_rustfmt` +# $OPTIONAL_RUSTFMT_CONFIGS: Optional configs passed to the script from $4 +function check_diff() { + echo "running rustfmt (master) on $1" + create_diff $RUSFMT_BIN rustfmt_diff.txt + + echo "running rustfmt (feature) on $1" + create_diff $FEATURE_BIN feature_diff.txt $OPTIONAL_RUSTFMT_CONFIGS + + echo "checking diff" + local diff; + # we don't add color to the diff since we added color when running rustfmt --check. + # tail -n + 6 removes the git diff header info + # cut -c 2- removes the leading diff characters("+","-"," ") from running git diff. + # Again, the diff output we care about was already added when we ran rustfmt --check + diff=$( + git --no-pager diff --color=never \ + --unified=0 --no-index rustfmt_diff.txt feature_diff.txt 2>&1 | tail -n +6 | cut -c 2- + ) + + if [ -z "$diff" ]; then + echo "no diff detected between rustfmt and the feture branch" + return 0 + else + echo "$diff" + return 1 + fi +} + +# Compiles and produces two rustfmt binaries. +# One for the current master, and another for the feature branch +# +# Parameters: +# $1: Directory where rustfmt will be cloned +# +# Globlas: +# $REMOTE_REPO: Clone URL to the rustfmt fork that we want to test +# $FEATURE_BRANCH: Name of the feature branch +# $OPTIONAL_COMMIT_HASH: Optional commit hash that will be checked out if provided +function compile_rustfmt() { + RUSTFMT_REPO="https://github.com/rust-lang/rustfmt.git" + clone_repo $RUSTFMT_REPO $1 + git remote add feature $REMOTE_REPO + git fetch feature $FEATURE_BRANCH + + cargo build --release --bin rustfmt && cp target/release/rustfmt $1/rustfmt + if [ -z "$OPTIONAL_COMMIT_HASH" ]; then + git switch $FEATURE_BRANCH + else + git switch $OPTIONAL_COMMIT_HASH --detach + fi + cargo build --release --bin rustfmt && cp target/release/rustfmt $1/feature_rustfmt + RUSFMT_BIN=$1/rustfmt + FEATURE_BIN=$1/feature_rustfmt +} + +# Check the diff for running rustfmt and the feature branch on all the .rs files in the repo. +# +# Parameters +# $1: Clone URL for the repo +# $2: Name of the repo (mostly used for logging) +# $3: Path to any submodules that should be initialized +function check_repo() { + WORKDIR=$(pwd) + REPO_URL=$1 + REPO_NAME=$2 + SUBMODULES=$3 + + local tmp_dir; + tmp_dir=$(mktemp -d -t $REPO_NAME-XXXXXXXX) + clone_repo $REPO_URL $tmp_dir + + if [ ! -z "$SUBMODULES" ]; then + init_submodules $SUBMODULES + fi + + check_diff $REPO_NAME + # append the status of running `check_diff` to the STATUSES array + STATUSES+=($?) + + echo "removing tmp_dir $tmp_dir" + rm -rf $tmp_dir + cd $WORKDIR +} + +function main() { + tmp_dir=$(mktemp -d -t rustfmt-XXXXXXXX) + echo Created tmp_dir $tmp_dir + + compile_rustfmt $tmp_dir + + # run checks + check_repo "https://github.com/rust-lang/rust.git" rust-lang-rust + check_repo "https://github.com/rust-lang/cargo.git" cargo + check_repo "https://github.com/rust-lang/miri.git" miri + check_repo "https://github.com/rust-lang/rust-analyzer.git" rust-analyzer + check_repo "https://github.com/bitflags/bitflags.git" bitflags + check_repo "https://github.com/rust-lang/log.git" log + check_repo "https://github.com/rust-lang/mdBook.git" mdBook + check_repo "https://github.com/rust-lang/packed_simd.git" packed_simd + check_repo "https://github.com/rust-lang/rust-semverver.git" check_repo + check_repo "https://github.com/Stebalien/tempfile.git" tempfile + check_repo "https://github.com/rust-lang/futures-rs.git" futures-rs + check_repo "https://github.com/dtolnay/anyhow.git" anyhow + check_repo "https://github.com/dtolnay/thiserror.git" thiserror + check_repo "https://github.com/dtolnay/syn.git" syn + check_repo "https://github.com/serde-rs/serde.git" serde + check_repo "https://github.com/rust-lang/rustlings.git" rustlings + check_repo "https://github.com/rust-lang/rustup.git" rustup + check_repo "https://github.com/SergioBenitez/Rocket.git" Rocket + check_repo "https://github.com/rustls/rustls.git" rustls + check_repo "https://github.com/rust-lang/rust-bindgen.git" rust-bindgen + check_repo "https://github.com/hyperium/hyper.git" hyper + check_repo "https://github.com/actix/actix.git" actix + check_repo "https://github.com/denoland/deno.git" denoland_deno + + # cleanup temp dir + echo removing tmp_dir $tmp_dir + rm -rf $tmp_dir + + # figure out the exit code + for status in ${STATUSES[@]} + do + if [ $status -eq 1 ]; then + echo "formatting diff found 💔" + return 1 + fi + done + + echo "no diff found 😊" +} + +main From 38659ec6ad5f341cf8eb3139725bf695872c6de7 Mon Sep 17 00:00:00 2001 From: A-Walrus <58790821+A-Walrus@users.noreply.github.com> Date: Fri, 19 Aug 2022 02:44:29 +0300 Subject: [PATCH 240/401] Unicode comment align (#5505) * Fix comment alignment with unicode Also added tests for this behaviour * Simplify tests * Improve tests --- src/lists.rs | 16 ++-- tests/source/comments_unicode.rs | 140 +++++++++++++++++++++++++++++++ tests/target/comments_unicode.rs | 140 +++++++++++++++++++++++++++++++ 3 files changed, 288 insertions(+), 8 deletions(-) create mode 100644 tests/source/comments_unicode.rs create mode 100644 tests/target/comments_unicode.rs diff --git a/src/lists.rs b/src/lists.rs index e87850507824..a878e6cf9b2f 100644 --- a/src/lists.rs +++ b/src/lists.rs @@ -297,9 +297,9 @@ where } else { inner_item.as_ref() }; - let mut item_last_line_width = item_last_line.len() + item_sep_len; + let mut item_last_line_width = unicode_str_width(item_last_line) + item_sep_len; if item_last_line.starts_with(&**indent_str) { - item_last_line_width -= indent_str.len(); + item_last_line_width -= unicode_str_width(indent_str); } if !item.is_substantial() { @@ -449,7 +449,7 @@ where } else if starts_with_newline(comment) { false } else { - comment.trim().contains('\n') || comment.trim().len() > width + comment.trim().contains('\n') || unicode_str_width(comment.trim()) > width }; rewrite_comment( @@ -465,7 +465,7 @@ where if !starts_with_newline(comment) { if formatting.align_comments { let mut comment_alignment = - post_comment_alignment(item_max_width, inner_item.len()); + post_comment_alignment(item_max_width, unicode_str_width(inner_item)); if first_line_width(&formatted_comment) + last_line_width(&result) + comment_alignment @@ -475,7 +475,7 @@ where item_max_width = None; formatted_comment = rewrite_post_comment(&mut item_max_width)?; comment_alignment = - post_comment_alignment(item_max_width, inner_item.len()); + post_comment_alignment(item_max_width, unicode_str_width(inner_item)); } for _ in 0..=comment_alignment { result.push(' '); @@ -533,7 +533,7 @@ where let mut first = true; for item in items.clone().into_iter().skip(i) { let item = item.as_ref(); - let inner_item_width = item.inner_as_ref().len(); + let inner_item_width = unicode_str_width(item.inner_as_ref()); if !first && (item.is_different_group() || item.post_comment.is_none() @@ -552,8 +552,8 @@ where max_width } -fn post_comment_alignment(item_max_width: Option, inner_item_len: usize) -> usize { - item_max_width.unwrap_or(0).saturating_sub(inner_item_len) +fn post_comment_alignment(item_max_width: Option, inner_item_width: usize) -> usize { + item_max_width.unwrap_or(0).saturating_sub(inner_item_width) } pub(crate) struct ListItems<'a, I, F1, F2, F3> diff --git a/tests/source/comments_unicode.rs b/tests/source/comments_unicode.rs new file mode 100644 index 000000000000..e65a245ba934 --- /dev/null +++ b/tests/source/comments_unicode.rs @@ -0,0 +1,140 @@ +impl Default for WhitespaceCharacters { + fn default() -> Self { + Self { + space: '·', // U+00B7 + nbsp: '⍽', // U+237D + tab: '→', // U+2192 + newline: '⏎', // U+23CE + } + } +} + +const RAINBOWS: &[&str] = &[ + "rаinЬοѡ", // hue: 0 + "raіnЬοw", // hue: 2 + "rаіɴЬow", // hue: 2 + "raіɴЬoѡ", // hue: 8 + "ʀainЬow", // hue: 8 + "ʀaіɴboѡ", // hue: 8 + "ʀаіnbοw", // hue: 11 + "rainЬoѡ", // hue: 14 + "raіɴbow", // hue: 14 + "rаiɴЬow", // hue: 20 + "raіnЬow", // hue: 26 + "ʀaiɴbοw", // hue: 32 + "raіɴboѡ", // hue: 35 + "rаiɴbow", // hue: 35 + "rаіnbοw", // hue: 38 + "rаinЬow", // hue: 47 + "ʀaіnboѡ", // hue: 47 + "ʀaіnЬoѡ", // hue: 47 + "ʀаіɴbοw", // hue: 53 + "ʀaіnЬοѡ", // hue: 57 + "raiɴЬoѡ", // hue: 68 + "ʀainbοѡ", // hue: 68 + "ʀаinboѡ", // hue: 68 + "ʀаiɴbοw", // hue: 68 + "ʀаіnbow", // hue: 68 + "rаіnЬοѡ", // hue: 69 + "ʀainЬοw", // hue: 71 + "raiɴbow", // hue: 73 + "raіnЬoѡ", // hue: 74 + "rаіɴbοw", // hue: 77 + "raіnЬοѡ", // hue: 81 + "raiɴЬow", // hue: 83 + "ʀainbοw", // hue: 83 + "ʀаinbow", // hue: 83 + "ʀаiɴbοѡ", // hue: 83 + "ʀаіnboѡ", // hue: 83 + "ʀаіɴЬοѡ", // hue: 84 + "rainЬow", // hue: 85 + "ʀаiɴЬοw", // hue: 86 + "ʀаіnbοѡ", // hue: 89 + "ʀаіnЬοw", // hue: 92 + "rаiɴbοw", // hue: 95 + "ʀаіɴbοѡ", // hue: 98 + "ʀаiɴЬοѡ", // hue: 99 + "raіnbοw", // hue: 101 + "ʀаіɴЬοw", // hue: 101 + "ʀaiɴboѡ", // hue: 104 + "ʀаinbοѡ", // hue: 104 + "rаiɴbοѡ", // hue: 107 + "ʀаinЬοw", // hue: 107 + "rаiɴЬοw", // hue: 110 + "rаіnboѡ", // hue: 110 + "rаіnbοѡ", // hue: 113 + "ʀainЬοѡ", // hue: 114 + "rаіnЬοw", // hue: 116 + "ʀaіɴЬow", // hue: 116 + "rаinbοw", // hue: 122 + "ʀаіɴboѡ", // hue: 125 + "rаinbοѡ", // hue: 131 + "rainbow", // hue: 134 + "rаinЬοw", // hue: 134 + "ʀаiɴboѡ", // hue: 140 + "rainЬοѡ", // hue: 141 + "raіɴЬow", // hue: 143 + "ʀainЬoѡ", // hue: 143 + "ʀaіɴbow", // hue: 143 + "ʀainbow", // hue: 148 + "rаіɴboѡ", // hue: 149 + "ʀainboѡ", // hue: 155 + "ʀaіnbow", // hue: 155 + "ʀaіnЬow", // hue: 155 + "raiɴbοw", // hue: 158 + "ʀаiɴЬoѡ", // hue: 158 + "rainbοw", // hue: 160 + "rаinbow", // hue: 160 + "ʀaіɴbοѡ", // hue: 164 + "ʀаiɴbow", // hue: 164 + "ʀаіnЬoѡ", // hue: 164 + "ʀaiɴЬοѡ", // hue: 165 + "rаiɴboѡ", // hue: 167 + "ʀaіɴЬοw", // hue: 167 + "ʀaіɴЬοѡ", // hue: 171 + "raіnboѡ", // hue: 173 + "ʀаіɴЬoѡ", // hue: 173 + "rаіɴbοѡ", // hue: 176 + "ʀаinЬow", // hue: 176 + "rаiɴЬοѡ", // hue: 177 + "rаіɴЬοw", // hue: 179 + "ʀаinЬoѡ", // hue: 179 + "ʀаіɴbow", // hue: 179 + "rаiɴЬoѡ", // hue: 182 + "raіɴbοѡ", // hue: 188 + "rаіnЬoѡ", // hue: 188 + "raiɴЬοѡ", // hue: 189 + "raіɴЬοw", // hue: 191 + "ʀaіɴbοw", // hue: 191 + "ʀаіnЬow", // hue: 191 + "rainbοѡ", // hue: 194 + "rаinboѡ", // hue: 194 + "rаіnbow", // hue: 194 + "rainЬοw", // hue: 197 + "rаinЬoѡ", // hue: 206 + "rаіɴbow", // hue: 206 + "rаіɴЬοѡ", // hue: 210 + "ʀaiɴЬow", // hue: 212 + "raіɴbοw", // hue: 218 + "rаіnЬow", // hue: 218 + "ʀaiɴbοѡ", // hue: 221 + "ʀaiɴЬοw", // hue: 224 + "ʀaіnbοѡ", // hue: 227 + "raiɴboѡ", // hue: 230 + "ʀaіnbοw", // hue: 230 + "ʀaіnЬοw", // hue: 230 + "ʀаinЬοѡ", // hue: 231 + "rainboѡ", // hue: 232 + "raіnbow", // hue: 232 + "ʀаіɴЬow", // hue: 233 + "ʀaіɴЬoѡ", // hue: 239 + "ʀаіnЬοѡ", // hue: 246 + "raiɴbοѡ", // hue: 248 + "ʀаiɴЬow", // hue: 248 + "raіɴЬοѡ", // hue: 249 + "raiɴЬοw", // hue: 251 + "rаіɴЬoѡ", // hue: 251 + "ʀaiɴbow", // hue: 251 + "ʀаinbοw", // hue: 251 + "raіnbοѡ", // hue: 254 +]; diff --git a/tests/target/comments_unicode.rs b/tests/target/comments_unicode.rs new file mode 100644 index 000000000000..3e1b6b0a28fe --- /dev/null +++ b/tests/target/comments_unicode.rs @@ -0,0 +1,140 @@ +impl Default for WhitespaceCharacters { + fn default() -> Self { + Self { + space: '·', // U+00B7 + nbsp: '⍽', // U+237D + tab: '→', // U+2192 + newline: '⏎', // U+23CE + } + } +} + +const RAINBOWS: &[&str] = &[ + "rаinЬοѡ", // hue: 0 + "raіnЬοw", // hue: 2 + "rаіɴЬow", // hue: 2 + "raіɴЬoѡ", // hue: 8 + "ʀainЬow", // hue: 8 + "ʀaіɴboѡ", // hue: 8 + "ʀаіnbοw", // hue: 11 + "rainЬoѡ", // hue: 14 + "raіɴbow", // hue: 14 + "rаiɴЬow", // hue: 20 + "raіnЬow", // hue: 26 + "ʀaiɴbοw", // hue: 32 + "raіɴboѡ", // hue: 35 + "rаiɴbow", // hue: 35 + "rаіnbοw", // hue: 38 + "rаinЬow", // hue: 47 + "ʀaіnboѡ", // hue: 47 + "ʀaіnЬoѡ", // hue: 47 + "ʀаіɴbοw", // hue: 53 + "ʀaіnЬοѡ", // hue: 57 + "raiɴЬoѡ", // hue: 68 + "ʀainbοѡ", // hue: 68 + "ʀаinboѡ", // hue: 68 + "ʀаiɴbοw", // hue: 68 + "ʀаіnbow", // hue: 68 + "rаіnЬοѡ", // hue: 69 + "ʀainЬοw", // hue: 71 + "raiɴbow", // hue: 73 + "raіnЬoѡ", // hue: 74 + "rаіɴbοw", // hue: 77 + "raіnЬοѡ", // hue: 81 + "raiɴЬow", // hue: 83 + "ʀainbοw", // hue: 83 + "ʀаinbow", // hue: 83 + "ʀаiɴbοѡ", // hue: 83 + "ʀаіnboѡ", // hue: 83 + "ʀаіɴЬοѡ", // hue: 84 + "rainЬow", // hue: 85 + "ʀаiɴЬοw", // hue: 86 + "ʀаіnbοѡ", // hue: 89 + "ʀаіnЬοw", // hue: 92 + "rаiɴbοw", // hue: 95 + "ʀаіɴbοѡ", // hue: 98 + "ʀаiɴЬοѡ", // hue: 99 + "raіnbοw", // hue: 101 + "ʀаіɴЬοw", // hue: 101 + "ʀaiɴboѡ", // hue: 104 + "ʀаinbοѡ", // hue: 104 + "rаiɴbοѡ", // hue: 107 + "ʀаinЬοw", // hue: 107 + "rаiɴЬοw", // hue: 110 + "rаіnboѡ", // hue: 110 + "rаіnbοѡ", // hue: 113 + "ʀainЬοѡ", // hue: 114 + "rаіnЬοw", // hue: 116 + "ʀaіɴЬow", // hue: 116 + "rаinbοw", // hue: 122 + "ʀаіɴboѡ", // hue: 125 + "rаinbοѡ", // hue: 131 + "rainbow", // hue: 134 + "rаinЬοw", // hue: 134 + "ʀаiɴboѡ", // hue: 140 + "rainЬοѡ", // hue: 141 + "raіɴЬow", // hue: 143 + "ʀainЬoѡ", // hue: 143 + "ʀaіɴbow", // hue: 143 + "ʀainbow", // hue: 148 + "rаіɴboѡ", // hue: 149 + "ʀainboѡ", // hue: 155 + "ʀaіnbow", // hue: 155 + "ʀaіnЬow", // hue: 155 + "raiɴbοw", // hue: 158 + "ʀаiɴЬoѡ", // hue: 158 + "rainbοw", // hue: 160 + "rаinbow", // hue: 160 + "ʀaіɴbοѡ", // hue: 164 + "ʀаiɴbow", // hue: 164 + "ʀаіnЬoѡ", // hue: 164 + "ʀaiɴЬοѡ", // hue: 165 + "rаiɴboѡ", // hue: 167 + "ʀaіɴЬοw", // hue: 167 + "ʀaіɴЬοѡ", // hue: 171 + "raіnboѡ", // hue: 173 + "ʀаіɴЬoѡ", // hue: 173 + "rаіɴbοѡ", // hue: 176 + "ʀаinЬow", // hue: 176 + "rаiɴЬοѡ", // hue: 177 + "rаіɴЬοw", // hue: 179 + "ʀаinЬoѡ", // hue: 179 + "ʀаіɴbow", // hue: 179 + "rаiɴЬoѡ", // hue: 182 + "raіɴbοѡ", // hue: 188 + "rаіnЬoѡ", // hue: 188 + "raiɴЬοѡ", // hue: 189 + "raіɴЬοw", // hue: 191 + "ʀaіɴbοw", // hue: 191 + "ʀаіnЬow", // hue: 191 + "rainbοѡ", // hue: 194 + "rаinboѡ", // hue: 194 + "rаіnbow", // hue: 194 + "rainЬοw", // hue: 197 + "rаinЬoѡ", // hue: 206 + "rаіɴbow", // hue: 206 + "rаіɴЬοѡ", // hue: 210 + "ʀaiɴЬow", // hue: 212 + "raіɴbοw", // hue: 218 + "rаіnЬow", // hue: 218 + "ʀaiɴbοѡ", // hue: 221 + "ʀaiɴЬοw", // hue: 224 + "ʀaіnbοѡ", // hue: 227 + "raiɴboѡ", // hue: 230 + "ʀaіnbοw", // hue: 230 + "ʀaіnЬοw", // hue: 230 + "ʀаinЬοѡ", // hue: 231 + "rainboѡ", // hue: 232 + "raіnbow", // hue: 232 + "ʀаіɴЬow", // hue: 233 + "ʀaіɴЬoѡ", // hue: 239 + "ʀаіnЬοѡ", // hue: 246 + "raiɴbοѡ", // hue: 248 + "ʀаiɴЬow", // hue: 248 + "raіɴЬοѡ", // hue: 249 + "raiɴЬοw", // hue: 251 + "rаіɴЬoѡ", // hue: 251 + "ʀaiɴbow", // hue: 251 + "ʀаinbοw", // hue: 251 + "raіnbοѡ", // hue: 254 +]; From ef91154250977b3b5d05448dafbca524a1168b47 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Mon, 19 Sep 2022 17:58:37 -0500 Subject: [PATCH 241/401] docs: expand behavior of imports_granularity re: groups (#5543) --- Configurations.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Configurations.md b/Configurations.md index e066553dc63f..400ad8c5d3bf 100644 --- a/Configurations.md +++ b/Configurations.md @@ -1857,13 +1857,16 @@ pub enum Foo {} ## `imports_granularity` -How imports should be grouped into `use` statements. Imports will be merged or split to the configured level of granularity. +Controls how imports are structured in `use` statements. Imports will be merged or split to the configured level of granularity. + +Similar to other `import` related configuration options, this option operates within the bounds of user-defined groups of imports. See [`group_imports`](#group_imports) for more information on import groups. + +Note that rustfmt will not modify the granularity of imports containing comments if doing so could potentially lose or misplace said comments. - **Default value**: `Preserve` - **Possible values**: `Preserve`, `Crate`, `Module`, `Item`, `One` - **Stable**: No (tracking issue: [#4991](https://github.com/rust-lang/rustfmt/issues/4991)) -Note that rustfmt will not modify the granularity of imports containing comments if doing so could potentially lose or misplace said comments. #### `Preserve` (default): From eb07a5ea417f4fb54cbfaa0a74992208c0435bad Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Mon, 24 Oct 2022 20:13:47 +0900 Subject: [PATCH 242/401] Remove `failure` integration test Signed-off-by: Yuki Okushi --- .github/workflows/integration.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 4d8899b434bb..314ce0e84c61 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -27,7 +27,6 @@ jobs: tempdir, futures-rs, rust-clippy, - failure, ] include: # Allowed Failures @@ -63,9 +62,6 @@ jobs: # Original comment was: temporal build failure due to breaking changes in the nightly compiler - integration: rust-semverver allow-failure: true - # Can be moved back to include section after https://github.com/rust-lang-nursery/failure/pull/298 is merged - - integration: failure - allow-failure: true steps: - name: checkout From ad9fb89c3009282a55582f1c478d215d0c6005b0 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Mon, 24 Oct 2022 20:21:44 +0900 Subject: [PATCH 243/401] Update Git repo URLs Signed-off-by: Yuki Okushi --- ci/integration.sh | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/ci/integration.sh b/ci/integration.sh index 562d5d70c70b..19d502bc5c7b 100755 --- a/ci/integration.sh +++ b/ci/integration.sh @@ -91,14 +91,28 @@ case ${INTEGRATION} in cd - ;; crater) - git clone --depth=1 https://github.com/rust-lang-nursery/${INTEGRATION}.git + git clone --depth=1 https://github.com/rust-lang/${INTEGRATION}.git cd ${INTEGRATION} show_head check_fmt_with_lib_tests cd - ;; + bitflags) + git clone --depth=1 https://github.com/bitflags/${INTEGRATION}.git + cd ${INTEGRATION} + show_head + check_fmt_with_all_tests + cd - + ;; + error-chain | tempdir) + git clone --depth=1 https://github.com/rust-lang-deprecated/${INTEGRATION}.git + cd ${INTEGRATION} + show_head + check_fmt_with_all_tests + cd - + ;; *) - git clone --depth=1 https://github.com/rust-lang-nursery/${INTEGRATION}.git + git clone --depth=1 https://github.com/rust-lang/${INTEGRATION}.git cd ${INTEGRATION} show_head check_fmt_with_all_tests From ee2bed96d60fd7e46b1fb868f6a8f27e3a8058d0 Mon Sep 17 00:00:00 2001 From: Alex Touchet Date: Sat, 5 Nov 2022 21:39:56 -0700 Subject: [PATCH 244/401] Fix spacing --- Configurations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Configurations.md b/Configurations.md index 400ad8c5d3bf..49e7e4e64892 100644 --- a/Configurations.md +++ b/Configurations.md @@ -425,7 +425,7 @@ fn example() { ## `comment_width` -Maximum length of comments. No effect unless`wrap_comments = true`. +Maximum length of comments. No effect unless `wrap_comments = true`. - **Default value**: `80` - **Possible values**: any positive integer From aae222c974a982e878f279b95bd1a9d0166ff229 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Mon, 23 Jan 2023 10:54:05 -0600 Subject: [PATCH 245/401] fix: correct span for structs with const generics --- src/items.rs | 2 +- tests/target/issue_5668.rs | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 tests/target/issue_5668.rs diff --git a/src/items.rs b/src/items.rs index 755a41f6bf0c..063a6428a08f 100644 --- a/src/items.rs +++ b/src/items.rs @@ -1245,7 +1245,7 @@ fn format_unit_struct( ) -> Option { let header_str = format_header(context, p.prefix, p.ident, p.vis, offset); let generics_str = if let Some(generics) = p.generics { - let hi = context.snippet_provider.span_before(p.span, ";"); + let hi = context.snippet_provider.span_before_last(p.span, ";"); format_generics( context, generics, diff --git a/tests/target/issue_5668.rs b/tests/target/issue_5668.rs new file mode 100644 index 000000000000..bbd9a530b81c --- /dev/null +++ b/tests/target/issue_5668.rs @@ -0,0 +1,8 @@ +type Foo = impl Send; +struct Struct< + const C: usize = { + let _: Foo = (); + //~^ ERROR: mismatched types + 0 + }, +>; From 18dd0757dbef84611fc0f1f522b15913248b8541 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Mon, 23 Jan 2023 21:44:03 -0600 Subject: [PATCH 246/401] chore: bump toolchain --- rust-toolchain | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust-toolchain b/rust-toolchain index f8ed76d2e6f9..22283b3d6200 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-08-06" -components = ["rustc-dev"] +channel = "nightly-2023-01-24" +components = ["llvm-tools", "rustc-dev"] From 5139b14620832d08a436ad85c1bef4037c36395b Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Tue, 24 Jan 2023 11:13:54 -0600 Subject: [PATCH 247/401] fix: version gate changes for multiline single generic bound --- src/types.rs | 23 ++++++++++++----------- tests/target/issue-4689/one.rs | 16 ++++++++-------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/types.rs b/src/types.rs index 2cae54981075..01e2fb6e61e1 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1057,21 +1057,22 @@ fn join_bounds_inner( }, )?; - // Whether retry the function with forced newline is needed: + // Whether to retry with a forced newline: // Only if result is not already multiline and did not exceed line width, // and either there is more than one item; // or the single item is of type `Trait`, // and any of the internal arrays contains more than one item; - let retry_with_force_newline = - if force_newline || (!result.0.contains('\n') && result.0.len() <= shape.width) { - false - } else { - if items.len() > 1 { - true - } else { - is_item_with_multi_items_array(&items[0]) - } - }; + let retry_with_force_newline = match context.config.version() { + Version::One => { + !force_newline + && items.len() > 1 + && (result.0.contains('\n') || result.0.len() > shape.width) + } + Version::Two if force_newline => false, + Version::Two if (!result.0.contains('\n') && result.0.len() <= shape.width) => false, + Version::Two if items.len() > 1 => true, + Version::Two => is_item_with_multi_items_array(&items[0]), + }; if retry_with_force_newline { join_bounds_inner(context, shape, items, need_indent, true) diff --git a/tests/target/issue-4689/one.rs b/tests/target/issue-4689/one.rs index df1a507bc1da..7735e34f3b5e 100644 --- a/tests/target/issue-4689/one.rs +++ b/tests/target/issue-4689/one.rs @@ -3,14 +3,14 @@ // Based on the issue description pub trait PrettyPrinter<'tcx>: Printer< - 'tcx, - Error = fmt::Error, - Path = Self, - Region = Self, - Type = Self, - DynExistential = Self, - Const = Self, - > + 'tcx, + Error = fmt::Error, + Path = Self, + Region = Self, + Type = Self, + DynExistential = Self, + Const = Self, +> { // } From 1d8491b120223272b13451fc81265aa64f7f4d5b Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Tue, 24 Jan 2023 13:08:52 -0600 Subject: [PATCH 248/401] chore: prep v1.5.2 release --- CHANGELOG.md | 26 ++++++++++++++++++++++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e9ce930dabc6..60f961fa12ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,32 @@ ## [Unreleased] +## [1.5.2] 2023-01-24 + +### Fixed + +- Resolve issue when comments are found within const generic defaults in unit structs [#5668](https://github.com/rust-lang/rustfmt/issues/5668) +- Resolve issue when block comments are found within trait generics [#5358](https://github.com/rust-lang/rustfmt/issues/5358) +- Correctly handle alignment of comments containing unicode characters [#5504](https://github.com/rust-lang/rustfmt/issues/5504) +- Properly indent a single generic bound that requires being written across multiple lines [#4689](https://github.com/rust-lang/rustfmt/issues/4689) (n.b. this change is version gated and will only appear when the `version` configuration option is set to `Two`) + +### Changed + +- Renamed `fn_args_layout` configuration option to `fn_params_layout` [#4149](https://github.com/rust-lang/rustfmt/issues/4149). Note that `fn_args_layout` has only been soft deprecated: `fn_args_layout` will continue to work without issue, but rustfmt will display a warning to encourage users to switch to the new name + +### Added + +- New configuration option (`skip_macro_invocations`)[https://rust-lang.github.io/rustfmt/?version=master&search=#skip_macro_invocations] [#5347](https://github.com/rust-lang/rustfmt/pull/5347) that can be used to globally define a single enumerated list of macro calls that rustfmt should skip formatting. rustfmt [currently also supports this via a custom tool attribute](https://github.com/rust-lang/rustfmt#tips), however, these cannot be used in all contexts because [custom inner attributes are unstable](https://github.com/rust-lang/rust/issues/54726) + +### Misc + +- rustfmt now internally supports the ability to have both stable and unstable variants of a configuration option [#5378](https://github.com/rust-lang/rustfmt/issues/5378). This ability will allow the rustfmt team to make certain configuration options available on stable toolchains more quickly because we no longer have to wait for _every_ variant to be stable-ready before stabilizing _any_ variant. + +### Install/Download Options +- **rustup (nightly)** - nightly-2023-01-24 +- **GitHub Release Binaries** - [Release v1.5.2](https://github.com/rust-lang/rustfmt/releases/tag/v1.5.2) +- **Build from source** - [Tag v1.5.2](https://github.com/rust-lang/rustfmt/tree/v1.5.2), see instructions for how to [install rustfmt from source][install-from-source] + ## [1.5.1] 2022-06-24 **N.B** A bug was introduced in v1.5.0/nightly-2022-06-15 which modified formatting. If you happened to run rustfmt over your code with one of those ~10 nightlies it's possible you may have seen formatting changes, and you may see additional changes after this fix since that bug has now been reverted. diff --git a/Cargo.lock b/Cargo.lock index e51755289706..24166d51c51f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -485,7 +485,7 @@ dependencies = [ [[package]] name = "rustfmt-nightly" -version = "1.5.1" +version = "1.5.2" dependencies = [ "annotate-snippets", "anyhow", diff --git a/Cargo.toml b/Cargo.toml index 7438335eaa78..87ce59d0217e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rustfmt-nightly" -version = "1.5.1" +version = "1.5.2" description = "Tool to find and fix Rust formatting issues" repository = "https://github.com/rust-lang/rustfmt" readme = "README.md" From 567bd977230f3fe64781e9946b9f692919d30d38 Mon Sep 17 00:00:00 2001 From: Arthur Carcano <53921575+krtab@users.noreply.github.com> Date: Sat, 28 Jan 2023 22:16:46 +0100 Subject: [PATCH 249/401] Document which comments are excluded from wrapping (#5637) * Document which comments are excluded from wrapping Cf: https://github.com/rust-lang/rustfmt/issues/5634 * Add examples in wrap_commments doc * fix failling tests --- Configurations.md | 20 ++++++++++++++++++++ src/comment.rs | 2 ++ 2 files changed, 22 insertions(+) diff --git a/Configurations.md b/Configurations.md index 49e7e4e64892..ac638ff91e6d 100644 --- a/Configurations.md +++ b/Configurations.md @@ -2997,6 +2997,10 @@ See also [`brace_style`](#brace_style), [`control_brace_style`](#control_brace_s Break comments to fit on the line +Note that no wrapping will happen if: +1. The comment is the start of a markdown header doc comment +2. An URL was found in the comment + - **Default value**: `false` - **Possible values**: `true`, `false` - **Stable**: No (tracking issue: [#3347](https://github.com/rust-lang/rustfmt/issues/3347)) @@ -3011,6 +3015,11 @@ Break comments to fit on the line // commodo consequat. // 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. + +// Information on the lorem ipsum can be found at the following url: https://en.wikipedia.org/wiki/Lorem_ipsum. Its text is: 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. + +/// # This doc comment is a very long header (it starts with a '#'). Had it not been a header it would have been wrapped. But because it is a header, it will not be. That is because wrapping a markdown header breaks it. +struct Foo {} ``` #### `true`: @@ -3021,6 +3030,17 @@ Break comments to fit on the line // magna aliqua. Ut enim ad minim veniam, quis nostrud // exercitation ullamco laboris nisi ut aliquip ex ea // commodo consequat. + +// 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. + +// Information on the lorem ipsum can be found at the following url: https://en.wikipedia.org/wiki/Lorem_ipsum. Its text is: 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. + +/// # This doc comment is a very long header (it starts with a '#'). Had it not been a header it would have been wrapped. But because it is a header, it will not be. That is because wrapping a markdown header breaks it. +struct Foo {} ``` # Internal Options diff --git a/src/comment.rs b/src/comment.rs index 4d565afc1e02..261fa5e93d84 100644 --- a/src/comment.rs +++ b/src/comment.rs @@ -801,6 +801,8 @@ impl<'a> CommentRewrite<'a> { // 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 comment + // If this changes, the documentation in ../Configurations.md#wrap_comments + // should be changed accordingly. let should_wrap_comment = self.fmt.config.wrap_comments() && !is_markdown_header_doc_comment && unicode_str_width(line) > self.fmt.shape.width From bcfc57ec8a8fb1120b9b447df21cd07aac6c228d Mon Sep 17 00:00:00 2001 From: amab8901 <83634595+amab8901@users.noreply.github.com> Date: Sat, 28 Jan 2023 22:18:52 +0100 Subject: [PATCH 250/401] added tests (#5619) --- tests/source/issue-5586.rs | 164 ++++++++++++++++++++++++++++++++++ tests/target/issue-5586.rs | 177 +++++++++++++++++++++++++++++++++++++ 2 files changed, 341 insertions(+) create mode 100644 tests/source/issue-5586.rs create mode 100644 tests/target/issue-5586.rs diff --git a/tests/source/issue-5586.rs b/tests/source/issue-5586.rs new file mode 100644 index 000000000000..9cf6c1d58ddc --- /dev/null +++ b/tests/source/issue-5586.rs @@ -0,0 +1,164 @@ +// rustfmt-version: Two +fn main() { + // sample 1 + { + { + { + { + { + let push_ident = + if let Some(&node_id) = subgraph_nodes.get(pull_to_push_idx) { + self.node_id_as_ident(node_id, false) + } else { + // Entire subgraph is pull (except for a single send/push handoff output). + assert_eq!( + 1, + send_ports.len(), + "If entire subgraph is pull, should have only one handoff output." +); + send_ports[0].clone() + }; + } + } + } + } + } + + // sample 2 + { + { + { + { + { + let push_ident = if let Some(&node_id) = + subgraph_nodes.get(pull_to_push_idx) + { + self.node_id_as_ident(node_id, false) + } else { + // Entire subgraph is pull (except for a single send/push handoff output). + assert_eq!( + 1, + send_ports.len(), + "If entire subgraph is pull, should have only one handoff output." + ); + send_ports[0].clone() + }; + } + } + } + } + } + + // sample 3 + { + { + { + { + { + let push_ident = + if let Some(&node_id) = subgraph_nodes.get(pull_to_push_idx) { + self.node_id_as_ident(node_id, false) + } else { + // Entire subgraph is pull (except for a single send/push handoff output). + assert_eq!( + 1, + send_ports.len(), + "If entire subgraph is pull, should have only one handoff output." + ); + send_ports[0].clone() + }; + } + } + } + } + } + + // sample 4 + {{{{{ + let push_ident = + if let Some(&node_id) = subgraph_nodes.get(pull_to_push_idx) { + self.node_id_as_ident(node_id, false) + } else { + // Entire subgraph is pull (except for a single send/push handoff output). + assert_eq!( + 1, + send_ports.len(), + "If entire subgraph is pull, should have only one handoff output." + ); + send_ports[0].clone() + }; + }}}}} + + // sample 5 + { + { + { + { + { + let push_ident = + if let Some(&node_id) = subgraph_nodes.get(pull_to_push_idx) { + self.node_id_as_ident(node_id, false) + } else { + // Entire subgraph is pull (except for a single send/push handoff output). + assert_eq!( + 1, + send_ports.len(), + "If entire subgraph is pull, should have only one handoff output." + ); + send_ports[0].clone() + }; + } + } + } + } + } + + // sample 6 + { + { + { + { + { + let push_ident = if let Some(&node_id) = + subgraph_nodes.get(pull_to_push_idx) + { + self.node_id_as_ident(node_id, false) + } else { + // Entire subgraph is pull (except for a single send/push handoff output). + assert_eq!( + 1, + send_ports.len(), + "If entire subgraph is pull, should have only one handoff output." + ); + send_ports[0].clone() + }; + } + } + } + } + } + + // sample 7 + { + { + { + { + { + let push_ident = + if let Some(&node_id) = subgraph_nodes.get(pull_to_push_idx) { + self.node_id_as_ident(node_id, false) + } else { + // Entire subgraph is pull (except for a single send/push handoff output). + assert_eq!( + 1, + send_ports.len(), + "If entire subgraph is pull, should have only one handoff output." + ); + send_ports[0].clone() + }; + } + } + } + } + } +} diff --git a/tests/target/issue-5586.rs b/tests/target/issue-5586.rs new file mode 100644 index 000000000000..7033ae975b3b --- /dev/null +++ b/tests/target/issue-5586.rs @@ -0,0 +1,177 @@ +// rustfmt-version: Two +fn main() { + // sample 1 + { + { + { + { + { + let push_ident = if let Some(&node_id) = + subgraph_nodes.get(pull_to_push_idx) + { + self.node_id_as_ident(node_id, false) + } else { + // Entire subgraph is pull (except for a single send/push handoff output). + assert_eq!( + 1, + send_ports.len(), + "If entire subgraph is pull, should have only one handoff output." + ); + send_ports[0].clone() + }; + } + } + } + } + } + + // sample 2 + { + { + { + { + { + let push_ident = if let Some(&node_id) = + subgraph_nodes.get(pull_to_push_idx) + { + self.node_id_as_ident(node_id, false) + } else { + // Entire subgraph is pull (except for a single send/push handoff output). + assert_eq!( + 1, + send_ports.len(), + "If entire subgraph is pull, should have only one handoff output." + ); + send_ports[0].clone() + }; + } + } + } + } + } + + // sample 3 + { + { + { + { + { + let push_ident = if let Some(&node_id) = + subgraph_nodes.get(pull_to_push_idx) + { + self.node_id_as_ident(node_id, false) + } else { + // Entire subgraph is pull (except for a single send/push handoff output). + assert_eq!( + 1, + send_ports.len(), + "If entire subgraph is pull, should have only one handoff output." + ); + send_ports[0].clone() + }; + } + } + } + } + } + + // sample 4 + { + { + { + { + { + let push_ident = if let Some(&node_id) = + subgraph_nodes.get(pull_to_push_idx) + { + self.node_id_as_ident(node_id, false) + } else { + // Entire subgraph is pull (except for a single send/push handoff output). + assert_eq!( + 1, + send_ports.len(), + "If entire subgraph is pull, should have only one handoff output." + ); + send_ports[0].clone() + }; + } + } + } + } + } + + // sample 5 + { + { + { + { + { + let push_ident = if let Some(&node_id) = + subgraph_nodes.get(pull_to_push_idx) + { + self.node_id_as_ident(node_id, false) + } else { + // Entire subgraph is pull (except for a single send/push handoff output). + assert_eq!( + 1, + send_ports.len(), + "If entire subgraph is pull, should have only one handoff output." + ); + send_ports[0].clone() + }; + } + } + } + } + } + + // sample 6 + { + { + { + { + { + let push_ident = if let Some(&node_id) = + subgraph_nodes.get(pull_to_push_idx) + { + self.node_id_as_ident(node_id, false) + } else { + // Entire subgraph is pull (except for a single send/push handoff output). + assert_eq!( + 1, + send_ports.len(), + "If entire subgraph is pull, should have only one handoff output." + ); + send_ports[0].clone() + }; + } + } + } + } + } + + // sample 7 + { + { + { + { + { + let push_ident = if let Some(&node_id) = + subgraph_nodes.get(pull_to_push_idx) + { + self.node_id_as_ident(node_id, false) + } else { + // Entire subgraph is pull (except for a single send/push handoff output). + assert_eq!( + 1, + send_ports.len(), + "If entire subgraph is pull, should have only one handoff output." + ); + send_ports[0].clone() + }; + } + } + } + } + } +} From b08130c59070bb9c95128341d712c0e02bba58d9 Mon Sep 17 00:00:00 2001 From: David Bar-On <61089727+davidBar-On@users.noreply.github.com> Date: Sun, 29 Jan 2023 20:55:14 +0200 Subject: [PATCH 251/401] Fix #5234 - handling of empty code block (#5601) --- src/comment.rs | 2 +- tests/source/issue-5234.rs | 51 ++++++++++++++++++++++++++++++++++++++ tests/target/issue-5234.rs | 47 +++++++++++++++++++++++++++++++++++ 3 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 tests/source/issue-5234.rs create mode 100644 tests/target/issue-5234.rs diff --git a/src/comment.rs b/src/comment.rs index 261fa5e93d84..0677fdc2b899 100644 --- a/src/comment.rs +++ b/src/comment.rs @@ -726,7 +726,7 @@ impl<'a> CommentRewrite<'a> { let code_block = match self.code_block_attr.as_ref().unwrap() { CodeBlockAttribute::Rust if self.fmt.config.format_code_in_doc_comments() - && !self.code_block_buffer.is_empty() => + && !self.code_block_buffer.trim().is_empty() => { let mut config = self.fmt.config.clone(); config.set().wrap_comments(false); diff --git a/tests/source/issue-5234.rs b/tests/source/issue-5234.rs new file mode 100644 index 000000000000..67266f485d3b --- /dev/null +++ b/tests/source/issue-5234.rs @@ -0,0 +1,51 @@ +// rustfmt-format_code_in_doc_comments: true + +/// ``` +/// ``` +fn foo() {} + +/// ``` +///Something +/// ``` +fn foo() {} + +/// ``` +/// +/// ``` +fn foo() {} + + +/// /// ``` +fn foo() {} + +/// /// ``` +/// ``` +/// +/// ``` +/// ``` +fn foo() {} + +fn foo() { +/// ``` +/// +/// ``` +struct bar {} +} + +/// ``` +/// fn com(){ +/// let i = 5; +/// +/// let j = 6; +/// } +/// ``` +fn foo() {} + +fn foo() { +/// ``` +///fn com(){ +///let i = 5; +///} +/// ``` +struct bar {} +} diff --git a/tests/target/issue-5234.rs b/tests/target/issue-5234.rs new file mode 100644 index 000000000000..7ee9e46d1efa --- /dev/null +++ b/tests/target/issue-5234.rs @@ -0,0 +1,47 @@ +// rustfmt-format_code_in_doc_comments: true + +/// ``` +/// ``` +fn foo() {} + +/// ``` +/// Something +/// ``` +fn foo() {} + +/// ``` +/// ``` +fn foo() {} + +/// /// ``` +fn foo() {} + +/// /// ``` +/// ``` +/// ``` +/// ``` +fn foo() {} + +fn foo() { + /// ``` + /// ``` + struct bar {} +} + +/// ``` +/// fn com() { +/// let i = 5; +/// +/// let j = 6; +/// } +/// ``` +fn foo() {} + +fn foo() { + /// ``` + /// fn com() { + /// let i = 5; + /// } + /// ``` + struct bar {} +} From 61d82acf53c62d19d366c47bead15b8fc67028bc Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Sun, 29 Jan 2023 22:43:40 +0000 Subject: [PATCH 252/401] refactor: fix typo in comment (#5581) --- src/bin/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/main.rs b/src/bin/main.rs index be64559e8774..8f5d980f561d 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -75,7 +75,7 @@ pub enum OperationError { #[error("{0}")] IoError(IoError), /// Attempt to use --emit with a mode which is not currently - /// supported with stdandard input. + /// supported with standard input. #[error("Emit mode {0} not supported with standard output.")] StdinBadEmit(EmitMode), } From 9cc0af54070f023fb52961160157313a09c55fb8 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Mon, 30 Jan 2023 07:44:58 +0900 Subject: [PATCH 253/401] Remove old CI config (#5572) * Remove old CI config Signed-off-by: Yuki Okushi * Replace outdated badges Signed-off-by: Yuki Okushi --------- Signed-off-by: Yuki Okushi --- .editorconfig | 3 -- .travis.yml | 77 --------------------------------------------------- README.md | 6 ++-- 3 files changed, 2 insertions(+), 84 deletions(-) delete mode 100644 .travis.yml diff --git a/.editorconfig b/.editorconfig index 5bb92df3e043..ed6894e5c273 100644 --- a/.editorconfig +++ b/.editorconfig @@ -21,6 +21,3 @@ indent_size = unset indent_style = unset trim_trailing_whitespace = unset insert_final_newline = unset - -[appveyor.yml] -end_of_line = unset diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index d699bd842eec..000000000000 --- a/.travis.yml +++ /dev/null @@ -1,77 +0,0 @@ -sudo: false -language: rust -rust: nightly -os: linux -cache: - directories: - - $HOME/.cargo - -addons: - apt: - packages: - - libcurl4-openssl-dev - - libelf-dev - - libdw-dev - -matrix: - include: - - env: DEPLOY=LINUX - - env: CFG_RELEASE_CHANNEL=beta - - os: osx - - env: INTEGRATION=bitflags - - env: INTEGRATION=chalk - - env: INTEGRATION=crater - - env: INTEGRATION=error-chain - - env: INTEGRATION=glob - - env: INTEGRATION=log - - env: INTEGRATION=mdbook - - env: INTEGRATION=packed_simd - - env: INTEGRATION=rust-semverver - - env: INTEGRATION=stdsimd TARGET=x86_64-unknown-linux-gnu - - env: INTEGRATION=tempdir - - env: INTEGRATION=futures-rs - allow_failures: - # Using old configuration option - - env: INTEGRATION=rand - # Doesn't build - keep this in allow_failures as it's fragile to breaking changes of rustc. - - env: INTEGRATION=rust-clippy - # Doesn't build - seems to be because of an option - - env: INTEGRATION=packed_simd - # Doesn't build - a temporal build failure due to breaking changes in the nightly compilre - - env: INTEGRATION=rust-semverver - # can be moved back to include section after https://github.com/rust-lang-nursery/failure/pull/298 is merged - - env: INTEGRATION=failure - # `cargo test` doesn't finish - disabling for now. - # - env: INTEGRATION=cargo - -script: - - | - if [ -z ${INTEGRATION} ]; then - export CFG_RELEASE_CHANNEL=nightly - export CFG_RELEASE=nightly - cargo build - cargo test - cargo test -- --ignored - else - ./ci/integration.sh - fi - -after_success: -- if [ -z ${INTEGRATION} ]; then travis-cargo coveralls --no-sudo; fi - -before_deploy: - # TODO: cross build - - cargo build --release --target=x86_64-unknown-linux-gnu - - tar czf rustfmt-x86_64-unknown-linux-gnu.tar.gz Contributing.md Design.md README.md -C target/x86_64-unknown-linux-gnu/release/rustfmt rustfmt - -deploy: - provider: releases - api_key: - secure: "your own encrypted key" - file: - - rustfmt-x86_64-unknown-linux-gnu.tar.gz - on: - repo: nrc/rustfmt - tags: true - condition: "$DEPLOY = LINUX" - skip_cleanup: true diff --git a/README.md b/README.md index 0f9652aecf9c..ef835371b02a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# rustfmt [![Build Status](https://travis-ci.com/rust-lang/rustfmt.svg?branch=master)](https://travis-ci.com/rust-lang/rustfmt) [![Build Status](https://ci.appveyor.com/api/projects/status/github/rust-lang/rustfmt?svg=true)](https://ci.appveyor.com/project/rust-lang-libs/rustfmt) [![crates.io](https://img.shields.io/crates/v/rustfmt-nightly.svg)](https://crates.io/crates/rustfmt-nightly) [![Travis Configuration Status](https://img.shields.io/travis/davidalber/rustfmt-travis.svg?label=travis%20example)](https://travis-ci.org/davidalber/rustfmt-travis) +# rustfmt [![linux](https://github.com/rust-lang/rustfmt/actions/workflows/linux.yml/badge.svg?event=push)](https://github.com/rust-lang/rustfmt/actions/workflows/linux.yml) [![mac](https://github.com/rust-lang/rustfmt/actions/workflows/mac.yml/badge.svg?event=push)](https://github.com/rust-lang/rustfmt/actions/workflows/mac.yml) [![windows](https://github.com/rust-lang/rustfmt/actions/workflows/windows.yml/badge.svg?event=push)](https://github.com/rust-lang/rustfmt/actions/workflows/windows.yml) [![crates.io](https://img.shields.io/crates/v/rustfmt-nightly.svg)](https://crates.io/crates/rustfmt-nightly) A tool for formatting Rust code according to style guidelines. @@ -7,9 +7,7 @@ If you'd like to help out (and you should, it's a fun project!), see Conduct](CODE_OF_CONDUCT.md). You can use rustfmt in Travis CI builds. We provide a minimal Travis CI -configuration (see [here](#checking-style-on-a-ci-server)) and verify its status -using another repository. The status of that repository's build is reported by -the "travis example" badge above. +configuration (see [here](#checking-style-on-a-ci-server)). ## Quick start From fb1a223eba3f31242f266b4936a8fe956b9d0e08 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Mon, 30 Jan 2023 07:45:26 +0900 Subject: [PATCH 254/401] Remove use of actions-rs (#5571) Signed-off-by: Yuki Okushi --- .github/workflows/upload-assets.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/upload-assets.yml b/.github/workflows/upload-assets.yml index 25699234a1ec..7dfaa4b9204c 100644 --- a/.github/workflows/upload-assets.yml +++ b/.github/workflows/upload-assets.yml @@ -46,10 +46,7 @@ jobs: shell: bash - name: Build release binaries - uses: actions-rs/cargo@v1 - with: - command: build - args: --release + run: cargo build --release - name: Build archive shell: bash From 846662cdb36b5e0663d2be533f5e4178705c982b Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Sat, 30 May 2020 09:36:44 -0700 Subject: [PATCH 255/401] Don't wrap comments that are part of a table Closes #4210 --- src/comment.rs | 15 ++++++++++++++- tests/target/issue-4210.rs | 15 +++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 tests/target/issue-4210.rs diff --git a/src/comment.rs b/src/comment.rs index 0677fdc2b899..7167783872f9 100644 --- a/src/comment.rs +++ b/src/comment.rs @@ -806,7 +806,8 @@ impl<'a> CommentRewrite<'a> { 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) + && !is_table_item(line); if should_wrap_comment { match rewrite_string(line, &self.fmt, self.max_width) { @@ -941,6 +942,18 @@ fn has_url(s: &str) -> bool { || REFERENCE_LINK_URL.is_match(s) } +/// Returns true if the given string may be part of a Markdown talble. +fn is_table_item(mut s: &str) -> bool { + // This function may return false positive, but should get its job done in most cases (i.e. + // markdown tables with two column delimiters). + s = s.trim_start(); + return s.starts_with('|') + && match s.rfind('|') { + Some(0) | None => false, + _ => true, + }; +} + /// Given the span, rewrite the missing comment inside it if available. /// Note that the given span must only include comments (or leading/trailing whitespaces). pub(crate) fn rewrite_missing_comment( diff --git a/tests/target/issue-4210.rs b/tests/target/issue-4210.rs new file mode 100644 index 000000000000..e96ba1b3fd05 --- /dev/null +++ b/tests/target/issue-4210.rs @@ -0,0 +1,15 @@ +// rustfmt-wrap_comments: true + +/// Table that is > 80 symbols: +/// +/// | table | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | +/// |-------|-----------------------------------------------------------------------------| +/// | val | x | +pub struct Item; + +/// Table that is > 80 symbols: +/// +/// | table | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +/// |-------|----------------------------------------------------------------------------- +/// | val | x +pub struct Item; From 368a63305cea7a472fec37757bf962cdfc12be26 Mon Sep 17 00:00:00 2001 From: Wim Looman Date: Sat, 10 Sep 2022 13:30:41 +0200 Subject: [PATCH 256/401] Fix typo --- src/comment.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/comment.rs b/src/comment.rs index 7167783872f9..17b4af1717d8 100644 --- a/src/comment.rs +++ b/src/comment.rs @@ -942,7 +942,7 @@ fn has_url(s: &str) -> bool { || REFERENCE_LINK_URL.is_match(s) } -/// Returns true if the given string may be part of a Markdown talble. +/// Returns true if the given string may be part of a Markdown table. fn is_table_item(mut s: &str) -> bool { // This function may return false positive, but should get its job done in most cases (i.e. // markdown tables with two column delimiters). From cdd0b56071cbc9247461bbd2e0551b46e653162f Mon Sep 17 00:00:00 2001 From: Wim Looman Date: Sat, 10 Sep 2022 13:31:08 +0200 Subject: [PATCH 257/401] Test that both table headers and values are not wrapped --- tests/target/issue-4210.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/target/issue-4210.rs b/tests/target/issue-4210.rs index e96ba1b3fd05..3fd966390826 100644 --- a/tests/target/issue-4210.rs +++ b/tests/target/issue-4210.rs @@ -7,9 +7,9 @@ /// | val | x | pub struct Item; -/// Table that is > 80 symbols: +/// Table value that is > 80 symbols: /// -/// | table | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -/// |-------|----------------------------------------------------------------------------- -/// | val | x -pub struct Item; +/// | table | heading +/// |----------|----------------------------------------------------------------------------- +/// | long val | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +pub struct Item2; From 3f36c997bf45eb7b6f101885241ae59ce00e72d9 Mon Sep 17 00:00:00 2001 From: Wim Looman Date: Sat, 10 Sep 2022 13:31:26 +0200 Subject: [PATCH 258/401] Test that with all comment wrapping disabled table comments are not wrapped --- tests/target/issue-4210-disabled.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 tests/target/issue-4210-disabled.rs diff --git a/tests/target/issue-4210-disabled.rs b/tests/target/issue-4210-disabled.rs new file mode 100644 index 000000000000..3ba87aab8c54 --- /dev/null +++ b/tests/target/issue-4210-disabled.rs @@ -0,0 +1,15 @@ +// rustfmt-wrap_comments: false + +/// Table that is > 80 symbols: +/// +/// | table | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | +/// |-------|-----------------------------------------------------------------------------| +/// | val | x | +pub struct Item; + +/// Table value that is > 80 symbols: +/// +/// | table | heading +/// |----------|----------------------------------------------------------------------------- +/// | long val | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +pub struct Item2; From e2996a807b411218bb3dca0f2a0e420839cd3875 Mon Sep 17 00:00:00 2001 From: Thaqib <65588695+thaqibm@users.noreply.github.com> Date: Wed, 1 Feb 2023 22:26:12 -0500 Subject: [PATCH 259/401] Lists doc comments fix4041 (#5560) * add + start of an itemized line * add test * fix format * fix tests * update doc comment --- src/comment.rs | 5 +++-- tests/source/issue-4041.rs | 5 +++++ tests/target/issue-4041.rs | 6 ++++++ 3 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 tests/source/issue-4041.rs create mode 100644 tests/target/issue-4041.rs diff --git a/src/comment.rs b/src/comment.rs index 17b4af1717d8..bc0e8774f494 100644 --- a/src/comment.rs +++ b/src/comment.rs @@ -432,7 +432,7 @@ impl CodeBlockAttribute { /// Block that is formatted as an item. /// -/// An item starts with either a star `*` a dash `-` or a greater-than `>`. +/// An item starts with either a star `*` a dash `-` a greater-than `>` or a plus '+'. /// Different level of indentation are handled by shrinking the shape accordingly. struct ItemizedBlock { /// the lines that are identified as part of an itemized block @@ -449,7 +449,8 @@ 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("> ") + let itemized_start = ["* ", "- ", "> ", "+ "]; + itemized_start.iter().any(|s| trimmed.starts_with(s)) } /// Creates a new ItemizedBlock described with the given line. diff --git a/tests/source/issue-4041.rs b/tests/source/issue-4041.rs new file mode 100644 index 000000000000..274b80f1bc5d --- /dev/null +++ b/tests/source/issue-4041.rs @@ -0,0 +1,5 @@ +// rustfmt-wrap_comments: true +//! List: +//! - Sub list: +//! + very long #1 blah blah blah blah blah blah blah blah blah blah blah blah foo baar baxxxxxxxx long line 1231421230912i3091238192038 +//! + very long #2 blah blah blah blah blah blah blah blah blah blah blah blah diff --git a/tests/target/issue-4041.rs b/tests/target/issue-4041.rs new file mode 100644 index 000000000000..e9c693836f2c --- /dev/null +++ b/tests/target/issue-4041.rs @@ -0,0 +1,6 @@ +// rustfmt-wrap_comments: true +//! List: +//! - Sub list: +//! + very long #1 blah blah blah blah blah blah blah blah blah blah blah blah +//! foo baar baxxxxxxxx long line 1231421230912i3091238192038 +//! + very long #2 blah blah blah blah blah blah blah blah blah blah blah blah From 5391847ea5a12262704657a0708b1674fb344403 Mon Sep 17 00:00:00 2001 From: Rajiv Sharma Date: Sun, 28 Aug 2022 00:36:16 +0100 Subject: [PATCH 260/401] Fix #5488 - prevent shorthand init for tuple struct Closes #5488 Fix for #5488. Before applying shorthand initialization for structs, check if identifier is a literal (e.g. tuple struct). If yes, then do not apply short hand initialization. Added test case to validate the changes for the fix. --- src/expr.rs | 6 ++++-- tests/source/issue-5488.rs | 17 +++++++++++++++++ tests/target/issue-5488.rs | 17 +++++++++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 tests/source/issue-5488.rs create mode 100644 tests/target/issue-5488.rs diff --git a/src/expr.rs b/src/expr.rs index 0be4c3cf168c..91f3cc81bae9 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -1723,9 +1723,11 @@ pub(crate) fn rewrite_field( let overhead = name.len() + separator.len(); let expr_shape = shape.offset_left(overhead)?; let expr = field.expr.rewrite(context, expr_shape); - + let is_lit = matches!(field.expr.kind, ast::ExprKind::Lit(_)); match expr { - Some(ref e) if e.as_str() == name && context.config.use_field_init_shorthand() => { + Some(ref e) + if !is_lit && e.as_str() == name && context.config.use_field_init_shorthand() => + { Some(attrs_str + name) } Some(e) => Some(format!("{}{}{}{}", attrs_str, name, separator, e)), diff --git a/tests/source/issue-5488.rs b/tests/source/issue-5488.rs new file mode 100644 index 000000000000..d361632e29ef --- /dev/null +++ b/tests/source/issue-5488.rs @@ -0,0 +1,17 @@ +// rustfmt-use_field_init_shorthand: true + +struct MyStruct(u32); +struct AnotherStruct { + a: u32, +} + +fn main() { + // Since MyStruct is a tuple struct, it should not be shorthanded to + // MyStruct { 0 } even if use_field_init_shorthand is enabled. + let instance = MyStruct { 0: 0 }; + + // Since AnotherStruct is not a tuple struct, the shorthand should + // apply. + let a = 10; + let instance = AnotherStruct { a: a }; +} diff --git a/tests/target/issue-5488.rs b/tests/target/issue-5488.rs new file mode 100644 index 000000000000..0cb37c56f393 --- /dev/null +++ b/tests/target/issue-5488.rs @@ -0,0 +1,17 @@ +// rustfmt-use_field_init_shorthand: true + +struct MyStruct(u32); +struct AnotherStruct { + a: u32, +} + +fn main() { + // Since MyStruct is a tuple struct, it should not be shorthanded to + // MyStruct { 0 } even if use_field_init_shorthand is enabled. + let instance = MyStruct { 0: 0 }; + + // Since AnotherStruct is not a tuple struct, the shorthand should + // apply. + let a = 10; + let instance = AnotherStruct { a }; +} From 34f9ca28f2f4ae3ac462c6dcd862b513e107e459 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Wed, 15 Feb 2023 22:24:03 -0600 Subject: [PATCH 261/401] fix: use correct span for struct generics --- src/items.rs | 2 +- tests/target/issue_5691.rs | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 tests/target/issue_5691.rs diff --git a/src/items.rs b/src/items.rs index 25e8a024857c..3c5293b6bf5a 100644 --- a/src/items.rs +++ b/src/items.rs @@ -1278,7 +1278,7 @@ pub(crate) fn format_struct_struct( let header_hi = struct_parts.ident.span.hi(); 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()); + let span = span.with_lo(generics.where_clause.span.hi()); context.snippet_provider.span_after(span, "{") } else { context.snippet_provider.span_after(span, "{") diff --git a/tests/target/issue_5691.rs b/tests/target/issue_5691.rs new file mode 100644 index 000000000000..e3aad15db0d5 --- /dev/null +++ b/tests/target/issue_5691.rs @@ -0,0 +1,16 @@ +struct S +where + [(); { num_slots!(C) }]:, { + /* An asterisk-based, or a double-slash-prefixed, comment here is + required to trigger the fmt bug. + + A single-line triple-slash-prefixed comment (with a field following it) is not enough - it will not trigger the fmt bug. + + Side note: If you have a combination of two, or all three of the + above mentioned types of comments here, some of them disappear + after `cargo fmt`. + + The bug gets triggered even if a field definition following the + (asterisk-based, or a double-slash-prefixed) comment, too. + */ +} From 475396a03c767a4a3a7e7d3be62f91751c2ba85e Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Thu, 30 Mar 2023 13:00:18 -0400 Subject: [PATCH 262/401] Prevent ICE when calling parse_attribute in parse_cfg_if_inner Fixes 5728 Previously we were ignoring the diagnostic error, which lead to the ICE. Now we properly cancel the error. --- src/parse/macros/cfg_if.rs | 5 ++++- tests/rustfmt/main.rs | 12 ++++++++++++ tests/target/issue_5728.rs | 5 +++++ 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 tests/target/issue_5728.rs diff --git a/src/parse/macros/cfg_if.rs b/src/parse/macros/cfg_if.rs index ace1a76b3fe7..ac03409a7844 100644 --- a/src/parse/macros/cfg_if.rs +++ b/src/parse/macros/cfg_if.rs @@ -44,7 +44,10 @@ fn parse_cfg_if_inner<'a>( // See also https://github.com/rust-lang/rust/pull/79433 parser .parse_attribute(rustc_parse::parser::attr::InnerAttrPolicy::Permitted) - .map_err(|_| "Failed to parse attributes")?; + .map_err(|e| { + e.cancel(); + "Failed to parse attributes" + })?; } if !parser.eat(&TokenKind::OpenDelim(Delimiter::Brace)) { diff --git a/tests/rustfmt/main.rs b/tests/rustfmt/main.rs index 7ff301e80195..f00b0e09604f 100644 --- a/tests/rustfmt/main.rs +++ b/tests/rustfmt/main.rs @@ -174,3 +174,15 @@ fn rustfmt_emits_error_on_line_overflow_true() { "line formatted, but exceeded maximum width (maximum: 100 (see `max_width` option)" )) } + +#[test] +#[allow(non_snake_case)] +fn dont_emit_ICE() { + let files = ["tests/target/issue_5728.rs"]; + + for file in files { + let args = [file]; + let (_stdout, stderr) = rustfmt(&args); + assert!(!stderr.contains("thread 'main' panicked")); + } +} diff --git a/tests/target/issue_5728.rs b/tests/target/issue_5728.rs new file mode 100644 index 000000000000..e1355416faf4 --- /dev/null +++ b/tests/target/issue_5728.rs @@ -0,0 +1,5 @@ +cfg_if::cfg_if! { + if #[cfg(windows)] { + } else if #(&cpus) { + } else [libc::CTL_HW, libc::HW_NCPU, 0, 0] +} From a3b2bfc2db33ec3bed266404e6391c145e000df0 Mon Sep 17 00:00:00 2001 From: Greg Jandl Date: Mon, 27 Mar 2023 12:02:02 -0500 Subject: [PATCH 263/401] Honor --color option when emitting errors Fixes issue 5717. --- src/parse/session.rs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/parse/session.rs b/src/parse/session.rs index 6bfec79cd703..1956e1b8cd74 100644 --- a/src/parse/session.rs +++ b/src/parse/session.rs @@ -12,6 +12,7 @@ use rustc_span::{ }; use crate::config::file_lines::LineRange; +use crate::config::options::Color; use crate::ignore_path::IgnorePathSet; use crate::parse::parser::{ModError, ModulePathSuccess}; use crate::source_map::LineRangeUtils; @@ -107,15 +108,26 @@ impl Emitter for SilentOnIgnoredFilesEmitter { } } +impl From for ColorConfig { + fn from(color: Color) -> Self { + match color { + Color::Auto => ColorConfig::Auto, + Color::Always => ColorConfig::Always, + Color::Never => ColorConfig::Never, + } + } +} + fn default_handler( source_map: Lrc, ignore_path_set: Lrc, can_reset: Lrc, hide_parse_errors: bool, + color: Color, ) -> Handler { let supports_color = term::stderr().map_or(false, |term| term.supports_color()); - let color_cfg = if supports_color { - ColorConfig::Auto + let emit_color = if supports_color { + ColorConfig::from(color) } else { ColorConfig::Never }; @@ -126,7 +138,7 @@ fn default_handler( let fallback_bundle = rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false); Box::new(EmitterWriter::stderr( - color_cfg, + emit_color, Some(source_map.clone()), None, fallback_bundle, @@ -164,6 +176,7 @@ impl ParseSess { Lrc::clone(&ignore_path_set), Lrc::clone(&can_reset_errors), config.hide_parse_errors(), + config.color(), ); let parse_sess = RawParseSess::with_span_handler(handler, source_map); From a44c7ea5923caa8f908ae0fdd6563033a7ad88da Mon Sep 17 00:00:00 2001 From: Lukas Markeffsky <@> Date: Thu, 27 Apr 2023 20:20:18 +0200 Subject: [PATCH 264/401] fix broken markdown --- src/config/options.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/options.rs b/src/config/options.rs index 257a17b2703a..408017d2432c 100644 --- a/src/config/options.rs +++ b/src/config/options.rs @@ -18,7 +18,7 @@ pub enum NewlineStyle { Auto, /// Force CRLF (`\r\n`). Windows, - /// Force CR (`\n). + /// Force CR (`\n`). Unix, /// `\r\n` in Windows, `\n` on other platforms. Native, From e903fcdaae20292d307605ee82ec793253adf4fa Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sun, 4 Jun 2023 09:09:28 -0700 Subject: [PATCH 265/401] Remove rustc-workspace-hack --- Cargo.lock | 7 ------- Cargo.toml | 5 ----- 2 files changed, 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 24166d51c51f..5b0fd5ae7352 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -468,12 +468,6 @@ version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" -[[package]] -name = "rustc-workspace-hack" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc71d2faa173b74b232dedc235e3ee1696581bb132fc116fa3626d6151a1a8fb" - [[package]] name = "rustfmt-config_proc_macro" version = "0.3.0" @@ -502,7 +496,6 @@ dependencies = [ "lazy_static", "log", "regex", - "rustc-workspace-hack", "rustfmt-config_proc_macro", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index 87ce59d0217e..12ed65453e19 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,11 +59,6 @@ unicode_categories = "0.1" rustfmt-config_proc_macro = { version = "0.3", path = "config_proc_macro" } -# A noop dependency that changes in the Rust repository, it's a bit of a hack. -# See the `src/tools/rustc-workspace-hack/README.md` file in `rust-lang/rust` -# for more information. -rustc-workspace-hack = "1.0.0" - # Rustc dependencies are loaded from the sysroot, Cargo doesn't know about them. [package.metadata.rust-analyzer] From 5f9de6bfc9f1a3308d97bf6598acd56229cbe293 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Sat, 10 Jun 2023 17:27:24 +0000 Subject: [PATCH 266/401] Recover comments between attrs and generic param Fixes #5320. --- src/overflow.rs | 4 ++ src/types.rs | 76 +++++++++++++++++------------ tests/target/doc-of-generic-item.rs | 18 +++++++ 3 files changed, 67 insertions(+), 31 deletions(-) diff --git a/src/overflow.rs b/src/overflow.rs index af0b95430a19..d81bf24dbd1c 100644 --- a/src/overflow.rs +++ b/src/overflow.rs @@ -589,6 +589,8 @@ impl<'a> Context<'a> { fn rewrite_items(&self) -> Option<(bool, String)> { let span = self.items_span(); + debug!("items: {:?}", self.items); + let items = itemize_list( self.context.snippet_provider, self.items.iter(), @@ -603,6 +605,8 @@ impl<'a> Context<'a> { ); let mut list_items: Vec<_> = items.collect(); + debug!("items: {list_items:?}"); + // Try letting the last argument overflow to the next line with block // indentation. If its first line fits on one line with the other arguments, // we format the function arguments horizontally. diff --git a/src/types.rs b/src/types.rs index 01e2fb6e61e1..b4be6546efbd 100644 --- a/src/types.rs +++ b/src/types.rs @@ -572,49 +572,41 @@ impl Rewrite for ast::GenericBounds { impl Rewrite for ast::GenericParam { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { - 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(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(' '); - } - } - _ => (), - } + let mut result = self.attrs.rewrite(context, shape).unwrap_or(String::new()); + let has_attrs = !result.is_empty(); - if let ast::GenericParamKind::Const { + let mut param = String::with_capacity(128); + + let param_start = if let ast::GenericParamKind::Const { ref ty, - kw_span: _, + kw_span, default, } = &self.kind { - result.push_str("const "); - result.push_str(rewrite_ident(context, self.ident)); - result.push_str(": "); - result.push_str(&ty.rewrite(context, shape)?); + param.push_str("const "); + param.push_str(rewrite_ident(context, self.ident)); + param.push_str(": "); + param.push_str(&ty.rewrite(context, shape)?); if let Some(default) = default { let eq_str = match context.config.type_punctuation_density() { TypeDensity::Compressed => "=", TypeDensity::Wide => " = ", }; - result.push_str(eq_str); - let budget = shape.width.checked_sub(result.len())?; + param.push_str(eq_str); + let budget = shape.width.checked_sub(param.len())?; let rewrite = default.rewrite(context, Shape::legacy(budget, shape.indent))?; - result.push_str(&rewrite); + param.push_str(&rewrite); } + kw_span.lo() } else { - result.push_str(rewrite_ident(context, self.ident)); - } + param.push_str(rewrite_ident(context, self.ident)); + self.ident.span.lo() + }; if !self.bounds.is_empty() { - result.push_str(type_bound_colon(context)); - result.push_str(&self.bounds.rewrite(context, shape)?) + param.push_str(type_bound_colon(context)); + param.push_str(&self.bounds.rewrite(context, shape)?) } if let ast::GenericParamKind::Type { default: Some(ref def), @@ -624,11 +616,33 @@ impl Rewrite for ast::GenericParam { TypeDensity::Compressed => "=", TypeDensity::Wide => " = ", }; - result.push_str(eq_str); - let budget = shape.width.checked_sub(result.len())?; + param.push_str(eq_str); + let budget = shape.width.checked_sub(param.len())?; let rewrite = - def.rewrite(context, Shape::legacy(budget, shape.indent + result.len()))?; - result.push_str(&rewrite); + def.rewrite(context, Shape::legacy(budget, shape.indent + param.len()))?; + param.push_str(&rewrite); + } + + if let Some(last_attr) = self.attrs.last().filter(|last_attr| { + contains_comment(context.snippet(mk_sp(last_attr.span.hi(), param_start))) + }) { + result = combine_strs_with_missing_comments( + context, + &result, + ¶m, + mk_sp(last_attr.span.hi(), param_start), + shape, + !last_attr.is_doc_comment(), + )?; + } else { + // 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 if has_attrs { + result.push(' '); + } + result.push_str(¶m); } Some(result) diff --git a/tests/target/doc-of-generic-item.rs b/tests/target/doc-of-generic-item.rs index 2efc5e09a3d3..80886e74f83e 100644 --- a/tests/target/doc-of-generic-item.rs +++ b/tests/target/doc-of-generic-item.rs @@ -12,3 +12,21 @@ struct Foo< /// doc of N const N: item, >; + +// 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 + // Non-doc post-comment of 'a + 'a, + // Non-doc pre-comment of T + /// doc of T + // Non-doc post-comment of T + T, + // Non-doc pre-comment of N + /// doc of N + // Non-doc post-comment of N + const N: item, +>; From 0441cc21e3b0ce457d030aedd2cc7776fef047c1 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Wed, 26 Apr 2023 20:23:08 -0400 Subject: [PATCH 267/401] Add release notes for closed PRs with `release-notes` label --- CHANGELOG.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 60f961fa12ac..932fdc33d242 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,41 @@ ## [Unreleased] +### Fixed + +- When formatting doc comments with `wrap_comments = true` rustfmt will no longer wrap markdown tables [#4210](https://github.com/rust-lang/rustfmt/issues/4210) +- rustfmt will no longer use shorthand initialization when rewriting a tuple struct even when `use_field_init_shorthand = true` as this lead to code that could no longer compile. + Take the following struct as an example `struct MyStruct(u64);`. rustfmt will no longer format `MyStruct { 0: 0 }` as `MyStruct { 0 }` [#5488](https://github.com/rust-lang/rustfmt/issues/5488) +- rustfmt no longer panics when formatting an empty code block in a doc comment with `format_code_in_doc_comments = true` [#5234](https://github.com/rust-lang/rustfmt/issues/5234). For example: + ```rust + /// ``` + /// + /// ``` + fn main() {} + ``` +- Use the correct span when rewriting struct generics. This prevents rustfmt from incorrectly duplicating where clause bounds when using const expression in where clause bounds with feature `#![feature(generic_const_exprs)]` [#5691](https://github.com/rust-lang/rustfmt/issues/5691). e.g.: + ```rust + struct S + where + [(); { num_slots!(C) }]:, { + // code ... + } + ``` + +### Changed + +- Users can now control whether rustc parser errors are displayed with color using rustfmt's `--color` option. To disable colored errors pass `--color=Never` to rustfmt [#5717](https://github.com/rust-lang/rustfmt/issues/5717) + + +### Added + +- rustfmt now recognises `+` as the start of a markdown list, and won't incorrectly wrap sublists that begin with `+` when formatting doc comments with `wrap_comments = true` [#5560](https://github.com/rust-lang/rustfmt/pull/5560) + +### Misc + +- Prevent ICE when parsing invalid attributes in `cfg_if!` macros [#5728](https://github.com/rust-lang/rustfmt/issues/5728) + + ## [1.5.2] 2023-01-24 ### Fixed @@ -56,6 +91,8 @@ - Simplify the rustfmt help text by eliding the full path to the rustfmt binary path from the usage string when running `rustfmt --help` [#5214](https://github.com/rust-lang/rustfmt/issues/5214) +- Bumped the version for serveral dependencies. Most notably `dirs` `v2.0.1` -> `v4.0.0`. This changed the global user config directory on macOS from `$HOME/Library/Preferences` to `$HOME/Library/Application Support` [#5237](https://github.com/rust-lang/rustfmt/pull/5237) + ### Fixed - Remove duplicate imports when `imports_granularity` is set to `Item` [#4725](https://github.com/rust-lang/rustfmt/issues/4725) From c9ebd6ce262ec37c46ff50ac3c56ba19a99254df Mon Sep 17 00:00:00 2001 From: xxchan Date: Mon, 19 Jun 2023 16:29:57 +0200 Subject: [PATCH 268/401] doc: remove installing from source also add "run from source" in contributing.md --- Contributing.md | 10 ++++++++++ README.md | 18 ------------------ 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/Contributing.md b/Contributing.md index 3073996019ee..b986a887c92f 100644 --- a/Contributing.md +++ b/Contributing.md @@ -91,6 +91,16 @@ Please try to avoid leaving `TODO`s in the code. There are a few around, but I wish there weren't. You can leave `FIXME`s, preferably with an issue number. +### Run Rustfmt from source code + +You may want to run a version of rustfmt from source code as part of a test or +hacking on the rustfmt codebase. It's strongly discouraged to install a version +of rustfmt from source. Instead, run it using `cargo run`, and `--manifest-path`. + +``` +cargo run --bin cargo-fmt -- --manifest-path path/to/project/you/want2test/Cargo.toml +``` + ### Version-gate formatting changes A change that introduces a different code-formatting should be gated on the diff --git a/README.md b/README.md index ef835371b02a..c05184fbb04b 100644 --- a/README.md +++ b/README.md @@ -71,24 +71,6 @@ because in the future Rustfmt might work on code where it currently does not): fixes to break our stability guarantees). -## Installation - -```sh -rustup component add rustfmt -``` - -## Installing from source - -To install from source (nightly required), first checkout to the tag or branch you want to install, then issue - -```sh -cargo install --path . -``` - -This will install `rustfmt` in your `~/.cargo/bin`. Make sure to add `~/.cargo/bin` directory to -your PATH variable. - - ## Running You can run Rustfmt by just typing `rustfmt filename` if you used `cargo From 8d95c269ed83da89c53cef4fd17cca88e7ab1aa8 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Wed, 12 Apr 2023 18:52:57 +0800 Subject: [PATCH 269/401] update config_proc_macro to use `syn` 2.0 --- config_proc_macro/Cargo.lock | 30 +++++++++++++++--------------- config_proc_macro/Cargo.toml | 4 ++-- config_proc_macro/src/attrs.rs | 18 +++++++++--------- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/config_proc_macro/Cargo.lock b/config_proc_macro/Cargo.lock index 49f2f72a8d21..7af746f0c965 100644 --- a/config_proc_macro/Cargo.lock +++ b/config_proc_macro/Cargo.lock @@ -4,18 +4,18 @@ version = 3 [[package]] name = "proc-macro2" -version = "1.0.3" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e98a83a9f9b331f54b924e68a66acb1bb35cb01fb0a23645139967abefb697e8" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ - "unicode-xid", + "unicode-ident", ] [[package]] name = "quote" -version = "1.0.2" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" dependencies = [ "proc-macro2", ] @@ -32,18 +32,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.99" +version = "1.0.160" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fec2851eb56d010dc9a21b89ca53ee75e6528bab60c11e89d38390904982da9f" +checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.99" +version = "1.0.160" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4dc18c61206b08dc98216c98faa0232f4337e1e1b8574551d5bad29ea1b425" +checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" dependencies = [ "proc-macro2", "quote", @@ -52,17 +52,17 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.5" +version = "2.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf" +checksum = "fcf316d5356ed6847742d036f8a39c3b8435cac10bd528a4bd461928a6ab34d5" dependencies = [ "proc-macro2", "quote", - "unicode-xid", + "unicode-ident", ] [[package]] -name = "unicode-xid" -version = "0.2.0" +name = "unicode-ident" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" diff --git a/config_proc_macro/Cargo.toml b/config_proc_macro/Cargo.toml index d10d0469cc40..34e8c237f556 100644 --- a/config_proc_macro/Cargo.toml +++ b/config_proc_macro/Cargo.toml @@ -13,10 +13,10 @@ proc-macro = true [dependencies] proc-macro2 = "1.0" quote = "1.0" -syn = { version = "1.0", features = ["full", "visit"] } +syn = { version = "2.0", features = ["full", "visit"] } [dev-dependencies] -serde = { version = "1.0", features = ["derive"] } +serde = { version = "1.0.160", features = ["derive"] } [features] default = [] diff --git a/config_proc_macro/src/attrs.rs b/config_proc_macro/src/attrs.rs index dd18ff572cb1..d8de9aae088d 100644 --- a/config_proc_macro/src/attrs.rs +++ b/config_proc_macro/src/attrs.rs @@ -51,26 +51,26 @@ pub fn is_unstable_variant(attr: &syn::Attribute) -> bool { } fn is_attr_name_value(attr: &syn::Attribute, name: &str) -> bool { - attr.parse_meta().ok().map_or(false, |meta| match meta { - syn::Meta::NameValue(syn::MetaNameValue { ref path, .. }) if path.is_ident(name) => true, + match &attr.meta { + syn::Meta::NameValue(syn::MetaNameValue { path, .. }) if path.is_ident(name) => true, _ => false, - }) + } } fn is_attr_path(attr: &syn::Attribute, name: &str) -> bool { - attr.parse_meta().ok().map_or(false, |meta| match meta { + match &attr.meta { syn::Meta::Path(path) if path.is_ident(name) => true, _ => false, - }) + } } fn get_name_value_str_lit(attr: &syn::Attribute, name: &str) -> Option { - attr.parse_meta().ok().and_then(|meta| match meta { + match &attr.meta { syn::Meta::NameValue(syn::MetaNameValue { - ref path, - lit: syn::Lit::Str(ref lit_str), + path, + value: syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(lit_str), .. }), .. }) if path.is_ident(name) => Some(lit_str.value()), _ => None, - }) + } } From 7d48be355a6342690b48fc0675103ae822024e71 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Wed, 12 Apr 2023 18:54:32 +0800 Subject: [PATCH 270/401] bump deps to new versions that use syn 2.0 --- Cargo.toml | 6 +++--- src/cargo-fmt/main.rs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 12ed65453e19..78b970c4c983 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,7 +37,7 @@ annotate-snippets = { version = "0.9", features = ["color"] } anyhow = "1.0" bytecount = "0.6" cargo_metadata = "0.14" -clap = { version = "3.1", features = ["derive"] } +clap = { version = "4.2.1", features = ["derive"] } derive-new = "0.5" diff = "0.1" dirs = "4.0" @@ -48,10 +48,10 @@ itertools = "0.10" lazy_static = "1.4" log = "0.4" regex = "1.5" -serde = { version = "1.0", features = ["derive"] } +serde = { version = "1.0.160", features = ["derive"] } serde_json = "1.0" term = "0.7" -thiserror = "1.0" +thiserror = "1.0.40" toml = "0.5" unicode-segmentation = "1.9" unicode-width = "0.1" diff --git a/src/cargo-fmt/main.rs b/src/cargo-fmt/main.rs index 2b714b68df00..a106181ec7eb 100644 --- a/src/cargo-fmt/main.rs +++ b/src/cargo-fmt/main.rs @@ -14,7 +14,7 @@ use std::path::{Path, PathBuf}; use std::process::Command; use std::str; -use clap::{AppSettings, CommandFactory, Parser}; +use clap::{CommandFactory, Parser}; #[path = "test/mod.rs"] #[cfg(test)] @@ -22,7 +22,7 @@ mod cargo_fmt_tests; #[derive(Parser)] #[clap( - global_setting(AppSettings::NoAutoVersion), + disable_version_flag = true, bin_name = "cargo fmt", about = "This utility formats all bin and lib files of \ the current crate using rustfmt." @@ -45,7 +45,7 @@ pub struct Opts { short = 'p', long = "package", value_name = "package", - multiple_values = true + num_args = 1.. )] packages: Vec, From a463f231f5410ceca88f3eb5dfb8a85b97a63b25 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Wed, 12 Apr 2023 18:55:07 +0800 Subject: [PATCH 271/401] remove `derive_new` dependency --- Cargo.lock | 422 +++++++++++++++++++++++++++++----------- Cargo.toml | 1 - src/attr/doc_comment.rs | 7 +- src/formatting.rs | 17 +- src/lib.rs | 2 - src/macros.rs | 5 +- src/pairs.rs | 9 +- 7 files changed, 346 insertions(+), 117 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5b0fd5ae7352..8487dc58b4ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,6 +21,46 @@ dependencies = [ "yansi-term", ] +[[package]] +name = "anstream" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "342258dd14006105c2b75ab1bd7543a03bdf0cfc94383303ac212a04939dff6f" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-wincon", + "concolor-override", + "concolor-query", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23ea9e81bd02e310c216d080f6223c179012256e5151c41db88d12c88a1684d2" + +[[package]] +name = "anstyle-parse" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7d1bb534e9efed14f3e5f44e7dd1a4f709384023a4165199a4241e18dff0116" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-wincon" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3127af6145b149f3287bb9a0d10ad9c5692dba8c53ad48285e5bec4063834fa" +dependencies = [ + "anstyle", + "windows-sys 0.45.0", +] + [[package]] name = "anyhow" version = "1.0.56" @@ -33,17 +73,11 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi", ] -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - [[package]] name = "bitflags" version = "1.3.2" @@ -99,6 +133,12 @@ dependencies = [ "serde_json", ] +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + [[package]] name = "cfg-if" version = "1.0.0" @@ -107,34 +147,61 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "3.1.8" +version = "4.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71c47df61d9e16dc010b55dba1952a57d8c215dbb533fd13cdd13369aac73b1c" +checksum = "046ae530c528f252094e4a77886ee1374437744b2bff1497aa898bbddbbb29b3" dependencies = [ - "atty", - "bitflags", + "clap_builder", "clap_derive", - "indexmap", - "lazy_static", - "os_str_bytes", + "once_cell", +] + +[[package]] +name = "clap_builder" +version = "4.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "223163f58c9a40c3b0a43e1c4b50a9ce09f007ea2cb1ec258a687945b4b7929f" +dependencies = [ + "anstream", + "anstyle", + "bitflags", + "clap_lex", "strsim", - "termcolor", - "textwrap", ] [[package]] name = "clap_derive" -version = "3.1.7" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3aab4734e083b809aaf5794e14e756d1c798d2c69c7f7de7a09a2f5214993c1" +checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" dependencies = [ "heck", - "proc-macro-error", "proc-macro2", "quote", "syn", ] +[[package]] +name = "clap_lex" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" + +[[package]] +name = "concolor-override" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a855d4a1978dc52fb0536a04d384c2c0c1aa273597f08b77c8c4d3b2eec6037f" + +[[package]] +name = "concolor-query" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d11d52c3d7ca2e6d0040212be9e4dbbcd78b6447f535b6b561f449427944cf" +dependencies = [ + "windows-sys 0.45.0", +] + [[package]] name = "crossbeam-utils" version = "0.8.8" @@ -145,17 +212,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "derive-new" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3418329ca0ad70234b9735dc4ceed10af4df60eff9c8e7b06cb5e520d92c3535" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "diff" version = "0.1.12" @@ -222,6 +278,27 @@ dependencies = [ "termcolor", ] +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "fnv" version = "1.0.7" @@ -261,12 +338,6 @@ dependencies = [ "regex", ] -[[package]] -name = "hashbrown" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" - [[package]] name = "heck" version = "0.4.0" @@ -282,6 +353,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + [[package]] name = "humantime" version = "2.1.0" @@ -307,13 +384,26 @@ dependencies = [ ] [[package]] -name = "indexmap" -version = "1.8.1" +name = "io-lifetimes" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" +checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" dependencies = [ - "autocfg", - "hashbrown", + "hermit-abi 0.3.1", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "is-terminal" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" +dependencies = [ + "hermit-abi 0.3.1", + "io-lifetimes", + "rustix", + "windows-sys 0.48.0", ] [[package]] @@ -339,9 +429,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.122" +version = "0.2.141" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec647867e2bf0772e28c8bcde4f0d19a9216916e890543b5a03ed8ef27b8f259" +checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" [[package]] name = "libm" @@ -349,6 +439,12 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" +[[package]] +name = "linux-raw-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" + [[package]] name = "log" version = "0.4.16" @@ -366,18 +462,9 @@ checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" [[package]] name = "once_cell" -version = "1.10.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" - -[[package]] -name = "os_str_bytes" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64" -dependencies = [ - "memchr", -] +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "packed_simd_2" @@ -389,44 +476,20 @@ dependencies = [ "libm", ] -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - [[package]] name = "proc-macro2" -version = "1.0.37" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ - "unicode-xid", + "unicode-ident", ] [[package]] name = "quote" -version = "1.0.17" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "632d02bff7f874a36f33ea8bb416cd484b90cc66c1194b1a1110d067a7013f58" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" dependencies = [ "proc-macro2", ] @@ -486,7 +549,6 @@ dependencies = [ "bytecount", "cargo_metadata", "clap", - "derive-new", "diff", "dirs", "env_logger", @@ -507,6 +569,20 @@ dependencies = [ "unicode_categories", ] +[[package]] +name = "rustix" +version = "0.37.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85597d61f83914ddeba6a47b3b8ffe7365107221c2e557ed94426489fefb5f77" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.48.0", +] + [[package]] name = "rustversion" version = "1.0.6" @@ -539,18 +615,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.136" +version = "1.0.160" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.136" +version = "1.0.160" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" dependencies = [ "proc-macro2", "quote", @@ -576,13 +652,13 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.91" +version = "2.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d" +checksum = "fcf316d5356ed6847742d036f8a39c3b8435cac10bd528a4bd461928a6ab34d5" dependencies = [ "proc-macro2", "quote", - "unicode-xid", + "unicode-ident", ] [[package]] @@ -605,26 +681,20 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "textwrap" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" - [[package]] name = "thiserror" -version = "1.0.30" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.30" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", @@ -649,6 +719,12 @@ dependencies = [ "serde", ] +[[package]] +name = "unicode-ident" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" + [[package]] name = "unicode-segmentation" version = "1.9.0" @@ -661,12 +737,6 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" -[[package]] -name = "unicode-xid" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" - [[package]] name = "unicode_categories" version = "0.1.1" @@ -674,10 +744,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" [[package]] -name = "version_check" -version = "0.9.4" +name = "utf8parse" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "walkdir" @@ -727,6 +797,138 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + [[package]] name = "yansi-term" version = "0.1.2" diff --git a/Cargo.toml b/Cargo.toml index 78b970c4c983..0093b975e862 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,6 @@ anyhow = "1.0" bytecount = "0.6" cargo_metadata = "0.14" clap = { version = "4.2.1", features = ["derive"] } -derive-new = "0.5" diff = "0.1" dirs = "4.0" env_logger = "0.9" diff --git a/src/attr/doc_comment.rs b/src/attr/doc_comment.rs index f653a12a8afe..25c8158df8c5 100644 --- a/src/attr/doc_comment.rs +++ b/src/attr/doc_comment.rs @@ -2,12 +2,17 @@ use crate::comment::CommentStyle; use std::fmt::{self, Display}; /// Formats a string as a doc comment using the given [`CommentStyle`]. -#[derive(new)] pub(super) struct DocCommentFormatter<'a> { literal: &'a str, style: CommentStyle<'a>, } +impl<'a> DocCommentFormatter<'a> { + pub(super) const fn new(literal: &'a str, style: CommentStyle<'a>) -> Self { + Self { literal, style } + } +} + impl Display for DocCommentFormatter<'_> { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { let opener = self.style.opener().trim_end(); diff --git a/src/formatting.rs b/src/formatting.rs index 1dfd8a514f0b..1f4ad6960e20 100644 --- a/src/formatting.rs +++ b/src/formatting.rs @@ -175,7 +175,6 @@ fn format_project( } // Used for formatting files. -#[derive(new)] struct FormatContext<'a, T: FormatHandler> { krate: &'a ast::Crate, report: FormatReport, @@ -185,6 +184,22 @@ struct FormatContext<'a, T: FormatHandler> { } impl<'a, T: FormatHandler + 'a> FormatContext<'a, T> { + fn new( + krate: &'a ast::Crate, + report: FormatReport, + parse_session: ParseSess, + config: &'a Config, + handler: &'a mut T, + ) -> Self { + FormatContext { + krate, + report, + parse_session, + config, + handler, + } + } + fn ignore_file(&self, path: &FileName) -> bool { self.parse_session.ignore_file(path) } diff --git a/src/lib.rs b/src/lib.rs index 487fcc3a0df1..0f111f32a4a4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,8 +4,6 @@ #![recursion_limit = "256"] #![allow(clippy::match_like_matches_macro)] -#[macro_use] -extern crate derive_new; #[cfg(test)] #[macro_use] extern crate lazy_static; diff --git a/src/macros.rs b/src/macros.rs index d58f7547fefb..5e8bbee7722e 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1119,12 +1119,15 @@ pub(crate) fn macro_style(mac: &ast::MacCall, context: &RewriteContext<'_>) -> D // A very simple parser that just parses a macros 2.0 definition into its branches. // Currently we do not attempt to parse any further than that. -#[derive(new)] struct MacroParser { toks: Cursor, } impl MacroParser { + const fn new(toks: Cursor) -> Self { + Self { toks } + } + // (`(` ... `)` `=>` `{` ... `}`)* fn parse(&mut self) -> Option { let mut branches = vec![]; diff --git a/src/pairs.rs b/src/pairs.rs index d1c75126ea4a..d135da7e3591 100644 --- a/src/pairs.rs +++ b/src/pairs.rs @@ -9,7 +9,7 @@ use crate::utils::{ }; /// Sigils that decorate a binop pair. -#[derive(new, Clone, Copy)] +#[derive(Clone, Copy)] pub(crate) struct PairParts<'a> { prefix: &'a str, infix: &'a str, @@ -17,6 +17,13 @@ pub(crate) struct PairParts<'a> { } impl<'a> PairParts<'a> { + pub(crate) const fn new(prefix: &'a str, infix: &'a str, suffix: &'a str) -> Self { + Self { + prefix, + infix, + suffix, + } + } pub(crate) fn infix(infix: &'a str) -> PairParts<'a> { PairParts { prefix: "", From ac2ebd3a78246d2dab16aec39d2aaace618a17eb Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Thu, 30 Mar 2023 13:18:28 -0400 Subject: [PATCH 272/401] Prevent ICE when calling parse_attribute without an attribute Fixes 5729 `parse_attribute` will panic if the first token is not a `#`. To prevent this we return early instead of trying to parse an invalid attribute. --- src/parse/macros/cfg_if.rs | 5 +++++ tests/rustfmt/main.rs | 2 +- tests/target/issue_5729.rs | 5 +++++ 3 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 tests/target/issue_5729.rs diff --git a/src/parse/macros/cfg_if.rs b/src/parse/macros/cfg_if.rs index ac03409a7844..cbc4c90b8f98 100644 --- a/src/parse/macros/cfg_if.rs +++ b/src/parse/macros/cfg_if.rs @@ -34,6 +34,11 @@ fn parse_cfg_if_inner<'a>( if !parser.eat_keyword(kw::If) { return Err("Expected `if`"); } + + if !matches!(parser.token.kind, TokenKind::Pound) { + return Err("Failed to parse attributes"); + } + // Inner attributes are not actually syntactically permitted here, but we don't // care about inner vs outer attributes in this position. Our purpose with this // special case parsing of cfg_if macros is to ensure we can correctly resolve diff --git a/tests/rustfmt/main.rs b/tests/rustfmt/main.rs index f00b0e09604f..4936a7174636 100644 --- a/tests/rustfmt/main.rs +++ b/tests/rustfmt/main.rs @@ -178,7 +178,7 @@ fn rustfmt_emits_error_on_line_overflow_true() { #[test] #[allow(non_snake_case)] fn dont_emit_ICE() { - let files = ["tests/target/issue_5728.rs"]; + let files = ["tests/target/issue_5728.rs", "tests/target/issue_5729.rs"]; for file in files { let args = [file]; diff --git a/tests/target/issue_5729.rs b/tests/target/issue_5729.rs new file mode 100644 index 000000000000..d63c83e88f84 --- /dev/null +++ b/tests/target/issue_5729.rs @@ -0,0 +1,5 @@ +cfg_if::cfg_if! { + if { + } else if #(&cpus) { + } else [libc::CTL_HW, libc::HW_NCPU, 0, 0] +} From 2c30fa5a8292ab5d32a3b1129c9ab897897b1689 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Tue, 7 Feb 2023 18:43:07 -0500 Subject: [PATCH 273/401] Adjust enum variant spans to exclude any explicit discriminant Fixes 5686 For reference, explicit discriminants were proposed in [RFC-2363]. `ast::Variant` spans extend to include explicit discriminants when they are present. Now we'll adjust the span of enum variants to exclude any explicit discriminant. [RFC-2363]: https://rust-lang.github.io/rfcs/2363-arbitrary-enum-discriminant.html --- src/items.rs | 25 ++++++++++++++++++++--- tests/source/issue_5686.rs | 40 ++++++++++++++++++++++++++++++++++++ tests/target/issue_5686.rs | 42 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+), 3 deletions(-) create mode 100644 tests/source/issue_5686.rs create mode 100644 tests/target/issue_5686.rs diff --git a/src/items.rs b/src/items.rs index 3c5293b6bf5a..714bcbc10b42 100644 --- a/src/items.rs +++ b/src/items.rs @@ -560,7 +560,7 @@ impl<'a> FmtVisitor<'a> { let variant_body = match field.data { ast::VariantData::Tuple(..) | ast::VariantData::Struct(..) => format_struct( &context, - &StructParts::from_variant(field), + &StructParts::from_variant(field, &context), self.block_indent, Some(one_line_width), )?, @@ -951,14 +951,14 @@ impl<'a> StructParts<'a> { format_header(context, self.prefix, self.ident, self.vis, offset) } - fn from_variant(variant: &'a ast::Variant) -> Self { + fn from_variant(variant: &'a ast::Variant, context: &RewriteContext<'_>) -> Self { StructParts { prefix: "", ident: variant.ident, vis: &DEFAULT_VISIBILITY, def: &variant.data, generics: None, - span: variant.span, + span: enum_variant_span(variant, context), } } @@ -979,6 +979,25 @@ impl<'a> StructParts<'a> { } } +fn enum_variant_span(variant: &ast::Variant, context: &RewriteContext<'_>) -> Span { + use ast::VariantData::*; + if let Some(ref anon_const) = variant.disr_expr { + let span_before_consts = variant.span.until(anon_const.value.span); + let hi = match &variant.data { + Struct(..) => context + .snippet_provider + .span_after_last(span_before_consts, "}"), + Tuple(..) => context + .snippet_provider + .span_after_last(span_before_consts, ")"), + Unit(..) => variant.ident.span.hi(), + }; + mk_sp(span_before_consts.lo(), hi) + } else { + variant.span + } +} + fn format_struct( context: &RewriteContext<'_>, struct_parts: &StructParts<'_>, diff --git a/tests/source/issue_5686.rs b/tests/source/issue_5686.rs new file mode 100644 index 000000000000..3bf96f73b2cd --- /dev/null +++ b/tests/source/issue_5686.rs @@ -0,0 +1,40 @@ +#[repr(u8)] +enum MyEnum { + UnitWithExplicitDiscriminant = 0, + EmptyStructSingleLineBlockComment { + /* Comment */ + } = 1, + EmptyStructMultiLineBlockComment { + /* + * Comment + */ + } = 2, + EmptyStructLineComment { + // comment + } = 3, + EmptyTupleSingleLineBlockComment( + /* Comment */ + ) = 4, + EmptyTupleMultiLineBlockComment( + /* + * Comment + */ + ) = 5, + EmptyTupleLineComment( + // comment + ) = 6, +} + +enum Animal { + Dog(/* tuple variant closer in comment -> ) */) = 1, + #[hello(world)] + Cat(/* tuple variant close in leading attribute */) = 2, + Bee(/* tuple variant closer on associated field attribute */ #[hello(world)] usize) = 3, + Fox(/* tuple variant closer on const fn call */) = some_const_fn(), + Ant(/* tuple variant closer on macro call */) = some_macro!(), + Snake {/* stuct variant closer in comment -> } */} = 6, + #[hell{world}] + Cobra {/* struct variant close in leading attribute */} = 6, + Eagle {/* struct variant closer on associated field attribute */ #[hell{world}]value: Sting} = 7, + Koala {/* struct variant closer on macro call */} = some_macro!{} +} diff --git a/tests/target/issue_5686.rs b/tests/target/issue_5686.rs new file mode 100644 index 000000000000..993f12b5316d --- /dev/null +++ b/tests/target/issue_5686.rs @@ -0,0 +1,42 @@ +#[repr(u8)] +enum MyEnum { + UnitWithExplicitDiscriminant = 0, + EmptyStructSingleLineBlockComment {/* Comment */} = 1, + EmptyStructMultiLineBlockComment { + /* + * Comment + */ + } = 2, + EmptyStructLineComment { + // comment + } = 3, + EmptyTupleSingleLineBlockComment(/* Comment */) = 4, + EmptyTupleMultiLineBlockComment( + /* + * Comment + */ + ) = 5, + EmptyTupleLineComment( + // comment + ) = 6, +} + +enum Animal { + Dog(/* tuple variant closer in comment -> ) */) = 1, + #[hello(world)] + Cat(/* tuple variant close in leading attribute */) = 2, + Bee( + /* tuple variant closer on associated field attribute */ #[hello(world)] usize, + ) = 3, + Fox(/* tuple variant closer on const fn call */) = some_const_fn(), + Ant(/* tuple variant closer on macro call */) = some_macro!(), + Snake {/* stuct variant closer in comment -> } */} = 6, + #[hell{world}] + Cobra {/* struct variant close in leading attribute */} = 6, + Eagle { + /* struct variant closer on associated field attribute */ + #[hell{world}] + value: Sting, + } = 7, + Koala {/* struct variant closer on macro call */} = some_macro! {}, +} From 66b9951dcdc76bbda5e6c75b4a2974d21a9e747f Mon Sep 17 00:00:00 2001 From: KaDiWa Date: Wed, 1 Feb 2023 00:20:15 +0100 Subject: [PATCH 274/401] update some dependencies --- Cargo.lock | 98 +++++++++++++++++++++++++---------- Cargo.toml | 6 +-- src/cargo-fmt/main.rs | 7 +-- src/cargo-fmt/test/targets.rs | 22 ++++---- 4 files changed, 90 insertions(+), 43 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8487dc58b4ed..a59e0b6819bf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -68,15 +68,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27" [[package]] -name = "atty" -version = "0.2.14" +name = "autocfg" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "bitflags" @@ -122,15 +117,16 @@ dependencies = [ [[package]] name = "cargo_metadata" -version = "0.14.2" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" +checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a" dependencies = [ "camino", "cargo-platform", "semver", "serde", "serde_json", + "thiserror", ] [[package]] @@ -267,12 +263,12 @@ checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" [[package]] name = "env_logger" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" dependencies = [ - "atty", "humantime", + "is-terminal", "log", "regex", "termcolor", @@ -338,21 +334,18 @@ dependencies = [ "regex", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + [[package]] name = "heck" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - [[package]] name = "hermit-abi" version = "0.3.1" @@ -383,13 +376,23 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown", +] + [[package]] name = "io-lifetimes" version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" dependencies = [ - "hermit-abi 0.3.1", + "hermit-abi", "libc", "windows-sys 0.48.0", ] @@ -400,7 +403,7 @@ version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" dependencies = [ - "hermit-abi 0.3.1", + "hermit-abi", "io-lifetimes", "rustix", "windows-sys 0.48.0", @@ -644,6 +647,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93107647184f6027e3b7dcb2e11034cf95ffa1e3a682c67951963ac69c1c007d" +dependencies = [ + "serde", +] + [[package]] name = "strsim" version = "0.10.0" @@ -712,11 +724,36 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.8" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +checksum = "d6135d499e69981f9ff0ef2167955a5333c35e36f6937d382974566b3d5b94ec" dependencies = [ "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a76a9312f5ba4c2dec6b9161fdf25d87ad8a09256ccea5a556fef03c706a10f" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", ] [[package]] @@ -929,6 +966,15 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +[[package]] +name = "winnow" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca0ace3845f0d96209f0375e6d367e3eb87eb65d27d445bdc9f1843a26f39448" +dependencies = [ + "memchr", +] + [[package]] name = "yansi-term" version = "0.1.2" diff --git a/Cargo.toml b/Cargo.toml index 0093b975e862..5f483693a116 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,11 +36,11 @@ generic-simd = ["bytecount/generic-simd"] annotate-snippets = { version = "0.9", features = ["color"] } anyhow = "1.0" bytecount = "0.6" -cargo_metadata = "0.14" +cargo_metadata = "0.15.4" clap = { version = "4.2.1", features = ["derive"] } diff = "0.1" dirs = "4.0" -env_logger = "0.9" +env_logger = "0.10.0" getopts = "0.2" ignore = "0.4" itertools = "0.10" @@ -51,7 +51,7 @@ serde = { version = "1.0.160", features = ["derive"] } serde_json = "1.0" term = "0.7" thiserror = "1.0.40" -toml = "0.5" +toml = "0.7.4" unicode-segmentation = "1.9" unicode-width = "0.1" unicode_categories = "0.1" diff --git a/src/cargo-fmt/main.rs b/src/cargo-fmt/main.rs index a106181ec7eb..bc9745275f20 100644 --- a/src/cargo-fmt/main.rs +++ b/src/cargo-fmt/main.rs @@ -14,6 +14,7 @@ use std::path::{Path, PathBuf}; use std::process::Command; use std::str; +use cargo_metadata::Edition; use clap::{CommandFactory, Parser}; #[path = "test/mod.rs"] @@ -270,7 +271,7 @@ pub struct Target { /// A kind of target (e.g., lib, bin, example, ...). kind: String, /// Rust edition for this target. - edition: String, + edition: Edition, } impl Target { @@ -281,7 +282,7 @@ impl Target { Target { path: canonicalized, kind: target.kind[0].clone(), - edition: target.edition.clone(), + edition: target.edition, } } } @@ -506,7 +507,7 @@ fn run_rustfmt( let mut command = rustfmt_command() .stdout(stdout) .args(files) - .args(&["--edition", edition]) + .args(&["--edition", edition.as_str()]) .args(fmt_args) .spawn() .map_err(|e| match e.kind() { diff --git a/src/cargo-fmt/test/targets.rs b/src/cargo-fmt/test/targets.rs index b7e7fabdf715..34accb2136a4 100644 --- a/src/cargo-fmt/test/targets.rs +++ b/src/cargo-fmt/test/targets.rs @@ -2,7 +2,7 @@ use super::*; struct ExpTarget { path: &'static str, - edition: &'static str, + edition: Edition, kind: &'static str, } @@ -26,7 +26,7 @@ mod all_targets { for target in exp_targets { assert!(targets.contains(&Target { path: get_path(target.path), - edition: target.edition.to_owned(), + edition: target.edition, kind: target.kind.to_owned(), })); } @@ -39,17 +39,17 @@ mod all_targets { let exp_targets = vec![ ExpTarget { path: "dependency-dir-name/subdep-dir-name/src/lib.rs", - edition: "2018", + edition: Edition::E2018, kind: "lib", }, ExpTarget { path: "dependency-dir-name/src/lib.rs", - edition: "2018", + edition: Edition::E2018, kind: "lib", }, ExpTarget { path: "src/main.rs", - edition: "2018", + edition: Edition::E2018, kind: "main", }, ]; @@ -79,32 +79,32 @@ mod all_targets { let exp_targets = vec![ ExpTarget { path: "ws/a/src/main.rs", - edition: "2018", + edition: Edition::E2018, kind: "bin", }, ExpTarget { path: "ws/b/src/main.rs", - edition: "2018", + edition: Edition::E2018, kind: "bin", }, ExpTarget { path: "ws/c/src/lib.rs", - edition: "2018", + edition: Edition::E2018, kind: "lib", }, ExpTarget { path: "ws/a/d/src/lib.rs", - edition: "2018", + edition: Edition::E2018, kind: "lib", }, ExpTarget { path: "e/src/main.rs", - edition: "2018", + edition: Edition::E2018, kind: "main", }, ExpTarget { path: "ws/a/d/f/src/lib.rs", - edition: "2018", + edition: Edition::E2018, kind: "lib", }, ]; From f4201ef2cb89c28bcdb82cab292720eb66b75695 Mon Sep 17 00:00:00 2001 From: Lukasz Anforowicz Date: Mon, 10 Oct 2022 16:33:31 +0000 Subject: [PATCH 275/401] Handling of numbered markdown lists. Fixes issue #5416 --- src/comment.rs | 174 +++++++++++++++++++++--- tests/source/itemized-blocks/no_wrap.rs | 36 ++++- tests/source/itemized-blocks/wrap.rs | 36 ++++- tests/target/itemized-blocks/no_wrap.rs | 36 ++++- tests/target/itemized-blocks/wrap.rs | 62 ++++++++- 5 files changed, 319 insertions(+), 25 deletions(-) diff --git a/src/comment.rs b/src/comment.rs index bc0e8774f494..85918ecc1164 100644 --- a/src/comment.rs +++ b/src/comment.rs @@ -432,12 +432,18 @@ impl CodeBlockAttribute { /// Block that is formatted as an item. /// -/// An item starts with either a star `*` a dash `-` a greater-than `>` or a plus '+'. +/// An item starts with either a star `*`, a dash `-`, a greater-than `>`, a plus '+', or a number +/// `12.` or `34)` (with at most 2 digits). An item represents CommonMark's ["list +/// items"](https://spec.commonmark.org/0.30/#list-items) and/or ["block +/// quotes"](https://spec.commonmark.org/0.30/#block-quotes), but note that only a subset of +/// CommonMark is recognized - see the doc comment of [`ItemizedBlock::get_marker_length`] for more +/// details. +/// /// 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 characters (typically whitespaces) up to the item sigil + /// the number of characters (typically whitespaces) up to the item marker indent: usize, /// the string that marks the start of an item opener: String, @@ -446,37 +452,70 @@ struct ItemizedBlock { } impl ItemizedBlock { - /// Returns `true` if the line is formatted as an item - fn is_itemized_line(line: &str) -> bool { - let trimmed = line.trim_start(); + /// Checks whether the `trimmed` line includes an item marker. Returns `None` if there is no + /// marker. Returns the length of the marker (in bytes) if one is present. Note that the length + /// includes the whitespace that follows the marker, for example the marker in `"* list item"` + /// has the length of 2. + /// + /// This function recognizes item markers that correspond to CommonMark's + /// ["bullet list marker"](https://spec.commonmark.org/0.30/#bullet-list-marker), + /// ["block quote marker"](https://spec.commonmark.org/0.30/#block-quote-marker), and/or + /// ["ordered list marker"](https://spec.commonmark.org/0.30/#ordered-list-marker). + /// + /// Compared to CommonMark specification, the number of digits that are allowed in an ["ordered + /// list marker"](https://spec.commonmark.org/0.30/#ordered-list-marker) is more limited (to at + /// most 2 digits). Limiting the length of the marker helps reduce the risk of recognizing + /// arbitrary numbers as markers. See also + /// which gives the + /// following example where a number (i.e. "1868") doesn't signify an ordered list: + /// ```md + /// The Captain died in + /// 1868. He wes buried in... + /// ``` + fn get_marker_length(trimmed: &str) -> Option { + // https://spec.commonmark.org/0.30/#bullet-list-marker or + // https://spec.commonmark.org/0.30/#block-quote-marker let itemized_start = ["* ", "- ", "> ", "+ "]; - itemized_start.iter().any(|s| trimmed.starts_with(s)) + if itemized_start.iter().any(|s| trimmed.starts_with(s)) { + return Some(2); // All items in `itemized_start` have length 2. + } + + // https://spec.commonmark.org/0.30/#ordered-list-marker, where at most 2 digits are + // allowed. + for suffix in [". ", ") "] { + if let Some((prefix, _)) = trimmed.split_once(suffix) { + if prefix.len() <= 2 && prefix.chars().all(|c| char::is_ascii_digit(&c)) { + return Some(prefix.len() + suffix.len()); + } + } + } + + None // No markers found. } - /// 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(); - // +2 = '* ', which will add the appropriate amount of whitespace to keep itemized - // content formatted correctly. - let mut indent = space_to_sigil + 2; + /// Creates a new `ItemizedBlock` described with the given `line`. + /// Returns `None` if `line` doesn't start an item. + fn new(line: &str) -> Option { + let marker_length = ItemizedBlock::get_marker_length(line.trim_start())?; + let space_to_marker = line.chars().take_while(|c| c.is_whitespace()).count(); + let mut indent = space_to_marker + marker_length; 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 the 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 { + Some(ItemizedBlock { lines: vec![line[indent..].to_string()], indent, opener: line[..indent].to_string(), line_start, - } + }) } /// Returns a `StringFormat` used for formatting the content of an item. @@ -495,7 +534,7 @@ impl ItemizedBlock { /// Returns `true` if the line is part of the current itemized block. /// If it is, then it is added to the internal lines list. fn add_line(&mut self, line: &str) -> bool { - if !ItemizedBlock::is_itemized_line(line) + if ItemizedBlock::get_marker_length(line.trim_start()).is_none() && self.indent <= line.chars().take_while(|c| c.is_whitespace()).count() { self.lines.push(line.to_string()); @@ -766,10 +805,11 @@ impl<'a> CommentRewrite<'a> { self.item_block = None; if let Some(stripped) = line.strip_prefix("```") { self.code_block_attr = Some(CodeBlockAttribute::new(stripped)) - } else if self.fmt.config.wrap_comments() && ItemizedBlock::is_itemized_line(line) { - let ib = ItemizedBlock::new(line); - self.item_block = Some(ib); - return false; + } else if self.fmt.config.wrap_comments() { + if let Some(ib) = ItemizedBlock::new(line) { + self.item_block = Some(ib); + return false; + } } if self.result == self.opener { @@ -2020,4 +2060,96 @@ fn main() { "#; assert_eq!(s, filter_normal_code(s_with_comment)); } + + #[test] + fn test_itemized_block_first_line_handling() { + fn run_test( + test_input: &str, + expected_line: &str, + expected_indent: usize, + expected_opener: &str, + expected_line_start: &str, + ) { + let block = ItemizedBlock::new(test_input).unwrap(); + assert_eq!(1, block.lines.len(), "test_input: {:?}", test_input); + assert_eq!( + expected_line, &block.lines[0], + "test_input: {:?}", + test_input + ); + assert_eq!( + expected_indent, block.indent, + "test_input: {:?}", + test_input + ); + assert_eq!( + expected_opener, &block.opener, + "test_input: {:?}", + test_input + ); + assert_eq!( + expected_line_start, &block.line_start, + "test_input: {:?}", + test_input + ); + } + + run_test("- foo", "foo", 2, "- ", " "); + run_test("* foo", "foo", 2, "* ", " "); + run_test("> foo", "foo", 2, "> ", "> "); + + run_test("1. foo", "foo", 3, "1. ", " "); + run_test("12. foo", "foo", 4, "12. ", " "); + run_test("1) foo", "foo", 3, "1) ", " "); + run_test("12) foo", "foo", 4, "12) ", " "); + + run_test(" - foo", "foo", 6, " - ", " "); + + // https://spec.commonmark.org/0.30 says: "A start number may begin with 0s": + run_test("0. foo", "foo", 3, "0. ", " "); + run_test("01. foo", "foo", 4, "01. ", " "); + } + + #[test] + fn test_itemized_block_nonobvious_markers_are_rejected() { + let test_inputs = vec![ + // Non-numeric item markers (e.g. `a.` or `iv.`) are not allowed by + // https://spec.commonmark.org/0.30/#ordered-list-marker. We also note that allowing + // them would risk misidentifying regular words as item markers. See also the + // discussion in https://talk.commonmark.org/t/blank-lines-before-lists-revisited/1990 + "word. rest of the paragraph.", + "a. maybe this is a list item? maybe not?", + "iv. maybe this is a list item? maybe not?", + // Numbers with 3 or more digits are not recognized as item markers, to avoid + // formatting the following example as a list: + // + // ``` + // The Captain died in + // 1868. He was buried in... + // ``` + "123. only 2-digit numbers are recognized as item markers.", + // Parens: + "123) giving some coverage to parens as well.", + "a) giving some coverage to parens as well.", + // https://spec.commonmark.org/0.30 says that "at least one space or tab is needed + // between the list marker and any following content": + "1.Not a list item.", + "1.2.3. Not a list item.", + "1)Not a list item.", + "-Not a list item.", + "+Not a list item.", + "+1 not a list item.", + // https://spec.commonmark.org/0.30 says: "A start number may not be negative": + "-1. Not a list item.", + "-1 Not a list item.", + ]; + for line in test_inputs.iter() { + let maybe_block = ItemizedBlock::new(line); + assert!( + maybe_block.is_none(), + "The following line shouldn't be classified as a list item: {}", + line + ); + } + } } diff --git a/tests/source/itemized-blocks/no_wrap.rs b/tests/source/itemized-blocks/no_wrap.rs index a7b6a10a0105..e5699e766848 100644 --- a/tests/source/itemized-blocks/no_wrap.rs +++ b/tests/source/itemized-blocks/no_wrap.rs @@ -1,7 +1,7 @@ // rustfmt-normalize_comments: true // rustfmt-format_code_in_doc_comments: true -//! This is a list: +//! This is an itemized markdown list (see also issue #3224): //! * Outer //! * Outer //! * Inner @@ -13,6 +13,40 @@ //! - when the log level is info, the level name is green and the rest of the line is white //! - when the log level is debug, the whole line is white //! - when the log level is trace, the whole line is gray ("bright black") +//! +//! This is a numbered markdown list (see also issue #5416): +//! 1. Long long long long long long long long long long long long long long long long long line +//! 2. Another very long long long long long long long long long long long long long long long line +//! 3. Nested list +//! 1. Long long long long long long long long long long long long long long long long line +//! 2. Another very long long long long long long long long long long long long long long line +//! 4. Last item +//! +//! Using the ')' instead of '.' character after the number: +//! 1) Long long long long long long long long long long long long long long long long long line +//! 2) Another very long long long long long long long long long long long long long long long line +//! +//! Deep list that mixes various bullet and number formats: +//! 1. First level with a long long long long long long long long long long long long long long +//! long long long line +//! 2. First level with another very long long long long long long long long long long long long +//! long long long line +//! * Second level with a long long long long long long long long long long long long long +//! long long long line +//! * Second level with another very long long long long long long long long long long long +//! long long long line +//! 1) Third level with a long long long long long long long long long long long long long +//! long long long line +//! 2) Third level with another very long long long long long long long long long long +//! long long long long line +//! - Forth level with a long long long long long long long long long long long long +//! long long long long line +//! - Forth level with another very long long long long long long long long long long +//! long long long long line +//! 3) One more item at the third level +//! 4) Last item of the third level +//! * Last item of second level +//! 3. Last item of first level /// All the parameters ***except for `from_theater`*** should be inserted as sent by the remote /// theater, i.e., as passed to [`Theater::send`] on the remote actor: diff --git a/tests/source/itemized-blocks/wrap.rs b/tests/source/itemized-blocks/wrap.rs index 955cc698b79f..768461a43f95 100644 --- a/tests/source/itemized-blocks/wrap.rs +++ b/tests/source/itemized-blocks/wrap.rs @@ -2,7 +2,7 @@ // rustfmt-format_code_in_doc_comments: true // rustfmt-max_width: 50 -//! This is a list: +//! This is an itemized markdown list (see also issue #3224): //! * Outer //! * Outer //! * Inner @@ -14,6 +14,40 @@ //! - when the log level is info, the level name is green and the rest of the line is white //! - when the log level is debug, the whole line is white //! - when the log level is trace, the whole line is gray ("bright black") +//! +//! This is a numbered markdown list (see also issue #5416): +//! 1. Long long long long long long long long long long long long long long long long long line +//! 2. Another very long long long long long long long long long long long long long long long line +//! 3. Nested list +//! 1. Long long long long long long long long long long long long long long long long line +//! 2. Another very long long long long long long long long long long long long long long line +//! 4. Last item +//! +//! Using the ')' instead of '.' character after the number: +//! 1) Long long long long long long long long long long long long long long long long long line +//! 2) Another very long long long long long long long long long long long long long long long line +//! +//! Deep list that mixes various bullet and number formats: +//! 1. First level with a long long long long long long long long long long long long long long +//! long long long line +//! 2. First level with another very long long long long long long long long long long long long +//! long long long line +//! * Second level with a long long long long long long long long long long long long long +//! long long long line +//! * Second level with another very long long long long long long long long long long long +//! long long long line +//! 1) Third level with a long long long long long long long long long long long long long +//! long long long line +//! 2) Third level with another very long long long long long long long long long long +//! long long long long line +//! - Forth level with a long long long long long long long long long long long long +//! long long long long line +//! - Forth level with another very long long long long long long long long long long +//! long long long long line +//! 3) One more item at the third level +//! 4) Last item of the third level +//! * Last item of second level +//! 3. Last item of first level // This example shows how to configure fern to output really nicely colored logs // - when the log level is error, the whole line is red diff --git a/tests/target/itemized-blocks/no_wrap.rs b/tests/target/itemized-blocks/no_wrap.rs index de8856382726..86818b447459 100644 --- a/tests/target/itemized-blocks/no_wrap.rs +++ b/tests/target/itemized-blocks/no_wrap.rs @@ -1,7 +1,7 @@ // rustfmt-normalize_comments: true // rustfmt-format_code_in_doc_comments: true -//! This is a list: +//! This is an itemized markdown list (see also issue #3224): //! * Outer //! * Outer //! * Inner @@ -13,6 +13,40 @@ //! - when the log level is info, the level name is green and the rest of the line is white //! - when the log level is debug, the whole line is white //! - when the log level is trace, the whole line is gray ("bright black") +//! +//! This is a numbered markdown list (see also issue #5416): +//! 1. Long long long long long long long long long long long long long long long long long line +//! 2. Another very long long long long long long long long long long long long long long long line +//! 3. Nested list +//! 1. Long long long long long long long long long long long long long long long long line +//! 2. Another very long long long long long long long long long long long long long long line +//! 4. Last item +//! +//! Using the ')' instead of '.' character after the number: +//! 1) Long long long long long long long long long long long long long long long long long line +//! 2) Another very long long long long long long long long long long long long long long long line +//! +//! Deep list that mixes various bullet and number formats: +//! 1. First level with a long long long long long long long long long long long long long long +//! long long long line +//! 2. First level with another very long long long long long long long long long long long long +//! long long long line +//! * Second level with a long long long long long long long long long long long long long +//! long long long line +//! * Second level with another very long long long long long long long long long long long +//! long long long line +//! 1) Third level with a long long long long long long long long long long long long long +//! long long long line +//! 2) Third level with another very long long long long long long long long long long +//! long long long long line +//! - Forth level with a long long long long long long long long long long long long +//! long long long long line +//! - Forth level with another very long long long long long long long long long long +//! long long long long line +//! 3) One more item at the third level +//! 4) Last item of the third level +//! * Last item of second level +//! 3. Last item of first level /// All the parameters ***except for `from_theater`*** should be inserted as sent by the remote /// theater, i.e., as passed to [`Theater::send`] on the remote actor: diff --git a/tests/target/itemized-blocks/wrap.rs b/tests/target/itemized-blocks/wrap.rs index a4907303c9e1..4826590ea59d 100644 --- a/tests/target/itemized-blocks/wrap.rs +++ b/tests/target/itemized-blocks/wrap.rs @@ -2,7 +2,8 @@ // rustfmt-format_code_in_doc_comments: true // rustfmt-max_width: 50 -//! This is a list: +//! This is an itemized markdown list (see also +//! issue #3224): //! * Outer //! * Outer //! * Inner @@ -23,6 +24,65 @@ //! is white //! - when the log level is trace, the whole line //! is gray ("bright black") +//! +//! This is a numbered markdown list (see also +//! issue #5416): +//! 1. Long long long long long long long long +//! long long long long long long long long +//! long line +//! 2. Another very long long long long long long +//! long long long long long long long long +//! long line +//! 3. Nested list +//! 1. Long long long long long long long long +//! long long long long long long long long +//! line +//! 2. Another very long long long long long +//! long long long long long long long long +//! long line +//! 4. Last item +//! +//! Using the ')' instead of '.' character after +//! the number: +//! 1) Long long long long long long long long +//! long long long long long long long long +//! long line +//! 2) Another very long long long long long long +//! long long long long long long long long +//! long line +//! +//! Deep list that mixes various bullet and number +//! formats: +//! 1. First level with a long long long long long +//! long long long long long long long long +//! long long long long line +//! 2. First level with another very long long +//! long long long long long long long long +//! long long long long long line +//! * Second level with a long long long long +//! long long long long long long long long +//! long long long long line +//! * Second level with another very long long +//! long long long long long long long long +//! long long long long line +//! 1) Third level with a long long long +//! long long long long long long long +//! long long long long long long line +//! 2) Third level with another very long +//! long long long long long long long +//! long long long long long long line +//! - Forth level with a long long +//! long long long long long long +//! long long long long long long +//! long long line +//! - Forth level with another very +//! long long long long long long +//! long long long long long long +//! long long line +//! 3) One more item at the third level +//! 4) Last item of the third level +//! * Last item of second level +//! 3. Last item of first level // This example shows how to configure fern to // output really nicely colored logs From 0b17d7ea4605fcd3675c80069e0b5e82c4332b05 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Mon, 19 Jun 2023 21:29:15 -0500 Subject: [PATCH 276/401] chore: address merge and bump toolchain --- config_proc_macro/src/attrs.rs | 7 ------- rust-toolchain | 2 +- src/macros.rs | 2 +- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/config_proc_macro/src/attrs.rs b/config_proc_macro/src/attrs.rs index 2c01b9a1321e..d8de9aae088d 100644 --- a/config_proc_macro/src/attrs.rs +++ b/config_proc_macro/src/attrs.rs @@ -64,13 +64,6 @@ fn is_attr_path(attr: &syn::Attribute, name: &str) -> bool { } } -fn is_attr_path(attr: &syn::Attribute, name: &str) -> bool { - attr.parse_meta().ok().map_or(false, |meta| match meta { - syn::Meta::Path(path) if path.is_ident(name) => true, - _ => false, - }) -} - fn get_name_value_str_lit(attr: &syn::Attribute, name: &str) -> Option { match &attr.meta { syn::Meta::NameValue(syn::MetaNameValue { diff --git a/rust-toolchain b/rust-toolchain index 22283b3d6200..03b909cd80c7 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2023-01-24" +channel = "nightly-2023-06-19" components = ["llvm-tools", "rustc-dev"] diff --git a/src/macros.rs b/src/macros.rs index 9f4beae73505..e9a298a27693 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1124,7 +1124,7 @@ struct MacroParser { } impl MacroParser { - const fn new(toks: Cursor) -> Self { + const fn new(toks: TokenTreeCursor) -> Self { Self { toks } } From 3f7c366fc0464e01ddcaefbd70647cb3da4202be Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Mon, 19 Jun 2023 22:23:11 -0500 Subject: [PATCH 277/401] chore: release v1.5.3 --- CHANGELOG.md | 40 ++++++++++------------------------------ Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 12 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd7d9f8d70b6..0d4e057223d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,14 @@ ## [Unreleased] +## [1.5.3] 2023-06-20 + ### Fixed - When formatting doc comments with `wrap_comments = true` rustfmt will no longer wrap markdown tables [#4210](https://github.com/rust-lang/rustfmt/issues/4210) -- rustfmt will no longer use shorthand initialization when rewriting a tuple struct even when `use_field_init_shorthand = true` as this lead to code that could no longer compile. +- Properly handle wrapping comments that include a numbered list in markdown [#5416](https://github.com/rust-lang/rustfmt/issues/5416) +- Properly handle markdown sublists that utilize a `+` [#4041](https://github.com/rust-lang/rustfmt/issues/4210) +- rustfmt will no longer use shorthand initialization when rewriting a tuple struct even when `use_field_init_shorthand = true` as this leads to code that could no longer compile. Take the following struct as an example `struct MyStruct(u64);`. rustfmt will no longer format `MyStruct { 0: 0 }` as `MyStruct { 0 }` [#5488](https://github.com/rust-lang/rustfmt/issues/5488) - rustfmt no longer panics when formatting an empty code block in a doc comment with `format_code_in_doc_comments = true` [#5234](https://github.com/rust-lang/rustfmt/issues/5234). For example: ```rust @@ -14,7 +18,7 @@ /// ``` fn main() {} ``` -- Use the correct span when rewriting struct generics. This prevents rustfmt from incorrectly duplicating where clause bounds when using const expression in where clause bounds with feature `#![feature(generic_const_exprs)]` [#5691](https://github.com/rust-lang/rustfmt/issues/5691). e.g.: +- rustfmt no longer incorrectly duplicates the where clause bounds when using const expression in where clause bounds with feature `#![feature(generic_const_exprs)]` [#5691](https://github.com/rust-lang/rustfmt/issues/5691). e.g.: ```rust struct S where @@ -22,6 +26,9 @@ // code ... } ``` +- Prevent ICE when parsing invalid attributes in `cfg_if!` macros [#5728](https://github.com/rust-lang/rustfmt/issues/5728), [#5729](https://github.com/rust-lang/rustfmt/issues/5729) +- rustfmt no longer loses comments placed between a doc comment and generic params [#5320](https://github.com/rust-lang/rustfmt/issues/5320) +- Handle explicit discriminants in enums with comments present [#5686](https://github.com/rust-lang/rustfmt/issues/5686) ### Changed @@ -34,34 +41,7 @@ ### Misc -- Prevent ICE when parsing invalid attributes in `cfg_if!` macros [#5728](https://github.com/rust-lang/rustfmt/issues/5728) - - -## [1.5.2] 2023-01-24 - -### Fixed - -- Resolve issue when comments are found within const generic defaults in unit structs [#5668](https://github.com/rust-lang/rustfmt/issues/5668) -- Resolve issue when block comments are found within trait generics [#5358](https://github.com/rust-lang/rustfmt/issues/5358) -- Correctly handle alignment of comments containing unicode characters [#5504](https://github.com/rust-lang/rustfmt/issues/5504) -- Properly indent a single generic bound that requires being written across multiple lines [#4689](https://github.com/rust-lang/rustfmt/issues/4689) (n.b. this change is version gated and will only appear when the `version` configuration option is set to `Two`) - -### Changed - -- Renamed `fn_args_layout` configuration option to `fn_params_layout` [#4149](https://github.com/rust-lang/rustfmt/issues/4149). Note that `fn_args_layout` has only been soft deprecated: `fn_args_layout` will continue to work without issue, but rustfmt will display a warning to encourage users to switch to the new name - -### Added - -- New configuration option (`skip_macro_invocations`)[https://rust-lang.github.io/rustfmt/?version=master&search=#skip_macro_invocations] [#5347](https://github.com/rust-lang/rustfmt/pull/5347) that can be used to globally define a single enumerated list of macro calls that rustfmt should skip formatting. rustfmt [currently also supports this via a custom tool attribute](https://github.com/rust-lang/rustfmt#tips), however, these cannot be used in all contexts because [custom inner attributes are unstable](https://github.com/rust-lang/rust/issues/54726) - -### Misc - -- rustfmt now internally supports the ability to have both stable and unstable variants of a configuration option [#5378](https://github.com/rust-lang/rustfmt/issues/5378). This ability will allow the rustfmt team to make certain configuration options available on stable toolchains more quickly because we no longer have to wait for _every_ variant to be stable-ready before stabilizing _any_ variant. - -### Install/Download Options -- **rustup (nightly)** - nightly-2023-01-24 -- **GitHub Release Binaries** - [Release v1.5.2](https://github.com/rust-lang/rustfmt/releases/tag/v1.5.2) -- **Build from source** - [Tag v1.5.2](https://github.com/rust-lang/rustfmt/tree/v1.5.2), see instructions for how to [install rustfmt from source][install-from-source] +- Update various dependencies, including `syn`, `cargo_metadata`, `env_logger`, and `toml` ## [1.5.2] 2023-01-24 diff --git a/Cargo.lock b/Cargo.lock index a59e0b6819bf..999125118f8f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -545,7 +545,7 @@ dependencies = [ [[package]] name = "rustfmt-nightly" -version = "1.5.2" +version = "1.5.3" dependencies = [ "annotate-snippets", "anyhow", diff --git a/Cargo.toml b/Cargo.toml index 5f483693a116..a8928bfcd505 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rustfmt-nightly" -version = "1.5.2" +version = "1.5.3" description = "Tool to find and fix Rust formatting issues" repository = "https://github.com/rust-lang/rustfmt" readme = "README.md" From 75870c55b94b1b927445a35131462fb9db402fd3 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Sun, 12 Feb 2023 13:51:50 -0500 Subject: [PATCH 278/401] Extract logic for rewriting `else` keyword into function The function properly handles recovering comments before and after the `else` keyword, and properly handles how to write the else when users configure `control_brace_style`. --- src/expr.rs | 80 +++++++++++++++++++++++----------------- tests/source/let_else.rs | 3 -- tests/target/let_else.rs | 3 -- 3 files changed, 46 insertions(+), 40 deletions(-) delete mode 100644 tests/source/let_else.rs delete mode 100644 tests/target/let_else.rs diff --git a/src/expr.rs b/src/expr.rs index e179b4646ef2..aa81d6a93ccd 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -1004,6 +1004,46 @@ impl<'a> ControlFlow<'a> { } } +/// Rewrite the `else` keyword with surrounding comments. +/// +/// is_last: true if this is an `else` and `false` if this is an `else if` block. +/// context: rewrite context +/// span: Span between the end of the last expression and the start of the else block, +/// which contains the `else` keyword +/// shape: Shape +pub(crate) fn rewrite_else_kw_with_comments( + is_last: bool, + context: &RewriteContext<'_>, + span: Span, + shape: Shape, +) -> String { + let else_kw_lo = context.snippet_provider.span_before(span, "else"); + let before_else_kw = mk_sp(span.lo(), else_kw_lo); + let before_else_kw_comment = extract_comment(before_else_kw, context, shape); + + let else_kw_hi = context.snippet_provider.span_after(span, "else"); + let after_else_kw = mk_sp(else_kw_hi, span.hi()); + let after_else_kw_comment = extract_comment(after_else_kw, context, shape); + + let newline_sep = &shape.indent.to_string_with_newline(context.config); + let before_sep = match context.config.control_brace_style() { + ControlBraceStyle::AlwaysNextLine | ControlBraceStyle::ClosingNextLine => { + newline_sep.as_ref() + } + ControlBraceStyle::AlwaysSameLine => " ", + }; + let after_sep = match context.config.control_brace_style() { + ControlBraceStyle::AlwaysNextLine if is_last => newline_sep.as_ref(), + _ => " ", + }; + + format!( + "{}else{}", + before_else_kw_comment.as_ref().map_or(before_sep, |s| &**s), + after_else_kw_comment.as_ref().map_or(after_sep, |s| &**s), + ) +} + impl<'a> Rewrite for ControlFlow<'a> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { debug!("ControlFlow::rewrite {:?} {:?}", self, shape); @@ -1070,41 +1110,13 @@ impl<'a> Rewrite for ControlFlow<'a> { } }; - let between_kwd_else_block = mk_sp( - self.block.span.hi(), - context - .snippet_provider - .span_before(mk_sp(self.block.span.hi(), else_block.span.lo()), "else"), + let else_kw = rewrite_else_kw_with_comments( + last_in_chain, + context, + self.block.span.between(else_block.span), + shape, ); - let between_kwd_else_block_comment = - extract_comment(between_kwd_else_block, context, shape); - - let after_else = mk_sp( - context - .snippet_provider - .span_after(mk_sp(self.block.span.hi(), else_block.span.lo()), "else"), - else_block.span.lo(), - ); - let after_else_comment = extract_comment(after_else, context, shape); - - let between_sep = match context.config.control_brace_style() { - ControlBraceStyle::AlwaysNextLine | ControlBraceStyle::ClosingNextLine => { - &*alt_block_sep - } - ControlBraceStyle::AlwaysSameLine => " ", - }; - let after_sep = match context.config.control_brace_style() { - ControlBraceStyle::AlwaysNextLine if last_in_chain => &*alt_block_sep, - _ => " ", - }; - - result.push_str(&format!( - "{}else{}", - between_kwd_else_block_comment - .as_ref() - .map_or(between_sep, |s| &**s), - after_else_comment.as_ref().map_or(after_sep, |s| &**s), - )); + result.push_str(&else_kw); result.push_str(&rewrite?); } diff --git a/tests/source/let_else.rs b/tests/source/let_else.rs deleted file mode 100644 index a6e816fb524b..000000000000 --- a/tests/source/let_else.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - let Some(1) = Some(1) else { return }; -} diff --git a/tests/target/let_else.rs b/tests/target/let_else.rs deleted file mode 100644 index a6e816fb524b..000000000000 --- a/tests/target/let_else.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - let Some(1) = Some(1) else { return }; -} From 9316df0ca2e709258d0d146fb311b24222116547 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Mon, 16 Jan 2023 12:19:47 -0500 Subject: [PATCH 279/401] Initial pass at implementing let-else --- src/items.rs | 20 +++++++++++++++----- tests/source/let_else.rs | 10 ++++++++++ tests/target/let_else.rs | 12 ++++++++++++ 3 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 tests/source/let_else.rs create mode 100644 tests/target/let_else.rs diff --git a/src/items.rs b/src/items.rs index 3ecdb5b4c607..75c42605ccf0 100644 --- a/src/items.rs +++ b/src/items.rs @@ -18,7 +18,7 @@ use crate::config::lists::*; use crate::config::{BraceStyle, Config, IndentStyle, Version}; use crate::expr::{ is_empty_block, is_simple_block_stmt, rewrite_assign_rhs, rewrite_assign_rhs_with, - rewrite_assign_rhs_with_comments, RhsAssignKind, RhsTactics, + rewrite_assign_rhs_with_comments, rewrite_else_kw_with_comments, RhsAssignKind, RhsTactics, }; use crate::lists::{definitive_tactic, itemize_list, write_list, ListFormatting, Separator}; use crate::macros::{rewrite_macro, MacroPosition}; @@ -44,7 +44,7 @@ fn type_annotation_separator(config: &Config) -> &str { } // Statements of the form -// let pat: ty = init; +// let pat: ty = init; or let pat: ty = init else { .. }; impl Rewrite for ast::Local { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { debug!( @@ -54,7 +54,7 @@ impl Rewrite for ast::Local { skip_out_of_file_lines_range!(context, self.span); - if contains_skip(&self.attrs) || matches!(self.kind, ast::LocalKind::InitElse(..)) { + if contains_skip(&self.attrs) { return None; } @@ -112,7 +112,7 @@ impl Rewrite for ast::Local { result.push_str(&infix); - if let Some((init, _els)) = self.kind.init_else_opt() { + if let Some((init, else_block)) = self.kind.init_else_opt() { // 1 = trailing semicolon; let nested_shape = shape.sub_width(1)?; @@ -123,7 +123,17 @@ impl Rewrite for ast::Local { &RhsAssignKind::Expr(&init.kind, init.span), nested_shape, )?; - // todo else + + if let Some(block) = else_block { + let else_kw = rewrite_else_kw_with_comments( + true, + context, + init.span.between(block.span), + shape, + ); + result.push_str(&else_kw); + result.push_str(&block.rewrite(context, shape)?); + }; } result.push(';'); diff --git a/tests/source/let_else.rs b/tests/source/let_else.rs new file mode 100644 index 000000000000..26e0ebf07364 --- /dev/null +++ b/tests/source/let_else.rs @@ -0,0 +1,10 @@ +fn main() { + let Some(x) = opt else { return }; + + let Some(x) = opt else { return; }; + + let Some(x) = opt else { + // nope + return; + }; +} diff --git a/tests/target/let_else.rs b/tests/target/let_else.rs new file mode 100644 index 000000000000..a910f5389a3a --- /dev/null +++ b/tests/target/let_else.rs @@ -0,0 +1,12 @@ +fn main() { + let Some(x) = opt else { return }; + + let Some(x) = opt else { + return; + }; + + let Some(x) = opt else { + // nope + return; + }; +} From 8be748dbb7bd95547bc770e2b0c75b7568ba97e6 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Sun, 12 Feb 2023 14:31:38 -0500 Subject: [PATCH 280/401] Allow callers to determine if blocks can be formatted on a single line This will make it easier to format the divergent blocks of `let-else` statements since it'll be easier to prevent the block from being formatted on a single line if the preconditions aren't met. --- src/expr.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/expr.rs b/src/expr.rs index aa81d6a93ccd..eadaf2067819 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -575,6 +575,17 @@ fn rewrite_block( label: Option, context: &RewriteContext<'_>, shape: Shape, +) -> Option { + rewrite_block_inner(block, attrs, label, true, context, shape) +} + +fn rewrite_block_inner( + block: &ast::Block, + attrs: Option<&[ast::Attribute]>, + label: Option, + allow_single_line: bool, + context: &RewriteContext<'_>, + shape: Shape, ) -> Option { let prefix = block_prefix(context, block, shape)?; @@ -586,7 +597,7 @@ fn rewrite_block( let result = rewrite_block_with_visitor(context, &prefix, block, attrs, label, shape, true); if let Some(ref result_str) = result { - if result_str.lines().count() <= 3 { + if allow_single_line && result_str.lines().count() <= 3 { if let rw @ Some(_) = rewrite_single_line_block(context, &prefix, block, attrs, label, shape) { From 7a3e4fca4003ab3f0ccfddaa0a92969e676a2881 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Sun, 12 Feb 2023 14:46:06 -0500 Subject: [PATCH 281/401] Implement `let-else` rewriting in terms of rewrite_let_else_block `rewrite_let_else_block` gives us more control over allowing the `else` block to be formatted on a single line than `::rewrite`. --- src/expr.rs | 10 ++++++++++ src/items.rs | 11 +++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/expr.rs b/src/expr.rs index eadaf2067819..a4e60659a0ed 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -609,6 +609,16 @@ fn rewrite_block_inner( result } +/// Rewrite the divergent block of a `let-else` statement. +pub(crate) fn rewrite_let_else_block( + block: &ast::Block, + allow_single_line: bool, + context: &RewriteContext<'_>, + shape: Shape, +) -> Option { + rewrite_block_inner(block, None, None, allow_single_line, context, shape) +} + // Rewrite condition if the given expression has one. pub(crate) fn rewrite_cond( context: &RewriteContext<'_>, diff --git a/src/items.rs b/src/items.rs index 75c42605ccf0..4693e57b8a16 100644 --- a/src/items.rs +++ b/src/items.rs @@ -18,7 +18,8 @@ use crate::config::lists::*; use crate::config::{BraceStyle, Config, IndentStyle, Version}; use crate::expr::{ is_empty_block, is_simple_block_stmt, rewrite_assign_rhs, rewrite_assign_rhs_with, - rewrite_assign_rhs_with_comments, rewrite_else_kw_with_comments, RhsAssignKind, RhsTactics, + rewrite_assign_rhs_with_comments, rewrite_else_kw_with_comments, rewrite_let_else_block, + RhsAssignKind, RhsTactics, }; use crate::lists::{definitive_tactic, itemize_list, write_list, ListFormatting, Separator}; use crate::macros::{rewrite_macro, MacroPosition}; @@ -132,7 +133,13 @@ impl Rewrite for ast::Local { shape, ); result.push_str(&else_kw); - result.push_str(&block.rewrite(context, shape)?); + let allow_single_line = !result.contains('\n'); + result.push_str(&rewrite_let_else_block( + block, + allow_single_line, + context, + shape, + )?); }; } From 00fef2d51d417b62c2154a05237a16bed37244f5 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Sun, 12 Feb 2023 14:50:44 -0500 Subject: [PATCH 282/401] Implement wrapping rules to force `else` on a newline in `let-else` --- src/expr.rs | 4 ++++ src/items.rs | 47 +++++++++++++++++++++++++++++++++++++++- tests/source/let_else.rs | 4 ++++ tests/target/let_else.rs | 25 +++++++++++++++++++++ 4 files changed, 79 insertions(+), 1 deletion(-) diff --git a/src/expr.rs b/src/expr.rs index a4e60659a0ed..715be57ada84 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -1027,12 +1027,14 @@ impl<'a> ControlFlow<'a> { /// Rewrite the `else` keyword with surrounding comments. /// +/// force_newline_else: whether or not to rewrite the `else` keyword on a newline. /// is_last: true if this is an `else` and `false` if this is an `else if` block. /// context: rewrite context /// span: Span between the end of the last expression and the start of the else block, /// which contains the `else` keyword /// shape: Shape pub(crate) fn rewrite_else_kw_with_comments( + force_newline_else: bool, is_last: bool, context: &RewriteContext<'_>, span: Span, @@ -1048,6 +1050,7 @@ pub(crate) fn rewrite_else_kw_with_comments( let newline_sep = &shape.indent.to_string_with_newline(context.config); let before_sep = match context.config.control_brace_style() { + _ if force_newline_else => newline_sep.as_ref(), ControlBraceStyle::AlwaysNextLine | ControlBraceStyle::ClosingNextLine => { newline_sep.as_ref() } @@ -1132,6 +1135,7 @@ impl<'a> Rewrite for ControlFlow<'a> { }; let else_kw = rewrite_else_kw_with_comments( + false, last_in_chain, context, self.block.span.between(else_block.span), diff --git a/src/items.rs b/src/items.rs index 4693e57b8a16..64d2ace2bee6 100644 --- a/src/items.rs +++ b/src/items.rs @@ -126,10 +126,14 @@ impl Rewrite for ast::Local { )?; if let Some(block) = else_block { + let else_kw_span = init.span.between(block.span); + let force_newline_else = + !same_line_else_kw_and_brace(&result, context, else_kw_span, nested_shape); let else_kw = rewrite_else_kw_with_comments( + force_newline_else, true, context, - init.span.between(block.span), + else_kw_span, shape, ); result.push_str(&else_kw); @@ -148,6 +152,47 @@ impl Rewrite for ast::Local { } } +/// When the initializer expression is multi-lined, then the else keyword and opening brace of the +/// block ( i.e. "else {") should be put on the same line as the end of the initializer expression +/// if all the following are true: +/// +/// 1. The initializer expression ends with one or more closing parentheses, square brackets, +/// or braces +/// 2. There is nothing else on that line +/// 3. That line is not indented beyond the indent on the first line of the let keyword +fn same_line_else_kw_and_brace( + init_str: &str, + context: &RewriteContext<'_>, + else_kw_span: Span, + init_shape: Shape, +) -> bool { + if !init_str.contains('\n') { + // initializer expression is single lined so the "else {" should be placed on the same line + return true; + } + + // 1. The initializer expression ends with one or more `)`, `]`, `}`. + if !init_str.ends_with([')', ']', '}']) { + return false; + } + + // 2. There is nothing else on that line + // For example, there are no comments + let else_kw_snippet = context.snippet(else_kw_span).trim(); + if else_kw_snippet != "else" { + return false; + } + + // 3. The last line of the initializer expression is not indented beyond the `let` keyword + let indent = init_shape.indent.to_string(context.config); + init_str + .lines() + .last() + .expect("initializer expression is multi-lined") + .strip_prefix(indent.as_ref()) + .map_or(false, |l| !l.starts_with(char::is_whitespace)) +} + // FIXME convert to using rewrite style rather than visitor // FIXME format modules in this style #[allow(dead_code)] diff --git a/tests/source/let_else.rs b/tests/source/let_else.rs index 26e0ebf07364..932a64a050ff 100644 --- a/tests/source/let_else.rs +++ b/tests/source/let_else.rs @@ -7,4 +7,8 @@ fn main() { // nope return; }; + + let Some(x) = y.foo("abc", fairly_long_identifier, "def", "123456", "string", "cheese") else { bar() }; + + let Some(x) = abcdef().foo("abc", some_really_really_really_long_ident, "ident", "123456").bar().baz().qux("fffffffffffffffff") else { foo_bar() }; } diff --git a/tests/target/let_else.rs b/tests/target/let_else.rs index a910f5389a3a..fafbe7932ead 100644 --- a/tests/target/let_else.rs +++ b/tests/target/let_else.rs @@ -9,4 +9,29 @@ fn main() { // nope return; }; + + let Some(x) = y.foo( + "abc", + fairly_long_identifier, + "def", + "123456", + "string", + "cheese", + ) else { + bar() + }; + + let Some(x) = abcdef() + .foo( + "abc", + some_really_really_really_long_ident, + "ident", + "123456", + ) + .bar() + .baz() + .qux("fffffffffffffffff") + else { + foo_bar() + }; } From 521f86bae5242b5553705e50e095a991ff1323d1 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Wed, 22 Mar 2023 21:32:29 -0400 Subject: [PATCH 283/401] Prevent single-line `let-else` if it would exceed `max_width` --- src/items.rs | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/src/items.rs b/src/items.rs index 64d2ace2bee6..c2b9a26708d6 100644 --- a/src/items.rs +++ b/src/items.rs @@ -137,13 +137,21 @@ impl Rewrite for ast::Local { shape, ); result.push_str(&else_kw); - let allow_single_line = !result.contains('\n'); - result.push_str(&rewrite_let_else_block( - block, - allow_single_line, - context, - shape, - )?); + + let allow_single_line = allow_single_line_let_else_block(&result, block); + + let mut rw_else_block = + rewrite_let_else_block(block, allow_single_line, context, shape)?; + + if allow_single_line && !rw_else_block.contains('\n') { + let available_space = shape.width.saturating_sub(result.len()); + if available_space <= rw_else_block.len() { + // writing this on one line would exceed the available width + rw_else_block = rewrite_let_else_block(block, false, context, shape)?; + } + } + + result.push_str(&rw_else_block); }; } @@ -193,6 +201,18 @@ fn same_line_else_kw_and_brace( .map_or(false, |l| !l.starts_with(char::is_whitespace)) } +fn allow_single_line_let_else_block(result: &str, block: &ast::Block) -> bool { + if result.contains('\n') { + return false; + } + + if block.stmts.len() <= 1 { + return true; + } + + false +} + // FIXME convert to using rewrite style rather than visitor // FIXME format modules in this style #[allow(dead_code)] From e4a9892b7ac85e4c1a1ef7b63e2c9fa4a5aaa786 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Thu, 23 Mar 2023 13:14:50 -0400 Subject: [PATCH 284/401] Add additional test cases These test cases try to cover various edge cases. For example, comments around the else keyword and long, unbreakable, single-line initializer expressions, and long patterns. --- tests/source/let_else.rs | 137 ++++++++++++++++++++++++++++ tests/target/let_else.rs | 191 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 328 insertions(+) diff --git a/tests/source/let_else.rs b/tests/source/let_else.rs index 932a64a050ff..78465e64edcf 100644 --- a/tests/source/let_else.rs +++ b/tests/source/let_else.rs @@ -1,6 +1,15 @@ fn main() { + // Although this won't compile it still parses so make sure we can format empty else blocks + let Some(x) = opt else {}; + + // let-else may be formatted on a single line if they are "short" + // and only contain a single expression let Some(x) = opt else { return }; + let Some(x) = opt else { + return + }; + let Some(x) = opt else { return; }; let Some(x) = opt else { @@ -8,7 +17,135 @@ fn main() { return; }; + let Some(x) = opt else { let y = 1; return y }; + let Some(x) = y.foo("abc", fairly_long_identifier, "def", "123456", "string", "cheese") else { bar() }; let Some(x) = abcdef().foo("abc", some_really_really_really_long_ident, "ident", "123456").bar().baz().qux("fffffffffffffffff") else { foo_bar() }; } + +fn with_comments_around_else_keyword() { + let Some(x) = opt /* pre else keyword block-comment */ else { return }; + + let Some(x) = opt else /* post else keyword block-comment */ { return }; + + let Some(x) = opt /* pre else keyword block-comment */ else /* post else keyword block-comment */ { return }; + + let Some(x) = opt // pre else keyword line-comment + else { return }; + + let Some(x) = opt else + // post else keyword line-comment + { return }; + + let Some(x) = opt // pre else keyword line-comment + else + // post else keyword line-comment + { return }; + +} + +fn unbreakable_initializer_expr_pre_formatting_let_else_length_near_max_width() { + // Pre Formatting: + // The length of `(indent)let pat = init else block;` is 100 (max_width) + // Post Formatting: + // The formatting is left unchanged! + let Some(x) = some_really_really_really_really_really_really_really_long_name_A else { return }; + + // Pre Formatting: + // The length of `(indent)let pat = init else block;` is 100 (max_width) + // Post Formatting: + // The else keyword and opening brace remain on the same line as the initializer expr, + // and the else block is formatted over multiple lines because we can't fit the + // else block on the same line as the initializer expr. + let Some(x) = some_really_really_really_really_really_really_really_long_name___B else {return}; + + // Pre Formatting: + // The length of `(indent)let pat = init else block;` is 100 (max_width) + // Post Formatting: + // The else keyword and opening brace remain on the same line as the initializer expr, + // and the else block is formatted over multiple lines because we can't fit the + // else block on the same line as the initializer expr. + let Some(x) = some_really_really_really_really_long_name_____C else {some_divergent_function()}; + + // Pre Formatting: + // The length of `(indent)let pat = init else block;` is 101 (> max_width) + // Post Formatting: + // The else keyword and opening brace remain on the same line as the initializer expr, + // and the else block is formatted over multiple lines because we can't fit the + // else block on the same line as the initializer expr. + let Some(x) = some_really_really_really_really_really_really_really_long_name__D else { return }; +} + +fn unbreakable_initializer_expr_pre_formatting_length_up_to_opening_brace_near_max_width() { + // Pre Formatting: + // The length of `(indent)let pat = init else {` is 100 (max_width) + // Post Formatting: + // The else keyword and opening brace remain on the same line as the initializer expr, + // and the else block is formatted over multiple lines because we can't fit the + // else block on the same line as the initializer expr. + let Some(x) = some_really_really_really_really_really_really_really_really_long_name____E else {return}; + + // Pre Formatting: + // The length of `(indent)let pat = init else {` is 101 (> max_width) + // Post Formatting: + // The else keyword and opening brace remain on the same line as the initializer expr, + // which leads to the `{` exceeding the max width + let Some(x) = some_really_really_really_really_really_really_really_really_long_name_____F else {return}; +} + +fn unbreakable_initializer_expr_pre_formatting_length_through_initializer_expr_near_max_width() { + // Pre Formatting: + // The length of `(indent)let pat = init` is 99 (< max_width) + // Post Formatting: + // The else keyword and opening brace remain on the same line as the initializer expr, + // which leads to the `else {` exceeding the max width + let Some(x) = some_really_really_really_really_really_really_really_really_really_long_name___G else {return}; + + // Pre Formatting: + // The length of `(indent)let pat = init` is 100 (max_width) + // Post Formatting: + // Break after the `=` and put the initializer expr on it's own line. + // Because the initializer expr is multi-lined the else is placed on it's own line. + let Some(x) = some_really_really_really_really_really_really_really_really_really_long_name____H else {return}; + + // Pre Formatting: + // The length of `(indent)let pat = init` is 109 (> max_width) + // Post Formatting: + // Break after the `=` and put the initializer expr on it's own line. + // Because the initializer expr is multi-lined the else is placed on it's own line. + // The initializer expr has a length of 91, which when indented on the next line + // The `(indent)init` line has a lengh of 99. This is the max length that the `init` can be + // before we start running into max_width issues. I suspect this is becuase the shape is + // accounting for the `;` at the end of the `let-else` statement. + let Some(x) = some_really_really_really_really_really_really_really_really_really_really_long_name______I else {return}; + + // Pre Formatting: + // The length of `(indent)let pat = init` is 110 (> max_width) + // Post Formatting: + // Max length issues prevent us from formatting. + // The initializer expr has a length of 92, which if it would be indented on the next line + // the `(indent)init` line has a lengh of 100 which == max_width of 100. + // One might expect formatting to succeed, but I suspect the reason we hit max_width issues is + // because the Shape is accounting for the `;` at the end of the `let-else` statement. + let Some(x) = some_really_really_really_really_really_really_really_really_really_really_really_long_nameJ else {return}; +} + +fn long_patterns() { + let Foo {x: Bar(..), y: FooBar(..), z: Baz(..)} = opt else { + return; + }; + + // with version=One we don't wrap long array patterns + let [aaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbb, cccccccccccccccccc, dddddddddddddddddd] = opt else { + return; + }; + + let ("aaaaaaaaaaaaaaaaaaa" | "bbbbbbbbbbbbbbbbb" | "cccccccccccccccccccccccc" | "dddddddddddddddd" | "eeeeeeeeeeeeeeee") = opt else { + return; + }; + + let Some(Ok((Message::ChangeColor(super::color::Color::Rgb(r, g, b)), Point { x, y, z }))) = opt else { + return; + }; +} diff --git a/tests/target/let_else.rs b/tests/target/let_else.rs index fafbe7932ead..5ada7d446579 100644 --- a/tests/target/let_else.rs +++ b/tests/target/let_else.rs @@ -1,4 +1,11 @@ fn main() { + // Although this won't compile it still parses so make sure we can format empty else blocks + let Some(x) = opt else {}; + + // let-else may be formatted on a single line if they are "short" + // and only contain a single expression + let Some(x) = opt else { return }; + let Some(x) = opt else { return }; let Some(x) = opt else { @@ -10,6 +17,11 @@ fn main() { return; }; + let Some(x) = opt else { + let y = 1; + return y; + }; + let Some(x) = y.foo( "abc", fairly_long_identifier, @@ -35,3 +47,182 @@ fn main() { foo_bar() }; } + +fn with_comments_around_else_keyword() { + let Some(x) = opt + /* pre else keyword block-comment */ + else { + return; + }; + + let Some(x) = opt else + /* post else keyword block-comment */ + { + return; + }; + + let Some(x) = opt + /* pre else keyword block-comment */ + else + /* post else keyword block-comment */ + { + return; + }; + + let Some(x) = opt + // pre else keyword line-comment + else { + return; + }; + + let Some(x) = opt else + // post else keyword line-comment + { + return; + }; + + let Some(x) = opt + // pre else keyword line-comment + else + // post else keyword line-comment + { + return; + }; +} + +fn unbreakable_initializer_expr_pre_formatting_let_else_length_near_max_width() { + // Pre Formatting: + // The length of `(indent)let pat = init else block;` is 100 (max_width) + // Post Formatting: + // The formatting is left unchanged! + let Some(x) = some_really_really_really_really_really_really_really_long_name_A else { return }; + + // Pre Formatting: + // The length of `(indent)let pat = init else block;` is 100 (max_width) + // Post Formatting: + // The else keyword and opening brace remain on the same line as the initializer expr, + // and the else block is formatted over multiple lines because we can't fit the + // else block on the same line as the initializer expr. + let Some(x) = some_really_really_really_really_really_really_really_long_name___B else { + return; + }; + + // Pre Formatting: + // The length of `(indent)let pat = init else block;` is 100 (max_width) + // Post Formatting: + // The else keyword and opening brace remain on the same line as the initializer expr, + // and the else block is formatted over multiple lines because we can't fit the + // else block on the same line as the initializer expr. + let Some(x) = some_really_really_really_really_long_name_____C else { + some_divergent_function() + }; + + // Pre Formatting: + // The length of `(indent)let pat = init else block;` is 101 (> max_width) + // Post Formatting: + // The else keyword and opening brace remain on the same line as the initializer expr, + // and the else block is formatted over multiple lines because we can't fit the + // else block on the same line as the initializer expr. + let Some(x) = some_really_really_really_really_really_really_really_long_name__D else { + return; + }; +} + +fn unbreakable_initializer_expr_pre_formatting_length_up_to_opening_brace_near_max_width() { + // Pre Formatting: + // The length of `(indent)let pat = init else {` is 100 (max_width) + // Post Formatting: + // The else keyword and opening brace remain on the same line as the initializer expr, + // and the else block is formatted over multiple lines because we can't fit the + // else block on the same line as the initializer expr. + let Some(x) = some_really_really_really_really_really_really_really_really_long_name____E else { + return; + }; + + // Pre Formatting: + // The length of `(indent)let pat = init else {` is 101 (> max_width) + // Post Formatting: + // The else keyword and opening brace remain on the same line as the initializer expr, + // which leads to the `{` exceeding the max width + let Some(x) = some_really_really_really_really_really_really_really_really_long_name_____F else { + return; + }; +} + +fn unbreakable_initializer_expr_pre_formatting_length_through_initializer_expr_near_max_width() { + // Pre Formatting: + // The length of `(indent)let pat = init` is 99 (< max_width) + // Post Formatting: + // The else keyword and opening brace remain on the same line as the initializer expr, + // which leads to the `else {` exceeding the max width + let Some(x) = some_really_really_really_really_really_really_really_really_really_long_name___G else { + return; + }; + + // Pre Formatting: + // The length of `(indent)let pat = init` is 100 (max_width) + // Post Formatting: + // Break after the `=` and put the initializer expr on it's own line. + // Because the initializer expr is multi-lined the else is placed on it's own line. + let Some(x) = + some_really_really_really_really_really_really_really_really_really_long_name____H + else { + return; + }; + + // Pre Formatting: + // The length of `(indent)let pat = init` is 109 (> max_width) + // Post Formatting: + // Break after the `=` and put the initializer expr on it's own line. + // Because the initializer expr is multi-lined the else is placed on it's own line. + // The initializer expr has a length of 91, which when indented on the next line + // The `(indent)init` line has a lengh of 99. This is the max length that the `init` can be + // before we start running into max_width issues. I suspect this is becuase the shape is + // accounting for the `;` at the end of the `let-else` statement. + let Some(x) = + some_really_really_really_really_really_really_really_really_really_really_long_name______I + else { + return; + }; + + // Pre Formatting: + // The length of `(indent)let pat = init` is 110 (> max_width) + // Post Formatting: + // Max length issues prevent us from formatting. + // The initializer expr has a length of 92, which if it would be indented on the next line + // the `(indent)init` line has a lengh of 100 which == max_width of 100. + // One might expect formatting to succeed, but I suspect the reason we hit max_width issues is + // because the Shape is accounting for the `;` at the end of the `let-else` statement. + let Some(x) = some_really_really_really_really_really_really_really_really_really_really_really_long_nameJ else {return}; +} + +fn long_patterns() { + let Foo { + x: Bar(..), + y: FooBar(..), + z: Baz(..), + } = opt + else { + return; + }; + + // with version=One we don't wrap long array patterns + let [aaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbb, cccccccccccccccccc, dddddddddddddddddd] = opt else { + return; + }; + + let ("aaaaaaaaaaaaaaaaaaa" + | "bbbbbbbbbbbbbbbbb" + | "cccccccccccccccccccccccc" + | "dddddddddddddddd" + | "eeeeeeeeeeeeeeee") = opt + else { + return; + }; + + let Some(Ok((Message::ChangeColor(super::color::Color::Rgb(r, g, b)), Point { x, y, z }))) = + opt + else { + return; + }; +} From 9386b32f5a72bb276a97461d33aab415314201d8 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Sat, 1 Apr 2023 11:02:27 -0400 Subject: [PATCH 285/401] wrap `else {` for long, single-lined initializer expressions This helps to prevent max width errors. --- src/items.rs | 6 ++++-- tests/source/let_else.rs | 12 ++++++------ tests/target/let_else.rs | 18 ++++++++++-------- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/items.rs b/src/items.rs index c2b9a26708d6..97a76f6bb321 100644 --- a/src/items.rs +++ b/src/items.rs @@ -175,8 +175,10 @@ fn same_line_else_kw_and_brace( init_shape: Shape, ) -> bool { if !init_str.contains('\n') { - // initializer expression is single lined so the "else {" should be placed on the same line - return true; + // initializer expression is single lined. The "else {" can only be placed on the same line + // as the initializer expression if there is enough room for it. + // 7 = ` else {` + return init_shape.width.saturating_sub(init_str.len()) >= 7; } // 1. The initializer expression ends with one or more `)`, `]`, `}`. diff --git a/tests/source/let_else.rs b/tests/source/let_else.rs index 78465e64edcf..9c02117c6e88 100644 --- a/tests/source/let_else.rs +++ b/tests/source/let_else.rs @@ -79,18 +79,18 @@ fn unbreakable_initializer_expr_pre_formatting_let_else_length_near_max_width() fn unbreakable_initializer_expr_pre_formatting_length_up_to_opening_brace_near_max_width() { // Pre Formatting: - // The length of `(indent)let pat = init else {` is 100 (max_width) + // The length of `(indent)let pat = init else {` is 99 (< max_width) // Post Formatting: // The else keyword and opening brace remain on the same line as the initializer expr, // and the else block is formatted over multiple lines because we can't fit the // else block on the same line as the initializer expr. - let Some(x) = some_really_really_really_really_really_really_really_really_long_name____E else {return}; + let Some(x) = some_really_really_really_really_really_really_really_really_long_name___E else {return}; // Pre Formatting: // The length of `(indent)let pat = init else {` is 101 (> max_width) // Post Formatting: - // The else keyword and opening brace remain on the same line as the initializer expr, - // which leads to the `{` exceeding the max width + // The else keyword and opening brace cannot fit on the same line as the initializer expr. + // They are formatted on the next line. let Some(x) = some_really_really_really_really_really_really_really_really_long_name_____F else {return}; } @@ -98,8 +98,8 @@ fn unbreakable_initializer_expr_pre_formatting_length_through_initializer_expr_n // Pre Formatting: // The length of `(indent)let pat = init` is 99 (< max_width) // Post Formatting: - // The else keyword and opening brace remain on the same line as the initializer expr, - // which leads to the `else {` exceeding the max width + // The else keyword and opening brace cannot fit on the same line as the initializer expr. + // They are formatted on the next line. let Some(x) = some_really_really_really_really_really_really_really_really_really_long_name___G else {return}; // Pre Formatting: diff --git a/tests/target/let_else.rs b/tests/target/let_else.rs index 5ada7d446579..88115d129aad 100644 --- a/tests/target/let_else.rs +++ b/tests/target/let_else.rs @@ -130,21 +130,22 @@ fn unbreakable_initializer_expr_pre_formatting_let_else_length_near_max_width() fn unbreakable_initializer_expr_pre_formatting_length_up_to_opening_brace_near_max_width() { // Pre Formatting: - // The length of `(indent)let pat = init else {` is 100 (max_width) + // The length of `(indent)let pat = init else {` is 99 (< max_width) // Post Formatting: // The else keyword and opening brace remain on the same line as the initializer expr, // and the else block is formatted over multiple lines because we can't fit the // else block on the same line as the initializer expr. - let Some(x) = some_really_really_really_really_really_really_really_really_long_name____E else { + let Some(x) = some_really_really_really_really_really_really_really_really_long_name___E else { return; }; // Pre Formatting: // The length of `(indent)let pat = init else {` is 101 (> max_width) // Post Formatting: - // The else keyword and opening brace remain on the same line as the initializer expr, - // which leads to the `{` exceeding the max width - let Some(x) = some_really_really_really_really_really_really_really_really_long_name_____F else { + // The else keyword and opening brace cannot fit on the same line as the initializer expr. + // They are formatted on the next line. + let Some(x) = some_really_really_really_really_really_really_really_really_long_name_____F + else { return; }; } @@ -153,9 +154,10 @@ fn unbreakable_initializer_expr_pre_formatting_length_through_initializer_expr_n // Pre Formatting: // The length of `(indent)let pat = init` is 99 (< max_width) // Post Formatting: - // The else keyword and opening brace remain on the same line as the initializer expr, - // which leads to the `else {` exceeding the max width - let Some(x) = some_really_really_really_really_really_really_really_really_really_long_name___G else { + // The else keyword and opening brace cannot fit on the same line as the initializer expr. + // They are formatted on the next line. + let Some(x) = some_really_really_really_really_really_really_really_really_really_long_name___G + else { return; }; From fe8b72d98e58e05420be9c1227ebdbe7a742dcf9 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Sun, 9 Apr 2023 02:13:11 -0400 Subject: [PATCH 286/401] implement single_line_let_else_max_width This allows users to configure the maximum length of a single line `let-else` statements. `let-else` statements that otherwise meet the requirements to be formatted on a single line will have their divergent `else` block formatted over multiple lines if they exceed this length. **Note**: `single_line_let_else_max_widt` will be introduced as a stable configuration option. --- Configurations.md | 77 +++++++++++++++++++ src/config/config_type.rs | 10 +++ src/config/mod.rs | 7 ++ src/config/options.rs | 6 ++ src/items.rs | 27 +++++-- .../single_line_let_else_max_width/100.rs | 40 ++++++++++ .../single_line_let_else_max_width/50.rs | 40 ++++++++++ .../single_line_let_else_max_width/zero.rs | 40 ++++++++++ .../configs/use_small_heuristics/default.rs | 10 +++ .../configs/use_small_heuristics/max.rs | 10 +++ .../configs/use_small_heuristics/off.rs | 10 +++ tests/source/let_else.rs | 11 +++ .../single_line_let_else_max_width/100.rs | 59 ++++++++++++++ .../single_line_let_else_max_width/50.rs | 61 +++++++++++++++ .../single_line_let_else_max_width/zero.rs | 65 ++++++++++++++++ .../configs/use_small_heuristics/default.rs | 12 +++ .../configs/use_small_heuristics/max.rs | 10 +++ .../configs/use_small_heuristics/off.rs | 16 ++++ tests/target/let_else.rs | 24 ++++++ 19 files changed, 528 insertions(+), 7 deletions(-) create mode 100644 tests/source/configs/single_line_let_else_max_width/100.rs create mode 100644 tests/source/configs/single_line_let_else_max_width/50.rs create mode 100644 tests/source/configs/single_line_let_else_max_width/zero.rs create mode 100644 tests/target/configs/single_line_let_else_max_width/100.rs create mode 100644 tests/target/configs/single_line_let_else_max_width/50.rs create mode 100644 tests/target/configs/single_line_let_else_max_width/zero.rs diff --git a/Configurations.md b/Configurations.md index ac638ff91e6d..ac5747800b25 100644 --- a/Configurations.md +++ b/Configurations.md @@ -2392,6 +2392,78 @@ By default this option is set as a percentage of [`max_width`](#max_width) provi See also [`max_width`](#max_width) and [`use_small_heuristics`](#use_small_heuristics) +## `single_line_let_else_max_width` + +Maximum line length for single line let-else statements. +See the [let-else statement section of the Rust Style Guide](https://github.com/rust-lang/rust/blob/master/src/doc/style-guide/src/statements.md#else-blocks-let-else-statements) for more details on when a let-else statement may be written on a single line. +A value of `0` (zero) means the divergent `else` block will always be formatted over multiple lines. +Note this occurs when `use_small_heuristics` is set to `Off`. + +By default this option is set as a percentage of [`max_width`](#max_width) provided by [`use_small_heuristics`](#use_small_heuristics), but a value set directly for `single_line_let_else_max_width` will take precedence. + +- **Default value**: `50` +- **Possible values**: any positive integer that is less than or equal to the value specified for [`max_width`](#max_width) +- **Stable**: Yes + +#### `50` (default): + +```rust +fn main() { + let Some(w) = opt else { return Ok(()) }; + + let Some(x) = opt else { return }; + + let Some(y) = opt else { + return; + }; + + let Some(z) = some_very_very_very_very_long_name else { + return; + }; +} +``` + +#### `0`: + +```rust +fn main() { + let Some(w) = opt else { + return Ok(()); + }; + + let Some(x) = opt else { + return; + }; + + let Some(y) = opt else { + return; + }; + + let Some(z) = some_very_very_very_very_long_name else { + return; + }; +} +``` + +#### `100`: + +```rust +fn main() { + let Some(w) = opt else { return Ok(()) }; + + let Some(x) = opt else { return }; + + let Some(y) = opt else { + return; + }; + + let Some(z) = some_very_very_very_very_long_name else { return }; +} +``` + +See also [`max_width`](#max_width) and [`use_small_heuristics`](#use_small_heuristics) + + ## `space_after_colon` Leave a space after the colon. @@ -2804,6 +2876,7 @@ The ratios are: * [`array_width`](#array_width) - `60%` * [`chain_width`](#chain_width) - `60%` * [`single_line_if_else_max_width`](#single_line_if_else_max_width) - `50%` +* [`single_line_let_else_max_width`](#single_line_let_else_max_width) - `50%` For example when `max_width` is set to `100`, the width settings are: * `fn_call_width=60` @@ -2813,6 +2886,7 @@ For example when `max_width` is set to `100`, the width settings are: * `array_width=60` * `chain_width=60` * `single_line_if_else_max_width=50` +* `single_line_let_else_max_width=50` and when `max_width` is set to `200`: * `fn_call_width=120` @@ -2822,6 +2896,7 @@ and when `max_width` is set to `200`: * `array_width=120` * `chain_width=120` * `single_line_if_else_max_width=100` +* `single_line_let_else_max_width=100` ```rust enum Lorem { @@ -2891,6 +2966,7 @@ So if `max_width` is set to `200`, then all the width settings are also set to ` * `array_width=200` * `chain_width=200` * `single_line_if_else_max_width=200` +* `single_line_let_else_max_width=200` ```rust enum Lorem { @@ -2918,6 +2994,7 @@ See also: * [`array_width`](#array_width) * [`chain_width`](#chain_width) * [`single_line_if_else_max_width`](#single_line_if_else_max_width) +* [`single_line_let_else_max_width`](#single_line_let_else_max_width) ## `use_try_shorthand` diff --git a/src/config/config_type.rs b/src/config/config_type.rs index 54ca7676dfc8..c836b4bbb789 100644 --- a/src/config/config_type.rs +++ b/src/config/config_type.rs @@ -121,6 +121,7 @@ macro_rules! create_config { | "use_small_heuristics" | "fn_call_width" | "single_line_if_else_max_width" + | "single_line_let_else_max_width" | "attr_fn_like_width" | "struct_lit_width" | "struct_variant_width" @@ -269,6 +270,7 @@ macro_rules! create_config { | "use_small_heuristics" | "fn_call_width" | "single_line_if_else_max_width" + | "single_line_let_else_max_width" | "attr_fn_like_width" | "struct_lit_width" | "struct_variant_width" @@ -407,6 +409,14 @@ macro_rules! create_config { "single_line_if_else_max_width", ); self.single_line_if_else_max_width.2 = single_line_if_else_max_width; + + let single_line_let_else_max_width = get_width_value( + self.was_set().single_line_let_else_max_width(), + self.single_line_let_else_max_width.2, + heuristics.single_line_let_else_max_width, + "single_line_let_else_max_width", + ); + self.single_line_let_else_max_width.2 = single_line_let_else_max_width; } fn set_heuristics(&mut self) { diff --git a/src/config/mod.rs b/src/config/mod.rs index 14f27f3f8b69..6f41b299e87d 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -58,6 +58,9 @@ create_config! { chain_width: usize, 60, true, "Maximum length of a chain to fit on a single line."; single_line_if_else_max_width: usize, 50, true, "Maximum line length for single line if-else \ expressions. A value of zero means always break if-else expressions."; + single_line_let_else_max_width: usize, 50, true, "Maximum line length for single line \ + let-else statements. A value of zero means always format the divergent `else` block \ + over multiple lines."; // Comments. macros, and strings wrap_comments: bool, false, false, "Break comments to fit on the line"; @@ -473,6 +476,9 @@ mod test { chain_width: usize, 60, true, "Maximum length of a chain to fit on a single line."; single_line_if_else_max_width: usize, 50, true, "Maximum line length for single \ line if-else expressions. A value of zero means always break if-else expressions."; + single_line_let_else_max_width: usize, 50, false, "Maximum line length for single \ + line let-else statements. A value of zero means always format the divergent \ + `else` block over multiple lines."; // Options that are used by the tests stable_option: bool, false, true, "A stable option"; @@ -619,6 +625,7 @@ struct_variant_width = 35 array_width = 60 chain_width = 60 single_line_if_else_max_width = 50 +single_line_let_else_max_width = 50 wrap_comments = false format_code_in_doc_comments = false doc_comment_code_block_width = 100 diff --git a/src/config/options.rs b/src/config/options.rs index 408017d2432c..3aa1a4de99d6 100644 --- a/src/config/options.rs +++ b/src/config/options.rs @@ -236,6 +236,9 @@ pub struct WidthHeuristics { // Maximum line length for single line if-else expressions. A value // of zero means always break if-else expressions. pub(crate) single_line_if_else_max_width: usize, + // Maximum line length for single line let-else statements. A value of zero means + // always format the divergent `else` block over multiple lines. + pub(crate) single_line_let_else_max_width: usize, } impl fmt::Display for WidthHeuristics { @@ -255,6 +258,7 @@ impl WidthHeuristics { array_width: usize::max_value(), chain_width: usize::max_value(), single_line_if_else_max_width: 0, + single_line_let_else_max_width: 0, } } @@ -267,6 +271,7 @@ impl WidthHeuristics { array_width: max_width, chain_width: max_width, single_line_if_else_max_width: max_width, + single_line_let_else_max_width: max_width, } } @@ -288,6 +293,7 @@ impl WidthHeuristics { array_width: (60.0 * max_width_ratio).round() as usize, chain_width: (60.0 * max_width_ratio).round() as usize, single_line_if_else_max_width: (50.0 * max_width_ratio).round() as usize, + single_line_let_else_max_width: (50.0 * max_width_ratio).round() as usize, } } } diff --git a/src/items.rs b/src/items.rs index 97a76f6bb321..157ae931a244 100644 --- a/src/items.rs +++ b/src/items.rs @@ -138,17 +138,30 @@ impl Rewrite for ast::Local { ); result.push_str(&else_kw); - let allow_single_line = allow_single_line_let_else_block(&result, block); + // At this point we've written `let {pat} = {expr} else' into the buffer, and we + // want to calculate up front if there's room to write the divergent block on the + // same line. The available space varies based on indentation so we clamp the width + // on the smaller of `shape.width` and `single_line_let_else_max_width`. + let max_width = + std::cmp::min(shape.width, context.config.single_line_let_else_max_width()); + + // If available_space hits zero we know for sure this will be a multi-lined block + let available_space = max_width.saturating_sub(result.len()); + + let allow_single_line = !force_newline_else + && available_space > 0 + && allow_single_line_let_else_block(&result, block); let mut rw_else_block = rewrite_let_else_block(block, allow_single_line, context, shape)?; - if allow_single_line && !rw_else_block.contains('\n') { - let available_space = shape.width.saturating_sub(result.len()); - if available_space <= rw_else_block.len() { - // writing this on one line would exceed the available width - rw_else_block = rewrite_let_else_block(block, false, context, shape)?; - } + let single_line_else = !rw_else_block.contains('\n'); + let else_block_exceeds_width = available_space <= rw_else_block.len(); + + if allow_single_line && single_line_else && else_block_exceeds_width { + // writing this on one line would exceed the available width + // so rewrite the else block over multiple lines. + rw_else_block = rewrite_let_else_block(block, false, context, shape)?; } result.push_str(&rw_else_block); diff --git a/tests/source/configs/single_line_let_else_max_width/100.rs b/tests/source/configs/single_line_let_else_max_width/100.rs new file mode 100644 index 000000000000..a73c9084bf2c --- /dev/null +++ b/tests/source/configs/single_line_let_else_max_width/100.rs @@ -0,0 +1,40 @@ +// rustfmt-single_line_let_else_max_width: 100 + +fn main() { + let Some(a) = opt else {}; + + let Some(b) = opt else { return }; + + let Some(c) = opt else { + return + }; + + let Some(c) = opt else { + // a comment should always force the block to be multi-lined + return + }; + + let Some(c) = opt else { /* a comment should always force the block to be multi-lined */ return }; + + let Some(d) = some_very_very_very_very_long_name else { return }; + + let Expr::Slice(ast::ExprSlice { lower, upper, step, range: _ }) = slice.as_ref() else { + return + }; + + let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true)? else { + return Ok(None) + }; + + let Some(doc_attr) = variant.attrs.iter().find(|attr| attr.path().is_ident("doc")) else { + return Err(Error::new(variant.span(), r#"expected a doc comment"#)) + }; + + let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true) else { + return Ok(None) + }; + + let Stmt::Expr(Expr::Call(ExprCall { args: some_args, .. }), _) = last_stmt else { + return Err(Error::new(last_stmt.span(), "expected last expression to be `Some(match (..) { .. })`")) + }; +} diff --git a/tests/source/configs/single_line_let_else_max_width/50.rs b/tests/source/configs/single_line_let_else_max_width/50.rs new file mode 100644 index 000000000000..87d0583c5520 --- /dev/null +++ b/tests/source/configs/single_line_let_else_max_width/50.rs @@ -0,0 +1,40 @@ +// rustfmt-single_line_let_else_max_width: 50 + +fn main() { + let Some(a) = opt else {}; + + let Some(b) = opt else { return }; + + let Some(c) = opt else { + return + }; + + let Some(c) = opt else { + // a comment should always force the block to be multi-lined + return + }; + + let Some(c) = opt else { /* a comment should always force the block to be multi-lined */ return }; + + let Some(d) = some_very_very_very_very_long_name else { return }; + + let Expr::Slice(ast::ExprSlice { lower, upper, step, range: _ }) = slice.as_ref() else { + return + }; + + let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true)? else { + return Ok(None) + }; + + let Some(doc_attr) = variant.attrs.iter().find(|attr| attr.path().is_ident("doc")) else { + return Err(Error::new(variant.span(), r#"expected a doc comment"#)) + }; + + let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true) else { + return Ok(None) + }; + + let Stmt::Expr(Expr::Call(ExprCall { args: some_args, .. }), _) = last_stmt else { + return Err(Error::new(last_stmt.span(), "expected last expression to be `Some(match (..) { .. })`")) + }; +} diff --git a/tests/source/configs/single_line_let_else_max_width/zero.rs b/tests/source/configs/single_line_let_else_max_width/zero.rs new file mode 100644 index 000000000000..afb9e5033073 --- /dev/null +++ b/tests/source/configs/single_line_let_else_max_width/zero.rs @@ -0,0 +1,40 @@ +// rustfmt-single_line_let_else_max_width: 0 + +fn main() { + let Some(a) = opt else {}; + + let Some(b) = opt else { return }; + + let Some(c) = opt else { + return + }; + + let Some(c) = opt else { + // a comment should always force the block to be multi-lined + return + }; + + let Some(c) = opt else { /* a comment should always force the block to be multi-lined */ return }; + + let Some(d) = some_very_very_very_very_long_name else { return }; + + let Expr::Slice(ast::ExprSlice { lower, upper, step, range: _ }) = slice.as_ref() else { + return + }; + + let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true)? else { + return Ok(None) + }; + + let Some(doc_attr) = variant.attrs.iter().find(|attr| attr.path().is_ident("doc")) else { + return Err(Error::new(variant.span(), r#"expected a doc comment"#)) + }; + + let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true) else { + return Ok(None) + }; + + let Stmt::Expr(Expr::Call(ExprCall { args: some_args, .. }), _) = last_stmt else { + return Err(Error::new(last_stmt.span(), "expected last expression to be `Some(match (..) { .. })`")) + }; +} diff --git a/tests/source/configs/use_small_heuristics/default.rs b/tests/source/configs/use_small_heuristics/default.rs index 68bc40271a1d..95238c548446 100644 --- a/tests/source/configs/use_small_heuristics/default.rs +++ b/tests/source/configs/use_small_heuristics/default.rs @@ -23,3 +23,13 @@ fn main() { sit }; } + +fn format_let_else() { + let Some(a) = opt else {}; + + let Some(b) = opt else { return }; + + let Some(c) = opt else { return }; + + let Some(d) = some_very_very_very_very_long_name else { return }; +} diff --git a/tests/source/configs/use_small_heuristics/max.rs b/tests/source/configs/use_small_heuristics/max.rs index 8d30932e2c24..b79302e22ab2 100644 --- a/tests/source/configs/use_small_heuristics/max.rs +++ b/tests/source/configs/use_small_heuristics/max.rs @@ -23,3 +23,13 @@ fn main() { sit }; } + +fn format_let_else() { + let Some(a) = opt else {}; + + let Some(b) = opt else { return }; + + let Some(c) = opt else { return }; + + let Some(d) = some_very_very_very_very_long_name else { return }; +} diff --git a/tests/source/configs/use_small_heuristics/off.rs b/tests/source/configs/use_small_heuristics/off.rs index f76392d2404b..80bcdd898968 100644 --- a/tests/source/configs/use_small_heuristics/off.rs +++ b/tests/source/configs/use_small_heuristics/off.rs @@ -23,3 +23,13 @@ fn main() { sit }; } + +fn format_let_else() { + let Some(a) = opt else {}; + + let Some(b) = opt else { return }; + + let Some(c) = opt else { return }; + + let Some(d) = some_very_very_very_very_long_name else { return }; +} diff --git a/tests/source/let_else.rs b/tests/source/let_else.rs index 9c02117c6e88..85b3604ad3c7 100644 --- a/tests/source/let_else.rs +++ b/tests/source/let_else.rs @@ -1,3 +1,5 @@ +// rustfmt-single_line_let_else_max_width: 100 + fn main() { // Although this won't compile it still parses so make sure we can format empty else blocks let Some(x) = opt else {}; @@ -149,3 +151,12 @@ fn long_patterns() { return; }; } + +fn with_trailing_try_operator() { + // Currently the trailing ? forces the else on the next line + // This may be revisited in style edition 2024 + let Some(next_bucket) = ranking_rules[cur_ranking_rule_index].next_bucket(ctx, logger, &ranking_rule_universes[cur_ranking_rule_index])? else { return }; + + // Maybe this is a workaround? + let Ok(Some(next_bucket)) = ranking_rules[cur_ranking_rule_index].next_bucket(ctx, logger, &ranking_rule_universes[cur_ranking_rule_index]) else { return }; +} diff --git a/tests/target/configs/single_line_let_else_max_width/100.rs b/tests/target/configs/single_line_let_else_max_width/100.rs new file mode 100644 index 000000000000..2310ff8a2282 --- /dev/null +++ b/tests/target/configs/single_line_let_else_max_width/100.rs @@ -0,0 +1,59 @@ +// rustfmt-single_line_let_else_max_width: 100 + +fn main() { + let Some(a) = opt else {}; + + let Some(b) = opt else { return }; + + let Some(c) = opt else { return }; + + let Some(c) = opt else { + // a comment should always force the block to be multi-lined + return; + }; + + let Some(c) = opt else { + /* a comment should always force the block to be multi-lined */ + return; + }; + + let Some(d) = some_very_very_very_very_long_name else { return }; + + let Expr::Slice(ast::ExprSlice { + lower, + upper, + step, + range: _, + }) = slice.as_ref() else { + return; + }; + + let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true)? else { + return Ok(None); + }; + + let Some(doc_attr) = variant + .attrs + .iter() + .find(|attr| attr.path().is_ident("doc")) + else { + return Err(Error::new(variant.span(), r#"expected a doc comment"#)); + }; + + let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true) else { + return Ok(None); + }; + + let Stmt::Expr( + Expr::Call(ExprCall { + args: some_args, .. + }), + _, + ) = last_stmt + else { + return Err(Error::new( + last_stmt.span(), + "expected last expression to be `Some(match (..) { .. })`", + )); + }; +} diff --git a/tests/target/configs/single_line_let_else_max_width/50.rs b/tests/target/configs/single_line_let_else_max_width/50.rs new file mode 100644 index 000000000000..df2c40d72d62 --- /dev/null +++ b/tests/target/configs/single_line_let_else_max_width/50.rs @@ -0,0 +1,61 @@ +// rustfmt-single_line_let_else_max_width: 50 + +fn main() { + let Some(a) = opt else {}; + + let Some(b) = opt else { return }; + + let Some(c) = opt else { return }; + + let Some(c) = opt else { + // a comment should always force the block to be multi-lined + return; + }; + + let Some(c) = opt else { + /* a comment should always force the block to be multi-lined */ + return; + }; + + let Some(d) = some_very_very_very_very_long_name else { + return; + }; + + let Expr::Slice(ast::ExprSlice { + lower, + upper, + step, + range: _, + }) = slice.as_ref() else { + return; + }; + + let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true)? else { + return Ok(None); + }; + + let Some(doc_attr) = variant + .attrs + .iter() + .find(|attr| attr.path().is_ident("doc")) + else { + return Err(Error::new(variant.span(), r#"expected a doc comment"#)); + }; + + let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true) else { + return Ok(None); + }; + + let Stmt::Expr( + Expr::Call(ExprCall { + args: some_args, .. + }), + _, + ) = last_stmt + else { + return Err(Error::new( + last_stmt.span(), + "expected last expression to be `Some(match (..) { .. })`", + )); + }; +} diff --git a/tests/target/configs/single_line_let_else_max_width/zero.rs b/tests/target/configs/single_line_let_else_max_width/zero.rs new file mode 100644 index 000000000000..f4d26ad3757e --- /dev/null +++ b/tests/target/configs/single_line_let_else_max_width/zero.rs @@ -0,0 +1,65 @@ +// rustfmt-single_line_let_else_max_width: 0 + +fn main() { + let Some(a) = opt else {}; + + let Some(b) = opt else { + return; + }; + + let Some(c) = opt else { + return; + }; + + let Some(c) = opt else { + // a comment should always force the block to be multi-lined + return; + }; + + let Some(c) = opt else { + /* a comment should always force the block to be multi-lined */ + return; + }; + + let Some(d) = some_very_very_very_very_long_name else { + return; + }; + + let Expr::Slice(ast::ExprSlice { + lower, + upper, + step, + range: _, + }) = slice.as_ref() else { + return; + }; + + let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true)? else { + return Ok(None); + }; + + let Some(doc_attr) = variant + .attrs + .iter() + .find(|attr| attr.path().is_ident("doc")) + else { + return Err(Error::new(variant.span(), r#"expected a doc comment"#)); + }; + + let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true) else { + return Ok(None); + }; + + let Stmt::Expr( + Expr::Call(ExprCall { + args: some_args, .. + }), + _, + ) = last_stmt + else { + return Err(Error::new( + last_stmt.span(), + "expected last expression to be `Some(match (..) { .. })`", + )); + }; +} diff --git a/tests/target/configs/use_small_heuristics/default.rs b/tests/target/configs/use_small_heuristics/default.rs index d67bd9aafaf0..ad40739233e7 100644 --- a/tests/target/configs/use_small_heuristics/default.rs +++ b/tests/target/configs/use_small_heuristics/default.rs @@ -24,3 +24,15 @@ fn main() { let lorem = if ipsum { dolor } else { sit }; } + +fn format_let_else() { + let Some(a) = opt else {}; + + let Some(b) = opt else { return }; + + let Some(c) = opt else { return }; + + let Some(d) = some_very_very_very_very_long_name else { + return; + }; +} diff --git a/tests/target/configs/use_small_heuristics/max.rs b/tests/target/configs/use_small_heuristics/max.rs index 785dfbea0143..fe57f853d9dd 100644 --- a/tests/target/configs/use_small_heuristics/max.rs +++ b/tests/target/configs/use_small_heuristics/max.rs @@ -13,3 +13,13 @@ fn main() { let lorem = if ipsum { dolor } else { sit }; } + +fn format_let_else() { + let Some(a) = opt else {}; + + let Some(b) = opt else { return }; + + let Some(c) = opt else { return }; + + let Some(d) = some_very_very_very_very_long_name else { return }; +} diff --git a/tests/target/configs/use_small_heuristics/off.rs b/tests/target/configs/use_small_heuristics/off.rs index f76392d2404b..b0b4e4ee49fe 100644 --- a/tests/target/configs/use_small_heuristics/off.rs +++ b/tests/target/configs/use_small_heuristics/off.rs @@ -23,3 +23,19 @@ fn main() { sit }; } + +fn format_let_else() { + let Some(a) = opt else {}; + + let Some(b) = opt else { + return; + }; + + let Some(c) = opt else { + return; + }; + + let Some(d) = some_very_very_very_very_long_name else { + return; + }; +} diff --git a/tests/target/let_else.rs b/tests/target/let_else.rs index 88115d129aad..6554a0961c0d 100644 --- a/tests/target/let_else.rs +++ b/tests/target/let_else.rs @@ -1,3 +1,5 @@ +// rustfmt-single_line_let_else_max_width: 100 + fn main() { // Although this won't compile it still parses so make sure we can format empty else blocks let Some(x) = opt else {}; @@ -228,3 +230,25 @@ fn long_patterns() { return; }; } + +fn with_trailing_try_operator() { + // Currently the trailing ? forces the else on the next line + // This may be revisited in style edition 2024 + let Some(next_bucket) = ranking_rules[cur_ranking_rule_index].next_bucket( + ctx, + logger, + &ranking_rule_universes[cur_ranking_rule_index], + )? + else { + return; + }; + + // Maybe this is a workaround? + let Ok(Some(next_bucket)) = ranking_rules[cur_ranking_rule_index].next_bucket( + ctx, + logger, + &ranking_rule_universes[cur_ranking_rule_index], + ) else { + return; + }; +} From 7b4e8a6d31a9b1ae404934a348f33cadf33ee764 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Fri, 23 Jun 2023 00:05:31 -0400 Subject: [PATCH 287/401] update `else_block_exceeds_width` calculation in `let-else` rewrite By reversing the logic I felt that the code became a clearer. Also, added a comment to make it clear that we need to take the trailing semicolon for the `let-else` statement into account. --- src/items.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/items.rs b/src/items.rs index 157ae931a244..fe35953715c1 100644 --- a/src/items.rs +++ b/src/items.rs @@ -156,7 +156,8 @@ impl Rewrite for ast::Local { rewrite_let_else_block(block, allow_single_line, context, shape)?; let single_line_else = !rw_else_block.contains('\n'); - let else_block_exceeds_width = available_space <= rw_else_block.len(); + // +1 for the trailing `;` + let else_block_exceeds_width = rw_else_block.len() + 1 > available_space; if allow_single_line && single_line_else && else_block_exceeds_width { // writing this on one line would exceed the available width From 1de65a2711d9274cffbd2cc0597b14327a053ebb Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Tue, 27 Jun 2023 12:30:27 -0400 Subject: [PATCH 288/401] wrap `else` to next line if `let-else` pattern is multi-lined This rule wasn't explicity stated in the style guide so it was missed, but luckily we caught it during testing. --- src/items.rs | 4 ++-- tests/target/configs/single_line_let_else_max_width/100.rs | 3 ++- tests/target/configs/single_line_let_else_max_width/50.rs | 3 ++- tests/target/configs/single_line_let_else_max_width/zero.rs | 3 ++- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/items.rs b/src/items.rs index fe35953715c1..d5bc38303e00 100644 --- a/src/items.rs +++ b/src/items.rs @@ -127,8 +127,8 @@ impl Rewrite for ast::Local { if let Some(block) = else_block { let else_kw_span = init.span.between(block.span); - let force_newline_else = - !same_line_else_kw_and_brace(&result, context, else_kw_span, nested_shape); + let force_newline_else = pat_str.contains('\n') + || !same_line_else_kw_and_brace(&result, context, else_kw_span, nested_shape); let else_kw = rewrite_else_kw_with_comments( force_newline_else, true, diff --git a/tests/target/configs/single_line_let_else_max_width/100.rs b/tests/target/configs/single_line_let_else_max_width/100.rs index 2310ff8a2282..0409124a5b08 100644 --- a/tests/target/configs/single_line_let_else_max_width/100.rs +++ b/tests/target/configs/single_line_let_else_max_width/100.rs @@ -24,7 +24,8 @@ fn main() { upper, step, range: _, - }) = slice.as_ref() else { + }) = slice.as_ref() + else { return; }; diff --git a/tests/target/configs/single_line_let_else_max_width/50.rs b/tests/target/configs/single_line_let_else_max_width/50.rs index df2c40d72d62..6afc2b6f2b06 100644 --- a/tests/target/configs/single_line_let_else_max_width/50.rs +++ b/tests/target/configs/single_line_let_else_max_width/50.rs @@ -26,7 +26,8 @@ fn main() { upper, step, range: _, - }) = slice.as_ref() else { + }) = slice.as_ref() + else { return; }; diff --git a/tests/target/configs/single_line_let_else_max_width/zero.rs b/tests/target/configs/single_line_let_else_max_width/zero.rs index f4d26ad3757e..b5fd0b9edaf9 100644 --- a/tests/target/configs/single_line_let_else_max_width/zero.rs +++ b/tests/target/configs/single_line_let_else_max_width/zero.rs @@ -30,7 +30,8 @@ fn main() { upper, step, range: _, - }) = slice.as_ref() else { + }) = slice.as_ref() + else { return; }; From aa691480c006fadc834d0c36872efe1cb994d07a Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Tue, 9 May 2023 21:29:15 +0000 Subject: [PATCH 289/401] Implement `become` expression formatting in rustfmt --- src/expr.rs | 1 + src/utils.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/expr.rs b/src/expr.rs index 715be57ada84..5b1b4fbd491c 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -232,6 +232,7 @@ pub(crate) fn format_expr( ast::ExprKind::Ret(Some(ref expr)) => { rewrite_unary_prefix(context, "return ", &**expr, shape) } + ast::ExprKind::Become(ref expr) => rewrite_unary_prefix(context, "become ", &**expr, shape), ast::ExprKind::Yeet(None) => Some("do yeet".to_owned()), ast::ExprKind::Yeet(Some(ref expr)) => { rewrite_unary_prefix(context, "do yeet ", &**expr, shape) diff --git a/src/utils.rs b/src/utils.rs index ca1716574071..890a05b8c825 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -505,6 +505,7 @@ pub(crate) fn is_block_expr(context: &RewriteContext<'_>, expr: &ast::Expr, repr | ast::ExprKind::Range(..) | ast::ExprKind::Repeat(..) | ast::ExprKind::Ret(..) + | ast::ExprKind::Become(..) | ast::ExprKind::Yeet(..) | ast::ExprKind::Tup(..) | ast::ExprKind::Type(..) From 23f48d9bb3e325eb5dcfd96b28dd1ce7e37da405 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Sat, 1 Jul 2023 01:29:50 -0500 Subject: [PATCH 290/401] deps: bump proc-macro2 and toolchain --- Cargo.lock | 4 ++-- rust-toolchain | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 999125118f8f..de044ea98447 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -481,9 +481,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" dependencies = [ "unicode-ident", ] diff --git a/rust-toolchain b/rust-toolchain index 03b909cd80c7..33ff8b03da2a 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2023-06-19" +channel = "nightly-2023-07-01" components = ["llvm-tools", "rustc-dev"] From 3045c03b223e05ed607adfe5e48c88d0b21edfd4 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Sat, 1 Jul 2023 01:43:07 -0500 Subject: [PATCH 291/401] deps: bump proc-macro2 in config --- config_proc_macro/Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config_proc_macro/Cargo.lock b/config_proc_macro/Cargo.lock index 7af746f0c965..6267958646bf 100644 --- a/config_proc_macro/Cargo.lock +++ b/config_proc_macro/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" dependencies = [ "unicode-ident", ] From dca1cf90ad6b8e45afbed2061803befbb2d159e9 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Sat, 1 Jul 2023 02:13:49 -0500 Subject: [PATCH 292/401] chore: prep v1.6.0 release --- CHANGELOG.md | 11 +++++++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d4e057223d4..fbcd0a57f4e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,17 @@ ## [Unreleased] + +## [1.6.0] 2023-07-02 + +### Added + +- Support for formatting let-else statements [#5690] +- New config option, `single_line_let_else_max_width`, that allows users to configure the maximum length of single line `let-else` statements. `let-else` statements that otherwise meet the requirements to be formatted on a single line will have their divergent`else` block formatted over multiple lines if they exceed this length [#5684] + +[#5690]: (https://github.com/rust-lang/rustfmt/pulls/5690) +[#5684]: https://github.com/rust-lang/rustfmt/issues/5684 + ## [1.5.3] 2023-06-20 ### Fixed diff --git a/Cargo.lock b/Cargo.lock index de044ea98447..bd28df7a7573 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -545,7 +545,7 @@ dependencies = [ [[package]] name = "rustfmt-nightly" -version = "1.5.3" +version = "1.6.0" dependencies = [ "annotate-snippets", "anyhow", diff --git a/Cargo.toml b/Cargo.toml index a8928bfcd505..8c312f47a28f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rustfmt-nightly" -version = "1.5.3" +version = "1.6.0" description = "Tool to find and fix Rust formatting issues" repository = "https://github.com/rust-lang/rustfmt" readme = "README.md" From 9a86da98c05a3fff9f52202c2c4de79928c240df Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Sun, 2 Jul 2023 18:59:27 -0500 Subject: [PATCH 293/401] docs: fix a few links --- CHANGELOG.md | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fbcd0a57f4e5..34fcd9a81c11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ - Support for formatting let-else statements [#5690] - New config option, `single_line_let_else_max_width`, that allows users to configure the maximum length of single line `let-else` statements. `let-else` statements that otherwise meet the requirements to be formatted on a single line will have their divergent`else` block formatted over multiple lines if they exceed this length [#5684] -[#5690]: (https://github.com/rust-lang/rustfmt/pulls/5690) +[#5690]: https://github.com/rust-lang/rustfmt/pulls/5690 [#5684]: https://github.com/rust-lang/rustfmt/issues/5684 ## [1.5.3] 2023-06-20 diff --git a/README.md b/README.md index c05184fbb04b..b68a942e4636 100644 --- a/README.md +++ b/README.md @@ -229,4 +229,4 @@ See [LICENSE-APACHE](LICENSE-APACHE) and [LICENSE-MIT](LICENSE-MIT) for details. [rust]: https://github.com/rust-lang/rust [fmt rfcs]: https://github.com/rust-dev-tools/fmt-rfcs -[style guide]: https://github.com/rust-dev-tools/fmt-rfcs/blob/master/guide/guide.md +[style guide]: https://doc.rust-lang.org/nightly/style-guide/ From 326af2bd2146e6502ea87b63f72a9de45ff38f85 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Sun, 25 Jun 2023 16:01:29 +0000 Subject: [PATCH 294/401] Switch to tracing for logging --- Cargo.lock | 165 +++++++++++++++++++++++++++++++--------- Cargo.toml | 6 +- src/bin/main.rs | 5 +- src/format-diff/main.rs | 7 +- src/git-rustfmt/main.rs | 7 +- src/test/mod.rs | 2 +- 6 files changed, 148 insertions(+), 44 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bd28df7a7573..e867f2cb840c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -261,19 +261,6 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" -[[package]] -name = "env_logger" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" -dependencies = [ - "humantime", - "is-terminal", - "log", - "regex", - "termcolor", -] - [[package]] name = "errno" version = "0.3.1" @@ -352,12 +339,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - [[package]] name = "ignore" version = "0.4.18" @@ -457,18 +438,43 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata", +] + [[package]] name = "memchr" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "once_cell" version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "packed_simd_2" version = "0.3.7" @@ -479,6 +485,12 @@ dependencies = [ "libm", ] +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + [[package]] name = "proc-macro2" version = "1.0.63" @@ -519,9 +531,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.5.5" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" +checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" dependencies = [ "aho-corasick", "memchr", @@ -529,10 +541,19 @@ dependencies = [ ] [[package]] -name = "regex-syntax" -version = "0.6.25" +name = "regex-automata" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "rustfmt-config_proc_macro" @@ -554,12 +575,10 @@ dependencies = [ "clap", "diff", "dirs", - "env_logger", "getopts", "ignore", "itertools", "lazy_static", - "log", "regex", "rustfmt-config_proc_macro", "serde", @@ -567,6 +586,8 @@ dependencies = [ "term", "thiserror", "toml", + "tracing", + "tracing-subscriber", "unicode-segmentation", "unicode-width", "unicode_categories", @@ -656,6 +677,21 @@ dependencies = [ "serde", ] +[[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + [[package]] name = "strsim" version = "0.10.0" @@ -684,15 +720,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "termcolor" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" -dependencies = [ - "winapi-util", -] - [[package]] name = "thiserror" version = "1.0.40" @@ -756,6 +783,68 @@ dependencies = [ "winnow", ] +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + [[package]] name = "unicode-ident" version = "1.0.8" @@ -786,6 +875,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "walkdir" version = "2.3.2" diff --git a/Cargo.toml b/Cargo.toml index 8c312f47a28f..f4c4bab37cb5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,18 +40,18 @@ cargo_metadata = "0.15.4" clap = { version = "4.2.1", features = ["derive"] } diff = "0.1" dirs = "4.0" -env_logger = "0.10.0" getopts = "0.2" ignore = "0.4" itertools = "0.10" lazy_static = "1.4" -log = "0.4" -regex = "1.5" +regex = "1.7" serde = { version = "1.0.160", features = ["derive"] } serde_json = "1.0" term = "0.7" thiserror = "1.0.40" toml = "0.7.4" +tracing = "0.1.37" +tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } unicode-segmentation = "1.9" unicode-width = "0.1" unicode_categories = "0.1" diff --git a/src/bin/main.rs b/src/bin/main.rs index 03b75c1b0410..2ff67d27e1b0 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -6,6 +6,7 @@ use io::Error as IoError; use thiserror::Error; use rustfmt_nightly as rustfmt; +use tracing_subscriber::EnvFilter; use std::collections::HashMap; use std::env; @@ -29,7 +30,9 @@ extern crate rustc_driver; fn main() { rustc_driver::install_ice_hook(BUG_REPORT_URL, |_| ()); - env_logger::Builder::from_env("RUSTFMT_LOG").init(); + tracing_subscriber::fmt() + .with_env_filter(EnvFilter::from_env("RUSTFMT_LOG")) + .init(); let opts = make_opts(); let exit_code = match execute(&opts) { diff --git a/src/format-diff/main.rs b/src/format-diff/main.rs index f6b739e1c2a3..db4a21ba09e9 100644 --- a/src/format-diff/main.rs +++ b/src/format-diff/main.rs @@ -5,11 +5,12 @@ #![deny(warnings)] #[macro_use] -extern crate log; +extern crate tracing; use serde::{Deserialize, Serialize}; use serde_json as json; use thiserror::Error; +use tracing_subscriber::EnvFilter; use std::collections::HashSet; use std::env; @@ -63,7 +64,9 @@ pub struct Opts { } fn main() { - env_logger::Builder::from_env("RUSTFMT_LOG").init(); + tracing_subscriber::fmt() + .with_env_filter(EnvFilter::from_env("RUSTFMT_LOG")) + .init(); let opts = Opts::parse(); if let Err(e) = run(opts) { println!("{}", e); diff --git a/src/git-rustfmt/main.rs b/src/git-rustfmt/main.rs index 579778edbe74..efeda53e7ec6 100644 --- a/src/git-rustfmt/main.rs +++ b/src/git-rustfmt/main.rs @@ -1,5 +1,5 @@ #[macro_use] -extern crate log; +extern crate tracing; use std::env; use std::io::stdout; @@ -9,6 +9,7 @@ use std::str::FromStr; use getopts::{Matches, Options}; use rustfmt_nightly as rustfmt; +use tracing_subscriber::EnvFilter; use crate::rustfmt::{load_config, CliOptions, FormatReportFormatterBuilder, Input, Session}; @@ -170,7 +171,9 @@ impl Config { } fn main() { - env_logger::Builder::from_env("RUSTFMT_LOG").init(); + tracing_subscriber::fmt() + .with_env_filter(EnvFilter::from_env("RUSTFMT_LOG")) + .init(); let opts = make_opts(); let matches = opts diff --git a/src/test/mod.rs b/src/test/mod.rs index cfad4a8ed0e3..f88ab3f194b6 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -47,7 +47,7 @@ const FILE_SKIP_LIST: &[&str] = &[ ]; fn init_log() { - let _ = env_logger::builder().is_test(true).try_init(); + let _ = tracing_subscriber::fmt().with_test_writer().try_init(); } struct TestSetting { From bb87a1b717a88d7cd0447b58bbb071f29ea8bcf5 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Wed, 21 Jun 2023 14:11:26 +0000 Subject: [PATCH 295/401] Rewrite float literals ending in dots with parens in method calls --- src/chains.rs | 74 +++++++++++++++++++++++++++++--------- src/expr.rs | 15 ++++---- tests/source/issue-5791.rs | 3 ++ tests/target/issue-5791.rs | 3 ++ 4 files changed, 71 insertions(+), 24 deletions(-) create mode 100644 tests/source/issue-5791.rs create mode 100644 tests/target/issue-5791.rs diff --git a/src/chains.rs b/src/chains.rs index 0afce7cf6596..bf09d817ed1b 100644 --- a/src/chains.rs +++ b/src/chains.rs @@ -153,7 +153,13 @@ enum CommentPosition { Top, } -// An expression plus trailing `?`s to be formatted together. +/// Information about an expression in a chain. +struct SubExpr { + expr: ast::Expr, + is_method_call_receiver: bool, +} + +/// An expression plus trailing `?`s to be formatted together. #[derive(Debug)] struct ChainItem { kind: ChainItemKind, @@ -166,7 +172,10 @@ struct ChainItem { // would remove a lot of cloning. #[derive(Debug)] enum ChainItemKind { - Parent(ast::Expr), + Parent { + expr: ast::Expr, + parens: bool, + }, MethodCall( ast::PathSegment, Vec, @@ -181,7 +190,7 @@ enum ChainItemKind { impl ChainItemKind { fn is_block_like(&self, context: &RewriteContext<'_>, reps: &str) -> bool { match self { - ChainItemKind::Parent(ref expr) => utils::is_block_expr(context, expr, reps), + ChainItemKind::Parent { expr, .. } => utils::is_block_expr(context, expr, reps), ChainItemKind::MethodCall(..) | ChainItemKind::StructField(..) | ChainItemKind::TupleField(..) @@ -199,7 +208,11 @@ impl ChainItemKind { } } - fn from_ast(context: &RewriteContext<'_>, expr: &ast::Expr) -> (ChainItemKind, Span) { + fn from_ast( + context: &RewriteContext<'_>, + expr: &ast::Expr, + is_method_call_receiver: bool, + ) -> (ChainItemKind, Span) { let (kind, span) = match expr.kind { ast::ExprKind::MethodCall(ref call) => { let types = if let Some(ref generic_args) = call.seg.args { @@ -236,7 +249,19 @@ impl ChainItemKind { let span = mk_sp(nested.span.hi(), expr.span.hi()); (ChainItemKind::Await, span) } - _ => return (ChainItemKind::Parent(expr.clone()), expr.span), + _ => { + return ( + ChainItemKind::Parent { + expr: expr.clone(), + parens: is_method_call_receiver + && matches!( + &expr.kind, + ast::ExprKind::Lit(lit) if crate::expr::lit_ends_in_dot(lit) + ), + }, + expr.span, + ); + } }; // Remove comments from the span. @@ -249,7 +274,14 @@ impl Rewrite for ChainItem { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { let shape = shape.sub_width(self.tries)?; let rewrite = match self.kind { - ChainItemKind::Parent(ref expr) => expr.rewrite(context, shape)?, + ChainItemKind::Parent { + ref expr, + parens: true, + } => crate::expr::rewrite_paren(context, &expr, shape, expr.span)?, + ChainItemKind::Parent { + ref expr, + parens: false, + } => expr.rewrite(context, shape)?, ChainItemKind::MethodCall(ref segment, ref types, ref exprs) => { Self::rewrite_method_call(segment.ident, types, exprs, self.span, context, shape)? } @@ -273,8 +305,9 @@ impl Rewrite for ChainItem { } impl ChainItem { - fn new(context: &RewriteContext<'_>, expr: &ast::Expr, tries: usize) -> ChainItem { - let (kind, span) = ChainItemKind::from_ast(context, expr); + fn new(context: &RewriteContext<'_>, expr: &SubExpr, tries: usize) -> ChainItem { + let (kind, span) = + ChainItemKind::from_ast(context, &expr.expr, expr.is_method_call_receiver); ChainItem { kind, tries, span } } @@ -327,7 +360,7 @@ impl Chain { let mut rev_children = vec![]; let mut sub_tries = 0; for subexpr in &subexpr_list { - match subexpr.kind { + match subexpr.expr.kind { ast::ExprKind::Try(_) => sub_tries += 1, _ => { rev_children.push(ChainItem::new(context, subexpr, sub_tries)); @@ -442,11 +475,14 @@ impl Chain { // Returns a Vec of the prefixes of the chain. // E.g., for input `a.b.c` we return [`a.b.c`, `a.b`, 'a'] - fn make_subexpr_list(expr: &ast::Expr, context: &RewriteContext<'_>) -> Vec { - let mut subexpr_list = vec![expr.clone()]; + fn make_subexpr_list(expr: &ast::Expr, context: &RewriteContext<'_>) -> Vec { + let mut subexpr_list = vec![SubExpr { + expr: expr.clone(), + is_method_call_receiver: false, + }]; while let Some(subexpr) = Self::pop_expr_chain(subexpr_list.last().unwrap(), context) { - subexpr_list.push(subexpr.clone()); + subexpr_list.push(subexpr); } subexpr_list @@ -454,12 +490,18 @@ impl Chain { // Returns the expression's subexpression, if it exists. When the subexpr // is a try! macro, we'll convert it to shorthand when the option is set. - fn pop_expr_chain(expr: &ast::Expr, context: &RewriteContext<'_>) -> Option { - match expr.kind { - ast::ExprKind::MethodCall(ref call) => Some(Self::convert_try(&call.receiver, context)), + fn pop_expr_chain(expr: &SubExpr, context: &RewriteContext<'_>) -> Option { + match expr.expr.kind { + ast::ExprKind::MethodCall(ref call) => Some(SubExpr { + expr: Self::convert_try(&call.receiver, context), + is_method_call_receiver: true, + }), ast::ExprKind::Field(ref subexpr, _) | ast::ExprKind::Try(ref subexpr) - | ast::ExprKind::Await(ref subexpr, _) => Some(Self::convert_try(subexpr, context)), + | ast::ExprKind::Await(ref subexpr, _) => Some(SubExpr { + expr: Self::convert_try(subexpr, context), + is_method_call_receiver: false, + }), _ => None, } } diff --git a/src/expr.rs b/src/expr.rs index 5b1b4fbd491c..41cc3651992a 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; use std::cmp::min; use itertools::Itertools; -use rustc_ast::token::{Delimiter, LitKind}; +use rustc_ast::token::{Delimiter, Lit, LitKind}; use rustc_ast::{ast, ptr, token}; use rustc_span::{BytePos, Span}; @@ -48,6 +48,10 @@ pub(crate) enum ExprType { SubExpression, } +pub(crate) fn lit_ends_in_dot(lit: &Lit) -> bool { + matches!(lit, Lit { kind: LitKind::Float, suffix: None, symbol } if symbol.as_str().ends_with('.')) +} + pub(crate) fn format_expr( expr: &ast::Expr, expr_type: ExprType, @@ -275,12 +279,7 @@ pub(crate) fn format_expr( fn needs_space_before_range(context: &RewriteContext<'_>, lhs: &ast::Expr) -> bool { match lhs.kind { - ast::ExprKind::Lit(token_lit) => match token_lit.kind { - token::LitKind::Float if token_lit.suffix.is_none() => { - context.snippet(lhs.span).ends_with('.') - } - _ => false, - }, + ast::ExprKind::Lit(token_lit) => lit_ends_in_dot(&token_lit), ast::ExprKind::Unary(_, ref expr) => needs_space_before_range(context, expr), _ => false, } @@ -1440,7 +1439,7 @@ pub(crate) fn span_ends_with_comma(context: &RewriteContext<'_>, span: Span) -> result } -fn rewrite_paren( +pub(crate) fn rewrite_paren( context: &RewriteContext<'_>, mut subexpr: &ast::Expr, shape: Shape, diff --git a/tests/source/issue-5791.rs b/tests/source/issue-5791.rs new file mode 100644 index 000000000000..40bc6daa9f91 --- /dev/null +++ b/tests/source/issue-5791.rs @@ -0,0 +1,3 @@ +pub fn main() { + 0. .to_string(); +} diff --git a/tests/target/issue-5791.rs b/tests/target/issue-5791.rs new file mode 100644 index 000000000000..3a44cf19a1b9 --- /dev/null +++ b/tests/target/issue-5791.rs @@ -0,0 +1,3 @@ +pub fn main() { + (0.).to_string(); +} From 3c3cf6192d5ba773760a97886e584de3bb089ce6 Mon Sep 17 00:00:00 2001 From: Cosmic Horror Date: Fri, 23 Jun 2023 19:05:46 -0600 Subject: [PATCH 296/401] Add failing test --- tests/target/issue-5797/retain_trailing_semicolon.rs | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 tests/target/issue-5797/retain_trailing_semicolon.rs diff --git a/tests/target/issue-5797/retain_trailing_semicolon.rs b/tests/target/issue-5797/retain_trailing_semicolon.rs new file mode 100644 index 000000000000..851073971f69 --- /dev/null +++ b/tests/target/issue-5797/retain_trailing_semicolon.rs @@ -0,0 +1,7 @@ +// rustfmt-trailing_semicolon: false + +fn foo() {} +fn main() { + return; + foo() +} From 885085474653573cb3c49ed29cd3030555bfcae8 Mon Sep 17 00:00:00 2001 From: Cosmic Horror Date: Fri, 23 Jun 2023 19:07:12 -0600 Subject: [PATCH 297/401] Don't skip semicolon if exprs follow --- src/stmt.rs | 13 ++++++++++--- src/utils.rs | 10 ++++++++-- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/stmt.rs b/src/stmt.rs index 0b3854425ea5..0148de8190b8 100644 --- a/src/stmt.rs +++ b/src/stmt.rs @@ -80,13 +80,19 @@ impl<'a> Rewrite for Stmt<'a> { } else { ExprType::Statement }; - format_stmt(context, shape, self.as_ast_node(), expr_type) + format_stmt( + context, + shape, + self.as_ast_node(), + expr_type, + Some(self.is_last_expr()), + ) } } impl Rewrite for ast::Stmt { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { - format_stmt(context, shape, self, ExprType::Statement) + format_stmt(context, shape, self, ExprType::Statement, None) } } @@ -95,13 +101,14 @@ fn format_stmt( shape: Shape, stmt: &ast::Stmt, expr_type: ExprType, + is_last_expr: Option, ) -> Option { skip_out_of_file_lines_range!(context, stmt.span()); let result = match stmt.kind { ast::StmtKind::Local(ref local) => local.rewrite(context, shape), ast::StmtKind::Expr(ref ex) | ast::StmtKind::Semi(ref ex) => { - let suffix = if semicolon_for_stmt(context, stmt) { + let suffix = if semicolon_for_stmt(context, stmt, is_last_expr) { ";" } else { "" diff --git a/src/utils.rs b/src/utils.rs index 890a05b8c825..e768ae0a1bc2 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -292,14 +292,20 @@ pub(crate) fn semicolon_for_expr(context: &RewriteContext<'_>, expr: &ast::Expr) } #[inline] -pub(crate) fn semicolon_for_stmt(context: &RewriteContext<'_>, stmt: &ast::Stmt) -> bool { +pub(crate) fn semicolon_for_stmt( + context: &RewriteContext<'_>, + stmt: &ast::Stmt, + is_last_expr: Option, +) -> bool { match stmt.kind { ast::StmtKind::Semi(ref expr) => match expr.kind { ast::ExprKind::While(..) | ast::ExprKind::Loop(..) | ast::ExprKind::ForLoop(..) => { false } ast::ExprKind::Break(..) | ast::ExprKind::Continue(..) | ast::ExprKind::Ret(..) => { - context.config.trailing_semicolon() + // The only time we can skip the semi-colon is if the config option is set to false + // **and** this is the last expr (even though any following exprs are unreachable) + context.config.trailing_semicolon() || !is_last_expr.unwrap_or(false) } _ => true, }, From d9a09925d7ecfb46fb6a218dbf5479b51acc67c6 Mon Sep 17 00:00:00 2001 From: Cosmic Horror Date: Sat, 24 Jun 2023 12:35:20 -0600 Subject: [PATCH 298/401] Switch `ast::Stmt` rewriting use to `stmt::Stmt::from_simple_block` --- src/expr.rs | 23 ++++++++++++----------- src/stmt.rs | 27 ++++++++++++++++++--------- src/utils.rs | 4 ++-- 3 files changed, 32 insertions(+), 22 deletions(-) diff --git a/src/expr.rs b/src/expr.rs index 41cc3651992a..9a605d6427e8 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -26,6 +26,7 @@ use crate::rewrite::{Rewrite, RewriteContext}; use crate::shape::{Indent, Shape}; use crate::source_map::{LineRangeUtils, SpanUtils}; use crate::spanned::Spanned; +use crate::stmt; use crate::string::{rewrite_string, StringFormat}; use crate::types::{rewrite_path, PathContext}; use crate::utils::{ @@ -515,9 +516,9 @@ fn rewrite_single_line_block( label: Option, shape: Shape, ) -> Option { - if is_simple_block(context, block, attrs) { + if let Some(block_expr) = stmt::Stmt::from_simple_block(context, block, attrs) { let expr_shape = shape.offset_left(last_line_width(prefix))?; - let expr_str = block.stmts[0].rewrite(context, expr_shape)?; + let expr_str = block_expr.rewrite(context, expr_shape)?; let label_str = rewrite_label(label); let result = format!("{}{}{{ {} }}", prefix, label_str, expr_str); if result.len() <= shape.width && !result.contains('\n') { @@ -799,19 +800,19 @@ impl<'a> ControlFlow<'a> { let fixed_cost = self.keyword.len() + " { } else { }".len(); if let ast::ExprKind::Block(ref else_node, _) = else_block.kind { - if !is_simple_block(context, self.block, None) - || !is_simple_block(context, else_node, None) - || pat_expr_str.contains('\n') - { - return None; - } + let (if_expr, else_expr) = match ( + stmt::Stmt::from_simple_block(context, self.block, None), + stmt::Stmt::from_simple_block(context, else_node, None), + pat_expr_str.contains('\n'), + ) { + (Some(if_expr), Some(else_expr), false) => (if_expr, else_expr), + _ => return None, + }; let new_width = width.checked_sub(pat_expr_str.len() + fixed_cost)?; - let expr = &self.block.stmts[0]; - let if_str = expr.rewrite(context, Shape::legacy(new_width, Indent::empty()))?; + let if_str = if_expr.rewrite(context, Shape::legacy(new_width, Indent::empty()))?; let new_width = new_width.checked_sub(if_str.len())?; - let else_expr = &else_node.stmts[0]; let else_str = else_expr.rewrite(context, Shape::legacy(new_width, Indent::empty()))?; if if_str.contains('\n') || else_str.contains('\n') { diff --git a/src/stmt.rs b/src/stmt.rs index 0148de8190b8..e3fe4ebca11f 100644 --- a/src/stmt.rs +++ b/src/stmt.rs @@ -3,7 +3,7 @@ use rustc_span::Span; use crate::comment::recover_comment_removed; use crate::config::Version; -use crate::expr::{format_expr, ExprType}; +use crate::expr::{format_expr, is_simple_block, ExprType}; use crate::rewrite::{Rewrite, RewriteContext}; use crate::shape::Shape; use crate::source_map::LineRangeUtils; @@ -33,6 +33,21 @@ impl<'a> Stmt<'a> { } } + pub(crate) fn from_simple_block( + context: &RewriteContext<'_>, + block: &'a ast::Block, + attrs: Option<&[ast::Attribute]>, + ) -> Option { + if is_simple_block(context, block, attrs) { + let inner = &block.stmts[0]; + // Simple blocks only contain one expr and no stmts + let is_last = true; + Some(Stmt { inner, is_last }) + } else { + None + } + } + pub(crate) fn from_ast_node(inner: &'a ast::Stmt, is_last: bool) -> Self { Stmt { inner, is_last } } @@ -85,23 +100,17 @@ impl<'a> Rewrite for Stmt<'a> { shape, self.as_ast_node(), expr_type, - Some(self.is_last_expr()), + self.is_last_expr(), ) } } -impl Rewrite for ast::Stmt { - fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { - format_stmt(context, shape, self, ExprType::Statement, None) - } -} - fn format_stmt( context: &RewriteContext<'_>, shape: Shape, stmt: &ast::Stmt, expr_type: ExprType, - is_last_expr: Option, + is_last_expr: bool, ) -> Option { skip_out_of_file_lines_range!(context, stmt.span()); diff --git a/src/utils.rs b/src/utils.rs index e768ae0a1bc2..b8a44d4bade3 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -295,7 +295,7 @@ pub(crate) fn semicolon_for_expr(context: &RewriteContext<'_>, expr: &ast::Expr) pub(crate) fn semicolon_for_stmt( context: &RewriteContext<'_>, stmt: &ast::Stmt, - is_last_expr: Option, + is_last_expr: bool, ) -> bool { match stmt.kind { ast::StmtKind::Semi(ref expr) => match expr.kind { @@ -305,7 +305,7 @@ pub(crate) fn semicolon_for_stmt( ast::ExprKind::Break(..) | ast::ExprKind::Continue(..) | ast::ExprKind::Ret(..) => { // The only time we can skip the semi-colon is if the config option is set to false // **and** this is the last expr (even though any following exprs are unreachable) - context.config.trailing_semicolon() || !is_last_expr.unwrap_or(false) + context.config.trailing_semicolon() || !is_last_expr } _ => true, }, From f2bad9c7af308e6290f69bdf31390f9359a0e77b Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Wed, 5 Jul 2023 14:05:53 -0500 Subject: [PATCH 299/401] fix: handle skip_macro_invocations from config file --- src/config/macro_names.rs | 14 ++++++++++++-- tests/config/issue-5816.toml | 1 + tests/source/skip_macro_invocations/config_file.rs | 9 +++++++++ tests/target/skip_macro_invocations/config_file.rs | 7 +++++++ 4 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 tests/config/issue-5816.toml create mode 100644 tests/source/skip_macro_invocations/config_file.rs create mode 100644 tests/target/skip_macro_invocations/config_file.rs diff --git a/src/config/macro_names.rs b/src/config/macro_names.rs index 26ad78d6dcae..61658f0a212d 100644 --- a/src/config/macro_names.rs +++ b/src/config/macro_names.rs @@ -3,7 +3,7 @@ use itertools::Itertools; use std::{fmt, str}; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize}; use serde_json as json; use thiserror::Error; @@ -30,12 +30,22 @@ impl From for String { } /// Defines a selector to match against a macro. -#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize)] +#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize)] pub enum MacroSelector { Name(MacroName), All, } +impl<'de> Deserialize<'de> for MacroSelector { + fn deserialize(de: D) -> Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(de)?; + std::str::FromStr::from_str(&s).map_err(serde::de::Error::custom) + } +} + impl fmt::Display for MacroSelector { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { diff --git a/tests/config/issue-5816.toml b/tests/config/issue-5816.toml new file mode 100644 index 000000000000..00375746e3fb --- /dev/null +++ b/tests/config/issue-5816.toml @@ -0,0 +1 @@ +skip_macro_invocations=["*", "println"] diff --git a/tests/source/skip_macro_invocations/config_file.rs b/tests/source/skip_macro_invocations/config_file.rs new file mode 100644 index 000000000000..e0f5ddf52e7f --- /dev/null +++ b/tests/source/skip_macro_invocations/config_file.rs @@ -0,0 +1,9 @@ +// rustfmt-unstable: true +// rustfmt-config: issue-5816.toml + +fn main() { + println!( "Hello, world!"); + let x = +7 +; +} diff --git a/tests/target/skip_macro_invocations/config_file.rs b/tests/target/skip_macro_invocations/config_file.rs new file mode 100644 index 000000000000..008e28db42d7 --- /dev/null +++ b/tests/target/skip_macro_invocations/config_file.rs @@ -0,0 +1,7 @@ +// rustfmt-unstable: true +// rustfmt-config: issue-5816.toml + +fn main() { + println!( "Hello, world!"); + let x = 7; +} From d0762f032b4a5ed636a1ebae0b82647f2cce2998 Mon Sep 17 00:00:00 2001 From: Oleksandr Babak Date: Tue, 4 Jul 2023 11:28:19 +0200 Subject: [PATCH 300/401] always emit consider `AutoImplCandidates` for them if they don't also have a `ProjectionCandidate` --- .editorconfig | 23 + .gitattributes | 7 + .github/workflows/check_diff.yml | 33 + .github/workflows/integration.yml | 81 + .github/workflows/linux.yml | 39 + .github/workflows/mac.yml | 36 + .github/workflows/rustdoc_check.yml | 25 + .github/workflows/upload-assets.yml | 78 + .github/workflows/windows.yml | 62 + .gitignore | 25 + CHANGELOG.md | 1415 +++++++ CODE_OF_CONDUCT.md | 40 + Cargo.lock | 985 +++++ Cargo.toml | 65 + Configurations.md | 3135 +++++++++++++++ Contributing.md | 261 ++ Design.md | 184 + LICENSE-APACHE | 201 + LICENSE-MIT | 25 + Makefile.toml | 71 + Processes.md | 53 + README.md | 232 ++ atom.md | 31 + bootstrap.sh | 17 + build.rs | 55 + ci/build_and_test.bat | 15 + ci/build_and_test.sh | 19 + ci/check_diff.sh | 199 + ci/integration.sh | 121 + config_proc_macro/.gitignore | 1 + config_proc_macro/Cargo.lock | 68 + config_proc_macro/Cargo.toml | 23 + config_proc_macro/src/attrs.rs | 76 + config_proc_macro/src/config_type.rs | 15 + config_proc_macro/src/item_enum.rs | 242 ++ config_proc_macro/src/item_struct.rs | 5 + config_proc_macro/src/lib.rs | 84 + config_proc_macro/src/utils.rs | 52 + config_proc_macro/tests/smoke.rs | 21 + docs/index.html | 311 ++ intellij.md | 35 + rust-toolchain | 3 + rustfmt.toml | 3 + src/attr.rs | 531 +++ src/attr/doc_comment.rs | 88 + src/bin/main.rs | 719 ++++ src/cargo-fmt/main.rs | 549 +++ src/cargo-fmt/test/message_format.rs | 80 + src/cargo-fmt/test/mod.rs | 141 + src/cargo-fmt/test/targets.rs | 134 + src/chains.rs | 942 +++++ src/closures.rs | 469 +++ src/comment.rs | 2155 ++++++++++ src/config/config_type.rs | 522 +++ src/config/file_lines.rs | 440 +++ src/config/lists.rs | 92 + src/config/macro_names.rs | 118 + src/config/mod.rs | 1060 +++++ src/config/options.rs | 470 +++ src/coverage.rs | 15 + src/emitter.rs | 52 + src/emitter/checkstyle.rs | 150 + src/emitter/checkstyle/xml.rs | 52 + src/emitter/diff.rs | 137 + src/emitter/files.rs | 37 + src/emitter/files_with_backup.rs | 31 + src/emitter/json.rs | 346 ++ src/emitter/modified_lines.rs | 24 + src/emitter/stdout.rs | 32 + src/expr.rs | 2205 +++++++++++ src/format-diff/main.rs | 281 ++ src/format-diff/test/bindgen.diff | 67 + src/format_report_formatter.rs | 150 + src/formatting.rs | 647 +++ src/formatting/generated.rs | 7 + src/formatting/newline_style.rs | 250 ++ src/git-rustfmt/main.rs | 192 + src/ignore_path.rs | 52 + src/imports.rs | 1506 +++++++ src/items.rs | 3456 +++++++++++++++++ src/lib.rs | 662 ++++ src/lists.rs | 943 +++++ src/macros.rs | 1415 +++++++ src/matches.rs | 601 +++ src/missed_spans.rs | 363 ++ src/modules.rs | 578 +++ src/modules/visitor.rs | 112 + src/overflow.rs | 789 ++++ src/pairs.rs | 325 ++ src/parse/macros/asm.rs | 11 + src/parse/macros/cfg_if.rs | 97 + src/parse/macros/lazy_static.rs | 50 + src/parse/macros/mod.rs | 229 ++ src/parse/mod.rs | 3 + src/parse/parser.rs | 175 + src/parse/session.rs | 538 +++ src/patterns.rs | 536 +++ src/release_channel.rs | 16 + src/reorder.rs | 330 ++ src/rewrite.rs | 98 + src/rustfmt_diff.rs | 400 ++ src/shape.rs | 373 ++ src/skip.rs | 127 + src/source_file.rs | 105 + src/source_map.rs | 82 + src/spanned.rs | 199 + src/stmt.rs | 116 + src/string.rs | 725 ++++ src/syntux.rs | 4 + src/test/configuration_snippet.rs | 327 ++ src/test/mod.rs | 1049 +++++ src/test/mod_resolver.rs | 82 + src/test/parser.rs | 57 + src/types.rs | 1142 ++++++ src/utils.rs | 712 ++++ src/vertical.rs | 302 ++ src/visitor.rs | 1019 +++++ tests/cargo-fmt/main.rs | 119 + .../divergent-crate-dir-names/Cargo.toml | 13 + .../dependency-dir-name/Cargo.toml | 10 + .../dependency-dir-name/src/lib.rs | 7 + .../subdep-dir-name/Cargo.toml | 7 + .../subdep-dir-name/src/lib.rs | 7 + .../divergent-crate-dir-names/src/main.rs | 3 + tests/cargo-fmt/source/issue_3164/Cargo.toml | 8 + tests/cargo-fmt/source/issue_3164/src/main.rs | 13 + .../workspaces/path-dep-above/e/Cargo.toml | 8 + .../workspaces/path-dep-above/e/src/main.rs | 1 + .../workspaces/path-dep-above/ws/Cargo.toml | 5 + .../workspaces/path-dep-above/ws/a/Cargo.toml | 6 + .../path-dep-above/ws/a/d/Cargo.toml | 7 + .../path-dep-above/ws/a/d/f/Cargo.toml | 4 + .../path-dep-above/ws/a/d/f/src/lib.rs | 1 + .../path-dep-above/ws/a/d/src/lib.rs | 1 + .../path-dep-above/ws/a/src/main.rs | 1 + .../workspaces/path-dep-above/ws/b/Cargo.toml | 6 + .../path-dep-above/ws/b/src/main.rs | 1 + .../workspaces/path-dep-above/ws/c/Cargo.toml | 4 + .../workspaces/path-dep-above/ws/c/src/lib.rs | 1 + tests/config/disable_all_formatting.toml | 1 + tests/config/issue-1111.toml | 1 + tests/config/issue-2641.toml | 1 + tests/config/issue-3779.toml | 3 + tests/config/skip_children.toml | 1 + tests/config/small_tabs.toml | 10 + tests/coverage/source/comments.rs | 7 + tests/coverage/target/comments.rs | 7 + tests/mod-resolver/issue-4874/bar/baz.rs | 5 + tests/mod-resolver/issue-4874/foo.rs | 1 + tests/mod-resolver/issue-4874/foo/qux.rs | 5 + tests/mod-resolver/issue-4874/main.rs | 8 + tests/mod-resolver/issue-5063/foo.rs | 2 + tests/mod-resolver/issue-5063/foo/bar/baz.rs | 1 + tests/mod-resolver/issue-5063/main.rs | 5 + tests/mod-resolver/issue-5167/src/a.rs | 0 tests/mod-resolver/issue-5167/src/a/mod.rs | 0 tests/mod-resolver/issue-5167/src/lib.rs | 1 + tests/mod-resolver/issue-5198/a.rs | 1 + tests/mod-resolver/issue-5198/lib.rs | 3 + tests/mod-resolver/issue-5198/lib/b.rs | 1 + tests/mod-resolver/issue-5198/lib/c/d.rs | 3 + .../issue-5198/lib/c/d/explanation.txt | 16 + tests/mod-resolver/issue-5198/lib/c/d/f.rs | 1 + .../mod-resolver/issue-5198/lib/c/d/g/mod.rs | 1 + tests/mod-resolver/issue-5198/lib/c/e.rs | 1 + tests/mod-resolver/issue-5198/lib/c/mod.rs | 3 + .../issue-5198/lib/explanation.txt | 16 + .../bad_path_attribute/lib.rs | 3 + .../module-not-found/relative_module/a.rs | 2 + .../module-not-found/relative_module/lib.rs | 1 + .../module-not-found/sibling_module/lib.rs | 2 + .../mod-resolver/skip-files-issue-5065/foo.rs | 5 + .../skip-files-issue-5065/foo/bar/baz.rs | 1 + .../skip-files-issue-5065/main.rs | 9 + .../mod-resolver/skip-files-issue-5065/one.rs | 1 + .../test-submodule-issue-5119/Cargo.toml | 8 + .../test-submodule-issue-5119/src/lib.rs | 7 + .../test-submodule-issue-5119/tests/test1.rs | 8 + .../tests/test1/sub1.rs | 6 + .../tests/test1/sub2.rs | 6 + .../tests/test1/sub3/mod.rs | 1 + .../tests/test1/sub3/sub4.rs | 0 tests/parser/issue-4126/invalid.rs | 6 + tests/parser/issue-4126/lib.rs | 1 + tests/parser/issue_4418.rs | 1 + tests/parser/unclosed-delims/issue_4466.rs | 11 + tests/rustfmt/main.rs | 188 + tests/source/5131_crate.rs | 14 + tests/source/5131_module.rs | 33 + tests/source/5131_one.rs | 15 + tests/source/alignment_2633/block_style.rs | 8 + tests/source/alignment_2633/visual_style.rs | 9 + tests/source/array_comment.rs | 19 + tests/source/assignment.rs | 34 + .../associated-types-bounds-wrapping.rs | 5 + tests/source/associated_type_bounds.rs | 13 + tests/source/async_block.rs | 51 + tests/source/async_fn.rs | 28 + tests/source/attrib.rs | 234 ++ tests/source/big-impl-block.rs | 123 + tests/source/big-impl-visual.rs | 106 + tests/source/binary-expr.rs | 10 + tests/source/binop-separator-back/bitwise.rs | 14 + tests/source/binop-separator-back/comp.rs | 23 + tests/source/binop-separator-back/logic.rs | 7 + tests/source/binop-separator-back/math.rs | 7 + tests/source/binop-separator-back/patterns.rs | 9 + tests/source/binop-separator-back/range.rs | 7 + tests/source/break-and-continue.rs | 23 + tests/source/catch.rs | 28 + tests/source/cfg_if/detect/arch/aarch64.rs | 106 + tests/source/cfg_if/detect/arch/arm.rs | 39 + tests/source/cfg_if/detect/arch/mips.rs | 29 + tests/source/cfg_if/detect/arch/mips64.rs | 29 + tests/source/cfg_if/detect/arch/powerpc.rs | 42 + tests/source/cfg_if/detect/arch/powerpc64.rs | 42 + tests/source/cfg_if/detect/arch/x86.rs | 348 ++ tests/source/cfg_if/detect/bit.rs | 9 + tests/source/cfg_if/detect/cache.rs | 164 + tests/source/cfg_if/detect/error_macros.rs | 150 + tests/source/cfg_if/detect/mod.rs | 85 + tests/source/cfg_if/detect/os/aarch64.rs | 79 + .../cfg_if/detect/os/freebsd/aarch64.rs | 28 + tests/source/cfg_if/detect/os/freebsd/arm.rs | 27 + .../source/cfg_if/detect/os/freebsd/auxvec.rs | 86 + tests/source/cfg_if/detect/os/freebsd/mod.rs | 22 + .../cfg_if/detect/os/freebsd/powerpc.rs | 27 + .../source/cfg_if/detect/os/linux/aarch64.rs | 157 + tests/source/cfg_if/detect/os/linux/arm.rs | 49 + tests/source/cfg_if/detect/os/linux/auxvec.rs | 307 ++ .../source/cfg_if/detect/os/linux/cpuinfo.rs | 301 ++ tests/source/cfg_if/detect/os/linux/mips.rs | 31 + tests/source/cfg_if/detect/os/linux/mod.rs | 28 + .../source/cfg_if/detect/os/linux/powerpc.rs | 41 + tests/source/cfg_if/detect/os/other.rs | 9 + tests/source/cfg_if/detect/os/x86.rs | 375 ++ tests/source/cfg_if/lib.rs | 49 + tests/source/cfg_if/mod.rs | 5 + tests/source/cfg_mod/bar.rs | 3 + tests/source/cfg_mod/dir/dir1/dir2/wasm32.rs | 6 + tests/source/cfg_mod/dir/dir1/dir3/wasm32.rs | 6 + tests/source/cfg_mod/foo.rs | 4 + tests/source/cfg_mod/mod.rs | 10 + tests/source/cfg_mod/other.rs | 1 + tests/source/cfg_mod/wasm32.rs | 4 + tests/source/chains-visual.rs | 158 + tests/source/chains.rs | 266 ++ tests/source/chains_with_comment.rs | 121 + tests/source/closure-block-inside-macro.rs | 9 + tests/source/closure.rs | 223 ++ tests/source/comment.rs | 90 + tests/source/comment2.rs | 4 + tests/source/comment3.rs | 5 + tests/source/comment4.rs | 52 + tests/source/comment5.rs | 14 + tests/source/comment6.rs | 10 + tests/source/comment_crlf_newline.rs | 4 + .../wrap-comments-not-normalized.rs | 129 + .../comments-in-lists/wrap-comments-true.rs | 130 + tests/source/comments_unicode.rs | 140 + .../configs/blank_lines_lower_bound/1.rs | 13 + .../brace_style/fn_always_next_line.rs | 14 + .../brace_style/fn_prefer_same_line.rs | 14 + .../configs/brace_style/fn_same_line_where.rs | 14 + .../brace_style/item_always_next_line.rs | 20 + .../brace_style/item_prefer_same_line.rs | 16 + .../brace_style/item_same_line_where.rs | 16 + tests/source/configs/chain_width/always.rs | 23 + tests/source/configs/chain_width/small.rs | 23 + tests/source/configs/chain_width/tiny.rs | 21 + tests/source/configs/comment_width/above.rs | 7 + tests/source/configs/comment_width/below.rs | 7 + tests/source/configs/comment_width/ignore.rs | 7 + .../condense_wildcard_suffixes/false.rs | 6 + .../condense_wildcard_suffixes/true.rs | 6 + .../control_brace_style/always_next_line.rs | 10 + .../control_brace_style/always_same_line.rs | 10 + .../control_brace_style/closing_next_line.rs | 10 + .../configs/disable_all_formatting/false.rs | 6 + .../configs/disable_all_formatting/true.rs | 6 + .../doc_comment_code_block_width/100.rs | 16 + .../100_greater_max_width.rs | 17 + .../doc_comment_code_block_width/50.rs | 16 + .../configs/empty_item_single_line/false.rs | 16 + .../configs/empty_item_single_line/true.rs | 16 + .../enum_discrim_align_threshold/40.rs | 34 + .../configs/error_on_line_overflow/false.rs | 6 + .../configs/fn_params_layout/compressed.rs | 16 + tests/source/configs/fn_params_layout/tall.rs | 16 + .../configs/fn_params_layout/vertical.rs | 16 + tests/source/configs/fn_single_line/false.rs | 11 + tests/source/configs/fn_single_line/true.rs | 11 + .../configs/force_explicit_abi/false.rs | 6 + .../source/configs/force_explicit_abi/true.rs | 6 + .../configs/force_multiline_block/false.rs | 22 + .../configs/force_multiline_block/true.rs | 18 + .../configs/format_generated_files/false.rs | 8 + .../configs/format_generated_files/true.rs | 8 + .../configs/format_macro_bodies/false.rs | 7 + .../configs/format_macro_bodies/true.rs | 7 + .../configs/format_macro_matchers/false.rs | 7 + .../configs/format_macro_matchers/true.rs | 6 + tests/source/configs/format_strings/false.rs | 8 + tests/source/configs/format_strings/true.rs | 7 + .../group_imports/One-merge_imports.rs | 17 + .../configs/group_imports/One-nested.rs | 7 + .../configs/group_imports/One-no_reorder.rs | 16 + tests/source/configs/group_imports/One.rs | 15 + .../StdExternalCrate-merge_imports.rs | 17 + .../group_imports/StdExternalCrate-nested.rs | 6 + .../StdExternalCrate-no_reorder.rs | 17 + .../StdExternalCrate-non_consecutive.rs | 27 + .../configs/group_imports/StdExternalCrate.rs | 15 + tests/source/configs/hard_tabs/false.rs | 6 + tests/source/configs/hard_tabs/true.rs | 6 + .../configs/imports_layout/merge_mixed.rs | 6 + .../source/configs/indent_style/block_args.rs | 26 + .../configs/indent_style/block_array.rs | 6 + .../source/configs/indent_style/block_call.rs | 133 + .../configs/indent_style/block_chain.rs | 6 + .../configs/indent_style/block_generic.rs | 6 + .../configs/indent_style/block_struct_lit.rs | 6 + .../block_trailing_comma_call/one.rs | 9 + .../block_trailing_comma_call/two.rs | 9 + .../configs/indent_style/block_where_pred.rs | 6 + tests/source/configs/indent_style/default.rs | 6 + .../source/configs/indent_style/rfc_where.rs | 6 + .../configs/indent_style/visual_args.rs | 32 + .../configs/indent_style/visual_array.rs | 6 + .../configs/indent_style/visual_call.rs | 6 + .../configs/indent_style/visual_chain.rs | 6 + .../configs/indent_style/visual_generics.rs | 6 + .../configs/indent_style/visual_struct_lit.rs | 6 + .../indent_style/visual_trailing_comma.rs | 7 + .../configs/indent_style/visual_where_pred.rs | 6 + .../source/configs/match_arm_blocks/false.rs | 11 + tests/source/configs/match_arm_blocks/true.rs | 11 + .../configs/match_arm_leading_pipes/always.rs | 27 + .../configs/match_arm_leading_pipes/never.rs | 28 + .../match_arm_leading_pipes/preserve.rs | 36 + .../match_block_trailing_comma/false.rs | 11 + .../match_block_trailing_comma/true.rs | 11 + tests/source/configs/merge_derives/true.rs | 46 + .../configs/normalize_comments/false.rs | 13 + .../source/configs/normalize_comments/true.rs | 13 + .../configs/normalize_doc_attributes/false.rs | 13 + .../configs/normalize_doc_attributes/true.rs | 13 + .../remove_nested_parens.rs | 5 + .../configs/reorder_impl_items/false.rs | 11 + .../source/configs/reorder_impl_items/true.rs | 11 + tests/source/configs/reorder_imports/false.rs | 7 + tests/source/configs/reorder_imports/true.rs | 19 + .../configs/reorder_modules/dolor/mod.rs | 1 + tests/source/configs/reorder_modules/false.rs | 7 + .../configs/reorder_modules/ipsum/mod.rs | 1 + .../configs/reorder_modules/lorem/mod.rs | 1 + .../source/configs/reorder_modules/sit/mod.rs | 1 + tests/source/configs/reorder_modules/true.rs | 7 + .../short_array_element_width_threshold/10.rs | 11 + .../short_array_element_width_threshold/20.rs | 11 + .../greater_than_max_width.rs | 12 + .../single_line_let_else_max_width/100.rs | 40 + .../single_line_let_else_max_width/50.rs | 40 + .../single_line_let_else_max_width/zero.rs | 40 + tests/source/configs/skip_children/foo/mod.rs | 3 + tests/source/configs/skip_children/true.rs | 4 + .../source/configs/space_before_colon/true.rs | 11 + .../configs/spaces_around_ranges/false.rs | 34 + .../configs/spaces_around_ranges/true.rs | 34 + .../struct_field_align_threshold/20.rs | 383 ++ .../configs/struct_lit_single_line/false.rs | 6 + tests/source/configs/tab_spaces/2.rs | 11 + tests/source/configs/tab_spaces/4.rs | 11 + tests/source/configs/trailing_comma/always.rs | 7 + tests/source/configs/trailing_comma/never.rs | 23 + .../source/configs/trailing_comma/vertical.rs | 7 + .../type_punctuation_density/compressed.rs | 37 + .../configs/type_punctuation_density/wide.rs | 37 + .../configs/use_field_init_shorthand/false.rs | 19 + .../configs/use_field_init_shorthand/true.rs | 19 + .../configs/use_small_heuristics/default.rs | 35 + .../configs/use_small_heuristics/max.rs | 35 + .../configs/use_small_heuristics/off.rs | 35 + .../source/configs/use_try_shorthand/false.rs | 6 + .../source/configs/use_try_shorthand/true.rs | 6 + .../source/configs/where_single_line/true.rs | 26 + tests/source/configs/wrap_comments/false.rs | 8 + tests/source/configs/wrap_comments/true.rs | 15 + tests/source/const_generics.rs | 44 + .../control-brace-style-always-next-line.rs | 44 + .../control-brace-style-always-same-line.rs | 42 + tests/source/doc-attrib.rs | 118 + tests/source/doc-comment-with-example.rs | 12 + tests/source/doc.rs | 5 + tests/source/dyn_trait.rs | 20 + .../else-if-brace-style-always-next-line.rs | 54 + .../else-if-brace-style-always-same-line.rs | 52 + .../else-if-brace-style-closing-next-line.rs | 54 + tests/source/empty-item-single-line-false.rs | 46 + tests/source/empty_file.rs | 0 tests/source/enum.rs | 212 + tests/source/existential_type.rs | 23 + tests/source/expr-block.rs | 300 ++ tests/source/expr-overflow-delimited.rs | 155 + tests/source/expr.rs | 568 +++ tests/source/extern.rs | 92 + tests/source/extern_not_explicit.rs | 14 + tests/source/file-lines-1.rs | 29 + tests/source/file-lines-2.rs | 29 + tests/source/file-lines-3.rs | 29 + tests/source/file-lines-4.rs | 30 + tests/source/file-lines-5.rs | 17 + tests/source/file-lines-6.rs | 18 + tests/source/file-lines-7.rs | 24 + tests/source/file-lines-item.rs | 21 + tests/source/fn-custom-2.rs | 35 + tests/source/fn-custom-3.rs | 31 + tests/source/fn-custom-4.rs | 13 + tests/source/fn-custom-6.rs | 40 + tests/source/fn-custom-7.rs | 24 + tests/source/fn-custom-8.rs | 48 + tests/source/fn-custom.rs | 13 + tests/source/fn-param-attributes.rs | 57 + tests/source/fn-simple.rs | 74 + tests/source/fn-single-line/version_one.rs | 80 + tests/source/fn-single-line/version_two.rs | 80 + tests/source/fn_args_indent-block.rs | 77 + tests/source/fn_args_layout-vertical.rs | 33 + tests/source/hard-tabs.rs | 84 + tests/source/hello.rs | 6 + tests/source/hello2.rs | 8 + tests/source/hex_literal_lower.rs | 5 + tests/source/hex_literal_upper.rs | 5 + tests/source/if_while_or_patterns.rs | 27 + tests/source/immovable_generators.rs | 7 + tests/source/impls.rs | 178 + tests/source/imports/imports-impl-only-use.rs | 4 + .../imports-reorder-lines-and-items.rs | 7 + tests/source/imports/imports-reorder-lines.rs | 32 + tests/source/imports/imports-reorder.rs | 5 + tests/source/imports/imports.rs | 107 + tests/source/imports/imports_block_indent.rs | 2 + .../imports/imports_granularity_crate.rs | 65 + .../imports_granularity_default-with-dups.rs | 6 + ...m-with-dups-StdExternalCrate-no-reorder.rs | 13 + .../imports_granularity_item-with-dups.rs | 11 + .../imports/imports_granularity_item.rs | 34 + .../imports/imports_granularity_module.rs | 47 + tests/source/imports_granularity_one.rs | 88 + .../imports_raw_identifiers/version_One.rs | 5 + .../imports_raw_identifiers/version_Two.rs | 5 + .../invalid-rust-code-in-doc-comment.rs | 20 + tests/source/issue-1021.rs | 22 + tests/source/issue-1049.rs | 18 + tests/source/issue-1111.rs | 1 + tests/source/issue-1120.rs | 9 + tests/source/issue-1124.rs | 15 + tests/source/issue-1127.rs | 23 + tests/source/issue-1158.rs | 3 + tests/source/issue-1177.rs | 7 + tests/source/issue-1192.rs | 3 + tests/source/issue-1210/a.rs | 12 + tests/source/issue-1210/b.rs | 12 + tests/source/issue-1210/c.rs | 5 + tests/source/issue-1210/d.rs | 4 + tests/source/issue-1210/e.rs | 8 + tests/source/issue-1211.rs | 15 + tests/source/issue-1216.rs | 5 + tests/source/issue-1239.rs | 9 + tests/source/issue-1278.rs | 9 + tests/source/issue-1350.rs | 16 + tests/source/issue-1366.rs | 12 + tests/source/issue-1468.rs | 27 + tests/source/issue-1693.rs | 3 + tests/source/issue-1800.rs | 3 + tests/source/issue-1914.rs | 6 + tests/source/issue-2025.rs | 8 + tests/source/issue-2111.rs | 26 + tests/source/issue-2164.rs | 4 + tests/source/issue-2179/one.rs | 36 + tests/source/issue-2179/two.rs | 36 + tests/source/issue-2256.rs | 12 + tests/source/issue-2342.rs | 5 + tests/source/issue-2445.rs | 21 + tests/source/issue-2446.rs | 11 + tests/source/issue-2479.rs | 2 + tests/source/issue-2482/a.rs | 9 + tests/source/issue-2482/b.rs | 1 + tests/source/issue-2482/c.rs | 1 + tests/source/issue-2496.rs | 16 + tests/source/issue-2520.rs | 13 + tests/source/issue-2523.rs | 18 + tests/source/issue-2582.rs | 1 + tests/source/issue-2641.rs | 3 + tests/source/issue-2644.rs | 11 + tests/source/issue-2728.rs | 8 + tests/source/issue-2761.rs | 15 + tests/source/issue-2781.rs | 11 + tests/source/issue-2794.rs | 7 + tests/source/issue-2835.rs | 7 + tests/source/issue-2863.rs | 25 + tests/source/issue-2869.rs | 41 + tests/source/issue-2896.rs | 161 + tests/source/issue-2916.rs | 2 + tests/source/issue-2917/packed_simd.rs | 63 + tests/source/issue-2922.rs | 9 + tests/source/issue-2927-2.rs | 7 + tests/source/issue-2927.rs | 7 + tests/source/issue-2930.rs | 5 + tests/source/issue-2936.rs | 19 + tests/source/issue-2955.rs | 6 + tests/source/issue-2973.rs | 158 + tests/source/issue-2977/impl.rs | 44 + tests/source/issue-2977/trait.rs | 44 + tests/source/issue-2985.rs | 35 + tests/source/issue-2995.rs | 7 + tests/source/issue-3029.rs | 94 + tests/source/issue-3038.rs | 20 + tests/source/issue-3049.rs | 45 + tests/source/issue-3055/original.rs | 43 + tests/source/issue-3059.rs | 7 + tests/source/issue-3066.rs | 7 + tests/source/issue-3131.rs | 8 + tests/source/issue-3153.rs | 9 + tests/source/issue-3158.rs | 74 + tests/source/issue-3194.rs | 13 + tests/source/issue-3198.rs | 99 + tests/source/issue-3213/version_one.rs | 9 + tests/source/issue-3213/version_two.rs | 9 + tests/source/issue-3217.rs | 6 + tests/source/issue-3227/two.rs | 13 + tests/source/issue-3234.rs | 14 + tests/source/issue-3241.rs | 11 + tests/source/issue-3253/bar.rs | 4 + tests/source/issue-3253/foo.rs | 6 + tests/source/issue-3253/lib.rs | 14 + tests/source/issue-3253/paths/bar_foo.rs | 3 + tests/source/issue-3253/paths/excluded.rs | 6 + tests/source/issue-3253/paths/foo_bar.rs | 4 + tests/source/issue-3265.rs | 14 + tests/source/issue-3270/one.rs | 12 + tests/source/issue-3270/two.rs | 12 + tests/source/issue-3272/v1.rs | 15 + tests/source/issue-3272/v2.rs | 15 + tests/source/issue-3278/version_one.rs | 8 + tests/source/issue-3278/version_two.rs | 8 + tests/source/issue-3295/two.rs | 13 + tests/source/issue-3302.rs | 43 + tests/source/issue-3343.rs | 47 + tests/source/issue-3423.rs | 5 + tests/source/issue-3434/lib.rs | 57 + tests/source/issue-3434/no_entry.rs | 18 + tests/source/issue-3434/not_skip_macro.rs | 8 + tests/source/issue-3465.rs | 42 + tests/source/issue-3494/crlf.rs | 8 + tests/source/issue-3494/lf.rs | 8 + tests/source/issue-3508.rs | 29 + tests/source/issue-3515.rs | 6 + tests/source/issue-3532.rs | 7 + tests/source/issue-3585/extern_crate.rs | 12 + .../issue-3585/reorder_imports_disabled.rs | 12 + .../issue-3585/reorder_imports_enabled.rs | 12 + tests/source/issue-3585/use.rs | 7 + tests/source/issue-3636.rs | 10 + tests/source/issue-3639.rs | 5 + tests/source/issue-3651.rs | 4 + tests/source/issue-3665/lib.rs | 33 + tests/source/issue-3665/not_skip_attribute.rs | 4 + tests/source/issue-3665/sub_mod.rs | 14 + tests/source/issue-3672.rs | 4 + tests/source/issue-3675.rs | 5 + tests/source/issue-3701/one.rs | 12 + tests/source/issue-3701/two.rs | 12 + tests/source/issue-3709.rs | 10 + tests/source/issue-3740.rs | 10 + tests/source/issue-3750.rs | 16 + tests/source/issue-3751.rs | 10 + tests/source/issue-3779/ice.rs | 3 + tests/source/issue-3779/lib.rs | 9 + tests/source/issue-3786.rs | 12 + tests/source/issue-3787.rs | 9 + .../issue-3840/version-one_hard-tabs.rs | 15 + .../issue-3840/version-one_soft-tabs.rs | 13 + .../issue-3840/version-two_hard-tabs.rs | 16 + .../issue-3840/version-two_soft-tabs.rs | 15 + .../issue-3987/format_macro_bodies_true.rs | 26 + tests/source/issue-4018.rs | 13 + tests/source/issue-4036/one.rs | 11 + tests/source/issue-4036/three.rs | 12 + tests/source/issue-4036/two.rs | 11 + tests/source/issue-4041.rs | 5 + tests/source/issue-4079.rs | 8 + tests/source/issue-4120.rs | 85 + tests/source/issue-4243.rs | 21 + tests/source/issue-4244.rs | 16 + tests/source/issue-4245.rs | 26 + tests/source/issue-4312.rs | 22 + tests/source/issue-4382.rs | 4 + tests/source/issue-4398.rs | 19 + tests/source/issue-4427.rs | 31 + tests/source/issue-447.rs | 39 + tests/source/issue-4530.rs | 4 + tests/source/issue-4577.rs | 20 + tests/source/issue-4603.rs | 47 + tests/source/issue-4615/minimum_example.rs | 4 + tests/source/issue-4643.rs | 23 + tests/source/issue-4646.rs | 20 + tests/source/issue-4656/format_me_please.rs | 2 + tests/source/issue-4656/lib.rs | 7 + tests/source/issue-4656/lib2.rs | 3 + tests/source/issue-4689/one.rs | 149 + tests/source/issue-4689/two.rs | 149 + tests/source/issue-4791/buggy.rs | 14 + tests/source/issue-4791/trailing_comma.rs | 14 + tests/source/issue-4816/lib.rs | 10 + .../source/issue-4926/deeply_nested_struct.rs | 35 + ...ply_nested_struct_with_long_field_names.rs | 43 + .../deeply_nested_struct_with_many_fields.rs | 44 + tests/source/issue-4926/enum_struct_field.rs | 35 + tests/source/issue-4926/minimum_example.rs | 10 + .../struct_with_long_field_names.rs | 21 + .../issue-4926/struct_with_many_fields.rs | 21 + tests/source/issue-4984/minimum_example.rs | 2 + tests/source/issue-4984/multi_line_derive.rs | 20 + .../issue-4984/multiple_comments_within.rs | 8 + tests/source/issue-5011.rs | 12 + tests/source/issue-5023.rs | 22 + tests/source/issue-5030.rs | 22 + .../multi-line_comment_with_trailing_comma.rs | 24 + ...lti-line_comment_without_trailing_comma.rs | 24 + ...single-line_comment_with_trailing_comma.rs | 9 + ...gle-line_comment_without_trailing_comma.rs | 10 + ..._nested_long_comment_wrap_comments_true.rs | 33 + ..._long_itemized_block_wrap_comments_true.rs | 19 + .../very_long_comment_wrap_comments_true.rs | 13 + tests/source/issue-510.rs | 37 + .../indented_itemized_markdown_blockquote.rs | 4 + .../nested_itemized_markdown_blockquote.rs | 10 + .../support_itemized_markdown_blockquote.rs | 4 + tests/source/issue-5234.rs | 51 + .../markdown_header_wrap_comments_false.rs | 11 + .../markdown_header_wrap_comments_true.rs | 11 + tests/source/issue-5260.rs | 14 + tests/source/issue-5270/merge_derives_true.rs | 62 + tests/source/issue-539.rs | 5 + tests/source/issue-5488.rs | 17 + tests/source/issue-5586.rs | 164 + tests/source/issue-683.rs | 5 + tests/source/issue-811.rs | 19 + tests/source/issue-850.rs | 1 + tests/source/issue-855.rs | 20 + tests/source/issue-913.rs | 20 + tests/source/issue-945.rs | 5 + tests/source/issue-977.rs | 7 + tests/source/issue_1306.rs | 29 + tests/source/issue_3245.rs | 4 + tests/source/issue_3561.rs | 6 + tests/source/issue_3839.rs | 8 + tests/source/issue_3844.rs | 3 + tests/source/issue_3853.rs | 52 + tests/source/issue_3868.rs | 13 + tests/source/issue_4032.rs | 4 + tests/source/issue_4057.rs | 15 + tests/source/issue_4086.rs | 2 + tests/source/issue_4257.rs | 10 + tests/source/issue_4322.rs | 3 + tests/source/issue_4374.rs | 13 + tests/source/issue_4475.rs | 27 + tests/source/issue_4528.rs | 8 + tests/source/issue_4579.rs | 15 + tests/source/issue_4584.rs | 19 + tests/source/issue_4636.rs | 13 + tests/source/issue_4675.rs | 8 + tests/source/issue_4823.rs | 5 + tests/source/issue_4854.rs | 113 + tests/source/issue_4911.rs | 5 + tests/source/issue_4943.rs | 7 + tests/source/issue_4954.rs | 5 + tests/source/issue_4963.rs | 5 + tests/source/issue_5027.rs | 7 + tests/source/issue_5086.rs | 2 + tests/source/issue_5686.rs | 40 + .../item-brace-style-always-next-line.rs | 64 + .../item-brace-style-prefer-same-line.rs | 29 + .../item-brace-style-same-line-where.rs | 29 + tests/source/itemized-blocks/no_wrap.rs | 81 + tests/source/itemized-blocks/rewrite_fail.rs | 11 + tests/source/itemized-blocks/urls.rs | 22 + tests/source/itemized-blocks/wrap.rs | 89 + tests/source/label_break.rs | 28 + tests/source/large-block.rs | 5 + tests/source/large_vec.rs | 29 + tests/source/lazy_static.rs | 45 + tests/source/let_else.rs | 162 + tests/source/long-fn-1/version_one.rs | 21 + tests/source/long-fn-1/version_two.rs | 21 + tests/source/long-match-arms-brace-newline.rs | 15 + tests/source/long-use-statement-issue-3154.rs | 3 + tests/source/long_field_access.rs | 3 + tests/source/loop.rs | 29 + tests/source/macro_not_expr.rs | 7 + tests/source/macro_rules.rs | 301 ++ tests/source/macros.rs | 486 +++ tests/source/markdown-comment-with-options.rs | 17 + tests/source/markdown-comment.rs | 15 + tests/source/match-block-trailing-comma.rs | 22 + tests/source/match-flattening.rs | 21 + tests/source/match-nowrap-trailing-comma.rs | 15 + tests/source/match-nowrap.rs | 12 + tests/source/match.rs | 589 +++ tests/source/match_overflow_expr.rs | 53 + tests/source/max-line-length-in-chars.rs | 4 + tests/source/merge_imports_true_compat.rs | 4 + tests/source/mod-1.rs | 29 + tests/source/mod-2.rs | 4 + tests/source/mod_skip_child.rs | 2 + tests/source/multiple.rs | 134 + tests/source/negative-impl.rs | 7 + tests/source/nested-if-else.rs | 11 + tests/source/nested_skipped/mod.rs | 3 + tests/source/nestedmod/mod.rs | 13 + tests/source/nestedmod/mod2a.rs | 4 + tests/source/nestedmod/mod2b.rs | 3 + tests/source/nestedmod/mod2c.rs | 3 + tests/source/nestedmod/mymod1/mod3a.rs | 2 + tests/source/nestedmod/submod2/a.rs | 6 + tests/source/nestedmod/submod2/mod.rs | 5 + tests/source/no_arg_with_commnet.rs | 2 + tests/source/no_new_line_beginning.rs | 2 + ...es_should_not_imply_format_doc_comments.rs | 15 + .../normalize_multiline_doc_attribute.rs | 12 + tests/source/one_line_if_v1.rs | 42 + tests/source/one_line_if_v2.rs | 42 + tests/source/other.rs | 5 + tests/source/paren.rs | 6 + tests/source/path_clarity/foo.rs | 2 + tests/source/path_clarity/foo/bar.rs | 3 + tests/source/paths.rs | 25 + tests/source/pattern-condense-wildcards.rs | 12 + tests/source/pattern.rs | 90 + .../preserves_carriage_return_for_unix.rs | 2 + .../preserves_carriage_return_for_windows.rs | 2 + tests/source/pub-restricted.rs | 51 + tests/source/remove_blank_lines.rs | 44 + tests/source/reorder-impl-items.rs | 15 + tests/source/single-line-if-else.rs | 49 + tests/source/single-line-macro/v1.rs | 10 + tests/source/single-line-macro/v2.rs | 10 + tests/source/skip_macro_invocations/all.rs | 11 + .../skip_macro_invocations/all_and_name.rs | 11 + tests/source/skip_macro_invocations/empty.rs | 11 + tests/source/skip_macro_invocations/name.rs | 11 + .../skip_macro_invocations/name_unknown.rs | 6 + tests/source/skip_macro_invocations/names.rs | 16 + .../path_qualified_invocation_mismatch.rs | 6 + .../path_qualified_match.rs | 6 + .../path_qualified_name_mismatch.rs | 6 + .../use_alias_examples.rs | 32 + tests/source/soft-wrapping.rs | 15 + tests/source/space-not-before-newline.rs | 8 + tests/source/spaces-around-ranges.rs | 15 + tests/source/statements.rs | 43 + tests/source/static.rs | 23 + tests/source/string-lit-2.rs | 25 + tests/source/string-lit.rs | 61 + tests/source/string_punctuation.rs | 9 + tests/source/struct-field-attributes.rs | 52 + tests/source/struct_field_doc_comment.rs | 72 + tests/source/struct_lits.rs | 143 + tests/source/struct_lits_multiline.rs | 81 + tests/source/struct_lits_visual.rs | 46 + tests/source/struct_lits_visual_multiline.rs | 44 + tests/source/struct_tuple_visual.rs | 36 + tests/source/structs.rs | 298 ++ tests/source/trailing-comma-never.rs | 45 + tests/source/trailing_commas.rs | 47 + tests/source/trailing_comments/hard_tabs.rs | 21 + tests/source/trailing_comments/soft_tabs.rs | 21 + tests/source/trait.rs | 183 + tests/source/try-conversion.rs | 18 + tests/source/try_block.rs | 30 + tests/source/tuple.rs | 63 + tests/source/tuple_v2.rs | 5 + tests/source/type.rs | 168 + tests/source/type_alias.rs | 34 + tests/source/unicode.rs | 33 + tests/source/unions.rs | 195 + tests/source/unsafe-mod.rs | 7 + tests/source/visibility.rs | 8 + tests/source/visual-fn-type.rs | 10 + tests/source/where-clause-rfc.rs | 73 + tests/source/where-clause.rs | 58 + tests/source/width-heuristics.rs | 28 + ...ts_should_not_imply_format_doc_comments.rs | 16 + tests/target/5131_crate.rs | 9 + tests/target/5131_module.rs | 32 + tests/target/5131_one.rs | 12 + tests/target/alignment_2633/block_style.rs | 10 + .../alignment_2633/horizontal_tactic.rs | 13 + tests/target/alignment_2633/visual_style.rs | 9 + tests/target/array_comment.rs | 18 + tests/target/assignment.rs | 39 + tests/target/associated-items.rs | 3 + .../associated-types-bounds-wrapping.rs | 6 + tests/target/associated_type_bounds.rs | 13 + tests/target/associated_type_defaults.rs | 4 + tests/target/async_block.rs | 35 + tests/target/async_closure.rs | 22 + tests/target/async_fn.rs | 24 + tests/target/attrib-block-expr.rs | 58 + tests/target/attrib-extern-crate.rs | 17 + tests/target/attrib.rs | 271 ++ tests/target/big-impl-block.rs | 82 + tests/target/big-impl-visual.rs | 65 + tests/target/binary-expr.rs | 16 + tests/target/binop-separator-back/bitwise.rs | 18 + tests/target/binop-separator-back/comp.rs | 33 + tests/target/binop-separator-back/logic.rs | 10 + tests/target/binop-separator-back/math.rs | 23 + tests/target/binop-separator-back/patterns.rs | 11 + tests/target/binop-separator-back/range.rs | 9 + tests/target/break-and-continue.rs | 23 + tests/target/catch.rs | 22 + tests/target/cfg_if/detect/arch/aarch64.rs | 98 + tests/target/cfg_if/detect/arch/arm.rs | 47 + tests/target/cfg_if/detect/arch/mips.rs | 30 + tests/target/cfg_if/detect/arch/mips64.rs | 30 + tests/target/cfg_if/detect/arch/powerpc.rs | 42 + tests/target/cfg_if/detect/arch/powerpc64.rs | 42 + tests/target/cfg_if/detect/arch/x86.rs | 333 ++ tests/target/cfg_if/detect/bit.rs | 9 + tests/target/cfg_if/detect/cache.rs | 164 + tests/target/cfg_if/detect/error_macros.rs | 150 + tests/target/cfg_if/detect/mod.rs | 85 + tests/target/cfg_if/detect/os/aarch64.rs | 88 + .../cfg_if/detect/os/freebsd/aarch64.rs | 28 + tests/target/cfg_if/detect/os/freebsd/arm.rs | 27 + .../target/cfg_if/detect/os/freebsd/auxvec.rs | 94 + tests/target/cfg_if/detect/os/freebsd/mod.rs | 22 + .../cfg_if/detect/os/freebsd/powerpc.rs | 27 + .../target/cfg_if/detect/os/linux/aarch64.rs | 160 + tests/target/cfg_if/detect/os/linux/arm.rs | 52 + tests/target/cfg_if/detect/os/linux/auxvec.rs | 304 ++ .../target/cfg_if/detect/os/linux/cpuinfo.rs | 300 ++ tests/target/cfg_if/detect/os/linux/mips.rs | 31 + tests/target/cfg_if/detect/os/linux/mod.rs | 28 + .../target/cfg_if/detect/os/linux/powerpc.rs | 41 + tests/target/cfg_if/detect/os/other.rs | 9 + tests/target/cfg_if/detect/os/x86.rs | 367 ++ tests/target/cfg_if/lib.rs | 49 + tests/target/cfg_if/mod.rs | 5 + tests/target/cfg_mod/bar.rs | 3 + tests/target/cfg_mod/dir/dir1/dir2/wasm32.rs | 3 + tests/target/cfg_mod/dir/dir1/dir3/wasm32.rs | 3 + tests/target/cfg_mod/foo.rs | 3 + tests/target/cfg_mod/mod.rs | 10 + tests/target/cfg_mod/other.rs | 3 + tests/target/cfg_mod/wasm32.rs | 3 + tests/target/chains-visual.rs | 158 + tests/target/chains.rs | 306 ++ tests/target/chains_with_comment.rs | 137 + tests/target/closure-block-inside-macro.rs | 9 + tests/target/closure.rs | 256 ++ tests/target/comment-inside-const.rs | 9 + tests/target/comment-not-disappear.rs | 38 + tests/target/comment.rs | 93 + tests/target/comment2.rs | 5 + tests/target/comment3.rs | 6 + tests/target/comment4.rs | 51 + tests/target/comment5.rs | 16 + tests/target/comment6.rs | 14 + tests/target/comment_crlf_newline.rs | 4 + tests/target/comments-fn.rs | 38 + .../comments-in-lists/format-doc-comments.rs | 94 + .../comments-in-lists/wrap-comments-false.rs | 83 + .../wrap-comments-not-normalized.rs | 142 + .../comments-in-lists/wrap-comments-true.rs | 143 + tests/target/comments_unicode.rs | 140 + .../configs/blank_lines_lower_bound/1.rs | 16 + .../brace_style/fn_always_next_line.rs | 19 + .../brace_style/fn_prefer_same_line.rs | 16 + .../configs/brace_style/fn_same_line_where.rs | 17 + .../brace_style/item_always_next_line.rs | 25 + .../brace_style/item_prefer_same_line.rs | 18 + .../brace_style/item_same_line_where.rs | 19 + tests/target/configs/chain_width/always.rs | 29 + tests/target/configs/chain_width/small.rs | 32 + tests/target/configs/chain_width/tiny.rs | 26 + .../configs/combine_control_expr/false.rs | 128 + .../configs/combine_control_expr/true.rs | 116 + tests/target/configs/comment_width/above.rs | 8 + tests/target/configs/comment_width/below.rs | 7 + tests/target/configs/comment_width/ignore.rs | 7 + .../condense_wildcard_suffixes/false.rs | 6 + .../condense_wildcard_suffixes/true.rs | 6 + .../control_brace_style/always_next_line.rs | 18 + .../control_brace_style/always_same_line.rs | 14 + .../control_brace_style/closing_next_line.rs | 15 + .../configs/disable_all_formatting/false.rs | 10 + .../configs/disable_all_formatting/true.rs | 6 + .../doc_comment_code_block_width/100.rs | 16 + .../100_greater_max_width.rs | 29 + .../doc_comment_code_block_width/50.rs | 22 + .../configs/empty_item_single_line/false.rs | 14 + .../configs/empty_item_single_line/true.rs | 10 + .../enum_discrim_align_threshold/40.rs | 34 + .../configs/error_on_line_overflow/false.rs | 6 + .../configs/error_on_unformatted/false.rs | 12 + .../configs/fn_params_layout/compressed.rs | 22 + tests/target/configs/fn_params_layout/tall.rs | 32 + .../configs/fn_params_layout/vertical.rs | 42 + tests/target/configs/fn_single_line/false.rs | 11 + tests/target/configs/fn_single_line/true.rs | 9 + .../configs/force_explicit_abi/false.rs | 6 + .../target/configs/force_explicit_abi/true.rs | 6 + .../configs/force_multiline_block/false.rs | 20 + .../configs/force_multiline_block/true.rs | 22 + .../configs/format_generated_files/false.rs | 8 + .../configs/format_generated_files/true.rs | 6 + .../configs/format_macro_bodies/false.rs | 6 + .../configs/format_macro_bodies/true.rs | 6 + .../configs/format_macro_matchers/false.rs | 6 + .../configs/format_macro_matchers/true.rs | 6 + tests/target/configs/format_strings/false.rs | 8 + tests/target/configs/format_strings/true.rs | 9 + .../group_imports/One-merge_imports.rs | 14 + .../configs/group_imports/One-nested.rs | 6 + .../configs/group_imports/One-no_reorder.rs | 12 + tests/target/configs/group_imports/One.rs | 11 + .../StdExternalCrate-merge_imports.rs | 16 + .../group_imports/StdExternalCrate-nested.rs | 7 + .../StdExternalCrate-no_reorder.rs | 15 + .../StdExternalCrate-non_consecutive.rs | 18 + .../configs/group_imports/StdExternalCrate.rs | 13 + tests/target/configs/hard_tabs/false.rs | 6 + tests/target/configs/hard_tabs/true.rs | 6 + tests/target/configs/imports_indent/block.rs | 7 + .../imports_layout/horizontal_vertical.rs | 18 + .../configs/imports_layout/merge_mixed.rs | 5 + tests/target/configs/imports_layout/mixed.rs | 9 + .../target/configs/indent_style/block_args.rs | 47 + .../configs/indent_style/block_array.rs | 14 + .../target/configs/indent_style/block_call.rs | 151 + .../configs/indent_style/block_chain.rs | 12 + .../configs/indent_style/block_generic.rs | 22 + .../configs/indent_style/block_struct_lit.rs | 9 + .../indent_style/block_tab_spaces_call.rs | 14 + .../block_trailing_comma_call/one.rs | 12 + .../block_trailing_comma_call/two.rs | 14 + .../configs/indent_style/block_where_pred.rs | 12 + tests/target/configs/indent_style/default.rs | 11 + .../configs/indent_style/rfc_control.rs | 39 + .../target/configs/indent_style/rfc_where.rs | 12 + .../configs/indent_style/visual_args.rs | 40 + .../configs/indent_style/visual_array.rs | 12 + .../configs/indent_style/visual_call.rs | 13 + .../configs/indent_style/visual_chain.rs | 11 + .../configs/indent_style/visual_generics.rs | 20 + .../configs/indent_style/visual_struct_lit.rs | 7 + .../indent_style/visual_trailing_comma.rs | 7 + .../configs/indent_style/visual_where_pred.rs | 11 + .../target/configs/match_arm_blocks/false.rs | 12 + tests/target/configs/match_arm_blocks/true.rs | 13 + .../configs/match_arm_leading_pipes/always.rs | 27 + .../configs/match_arm_leading_pipes/never.rs | 27 + .../match_arm_leading_pipes/preserve.rs | 35 + .../match_block_trailing_comma/false.rs | 11 + .../match_block_trailing_comma/true.rs | 11 + tests/target/configs/merge_derives/true.rs | 40 + .../configs/normalize_comments/false.rs | 13 + .../target/configs/normalize_comments/true.rs | 13 + .../configs/normalize_doc_attributes/false.rs | 13 + .../configs/normalize_doc_attributes/true.rs | 13 + .../remove_nested_parens.rs | 5 + .../configs/reorder_impl_items/false.rs | 11 + .../target/configs/reorder_impl_items/true.rs | 11 + tests/target/configs/reorder_imports/false.rs | 7 + tests/target/configs/reorder_imports/true.rs | 19 + .../configs/reorder_modules/dolor/mod.rs | 1 + tests/target/configs/reorder_modules/false.rs | 7 + .../configs/reorder_modules/ipsum/mod.rs | 1 + .../configs/reorder_modules/lorem/mod.rs | 1 + .../target/configs/reorder_modules/sit/mod.rs | 1 + tests/target/configs/reorder_modules/true.rs | 7 + .../short_array_element_width_threshold/10.rs | 11 + .../short_array_element_width_threshold/20.rs | 8 + .../greater_than_max_width.rs | 12 + .../single_line_let_else_max_width/100.rs | 60 + .../single_line_let_else_max_width/50.rs | 62 + .../single_line_let_else_max_width/zero.rs | 66 + tests/target/configs/skip_children/foo/mod.rs | 3 + tests/target/configs/skip_children/true.rs | 4 + .../target/configs/space_before_colon/true.rs | 11 + .../configs/spaces_around_ranges/false.rs | 34 + .../configs/spaces_around_ranges/true.rs | 34 + .../struct_field_align_threshold/20.rs | 471 +++ .../configs/struct_lit_single_line/false.rs | 9 + tests/target/configs/tab_spaces/2.rs | 14 + tests/target/configs/tab_spaces/4.rs | 14 + tests/target/configs/trailing_comma/always.rs | 14 + tests/target/configs/trailing_comma/never.rs | 35 + .../target/configs/trailing_comma/vertical.rs | 14 + .../configs/trailing_semicolon/false.rs | 27 + .../target/configs/trailing_semicolon/true.rs | 27 + .../type_punctuation_density/compressed.rs | 41 + .../configs/type_punctuation_density/wide.rs | 41 + .../configs/use_field_init_shorthand/false.rs | 15 + .../configs/use_field_init_shorthand/true.rs | 15 + .../configs/use_small_heuristics/default.rs | 38 + .../configs/use_small_heuristics/max.rs | 25 + .../configs/use_small_heuristics/off.rs | 41 + .../target/configs/use_try_shorthand/false.rs | 6 + .../target/configs/use_try_shorthand/true.rs | 6 + .../true-with-brace-style.rs | 22 + .../target/configs/where_single_line/true.rs | 30 + tests/target/configs/wrap_comments/false.rs | 8 + tests/target/configs/wrap_comments/true.rs | 20 + tests/target/const_generics.rs | 37 + .../control-brace-style-always-next-line.rs | 50 + .../control-brace-style-always-same-line.rs | 40 + tests/target/doc-attrib.rs | 131 + tests/target/doc-comment-with-example.rs | 11 + tests/target/doc-of-generic-item.rs | 32 + tests/target/doc.rs | 5 + tests/target/dyn_trait.rs | 27 + .../else-if-brace-style-always-next-line.rs | 53 + .../else-if-brace-style-always-same-line.rs | 43 + .../else-if-brace-style-closing-next-line.rs | 49 + tests/target/empty-item-single-line-false.rs | 41 + ...mpty-tuple-no-conversion-to-unit-struct.rs | 12 + tests/target/empty_file.rs | 1 + tests/target/enum.rs | 289 ++ tests/target/existential_type.rs | 23 + tests/target/expr-block.rs | 305 ++ tests/target/expr-overflow-delimited.rs | 120 + tests/target/expr.rs | 657 ++++ tests/target/extern.rs | 97 + tests/target/extern_not_explicit.rs | 18 + tests/target/file-lines-1.rs | 30 + tests/target/file-lines-2.rs | 24 + tests/target/file-lines-3.rs | 25 + tests/target/file-lines-4.rs | 30 + tests/target/file-lines-5.rs | 17 + tests/target/file-lines-6.rs | 18 + tests/target/file-lines-7.rs | 21 + tests/target/file-lines-item.rs | 21 + .../target/fn-args-with-last-line-comment.rs | 24 + tests/target/fn-custom-2.rs | 77 + tests/target/fn-custom-3.rs | 71 + tests/target/fn-custom-4.rs | 26 + tests/target/fn-custom-6.rs | 71 + tests/target/fn-custom-7.rs | 36 + tests/target/fn-custom-8.rs | 77 + tests/target/fn-custom.rs | 19 + tests/target/fn-param-attributes.rs | 64 + tests/target/fn-simple.rs | 120 + tests/target/fn-single-line/version_one.rs | 71 + tests/target/fn-single-line/version_two.rs | 67 + tests/target/fn-ty.rs | 14 + tests/target/fn.rs | 120 + tests/target/fn_args_indent-block.rs | 143 + tests/target/fn_args_layout-vertical.rs | 39 + tests/target/fn_once.rs | 8 + tests/target/format_strings/issue-202.rs | 25 + tests/target/format_strings/issue-2833.rs | 15 + tests/target/format_strings/issue-3263.rs | 26 + tests/target/format_strings/issue-687.rs | 10 + tests/target/format_strings/issue564.rs | 7 + tests/target/hard-tabs.rs | 98 + tests/target/hello.rs | 8 + tests/target/hex_literal_lower.rs | 5 + tests/target/hex_literal_preserve.rs | 5 + tests/target/hex_literal_upper.rs | 5 + tests/target/if_while_or_patterns.rs | 38 + tests/target/immovable_generators.rs | 7 + tests/target/impl.rs | 43 + tests/target/impls.rs | 252 ++ .../target/imports/import-fencepost-length.rs | 7 + tests/target/imports/imports-impl-only-use.rs | 4 + .../imports-reorder-lines-and-items.rs | 7 + tests/target/imports/imports-reorder-lines.rs | 31 + tests/target/imports/imports-reorder.rs | 5 + tests/target/imports/imports.rs | 129 + tests/target/imports/imports_2021_edition.rs | 3 + tests/target/imports/imports_block_indent.rs | 4 + .../imports/imports_granularity_crate.rs | 59 + .../imports_granularity_default-with-dups.rs | 6 + ...m-with-dups-StdExternalCrate-no-reorder.rs | 7 + .../imports_granularity_item-with-dups.rs | 5 + .../imports/imports_granularity_item.rs | 45 + .../imports/imports_granularity_module.rs | 55 + tests/target/imports_granularity_one.rs | 109 + .../imports_raw_identifiers/version_One.rs | 5 + .../imports_raw_identifiers/version_Two.rs | 5 + tests/target/indented-impl.rs | 13 + tests/target/inner-module-path/b.rs | 1 + tests/target/inner-module-path/c/d.rs | 1 + tests/target/inner-module-path/lib.rs | 8 + .../invalid-rust-code-in-doc-comment.rs | 18 + tests/target/issue-1021.rs | 22 + tests/target/issue-1049.rs | 29 + tests/target/issue-1055.rs | 3 + tests/target/issue-1096.rs | 71 + tests/target/issue-1111.rs | 1 + tests/target/issue-1113.rs | 33 + tests/target/issue-1120.rs | 11 + tests/target/issue-1124.rs | 21 + tests/target/issue-1127.rs | 25 + tests/target/issue-1158.rs | 3 + tests/target/issue-1177.rs | 7 + tests/target/issue-1192.rs | 3 + tests/target/issue-1210/a.rs | 16 + tests/target/issue-1210/b.rs | 16 + tests/target/issue-1210/c.rs | 7 + tests/target/issue-1210/d.rs | 4 + tests/target/issue-1210/e.rs | 11 + tests/target/issue-1211.rs | 13 + tests/target/issue-1214.rs | 8 + tests/target/issue-1216.rs | 5 + tests/target/issue-1239.rs | 11 + tests/target/issue-1247.rs | 8 + tests/target/issue-1255.rs | 10 + tests/target/issue-1278.rs | 9 + tests/target/issue-1350.rs | 14 + tests/target/issue-1366.rs | 13 + tests/target/issue-1397.rs | 25 + tests/target/issue-1468.rs | 29 + tests/target/issue-1598.rs | 6 + tests/target/issue-1624.rs | 6 + tests/target/issue-1681.rs | 21 + tests/target/issue-1693.rs | 10 + tests/target/issue-1703.rs | 9 + tests/target/issue-1800.rs | 3 + tests/target/issue-1802.rs | 9 + tests/target/issue-1824.rs | 5 + tests/target/issue-1914.rs | 7 + tests/target/issue-2025.rs | 4 + tests/target/issue-2103.rs | 14 + tests/target/issue-2111.rs | 26 + tests/target/issue-2123.rs | 6 + tests/target/issue-2164.rs | 135 + tests/target/issue-2179/one.rs | 37 + tests/target/issue-2179/two.rs | 40 + tests/target/issue-2197.rs | 17 + tests/target/issue-2256.rs | 7 + tests/target/issue-2324.rs | 7 + tests/target/issue-2329.rs | 30 + tests/target/issue-2342.rs | 6 + tests/target/issue-2346.rs | 4 + tests/target/issue-2401.rs | 7 + tests/target/issue-2445.rs | 21 + tests/target/issue-2446.rs | 9 + tests/target/issue-2479.rs | 12 + tests/target/issue-2482/a.rs | 9 + tests/target/issue-2482/b.rs | 1 + tests/target/issue-2482/c.rs | 1 + tests/target/issue-2496.rs | 14 + tests/target/issue-2520.rs | 13 + tests/target/issue-2523.rs | 20 + tests/target/issue-2526.rs | 8 + .../issue-2534/format_macro_matchers_false.rs | 6 + .../issue-2534/format_macro_matchers_true.rs | 6 + tests/target/issue-2551.rs | 3 + tests/target/issue-2554.rs | 13 + tests/target/issue-2582.rs | 1 + tests/target/issue-2641.rs | 3 + tests/target/issue-2644.rs | 8 + tests/target/issue-2673-nonmodrs-mods/foo.rs | 4 + .../issue-2673-nonmodrs-mods/foo/bar.rs | 1 + tests/target/issue-2673-nonmodrs-mods/lib.rs | 6 + tests/target/issue-2728.rs | 8 + tests/target/issue-2759.rs | 65 + tests/target/issue-2761.rs | 15 + tests/target/issue-2781.rs | 11 + tests/target/issue-2794.rs | 12 + tests/target/issue-2810.rs | 14 + tests/target/issue-2835.rs | 4 + tests/target/issue-2863.rs | 54 + tests/target/issue-2869.rs | 41 + tests/target/issue-2896.rs | 165 + tests/target/issue-2916.rs | 2 + tests/target/issue-2917/minimal.rs | 8 + tests/target/issue-2917/packed_simd.rs | 63 + tests/target/issue-2922.rs | 10 + tests/target/issue-2927-2.rs | 7 + tests/target/issue-2927.rs | 7 + tests/target/issue-2930.rs | 5 + tests/target/issue-2936.rs | 21 + tests/target/issue-2941.rs | 5 + tests/target/issue-2955.rs | 6 + tests/target/issue-2973.rs | 158 + tests/target/issue-2976.rs | 3 + tests/target/issue-2977/block.rs | 11 + tests/target/issue-2977/impl.rs | 44 + tests/target/issue-2977/item.rs | 11 + tests/target/issue-2977/trait.rs | 44 + tests/target/issue-2985.rs | 36 + tests/target/issue-2995.rs | 7 + tests/target/issue-3029.rs | 94 + tests/target/issue-3032.rs | 36 + tests/target/issue-3038.rs | 29 + tests/target/issue-3043.rs | 5 + tests/target/issue-3049.rs | 45 + tests/target/issue-3055/backtick.rs | 10 + tests/target/issue-3055/empty-code-block.rs | 18 + tests/target/issue-3055/original.rs | 42 + tests/target/issue-3059.rs | 8 + tests/target/issue-3066.rs | 7 + tests/target/issue-3105.rs | 48 + tests/target/issue-3118.rs | 11 + tests/target/issue-3124.rs | 14 + tests/target/issue-3131.rs | 8 + tests/target/issue-3132.rs | 15 + tests/target/issue-3153.rs | 9 + tests/target/issue-3158.rs | 74 + tests/target/issue-3182.rs | 10 + tests/target/issue-3184.rs | 5 + tests/target/issue-3194.rs | 52 + tests/target/issue-3198.rs | 67 + tests/target/issue-3213/version_one.rs | 13 + tests/target/issue-3213/version_two.rs | 13 + tests/target/issue-3217.rs | 22 + tests/target/issue-3224.rs | 11 + tests/target/issue-3227/one.rs | 13 + tests/target/issue-3227/two.rs | 15 + tests/target/issue-3234.rs | 14 + tests/target/issue-3241.rs | 11 + tests/target/issue-3253/bar.rs | 2 + tests/target/issue-3253/foo.rs | 3 + tests/target/issue-3253/lib.rs | 14 + tests/target/issue-3253/paths/bar_foo.rs | 3 + tests/target/issue-3253/paths/excluded.rs | 17 + tests/target/issue-3253/paths/foo_bar.rs | 5 + tests/target/issue-3265.rs | 14 + tests/target/issue-3270/one.rs | 12 + tests/target/issue-3270/two.rs | 12 + tests/target/issue-3270/wrap.rs | 13 + tests/target/issue-3272/v1.rs | 15 + tests/target/issue-3272/v2.rs | 17 + tests/target/issue-3278/version_one.rs | 8 + tests/target/issue-3278/version_two.rs | 8 + tests/target/issue-3295/two.rs | 14 + tests/target/issue-3302.rs | 43 + tests/target/issue-3304.rs | 42 + tests/target/issue-3314.rs | 5 + tests/target/issue-3343.rs | 44 + tests/target/issue-3423.rs | 5 + tests/target/issue-3434/lib.rs | 57 + tests/target/issue-3434/no_entry.rs | 19 + tests/target/issue-3434/not_skip_macro.rs | 8 + tests/target/issue-3442.rs | 10 + tests/target/issue-3465.rs | 42 + tests/target/issue-3494/crlf.rs | 8 + tests/target/issue-3494/lf.rs | 8 + tests/target/issue-3499.rs | 1 + tests/target/issue-3508.rs | 22 + tests/target/issue-3515.rs | 6 + tests/target/issue-3532.rs | 6 + tests/target/issue-3539.rs | 8 + tests/target/issue-3554.rs | 4 + tests/target/issue-3567.rs | 3 + tests/target/issue-3568.rs | 1 + tests/target/issue-3585/extern_crate.rs | 10 + .../issue-3585/reorder_imports_disabled.rs | 8 + .../issue-3585/reorder_imports_enabled.rs | 8 + tests/target/issue-3585/use.rs | 5 + tests/target/issue-3595.rs | 4 + tests/target/issue-3601.rs | 11 + tests/target/issue-3614/version_one.rs | 15 + tests/target/issue-3614/version_two.rs | 8 + tests/target/issue-3636.rs | 8 + tests/target/issue-3639.rs | 5 + tests/target/issue-3645.rs | 3 + tests/target/issue-3651.rs | 7 + tests/target/issue-3665/lib.rs | 31 + tests/target/issue-3665/not_skip_attribute.rs | 4 + tests/target/issue-3665/sub_mod.rs | 13 + tests/target/issue-3672.rs | 3 + tests/target/issue-3675.rs | 6 + tests/target/issue-3701/one.rs | 12 + tests/target/issue-3701/two.rs | 14 + tests/target/issue-3709.rs | 10 + tests/target/issue-3711.rs | 6 + tests/target/issue-3717.rs | 7 + tests/target/issue-3718.rs | 7 + tests/target/issue-3740.rs | 8 + tests/target/issue-3741.rs | 5 + tests/target/issue-3750.rs | 15 + tests/target/issue-3751.rs | 10 + tests/target/issue-3759.rs | 27 + tests/target/issue-3779/ice.rs | 3 + tests/target/issue-3779/lib.rs | 9 + tests/target/issue-3786.rs | 9 + tests/target/issue-3787.rs | 13 + tests/target/issue-3815.rs | 4 + .../issue-3840/version-one_hard-tabs.rs | 25 + .../issue-3840/version-one_soft-tabs.rs | 23 + .../issue-3840/version-two_hard-tabs.rs | 26 + .../issue-3840/version-two_soft-tabs.rs | 25 + tests/target/issue-3845.rs | 8 + tests/target/issue-3882.rs | 4 + tests/target/issue-3974.rs | 10 + .../issue-3987/format_macro_bodies_false.rs | 26 + .../issue-3987/format_macro_bodies_true.rs | 21 + tests/target/issue-4018.rs | 11 + tests/target/issue-4020.rs | 9 + tests/target/issue-4029.rs | 7 + tests/target/issue-4036/one.rs | 12 + tests/target/issue-4036/three.rs | 17 + tests/target/issue-4036/two.rs | 16 + tests/target/issue-4041.rs | 6 + tests/target/issue-4068.rs | 3 + tests/target/issue-4079.rs | 11 + tests/target/issue-4115.rs | 8 + tests/target/issue-4120.rs | 85 + tests/target/issue-4152.rs | 18 + tests/target/issue-4159.rs | 18 + tests/target/issue-4210-disabled.rs | 15 + tests/target/issue-4210.rs | 15 + tests/target/issue-4243.rs | 28 + tests/target/issue-4244.rs | 20 + tests/target/issue-4245.rs | 34 + tests/target/issue-4310.rs | 9 + tests/target/issue-4312.rs | 22 + tests/target/issue-4313.rs | 5 + tests/target/issue-4382.rs | 10 + tests/target/issue-4398.rs | 19 + tests/target/issue-4427.rs | 30 + tests/target/issue-447.rs | 40 + tests/target/issue-4530.rs | 9 + tests/target/issue-4577.rs | 15 + tests/target/issue-4603.rs | 47 + tests/target/issue-4615/minimum_example.rs | 5 + tests/target/issue-4643.rs | 19 + tests/target/issue-4646.rs | 20 + tests/target/issue-4656/format_me_please.rs | 1 + tests/target/issue-4656/lib.rs | 7 + tests/target/issue-4656/lib2.rs | 3 + tests/target/issue-4689/one.rs | 150 + tests/target/issue-4689/two.rs | 152 + tests/target/issue-4791/buggy.rs | 14 + tests/target/issue-4791/issue_4928.rs | 70 + tests/target/issue-4791/no_trailing_comma.rs | 8 + tests/target/issue-4791/trailing_comma.rs | 14 + tests/target/issue-4816/lib.rs | 35 + tests/target/issue-4908-2.rs | 20 + tests/target/issue-4908.rs | 34 + .../target/issue-4926/deeply_nested_struct.rs | 38 + ...ply_nested_struct_with_long_field_names.rs | 44 + .../deeply_nested_struct_with_many_fields.rs | 54 + tests/target/issue-4926/enum_struct_field.rs | 41 + tests/target/issue-4926/minimum_example.rs | 10 + .../struct_with_long_field_names.rs | 24 + .../issue-4926/struct_with_many_fields.rs | 34 + tests/target/issue-4984/minimum_example.rs | 2 + tests/target/issue-4984/multi_line_derive.rs | 26 + .../issue-4984/multiple_comments_within.rs | 11 + tests/target/issue-4984/should_not_change.rs | 5 + tests/target/issue-5005/minimum_example.rs | 9 + tests/target/issue-5009/1_minimum_example.rs | 4 + .../2_many_in_connectors_in_pattern.rs | 3 + ...sted_for_loop_with_connector_in_pattern.rs | 5 + .../4_nested_for_loop_with_if_elseif_else.rs | 13 + ...r_loop_with_connector_in_if_elseif_else.rs | 15 + ...sted_for_loop_with_connector_in_pattern.rs | 32 + tests/target/issue-5011.rs | 8 + .../issue-5012/trailing_comma_always.rs | 8 + .../target/issue-5012/trailing_comma_never.rs | 8 + tests/target/issue-5023.rs | 23 + tests/target/issue-5030.rs | 21 + tests/target/issue-5033/minimum_example.rs | 8 + tests/target/issue-5033/nested_modules.rs | 11 + .../multi-line_comment_with_trailing_comma.rs | 24 + ...lti-line_comment_without_trailing_comma.rs | 24 + ...single-line_comment_with_trailing_comma.rs | 7 + ...gle-line_comment_without_trailing_comma.rs | 7 + ...railing_comma_always_struct_lit_width_0.rs | 10 + ...trailing_comma_never_struct_lit_width_0.rs | 10 + ..._line_struct_with_trailing_comma_always.rs | 10 + ...i_line_struct_with_trailing_comma_never.rs | 10 + .../issue-5066/with_trailing_comma_always.rs | 5 + .../issue-5066/with_trailing_comma_never.rs | 5 + ...nested_long_comment_wrap_comments_false.rs | 33 + ..._nested_long_comment_wrap_comments_true.rs | 49 + ...line_itemized_block_wrap_comments_false.rs | 17 + ..._line_itemized_block_wrap_comments_true.rs | 17 + ...with_itemized_block_wrap_comments_false.rs | 37 + ..._with_itemized_block_wrap_comments_true.rs | 37 + ...line_itemized_block_wrap_comments_false.rs | 9 + ..._line_itemized_block_wrap_comments_true.rs | 9 + ...long_itemized_block_wrap_comments_false.rs | 19 + ..._long_itemized_block_wrap_comments_true.rs | 27 + ..._with_empty_comment_wrap_comments_false.rs | 17 + ...t_with_empty_comment_wrap_comments_true.rs | 17 + .../very_long_comment_wrap_comments_false.rs | 13 + .../very_long_comment_wrap_comments_true.rs | 21 + tests/target/issue-5095.rs | 27 + tests/target/issue-510.rs | 41 + .../attributes_in_formal_fuction_parameter.rs | 6 + .../long_parameter_in_different_positions.rs | 24 + tests/target/issue-5125/minimum_example.rs | 6 + .../with_leading_and_inline_comments.rs | 7 + tests/target/issue-5151/minimum_example.rs | 16 + .../indented_itemized_markdown_blockquote.rs | 6 + .../nested_itemized_markdown_blockquote.rs | 18 + .../support_itemized_markdown_blockquote.rs | 6 + tests/target/issue-5234.rs | 47 + .../markdown_header_wrap_comments_false.rs | 11 + .../markdown_header_wrap_comments_true.rs | 14 + tests/target/issue-5260.rs | 13 + .../target/issue-5270/merge_derives_false.rs | 62 + tests/target/issue-5270/merge_derives_true.rs | 60 + tests/target/issue-5358.rs | 4 + tests/target/issue-539.rs | 3 + tests/target/issue-5488.rs | 17 + tests/target/issue-5586.rs | 177 + tests/target/issue-64.rs | 7 + tests/target/issue-683.rs | 3 + tests/target/issue-691.rs | 9 + tests/target/issue-770.rs | 10 + tests/target/issue-811.rs | 19 + tests/target/issue-831.rs | 9 + tests/target/issue-850.rs | 1 + tests/target/issue-855.rs | 27 + tests/target/issue-913.rs | 22 + tests/target/issue-945.rs | 17 + tests/target/issue-977.rs | 16 + tests/target/issue_1306.rs | 33 + tests/target/issue_3033.rs | 2 + tests/target/issue_3245.rs | 4 + tests/target/issue_3561.rs | 7 + tests/target/issue_3839.rs | 8 + tests/target/issue_3844.rs | 3 + tests/target/issue_3853.rs | 47 + tests/target/issue_3854.rs | 3 + tests/target/issue_3868.rs | 9 + tests/target/issue_3934.rs | 8 + tests/target/issue_3937.rs | 13 + tests/target/issue_4031.rs | 21 + tests/target/issue_4032.rs | 18 + tests/target/issue_4049.rs | 26 + tests/target/issue_4057.rs | 15 + tests/target/issue_4086.rs | 2 + tests/target/issue_4110.rs | 56 + tests/target/issue_4257.rs | 15 + tests/target/issue_4322.rs | 5 + tests/target/issue_4350.rs | 13 + tests/target/issue_4374.rs | 13 + tests/target/issue_4467.rs | 6 + tests/target/issue_4475.rs | 29 + tests/target/issue_4522.rs | 6 + tests/target/issue_4528.rs | 8 + tests/target/issue_4545.rs | 5 + tests/target/issue_4573.rs | 245 ++ tests/target/issue_4579.rs | 16 + tests/target/issue_4584.rs | 32 + tests/target/issue_4636.rs | 13 + tests/target/issue_4675.rs | 8 + tests/target/issue_4823.rs | 5 + tests/target/issue_4850.rs | 4 + tests/target/issue_4854.rs | 115 + tests/target/issue_4868.rs | 17 + tests/target/issue_4911.rs | 8 + tests/target/issue_4936.rs | 10 + tests/target/issue_4943.rs | 8 + tests/target/issue_4954.rs | 7 + tests/target/issue_4963.rs | 9 + tests/target/issue_5027.rs | 17 + tests/target/issue_5086.rs | 2 + tests/target/issue_5273.rs | 3 + tests/target/issue_5399.rs | 48 + tests/target/issue_5668.rs | 8 + tests/target/issue_5686.rs | 42 + tests/target/issue_5691.rs | 16 + tests/target/issue_5728.rs | 5 + tests/target/issue_5729.rs | 5 + .../item-brace-style-always-next-line.rs | 71 + .../item-brace-style-prefer-same-line.rs | 35 + .../item-brace-style-same-line-where.rs | 37 + tests/target/itemized-blocks/no_wrap.rs | 81 + tests/target/itemized-blocks/rewrite_fail.rs | 14 + tests/target/itemized-blocks/urls.rs | 25 + tests/target/itemized-blocks/wrap.rs | 149 + tests/target/label_break.rs | 28 + tests/target/large-block.rs | 5 + tests/target/large_vec.rs | 42 + tests/target/lazy_static.rs | 49 + tests/target/let_else.rs | 254 ++ tests/target/long-fn-1/version_one.rs | 29 + tests/target/long-fn-1/version_two.rs | 29 + tests/target/long-match-arms-brace-newline.rs | 15 + tests/target/long-use-statement-issue-3154.rs | 3 + tests/target/long_field_access.rs | 4 + tests/target/loop.rs | 34 + tests/target/macro_not_expr.rs | 7 + tests/target/macro_rules.rs | 360 ++ tests/target/macro_rules_semi.rs | 22 + tests/target/macros.rs | 1058 +++++ tests/target/markdown-comment-with-options.rs | 17 + tests/target/markdown-comment.rs | 15 + tests/target/match-block-trailing-comma.rs | 26 + tests/target/match-flattening.rs | 23 + tests/target/match-nowrap-trailing-comma.rs | 17 + tests/target/match-nowrap.rs | 13 + tests/target/match.rs | 629 +++ tests/target/match_overflow_expr.rs | 50 + tests/target/max-line-length-in-chars.rs | 4 + tests/target/merge_imports_true_compat.rs | 3 + tests/target/mod-1.rs | 37 + tests/target/mod-2.rs | 5 + tests/target/mod_skip_child.rs | 2 + tests/target/mulit-file.rs | 10 + tests/target/multiline_string_in_macro_def.rs | 14 + tests/target/multiple.rs | 180 + tests/target/negative-bounds.rs | 11 + tests/target/negative-impl.rs | 14 + tests/target/nested-if-else.rs | 11 + tests/target/nested-visual-block.rs | 60 + tests/target/nested_skipped/mod.rs | 3 + tests/target/nestedmod/mod.rs | 12 + tests/target/nestedmod/mod2a.rs | 4 + tests/target/nestedmod/mod2b.rs | 2 + tests/target/nestedmod/mod2c.rs | 3 + tests/target/nestedmod/mymod1/mod3a.rs | 2 + tests/target/nestedmod/submod2/a.rs | 6 + tests/target/nestedmod/submod2/mod.rs | 5 + tests/target/no_arg_with_commnet.rs | 1 + tests/target/no_new_line_beginning.rs | 1 + ...es_should_not_imply_format_doc_comments.rs | 15 + .../normalize_multiline_doc_attribute.rs | 12 + tests/target/obsolete_in_place.rs | 9 + tests/target/one_line_if_v1.rs | 46 + tests/target/one_line_if_v2.rs | 38 + tests/target/other.rs | 5 + tests/target/paren.rs | 6 + tests/target/path_clarity/foo.rs | 2 + tests/target/path_clarity/foo/bar.rs | 3 + tests/target/paths.rs | 28 + tests/target/pattern-condense-wildcards.rs | 12 + tests/target/pattern.rs | 98 + .../preserves_carriage_return_for_unix.rs | 2 + .../preserves_carriage_return_for_windows.rs | 2 + tests/target/pub-restricted.rs | 51 + tests/target/raw_identifiers.rs | 66 + tests/target/remove_blank_lines.rs | 28 + tests/target/reorder-impl-items.rs | 15 + ...t_string_when_format_strings_is_not_set.rs | 16 + tests/target/single-line-if-else.rs | 58 + tests/target/single-line-macro/v1.rs | 10 + tests/target/single-line-macro/v2.rs | 14 + tests/target/skip.rs | 87 + tests/target/skip/foo.rs | 5 + tests/target/skip/main.rs | 5 + .../target/skip/preserve_trailing_comment.rs | 7 + tests/target/skip_macro_invocations/all.rs | 11 + .../skip_macro_invocations/all_and_name.rs | 11 + tests/target/skip_macro_invocations/empty.rs | 11 + tests/target/skip_macro_invocations/name.rs | 11 + .../skip_macro_invocations/name_unknown.rs | 6 + tests/target/skip_macro_invocations/names.rs | 16 + .../path_qualified_invocation_mismatch.rs | 6 + .../path_qualified_match.rs | 6 + .../path_qualified_name_mismatch.rs | 6 + .../use_alias_examples.rs | 32 + tests/target/skip_mod.rs | 3 + tests/target/soft-wrapping.rs | 15 + tests/target/space-not-before-newline.rs | 8 + tests/target/spaces-around-ranges.rs | 15 + tests/target/statements.rs | 42 + tests/target/static.rs | 27 + tests/target/string-lit-2.rs | 25 + tests/target/string-lit-custom.rs | 20 + tests/target/string-lit.rs | 63 + tests/target/string_punctuation.rs | 24 + tests/target/struct-field-attributes.rs | 62 + tests/target/struct_field_doc_comment.rs | 69 + tests/target/struct_lits.rs | 190 + tests/target/struct_lits_multiline.rs | 117 + tests/target/struct_lits_visual.rs | 49 + tests/target/struct_lits_visual_multiline.rs | 49 + tests/target/struct_tuple_visual.rs | 36 + tests/target/structs.rs | 358 ++ tests/target/trailing-comma-never.rs | 35 + tests/target/trailing_commas.rs | 78 + tests/target/trailing_comments/hard_tabs.rs | 30 + tests/target/trailing_comments/soft_tabs.rs | 30 + tests/target/trait.rs | 220 ++ tests/target/try-conversion.rs | 28 + tests/target/try_block.rs | 29 + tests/target/tuple.rs | 100 + tests/target/tuple_v2.rs | 5 + tests/target/type.rs | 175 + tests/target/type_alias.rs | 76 + tests/target/unicode.rs | 30 + tests/target/unindent_if_else_cond_comment.rs | 27 + tests/target/unions.rs | 198 + tests/target/unsafe-mod.rs | 7 + tests/target/visibility.rs | 8 + tests/target/visual-fn-type.rs | 9 + tests/target/where-clause-rfc.rs | 156 + tests/target/where-clause.rs | 107 + tests/target/width-heuristics.rs | 24 + ...ts_should_not_imply_format_doc_comments.rs | 16 + tests/writemode/source/fn-single-line.rs | 80 + tests/writemode/source/json.rs | 80 + tests/writemode/source/modified.rs | 14 + tests/writemode/source/stdin.rs | 6 + tests/writemode/target/checkstyle.xml | 2 + tests/writemode/target/modified.txt | 5 + tests/writemode/target/output.json | 1 + tests/writemode/target/stdin.json | 1 + tests/writemode/target/stdin.xml | 2 + triagebot.toml | 1 + 1613 files changed, 89154 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .github/workflows/check_diff.yml create mode 100644 .github/workflows/integration.yml create mode 100644 .github/workflows/linux.yml create mode 100644 .github/workflows/mac.yml create mode 100644 .github/workflows/rustdoc_check.yml create mode 100644 .github/workflows/upload-assets.yml create mode 100644 .github/workflows/windows.yml create mode 100644 .gitignore create mode 100644 CHANGELOG.md create mode 100644 CODE_OF_CONDUCT.md create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 Configurations.md create mode 100644 Contributing.md create mode 100644 Design.md create mode 100644 LICENSE-APACHE create mode 100644 LICENSE-MIT create mode 100644 Makefile.toml create mode 100644 Processes.md create mode 100644 README.md create mode 100644 atom.md create mode 100755 bootstrap.sh create mode 100644 build.rs create mode 100755 ci/build_and_test.bat create mode 100755 ci/build_and_test.sh create mode 100755 ci/check_diff.sh create mode 100755 ci/integration.sh create mode 100644 config_proc_macro/.gitignore create mode 100644 config_proc_macro/Cargo.lock create mode 100644 config_proc_macro/Cargo.toml create mode 100644 config_proc_macro/src/attrs.rs create mode 100644 config_proc_macro/src/config_type.rs create mode 100644 config_proc_macro/src/item_enum.rs create mode 100644 config_proc_macro/src/item_struct.rs create mode 100644 config_proc_macro/src/lib.rs create mode 100644 config_proc_macro/src/utils.rs create mode 100644 config_proc_macro/tests/smoke.rs create mode 100644 docs/index.html create mode 100644 intellij.md create mode 100644 rust-toolchain create mode 100644 rustfmt.toml create mode 100644 src/attr.rs create mode 100644 src/attr/doc_comment.rs create mode 100644 src/bin/main.rs create mode 100644 src/cargo-fmt/main.rs create mode 100644 src/cargo-fmt/test/message_format.rs create mode 100644 src/cargo-fmt/test/mod.rs create mode 100644 src/cargo-fmt/test/targets.rs create mode 100644 src/chains.rs create mode 100644 src/closures.rs create mode 100644 src/comment.rs create mode 100644 src/config/config_type.rs create mode 100644 src/config/file_lines.rs create mode 100644 src/config/lists.rs create mode 100644 src/config/macro_names.rs create mode 100644 src/config/mod.rs create mode 100644 src/config/options.rs create mode 100644 src/coverage.rs create mode 100644 src/emitter.rs create mode 100644 src/emitter/checkstyle.rs create mode 100644 src/emitter/checkstyle/xml.rs create mode 100644 src/emitter/diff.rs create mode 100644 src/emitter/files.rs create mode 100644 src/emitter/files_with_backup.rs create mode 100644 src/emitter/json.rs create mode 100644 src/emitter/modified_lines.rs create mode 100644 src/emitter/stdout.rs create mode 100644 src/expr.rs create mode 100644 src/format-diff/main.rs create mode 100644 src/format-diff/test/bindgen.diff create mode 100644 src/format_report_formatter.rs create mode 100644 src/formatting.rs create mode 100644 src/formatting/generated.rs create mode 100644 src/formatting/newline_style.rs create mode 100644 src/git-rustfmt/main.rs create mode 100644 src/ignore_path.rs create mode 100644 src/imports.rs create mode 100644 src/items.rs create mode 100644 src/lib.rs create mode 100644 src/lists.rs create mode 100644 src/macros.rs create mode 100644 src/matches.rs create mode 100644 src/missed_spans.rs create mode 100644 src/modules.rs create mode 100644 src/modules/visitor.rs create mode 100644 src/overflow.rs create mode 100644 src/pairs.rs create mode 100644 src/parse/macros/asm.rs create mode 100644 src/parse/macros/cfg_if.rs create mode 100644 src/parse/macros/lazy_static.rs create mode 100644 src/parse/macros/mod.rs create mode 100644 src/parse/mod.rs create mode 100644 src/parse/parser.rs create mode 100644 src/parse/session.rs create mode 100644 src/patterns.rs create mode 100644 src/release_channel.rs create mode 100644 src/reorder.rs create mode 100644 src/rewrite.rs create mode 100644 src/rustfmt_diff.rs create mode 100644 src/shape.rs create mode 100644 src/skip.rs create mode 100644 src/source_file.rs create mode 100644 src/source_map.rs create mode 100644 src/spanned.rs create mode 100644 src/stmt.rs create mode 100644 src/string.rs create mode 100644 src/syntux.rs create mode 100644 src/test/configuration_snippet.rs create mode 100644 src/test/mod.rs create mode 100644 src/test/mod_resolver.rs create mode 100644 src/test/parser.rs create mode 100644 src/types.rs create mode 100644 src/utils.rs create mode 100644 src/vertical.rs create mode 100644 src/visitor.rs create mode 100644 tests/cargo-fmt/main.rs create mode 100644 tests/cargo-fmt/source/divergent-crate-dir-names/Cargo.toml create mode 100644 tests/cargo-fmt/source/divergent-crate-dir-names/dependency-dir-name/Cargo.toml create mode 100644 tests/cargo-fmt/source/divergent-crate-dir-names/dependency-dir-name/src/lib.rs create mode 100644 tests/cargo-fmt/source/divergent-crate-dir-names/dependency-dir-name/subdep-dir-name/Cargo.toml create mode 100644 tests/cargo-fmt/source/divergent-crate-dir-names/dependency-dir-name/subdep-dir-name/src/lib.rs create mode 100644 tests/cargo-fmt/source/divergent-crate-dir-names/src/main.rs create mode 100644 tests/cargo-fmt/source/issue_3164/Cargo.toml create mode 100644 tests/cargo-fmt/source/issue_3164/src/main.rs create mode 100644 tests/cargo-fmt/source/workspaces/path-dep-above/e/Cargo.toml create mode 100644 tests/cargo-fmt/source/workspaces/path-dep-above/e/src/main.rs create mode 100644 tests/cargo-fmt/source/workspaces/path-dep-above/ws/Cargo.toml create mode 100644 tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/Cargo.toml create mode 100644 tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/d/Cargo.toml create mode 100644 tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/d/f/Cargo.toml create mode 100644 tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/d/f/src/lib.rs create mode 100644 tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/d/src/lib.rs create mode 100644 tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/src/main.rs create mode 100644 tests/cargo-fmt/source/workspaces/path-dep-above/ws/b/Cargo.toml create mode 100644 tests/cargo-fmt/source/workspaces/path-dep-above/ws/b/src/main.rs create mode 100644 tests/cargo-fmt/source/workspaces/path-dep-above/ws/c/Cargo.toml create mode 100644 tests/cargo-fmt/source/workspaces/path-dep-above/ws/c/src/lib.rs create mode 100644 tests/config/disable_all_formatting.toml create mode 100644 tests/config/issue-1111.toml create mode 100644 tests/config/issue-2641.toml create mode 100644 tests/config/issue-3779.toml create mode 100644 tests/config/skip_children.toml create mode 100644 tests/config/small_tabs.toml create mode 100644 tests/coverage/source/comments.rs create mode 100644 tests/coverage/target/comments.rs create mode 100644 tests/mod-resolver/issue-4874/bar/baz.rs create mode 100644 tests/mod-resolver/issue-4874/foo.rs create mode 100644 tests/mod-resolver/issue-4874/foo/qux.rs create mode 100644 tests/mod-resolver/issue-4874/main.rs create mode 100644 tests/mod-resolver/issue-5063/foo.rs create mode 100644 tests/mod-resolver/issue-5063/foo/bar/baz.rs create mode 100644 tests/mod-resolver/issue-5063/main.rs create mode 100644 tests/mod-resolver/issue-5167/src/a.rs create mode 100644 tests/mod-resolver/issue-5167/src/a/mod.rs create mode 100644 tests/mod-resolver/issue-5167/src/lib.rs create mode 100644 tests/mod-resolver/issue-5198/a.rs create mode 100644 tests/mod-resolver/issue-5198/lib.rs create mode 100644 tests/mod-resolver/issue-5198/lib/b.rs create mode 100644 tests/mod-resolver/issue-5198/lib/c/d.rs create mode 100644 tests/mod-resolver/issue-5198/lib/c/d/explanation.txt create mode 100644 tests/mod-resolver/issue-5198/lib/c/d/f.rs create mode 100644 tests/mod-resolver/issue-5198/lib/c/d/g/mod.rs create mode 100644 tests/mod-resolver/issue-5198/lib/c/e.rs create mode 100644 tests/mod-resolver/issue-5198/lib/c/mod.rs create mode 100644 tests/mod-resolver/issue-5198/lib/explanation.txt create mode 100644 tests/mod-resolver/module-not-found/bad_path_attribute/lib.rs create mode 100644 tests/mod-resolver/module-not-found/relative_module/a.rs create mode 100644 tests/mod-resolver/module-not-found/relative_module/lib.rs create mode 100644 tests/mod-resolver/module-not-found/sibling_module/lib.rs create mode 100644 tests/mod-resolver/skip-files-issue-5065/foo.rs create mode 100644 tests/mod-resolver/skip-files-issue-5065/foo/bar/baz.rs create mode 100644 tests/mod-resolver/skip-files-issue-5065/main.rs create mode 100644 tests/mod-resolver/skip-files-issue-5065/one.rs create mode 100644 tests/mod-resolver/test-submodule-issue-5119/Cargo.toml create mode 100644 tests/mod-resolver/test-submodule-issue-5119/src/lib.rs create mode 100644 tests/mod-resolver/test-submodule-issue-5119/tests/test1.rs create mode 100644 tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub1.rs create mode 100644 tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub2.rs create mode 100644 tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/mod.rs create mode 100644 tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/sub4.rs create mode 100644 tests/parser/issue-4126/invalid.rs create mode 100644 tests/parser/issue-4126/lib.rs create mode 100644 tests/parser/issue_4418.rs create mode 100644 tests/parser/unclosed-delims/issue_4466.rs create mode 100644 tests/rustfmt/main.rs create mode 100644 tests/source/5131_crate.rs create mode 100644 tests/source/5131_module.rs create mode 100644 tests/source/5131_one.rs create mode 100644 tests/source/alignment_2633/block_style.rs create mode 100644 tests/source/alignment_2633/visual_style.rs create mode 100644 tests/source/array_comment.rs create mode 100644 tests/source/assignment.rs create mode 100644 tests/source/associated-types-bounds-wrapping.rs create mode 100644 tests/source/associated_type_bounds.rs create mode 100644 tests/source/async_block.rs create mode 100644 tests/source/async_fn.rs create mode 100644 tests/source/attrib.rs create mode 100644 tests/source/big-impl-block.rs create mode 100644 tests/source/big-impl-visual.rs create mode 100644 tests/source/binary-expr.rs create mode 100644 tests/source/binop-separator-back/bitwise.rs create mode 100644 tests/source/binop-separator-back/comp.rs create mode 100644 tests/source/binop-separator-back/logic.rs create mode 100644 tests/source/binop-separator-back/math.rs create mode 100644 tests/source/binop-separator-back/patterns.rs create mode 100644 tests/source/binop-separator-back/range.rs create mode 100644 tests/source/break-and-continue.rs create mode 100644 tests/source/catch.rs create mode 100644 tests/source/cfg_if/detect/arch/aarch64.rs create mode 100644 tests/source/cfg_if/detect/arch/arm.rs create mode 100644 tests/source/cfg_if/detect/arch/mips.rs create mode 100644 tests/source/cfg_if/detect/arch/mips64.rs create mode 100644 tests/source/cfg_if/detect/arch/powerpc.rs create mode 100644 tests/source/cfg_if/detect/arch/powerpc64.rs create mode 100644 tests/source/cfg_if/detect/arch/x86.rs create mode 100644 tests/source/cfg_if/detect/bit.rs create mode 100644 tests/source/cfg_if/detect/cache.rs create mode 100644 tests/source/cfg_if/detect/error_macros.rs create mode 100644 tests/source/cfg_if/detect/mod.rs create mode 100644 tests/source/cfg_if/detect/os/aarch64.rs create mode 100644 tests/source/cfg_if/detect/os/freebsd/aarch64.rs create mode 100644 tests/source/cfg_if/detect/os/freebsd/arm.rs create mode 100644 tests/source/cfg_if/detect/os/freebsd/auxvec.rs create mode 100644 tests/source/cfg_if/detect/os/freebsd/mod.rs create mode 100644 tests/source/cfg_if/detect/os/freebsd/powerpc.rs create mode 100644 tests/source/cfg_if/detect/os/linux/aarch64.rs create mode 100644 tests/source/cfg_if/detect/os/linux/arm.rs create mode 100644 tests/source/cfg_if/detect/os/linux/auxvec.rs create mode 100644 tests/source/cfg_if/detect/os/linux/cpuinfo.rs create mode 100644 tests/source/cfg_if/detect/os/linux/mips.rs create mode 100644 tests/source/cfg_if/detect/os/linux/mod.rs create mode 100644 tests/source/cfg_if/detect/os/linux/powerpc.rs create mode 100644 tests/source/cfg_if/detect/os/other.rs create mode 100644 tests/source/cfg_if/detect/os/x86.rs create mode 100644 tests/source/cfg_if/lib.rs create mode 100644 tests/source/cfg_if/mod.rs create mode 100644 tests/source/cfg_mod/bar.rs create mode 100644 tests/source/cfg_mod/dir/dir1/dir2/wasm32.rs create mode 100644 tests/source/cfg_mod/dir/dir1/dir3/wasm32.rs create mode 100644 tests/source/cfg_mod/foo.rs create mode 100644 tests/source/cfg_mod/mod.rs create mode 100644 tests/source/cfg_mod/other.rs create mode 100644 tests/source/cfg_mod/wasm32.rs create mode 100644 tests/source/chains-visual.rs create mode 100644 tests/source/chains.rs create mode 100644 tests/source/chains_with_comment.rs create mode 100644 tests/source/closure-block-inside-macro.rs create mode 100644 tests/source/closure.rs create mode 100644 tests/source/comment.rs create mode 100644 tests/source/comment2.rs create mode 100644 tests/source/comment3.rs create mode 100644 tests/source/comment4.rs create mode 100644 tests/source/comment5.rs create mode 100644 tests/source/comment6.rs create mode 100644 tests/source/comment_crlf_newline.rs create mode 100644 tests/source/comments-in-lists/wrap-comments-not-normalized.rs create mode 100644 tests/source/comments-in-lists/wrap-comments-true.rs create mode 100644 tests/source/comments_unicode.rs create mode 100644 tests/source/configs/blank_lines_lower_bound/1.rs create mode 100644 tests/source/configs/brace_style/fn_always_next_line.rs create mode 100644 tests/source/configs/brace_style/fn_prefer_same_line.rs create mode 100644 tests/source/configs/brace_style/fn_same_line_where.rs create mode 100644 tests/source/configs/brace_style/item_always_next_line.rs create mode 100644 tests/source/configs/brace_style/item_prefer_same_line.rs create mode 100644 tests/source/configs/brace_style/item_same_line_where.rs create mode 100644 tests/source/configs/chain_width/always.rs create mode 100644 tests/source/configs/chain_width/small.rs create mode 100644 tests/source/configs/chain_width/tiny.rs create mode 100644 tests/source/configs/comment_width/above.rs create mode 100644 tests/source/configs/comment_width/below.rs create mode 100644 tests/source/configs/comment_width/ignore.rs create mode 100644 tests/source/configs/condense_wildcard_suffixes/false.rs create mode 100644 tests/source/configs/condense_wildcard_suffixes/true.rs create mode 100644 tests/source/configs/control_brace_style/always_next_line.rs create mode 100644 tests/source/configs/control_brace_style/always_same_line.rs create mode 100644 tests/source/configs/control_brace_style/closing_next_line.rs create mode 100644 tests/source/configs/disable_all_formatting/false.rs create mode 100644 tests/source/configs/disable_all_formatting/true.rs create mode 100644 tests/source/configs/doc_comment_code_block_width/100.rs create mode 100644 tests/source/configs/doc_comment_code_block_width/100_greater_max_width.rs create mode 100644 tests/source/configs/doc_comment_code_block_width/50.rs create mode 100644 tests/source/configs/empty_item_single_line/false.rs create mode 100644 tests/source/configs/empty_item_single_line/true.rs create mode 100644 tests/source/configs/enum_discrim_align_threshold/40.rs create mode 100644 tests/source/configs/error_on_line_overflow/false.rs create mode 100644 tests/source/configs/fn_params_layout/compressed.rs create mode 100644 tests/source/configs/fn_params_layout/tall.rs create mode 100644 tests/source/configs/fn_params_layout/vertical.rs create mode 100644 tests/source/configs/fn_single_line/false.rs create mode 100644 tests/source/configs/fn_single_line/true.rs create mode 100644 tests/source/configs/force_explicit_abi/false.rs create mode 100644 tests/source/configs/force_explicit_abi/true.rs create mode 100644 tests/source/configs/force_multiline_block/false.rs create mode 100644 tests/source/configs/force_multiline_block/true.rs create mode 100644 tests/source/configs/format_generated_files/false.rs create mode 100644 tests/source/configs/format_generated_files/true.rs create mode 100644 tests/source/configs/format_macro_bodies/false.rs create mode 100644 tests/source/configs/format_macro_bodies/true.rs create mode 100644 tests/source/configs/format_macro_matchers/false.rs create mode 100644 tests/source/configs/format_macro_matchers/true.rs create mode 100644 tests/source/configs/format_strings/false.rs create mode 100644 tests/source/configs/format_strings/true.rs create mode 100644 tests/source/configs/group_imports/One-merge_imports.rs create mode 100644 tests/source/configs/group_imports/One-nested.rs create mode 100644 tests/source/configs/group_imports/One-no_reorder.rs create mode 100644 tests/source/configs/group_imports/One.rs create mode 100644 tests/source/configs/group_imports/StdExternalCrate-merge_imports.rs create mode 100644 tests/source/configs/group_imports/StdExternalCrate-nested.rs create mode 100644 tests/source/configs/group_imports/StdExternalCrate-no_reorder.rs create mode 100644 tests/source/configs/group_imports/StdExternalCrate-non_consecutive.rs create mode 100644 tests/source/configs/group_imports/StdExternalCrate.rs create mode 100644 tests/source/configs/hard_tabs/false.rs create mode 100644 tests/source/configs/hard_tabs/true.rs create mode 100644 tests/source/configs/imports_layout/merge_mixed.rs create mode 100644 tests/source/configs/indent_style/block_args.rs create mode 100644 tests/source/configs/indent_style/block_array.rs create mode 100644 tests/source/configs/indent_style/block_call.rs create mode 100644 tests/source/configs/indent_style/block_chain.rs create mode 100644 tests/source/configs/indent_style/block_generic.rs create mode 100644 tests/source/configs/indent_style/block_struct_lit.rs create mode 100644 tests/source/configs/indent_style/block_trailing_comma_call/one.rs create mode 100644 tests/source/configs/indent_style/block_trailing_comma_call/two.rs create mode 100644 tests/source/configs/indent_style/block_where_pred.rs create mode 100644 tests/source/configs/indent_style/default.rs create mode 100644 tests/source/configs/indent_style/rfc_where.rs create mode 100644 tests/source/configs/indent_style/visual_args.rs create mode 100644 tests/source/configs/indent_style/visual_array.rs create mode 100644 tests/source/configs/indent_style/visual_call.rs create mode 100644 tests/source/configs/indent_style/visual_chain.rs create mode 100644 tests/source/configs/indent_style/visual_generics.rs create mode 100644 tests/source/configs/indent_style/visual_struct_lit.rs create mode 100644 tests/source/configs/indent_style/visual_trailing_comma.rs create mode 100644 tests/source/configs/indent_style/visual_where_pred.rs create mode 100644 tests/source/configs/match_arm_blocks/false.rs create mode 100644 tests/source/configs/match_arm_blocks/true.rs create mode 100644 tests/source/configs/match_arm_leading_pipes/always.rs create mode 100644 tests/source/configs/match_arm_leading_pipes/never.rs create mode 100644 tests/source/configs/match_arm_leading_pipes/preserve.rs create mode 100644 tests/source/configs/match_block_trailing_comma/false.rs create mode 100644 tests/source/configs/match_block_trailing_comma/true.rs create mode 100644 tests/source/configs/merge_derives/true.rs create mode 100644 tests/source/configs/normalize_comments/false.rs create mode 100644 tests/source/configs/normalize_comments/true.rs create mode 100644 tests/source/configs/normalize_doc_attributes/false.rs create mode 100644 tests/source/configs/normalize_doc_attributes/true.rs create mode 100644 tests/source/configs/remove_nested_parens/remove_nested_parens.rs create mode 100644 tests/source/configs/reorder_impl_items/false.rs create mode 100644 tests/source/configs/reorder_impl_items/true.rs create mode 100644 tests/source/configs/reorder_imports/false.rs create mode 100644 tests/source/configs/reorder_imports/true.rs create mode 100644 tests/source/configs/reorder_modules/dolor/mod.rs create mode 100644 tests/source/configs/reorder_modules/false.rs create mode 100644 tests/source/configs/reorder_modules/ipsum/mod.rs create mode 100644 tests/source/configs/reorder_modules/lorem/mod.rs create mode 100644 tests/source/configs/reorder_modules/sit/mod.rs create mode 100644 tests/source/configs/reorder_modules/true.rs create mode 100644 tests/source/configs/short_array_element_width_threshold/10.rs create mode 100644 tests/source/configs/short_array_element_width_threshold/20.rs create mode 100644 tests/source/configs/short_array_element_width_threshold/greater_than_max_width.rs create mode 100644 tests/source/configs/single_line_let_else_max_width/100.rs create mode 100644 tests/source/configs/single_line_let_else_max_width/50.rs create mode 100644 tests/source/configs/single_line_let_else_max_width/zero.rs create mode 100644 tests/source/configs/skip_children/foo/mod.rs create mode 100644 tests/source/configs/skip_children/true.rs create mode 100644 tests/source/configs/space_before_colon/true.rs create mode 100644 tests/source/configs/spaces_around_ranges/false.rs create mode 100644 tests/source/configs/spaces_around_ranges/true.rs create mode 100644 tests/source/configs/struct_field_align_threshold/20.rs create mode 100644 tests/source/configs/struct_lit_single_line/false.rs create mode 100644 tests/source/configs/tab_spaces/2.rs create mode 100644 tests/source/configs/tab_spaces/4.rs create mode 100644 tests/source/configs/trailing_comma/always.rs create mode 100644 tests/source/configs/trailing_comma/never.rs create mode 100644 tests/source/configs/trailing_comma/vertical.rs create mode 100644 tests/source/configs/type_punctuation_density/compressed.rs create mode 100644 tests/source/configs/type_punctuation_density/wide.rs create mode 100644 tests/source/configs/use_field_init_shorthand/false.rs create mode 100644 tests/source/configs/use_field_init_shorthand/true.rs create mode 100644 tests/source/configs/use_small_heuristics/default.rs create mode 100644 tests/source/configs/use_small_heuristics/max.rs create mode 100644 tests/source/configs/use_small_heuristics/off.rs create mode 100644 tests/source/configs/use_try_shorthand/false.rs create mode 100644 tests/source/configs/use_try_shorthand/true.rs create mode 100644 tests/source/configs/where_single_line/true.rs create mode 100644 tests/source/configs/wrap_comments/false.rs create mode 100644 tests/source/configs/wrap_comments/true.rs create mode 100644 tests/source/const_generics.rs create mode 100644 tests/source/control-brace-style-always-next-line.rs create mode 100644 tests/source/control-brace-style-always-same-line.rs create mode 100644 tests/source/doc-attrib.rs create mode 100644 tests/source/doc-comment-with-example.rs create mode 100644 tests/source/doc.rs create mode 100644 tests/source/dyn_trait.rs create mode 100644 tests/source/else-if-brace-style-always-next-line.rs create mode 100644 tests/source/else-if-brace-style-always-same-line.rs create mode 100644 tests/source/else-if-brace-style-closing-next-line.rs create mode 100644 tests/source/empty-item-single-line-false.rs create mode 100644 tests/source/empty_file.rs create mode 100644 tests/source/enum.rs create mode 100644 tests/source/existential_type.rs create mode 100644 tests/source/expr-block.rs create mode 100644 tests/source/expr-overflow-delimited.rs create mode 100644 tests/source/expr.rs create mode 100644 tests/source/extern.rs create mode 100644 tests/source/extern_not_explicit.rs create mode 100644 tests/source/file-lines-1.rs create mode 100644 tests/source/file-lines-2.rs create mode 100644 tests/source/file-lines-3.rs create mode 100644 tests/source/file-lines-4.rs create mode 100644 tests/source/file-lines-5.rs create mode 100644 tests/source/file-lines-6.rs create mode 100644 tests/source/file-lines-7.rs create mode 100644 tests/source/file-lines-item.rs create mode 100644 tests/source/fn-custom-2.rs create mode 100644 tests/source/fn-custom-3.rs create mode 100644 tests/source/fn-custom-4.rs create mode 100644 tests/source/fn-custom-6.rs create mode 100644 tests/source/fn-custom-7.rs create mode 100644 tests/source/fn-custom-8.rs create mode 100644 tests/source/fn-custom.rs create mode 100644 tests/source/fn-param-attributes.rs create mode 100644 tests/source/fn-simple.rs create mode 100644 tests/source/fn-single-line/version_one.rs create mode 100644 tests/source/fn-single-line/version_two.rs create mode 100644 tests/source/fn_args_indent-block.rs create mode 100644 tests/source/fn_args_layout-vertical.rs create mode 100644 tests/source/hard-tabs.rs create mode 100644 tests/source/hello.rs create mode 100644 tests/source/hello2.rs create mode 100644 tests/source/hex_literal_lower.rs create mode 100644 tests/source/hex_literal_upper.rs create mode 100644 tests/source/if_while_or_patterns.rs create mode 100644 tests/source/immovable_generators.rs create mode 100644 tests/source/impls.rs create mode 100644 tests/source/imports/imports-impl-only-use.rs create mode 100644 tests/source/imports/imports-reorder-lines-and-items.rs create mode 100644 tests/source/imports/imports-reorder-lines.rs create mode 100644 tests/source/imports/imports-reorder.rs create mode 100644 tests/source/imports/imports.rs create mode 100644 tests/source/imports/imports_block_indent.rs create mode 100644 tests/source/imports/imports_granularity_crate.rs create mode 100644 tests/source/imports/imports_granularity_default-with-dups.rs create mode 100644 tests/source/imports/imports_granularity_item-with-dups-StdExternalCrate-no-reorder.rs create mode 100644 tests/source/imports/imports_granularity_item-with-dups.rs create mode 100644 tests/source/imports/imports_granularity_item.rs create mode 100644 tests/source/imports/imports_granularity_module.rs create mode 100644 tests/source/imports_granularity_one.rs create mode 100644 tests/source/imports_raw_identifiers/version_One.rs create mode 100644 tests/source/imports_raw_identifiers/version_Two.rs create mode 100644 tests/source/invalid-rust-code-in-doc-comment.rs create mode 100644 tests/source/issue-1021.rs create mode 100644 tests/source/issue-1049.rs create mode 100644 tests/source/issue-1111.rs create mode 100644 tests/source/issue-1120.rs create mode 100644 tests/source/issue-1124.rs create mode 100644 tests/source/issue-1127.rs create mode 100644 tests/source/issue-1158.rs create mode 100644 tests/source/issue-1177.rs create mode 100644 tests/source/issue-1192.rs create mode 100644 tests/source/issue-1210/a.rs create mode 100644 tests/source/issue-1210/b.rs create mode 100644 tests/source/issue-1210/c.rs create mode 100644 tests/source/issue-1210/d.rs create mode 100644 tests/source/issue-1210/e.rs create mode 100644 tests/source/issue-1211.rs create mode 100644 tests/source/issue-1216.rs create mode 100644 tests/source/issue-1239.rs create mode 100644 tests/source/issue-1278.rs create mode 100644 tests/source/issue-1350.rs create mode 100644 tests/source/issue-1366.rs create mode 100644 tests/source/issue-1468.rs create mode 100644 tests/source/issue-1693.rs create mode 100644 tests/source/issue-1800.rs create mode 100644 tests/source/issue-1914.rs create mode 100644 tests/source/issue-2025.rs create mode 100644 tests/source/issue-2111.rs create mode 100644 tests/source/issue-2164.rs create mode 100644 tests/source/issue-2179/one.rs create mode 100644 tests/source/issue-2179/two.rs create mode 100644 tests/source/issue-2256.rs create mode 100644 tests/source/issue-2342.rs create mode 100644 tests/source/issue-2445.rs create mode 100644 tests/source/issue-2446.rs create mode 100644 tests/source/issue-2479.rs create mode 100644 tests/source/issue-2482/a.rs create mode 100644 tests/source/issue-2482/b.rs create mode 100644 tests/source/issue-2482/c.rs create mode 100644 tests/source/issue-2496.rs create mode 100644 tests/source/issue-2520.rs create mode 100644 tests/source/issue-2523.rs create mode 100644 tests/source/issue-2582.rs create mode 100644 tests/source/issue-2641.rs create mode 100644 tests/source/issue-2644.rs create mode 100644 tests/source/issue-2728.rs create mode 100644 tests/source/issue-2761.rs create mode 100644 tests/source/issue-2781.rs create mode 100644 tests/source/issue-2794.rs create mode 100644 tests/source/issue-2835.rs create mode 100644 tests/source/issue-2863.rs create mode 100644 tests/source/issue-2869.rs create mode 100644 tests/source/issue-2896.rs create mode 100644 tests/source/issue-2916.rs create mode 100644 tests/source/issue-2917/packed_simd.rs create mode 100644 tests/source/issue-2922.rs create mode 100644 tests/source/issue-2927-2.rs create mode 100644 tests/source/issue-2927.rs create mode 100644 tests/source/issue-2930.rs create mode 100644 tests/source/issue-2936.rs create mode 100644 tests/source/issue-2955.rs create mode 100644 tests/source/issue-2973.rs create mode 100644 tests/source/issue-2977/impl.rs create mode 100644 tests/source/issue-2977/trait.rs create mode 100644 tests/source/issue-2985.rs create mode 100644 tests/source/issue-2995.rs create mode 100644 tests/source/issue-3029.rs create mode 100644 tests/source/issue-3038.rs create mode 100644 tests/source/issue-3049.rs create mode 100644 tests/source/issue-3055/original.rs create mode 100644 tests/source/issue-3059.rs create mode 100644 tests/source/issue-3066.rs create mode 100644 tests/source/issue-3131.rs create mode 100644 tests/source/issue-3153.rs create mode 100644 tests/source/issue-3158.rs create mode 100644 tests/source/issue-3194.rs create mode 100644 tests/source/issue-3198.rs create mode 100644 tests/source/issue-3213/version_one.rs create mode 100644 tests/source/issue-3213/version_two.rs create mode 100644 tests/source/issue-3217.rs create mode 100644 tests/source/issue-3227/two.rs create mode 100644 tests/source/issue-3234.rs create mode 100644 tests/source/issue-3241.rs create mode 100644 tests/source/issue-3253/bar.rs create mode 100644 tests/source/issue-3253/foo.rs create mode 100644 tests/source/issue-3253/lib.rs create mode 100644 tests/source/issue-3253/paths/bar_foo.rs create mode 100644 tests/source/issue-3253/paths/excluded.rs create mode 100644 tests/source/issue-3253/paths/foo_bar.rs create mode 100644 tests/source/issue-3265.rs create mode 100644 tests/source/issue-3270/one.rs create mode 100644 tests/source/issue-3270/two.rs create mode 100644 tests/source/issue-3272/v1.rs create mode 100644 tests/source/issue-3272/v2.rs create mode 100644 tests/source/issue-3278/version_one.rs create mode 100644 tests/source/issue-3278/version_two.rs create mode 100644 tests/source/issue-3295/two.rs create mode 100644 tests/source/issue-3302.rs create mode 100644 tests/source/issue-3343.rs create mode 100644 tests/source/issue-3423.rs create mode 100644 tests/source/issue-3434/lib.rs create mode 100644 tests/source/issue-3434/no_entry.rs create mode 100644 tests/source/issue-3434/not_skip_macro.rs create mode 100644 tests/source/issue-3465.rs create mode 100644 tests/source/issue-3494/crlf.rs create mode 100644 tests/source/issue-3494/lf.rs create mode 100644 tests/source/issue-3508.rs create mode 100644 tests/source/issue-3515.rs create mode 100644 tests/source/issue-3532.rs create mode 100644 tests/source/issue-3585/extern_crate.rs create mode 100644 tests/source/issue-3585/reorder_imports_disabled.rs create mode 100644 tests/source/issue-3585/reorder_imports_enabled.rs create mode 100644 tests/source/issue-3585/use.rs create mode 100644 tests/source/issue-3636.rs create mode 100644 tests/source/issue-3639.rs create mode 100644 tests/source/issue-3651.rs create mode 100644 tests/source/issue-3665/lib.rs create mode 100644 tests/source/issue-3665/not_skip_attribute.rs create mode 100644 tests/source/issue-3665/sub_mod.rs create mode 100644 tests/source/issue-3672.rs create mode 100644 tests/source/issue-3675.rs create mode 100644 tests/source/issue-3701/one.rs create mode 100644 tests/source/issue-3701/two.rs create mode 100644 tests/source/issue-3709.rs create mode 100644 tests/source/issue-3740.rs create mode 100644 tests/source/issue-3750.rs create mode 100644 tests/source/issue-3751.rs create mode 100644 tests/source/issue-3779/ice.rs create mode 100644 tests/source/issue-3779/lib.rs create mode 100644 tests/source/issue-3786.rs create mode 100644 tests/source/issue-3787.rs create mode 100644 tests/source/issue-3840/version-one_hard-tabs.rs create mode 100644 tests/source/issue-3840/version-one_soft-tabs.rs create mode 100644 tests/source/issue-3840/version-two_hard-tabs.rs create mode 100644 tests/source/issue-3840/version-two_soft-tabs.rs create mode 100644 tests/source/issue-3987/format_macro_bodies_true.rs create mode 100644 tests/source/issue-4018.rs create mode 100644 tests/source/issue-4036/one.rs create mode 100644 tests/source/issue-4036/three.rs create mode 100644 tests/source/issue-4036/two.rs create mode 100644 tests/source/issue-4041.rs create mode 100644 tests/source/issue-4079.rs create mode 100644 tests/source/issue-4120.rs create mode 100644 tests/source/issue-4243.rs create mode 100644 tests/source/issue-4244.rs create mode 100644 tests/source/issue-4245.rs create mode 100644 tests/source/issue-4312.rs create mode 100644 tests/source/issue-4382.rs create mode 100644 tests/source/issue-4398.rs create mode 100644 tests/source/issue-4427.rs create mode 100644 tests/source/issue-447.rs create mode 100644 tests/source/issue-4530.rs create mode 100644 tests/source/issue-4577.rs create mode 100644 tests/source/issue-4603.rs create mode 100644 tests/source/issue-4615/minimum_example.rs create mode 100644 tests/source/issue-4643.rs create mode 100644 tests/source/issue-4646.rs create mode 100644 tests/source/issue-4656/format_me_please.rs create mode 100644 tests/source/issue-4656/lib.rs create mode 100644 tests/source/issue-4656/lib2.rs create mode 100644 tests/source/issue-4689/one.rs create mode 100644 tests/source/issue-4689/two.rs create mode 100644 tests/source/issue-4791/buggy.rs create mode 100644 tests/source/issue-4791/trailing_comma.rs create mode 100644 tests/source/issue-4816/lib.rs create mode 100644 tests/source/issue-4926/deeply_nested_struct.rs create mode 100644 tests/source/issue-4926/deeply_nested_struct_with_long_field_names.rs create mode 100644 tests/source/issue-4926/deeply_nested_struct_with_many_fields.rs create mode 100644 tests/source/issue-4926/enum_struct_field.rs create mode 100644 tests/source/issue-4926/minimum_example.rs create mode 100644 tests/source/issue-4926/struct_with_long_field_names.rs create mode 100644 tests/source/issue-4926/struct_with_many_fields.rs create mode 100644 tests/source/issue-4984/minimum_example.rs create mode 100644 tests/source/issue-4984/multi_line_derive.rs create mode 100644 tests/source/issue-4984/multiple_comments_within.rs create mode 100644 tests/source/issue-5011.rs create mode 100644 tests/source/issue-5023.rs create mode 100644 tests/source/issue-5030.rs create mode 100644 tests/source/issue-5042/multi-line_comment_with_trailing_comma.rs create mode 100644 tests/source/issue-5042/multi-line_comment_without_trailing_comma.rs create mode 100644 tests/source/issue-5042/single-line_comment_with_trailing_comma.rs create mode 100644 tests/source/issue-5042/single-line_comment_without_trailing_comma.rs create mode 100644 tests/source/issue-5088/deeply_nested_long_comment_wrap_comments_true.rs create mode 100644 tests/source/issue-5088/start_with_empty_comment_very_long_itemized_block_wrap_comments_true.rs create mode 100644 tests/source/issue-5088/very_long_comment_wrap_comments_true.rs create mode 100644 tests/source/issue-510.rs create mode 100644 tests/source/issue-5157/indented_itemized_markdown_blockquote.rs create mode 100644 tests/source/issue-5157/nested_itemized_markdown_blockquote.rs create mode 100644 tests/source/issue-5157/support_itemized_markdown_blockquote.rs create mode 100644 tests/source/issue-5234.rs create mode 100644 tests/source/issue-5238/markdown_header_wrap_comments_false.rs create mode 100644 tests/source/issue-5238/markdown_header_wrap_comments_true.rs create mode 100644 tests/source/issue-5260.rs create mode 100644 tests/source/issue-5270/merge_derives_true.rs create mode 100644 tests/source/issue-539.rs create mode 100644 tests/source/issue-5488.rs create mode 100644 tests/source/issue-5586.rs create mode 100644 tests/source/issue-683.rs create mode 100644 tests/source/issue-811.rs create mode 100644 tests/source/issue-850.rs create mode 100644 tests/source/issue-855.rs create mode 100644 tests/source/issue-913.rs create mode 100644 tests/source/issue-945.rs create mode 100644 tests/source/issue-977.rs create mode 100644 tests/source/issue_1306.rs create mode 100644 tests/source/issue_3245.rs create mode 100644 tests/source/issue_3561.rs create mode 100644 tests/source/issue_3839.rs create mode 100644 tests/source/issue_3844.rs create mode 100644 tests/source/issue_3853.rs create mode 100644 tests/source/issue_3868.rs create mode 100644 tests/source/issue_4032.rs create mode 100644 tests/source/issue_4057.rs create mode 100644 tests/source/issue_4086.rs create mode 100644 tests/source/issue_4257.rs create mode 100644 tests/source/issue_4322.rs create mode 100644 tests/source/issue_4374.rs create mode 100644 tests/source/issue_4475.rs create mode 100644 tests/source/issue_4528.rs create mode 100644 tests/source/issue_4579.rs create mode 100644 tests/source/issue_4584.rs create mode 100644 tests/source/issue_4636.rs create mode 100644 tests/source/issue_4675.rs create mode 100644 tests/source/issue_4823.rs create mode 100644 tests/source/issue_4854.rs create mode 100644 tests/source/issue_4911.rs create mode 100644 tests/source/issue_4943.rs create mode 100644 tests/source/issue_4954.rs create mode 100644 tests/source/issue_4963.rs create mode 100644 tests/source/issue_5027.rs create mode 100644 tests/source/issue_5086.rs create mode 100644 tests/source/issue_5686.rs create mode 100644 tests/source/item-brace-style-always-next-line.rs create mode 100644 tests/source/item-brace-style-prefer-same-line.rs create mode 100644 tests/source/item-brace-style-same-line-where.rs create mode 100644 tests/source/itemized-blocks/no_wrap.rs create mode 100644 tests/source/itemized-blocks/rewrite_fail.rs create mode 100644 tests/source/itemized-blocks/urls.rs create mode 100644 tests/source/itemized-blocks/wrap.rs create mode 100644 tests/source/label_break.rs create mode 100644 tests/source/large-block.rs create mode 100644 tests/source/large_vec.rs create mode 100644 tests/source/lazy_static.rs create mode 100644 tests/source/let_else.rs create mode 100644 tests/source/long-fn-1/version_one.rs create mode 100644 tests/source/long-fn-1/version_two.rs create mode 100644 tests/source/long-match-arms-brace-newline.rs create mode 100644 tests/source/long-use-statement-issue-3154.rs create mode 100644 tests/source/long_field_access.rs create mode 100644 tests/source/loop.rs create mode 100644 tests/source/macro_not_expr.rs create mode 100644 tests/source/macro_rules.rs create mode 100644 tests/source/macros.rs create mode 100644 tests/source/markdown-comment-with-options.rs create mode 100644 tests/source/markdown-comment.rs create mode 100644 tests/source/match-block-trailing-comma.rs create mode 100644 tests/source/match-flattening.rs create mode 100644 tests/source/match-nowrap-trailing-comma.rs create mode 100644 tests/source/match-nowrap.rs create mode 100644 tests/source/match.rs create mode 100644 tests/source/match_overflow_expr.rs create mode 100644 tests/source/max-line-length-in-chars.rs create mode 100644 tests/source/merge_imports_true_compat.rs create mode 100644 tests/source/mod-1.rs create mode 100644 tests/source/mod-2.rs create mode 100644 tests/source/mod_skip_child.rs create mode 100644 tests/source/multiple.rs create mode 100644 tests/source/negative-impl.rs create mode 100644 tests/source/nested-if-else.rs create mode 100644 tests/source/nested_skipped/mod.rs create mode 100644 tests/source/nestedmod/mod.rs create mode 100644 tests/source/nestedmod/mod2a.rs create mode 100644 tests/source/nestedmod/mod2b.rs create mode 100644 tests/source/nestedmod/mod2c.rs create mode 100644 tests/source/nestedmod/mymod1/mod3a.rs create mode 100644 tests/source/nestedmod/submod2/a.rs create mode 100644 tests/source/nestedmod/submod2/mod.rs create mode 100644 tests/source/no_arg_with_commnet.rs create mode 100644 tests/source/no_new_line_beginning.rs create mode 100644 tests/source/normalize_doc_attributes_should_not_imply_format_doc_comments.rs create mode 100644 tests/source/normalize_multiline_doc_attribute.rs create mode 100644 tests/source/one_line_if_v1.rs create mode 100644 tests/source/one_line_if_v2.rs create mode 100644 tests/source/other.rs create mode 100644 tests/source/paren.rs create mode 100644 tests/source/path_clarity/foo.rs create mode 100644 tests/source/path_clarity/foo/bar.rs create mode 100644 tests/source/paths.rs create mode 100644 tests/source/pattern-condense-wildcards.rs create mode 100644 tests/source/pattern.rs create mode 100644 tests/source/preserves_carriage_return_for_unix.rs create mode 100644 tests/source/preserves_carriage_return_for_windows.rs create mode 100644 tests/source/pub-restricted.rs create mode 100644 tests/source/remove_blank_lines.rs create mode 100644 tests/source/reorder-impl-items.rs create mode 100644 tests/source/single-line-if-else.rs create mode 100644 tests/source/single-line-macro/v1.rs create mode 100644 tests/source/single-line-macro/v2.rs create mode 100644 tests/source/skip_macro_invocations/all.rs create mode 100644 tests/source/skip_macro_invocations/all_and_name.rs create mode 100644 tests/source/skip_macro_invocations/empty.rs create mode 100644 tests/source/skip_macro_invocations/name.rs create mode 100644 tests/source/skip_macro_invocations/name_unknown.rs create mode 100644 tests/source/skip_macro_invocations/names.rs create mode 100644 tests/source/skip_macro_invocations/path_qualified_invocation_mismatch.rs create mode 100644 tests/source/skip_macro_invocations/path_qualified_match.rs create mode 100644 tests/source/skip_macro_invocations/path_qualified_name_mismatch.rs create mode 100644 tests/source/skip_macro_invocations/use_alias_examples.rs create mode 100644 tests/source/soft-wrapping.rs create mode 100644 tests/source/space-not-before-newline.rs create mode 100644 tests/source/spaces-around-ranges.rs create mode 100644 tests/source/statements.rs create mode 100644 tests/source/static.rs create mode 100644 tests/source/string-lit-2.rs create mode 100644 tests/source/string-lit.rs create mode 100644 tests/source/string_punctuation.rs create mode 100644 tests/source/struct-field-attributes.rs create mode 100644 tests/source/struct_field_doc_comment.rs create mode 100644 tests/source/struct_lits.rs create mode 100644 tests/source/struct_lits_multiline.rs create mode 100644 tests/source/struct_lits_visual.rs create mode 100644 tests/source/struct_lits_visual_multiline.rs create mode 100644 tests/source/struct_tuple_visual.rs create mode 100644 tests/source/structs.rs create mode 100644 tests/source/trailing-comma-never.rs create mode 100644 tests/source/trailing_commas.rs create mode 100644 tests/source/trailing_comments/hard_tabs.rs create mode 100644 tests/source/trailing_comments/soft_tabs.rs create mode 100644 tests/source/trait.rs create mode 100644 tests/source/try-conversion.rs create mode 100644 tests/source/try_block.rs create mode 100644 tests/source/tuple.rs create mode 100644 tests/source/tuple_v2.rs create mode 100644 tests/source/type.rs create mode 100644 tests/source/type_alias.rs create mode 100644 tests/source/unicode.rs create mode 100644 tests/source/unions.rs create mode 100644 tests/source/unsafe-mod.rs create mode 100644 tests/source/visibility.rs create mode 100644 tests/source/visual-fn-type.rs create mode 100644 tests/source/where-clause-rfc.rs create mode 100644 tests/source/where-clause.rs create mode 100644 tests/source/width-heuristics.rs create mode 100644 tests/source/wrap_comments_should_not_imply_format_doc_comments.rs create mode 100644 tests/target/5131_crate.rs create mode 100644 tests/target/5131_module.rs create mode 100644 tests/target/5131_one.rs create mode 100644 tests/target/alignment_2633/block_style.rs create mode 100644 tests/target/alignment_2633/horizontal_tactic.rs create mode 100644 tests/target/alignment_2633/visual_style.rs create mode 100644 tests/target/array_comment.rs create mode 100644 tests/target/assignment.rs create mode 100644 tests/target/associated-items.rs create mode 100644 tests/target/associated-types-bounds-wrapping.rs create mode 100644 tests/target/associated_type_bounds.rs create mode 100644 tests/target/associated_type_defaults.rs create mode 100644 tests/target/async_block.rs create mode 100644 tests/target/async_closure.rs create mode 100644 tests/target/async_fn.rs create mode 100644 tests/target/attrib-block-expr.rs create mode 100644 tests/target/attrib-extern-crate.rs create mode 100644 tests/target/attrib.rs create mode 100644 tests/target/big-impl-block.rs create mode 100644 tests/target/big-impl-visual.rs create mode 100644 tests/target/binary-expr.rs create mode 100644 tests/target/binop-separator-back/bitwise.rs create mode 100644 tests/target/binop-separator-back/comp.rs create mode 100644 tests/target/binop-separator-back/logic.rs create mode 100644 tests/target/binop-separator-back/math.rs create mode 100644 tests/target/binop-separator-back/patterns.rs create mode 100644 tests/target/binop-separator-back/range.rs create mode 100644 tests/target/break-and-continue.rs create mode 100644 tests/target/catch.rs create mode 100644 tests/target/cfg_if/detect/arch/aarch64.rs create mode 100644 tests/target/cfg_if/detect/arch/arm.rs create mode 100644 tests/target/cfg_if/detect/arch/mips.rs create mode 100644 tests/target/cfg_if/detect/arch/mips64.rs create mode 100644 tests/target/cfg_if/detect/arch/powerpc.rs create mode 100644 tests/target/cfg_if/detect/arch/powerpc64.rs create mode 100644 tests/target/cfg_if/detect/arch/x86.rs create mode 100644 tests/target/cfg_if/detect/bit.rs create mode 100644 tests/target/cfg_if/detect/cache.rs create mode 100644 tests/target/cfg_if/detect/error_macros.rs create mode 100644 tests/target/cfg_if/detect/mod.rs create mode 100644 tests/target/cfg_if/detect/os/aarch64.rs create mode 100644 tests/target/cfg_if/detect/os/freebsd/aarch64.rs create mode 100644 tests/target/cfg_if/detect/os/freebsd/arm.rs create mode 100644 tests/target/cfg_if/detect/os/freebsd/auxvec.rs create mode 100644 tests/target/cfg_if/detect/os/freebsd/mod.rs create mode 100644 tests/target/cfg_if/detect/os/freebsd/powerpc.rs create mode 100644 tests/target/cfg_if/detect/os/linux/aarch64.rs create mode 100644 tests/target/cfg_if/detect/os/linux/arm.rs create mode 100644 tests/target/cfg_if/detect/os/linux/auxvec.rs create mode 100644 tests/target/cfg_if/detect/os/linux/cpuinfo.rs create mode 100644 tests/target/cfg_if/detect/os/linux/mips.rs create mode 100644 tests/target/cfg_if/detect/os/linux/mod.rs create mode 100644 tests/target/cfg_if/detect/os/linux/powerpc.rs create mode 100644 tests/target/cfg_if/detect/os/other.rs create mode 100644 tests/target/cfg_if/detect/os/x86.rs create mode 100644 tests/target/cfg_if/lib.rs create mode 100644 tests/target/cfg_if/mod.rs create mode 100644 tests/target/cfg_mod/bar.rs create mode 100644 tests/target/cfg_mod/dir/dir1/dir2/wasm32.rs create mode 100644 tests/target/cfg_mod/dir/dir1/dir3/wasm32.rs create mode 100644 tests/target/cfg_mod/foo.rs create mode 100644 tests/target/cfg_mod/mod.rs create mode 100644 tests/target/cfg_mod/other.rs create mode 100644 tests/target/cfg_mod/wasm32.rs create mode 100644 tests/target/chains-visual.rs create mode 100644 tests/target/chains.rs create mode 100644 tests/target/chains_with_comment.rs create mode 100644 tests/target/closure-block-inside-macro.rs create mode 100644 tests/target/closure.rs create mode 100644 tests/target/comment-inside-const.rs create mode 100644 tests/target/comment-not-disappear.rs create mode 100644 tests/target/comment.rs create mode 100644 tests/target/comment2.rs create mode 100644 tests/target/comment3.rs create mode 100644 tests/target/comment4.rs create mode 100644 tests/target/comment5.rs create mode 100644 tests/target/comment6.rs create mode 100644 tests/target/comment_crlf_newline.rs create mode 100644 tests/target/comments-fn.rs create mode 100644 tests/target/comments-in-lists/format-doc-comments.rs create mode 100644 tests/target/comments-in-lists/wrap-comments-false.rs create mode 100644 tests/target/comments-in-lists/wrap-comments-not-normalized.rs create mode 100644 tests/target/comments-in-lists/wrap-comments-true.rs create mode 100644 tests/target/comments_unicode.rs create mode 100644 tests/target/configs/blank_lines_lower_bound/1.rs create mode 100644 tests/target/configs/brace_style/fn_always_next_line.rs create mode 100644 tests/target/configs/brace_style/fn_prefer_same_line.rs create mode 100644 tests/target/configs/brace_style/fn_same_line_where.rs create mode 100644 tests/target/configs/brace_style/item_always_next_line.rs create mode 100644 tests/target/configs/brace_style/item_prefer_same_line.rs create mode 100644 tests/target/configs/brace_style/item_same_line_where.rs create mode 100644 tests/target/configs/chain_width/always.rs create mode 100644 tests/target/configs/chain_width/small.rs create mode 100644 tests/target/configs/chain_width/tiny.rs create mode 100644 tests/target/configs/combine_control_expr/false.rs create mode 100644 tests/target/configs/combine_control_expr/true.rs create mode 100644 tests/target/configs/comment_width/above.rs create mode 100644 tests/target/configs/comment_width/below.rs create mode 100644 tests/target/configs/comment_width/ignore.rs create mode 100644 tests/target/configs/condense_wildcard_suffixes/false.rs create mode 100644 tests/target/configs/condense_wildcard_suffixes/true.rs create mode 100644 tests/target/configs/control_brace_style/always_next_line.rs create mode 100644 tests/target/configs/control_brace_style/always_same_line.rs create mode 100644 tests/target/configs/control_brace_style/closing_next_line.rs create mode 100644 tests/target/configs/disable_all_formatting/false.rs create mode 100644 tests/target/configs/disable_all_formatting/true.rs create mode 100644 tests/target/configs/doc_comment_code_block_width/100.rs create mode 100644 tests/target/configs/doc_comment_code_block_width/100_greater_max_width.rs create mode 100644 tests/target/configs/doc_comment_code_block_width/50.rs create mode 100644 tests/target/configs/empty_item_single_line/false.rs create mode 100644 tests/target/configs/empty_item_single_line/true.rs create mode 100644 tests/target/configs/enum_discrim_align_threshold/40.rs create mode 100644 tests/target/configs/error_on_line_overflow/false.rs create mode 100644 tests/target/configs/error_on_unformatted/false.rs create mode 100644 tests/target/configs/fn_params_layout/compressed.rs create mode 100644 tests/target/configs/fn_params_layout/tall.rs create mode 100644 tests/target/configs/fn_params_layout/vertical.rs create mode 100644 tests/target/configs/fn_single_line/false.rs create mode 100644 tests/target/configs/fn_single_line/true.rs create mode 100644 tests/target/configs/force_explicit_abi/false.rs create mode 100644 tests/target/configs/force_explicit_abi/true.rs create mode 100644 tests/target/configs/force_multiline_block/false.rs create mode 100644 tests/target/configs/force_multiline_block/true.rs create mode 100644 tests/target/configs/format_generated_files/false.rs create mode 100644 tests/target/configs/format_generated_files/true.rs create mode 100644 tests/target/configs/format_macro_bodies/false.rs create mode 100644 tests/target/configs/format_macro_bodies/true.rs create mode 100644 tests/target/configs/format_macro_matchers/false.rs create mode 100644 tests/target/configs/format_macro_matchers/true.rs create mode 100644 tests/target/configs/format_strings/false.rs create mode 100644 tests/target/configs/format_strings/true.rs create mode 100644 tests/target/configs/group_imports/One-merge_imports.rs create mode 100644 tests/target/configs/group_imports/One-nested.rs create mode 100644 tests/target/configs/group_imports/One-no_reorder.rs create mode 100644 tests/target/configs/group_imports/One.rs create mode 100644 tests/target/configs/group_imports/StdExternalCrate-merge_imports.rs create mode 100644 tests/target/configs/group_imports/StdExternalCrate-nested.rs create mode 100644 tests/target/configs/group_imports/StdExternalCrate-no_reorder.rs create mode 100644 tests/target/configs/group_imports/StdExternalCrate-non_consecutive.rs create mode 100644 tests/target/configs/group_imports/StdExternalCrate.rs create mode 100644 tests/target/configs/hard_tabs/false.rs create mode 100644 tests/target/configs/hard_tabs/true.rs create mode 100644 tests/target/configs/imports_indent/block.rs create mode 100644 tests/target/configs/imports_layout/horizontal_vertical.rs create mode 100644 tests/target/configs/imports_layout/merge_mixed.rs create mode 100644 tests/target/configs/imports_layout/mixed.rs create mode 100644 tests/target/configs/indent_style/block_args.rs create mode 100644 tests/target/configs/indent_style/block_array.rs create mode 100644 tests/target/configs/indent_style/block_call.rs create mode 100644 tests/target/configs/indent_style/block_chain.rs create mode 100644 tests/target/configs/indent_style/block_generic.rs create mode 100644 tests/target/configs/indent_style/block_struct_lit.rs create mode 100644 tests/target/configs/indent_style/block_tab_spaces_call.rs create mode 100644 tests/target/configs/indent_style/block_trailing_comma_call/one.rs create mode 100644 tests/target/configs/indent_style/block_trailing_comma_call/two.rs create mode 100644 tests/target/configs/indent_style/block_where_pred.rs create mode 100644 tests/target/configs/indent_style/default.rs create mode 100644 tests/target/configs/indent_style/rfc_control.rs create mode 100644 tests/target/configs/indent_style/rfc_where.rs create mode 100644 tests/target/configs/indent_style/visual_args.rs create mode 100644 tests/target/configs/indent_style/visual_array.rs create mode 100644 tests/target/configs/indent_style/visual_call.rs create mode 100644 tests/target/configs/indent_style/visual_chain.rs create mode 100644 tests/target/configs/indent_style/visual_generics.rs create mode 100644 tests/target/configs/indent_style/visual_struct_lit.rs create mode 100644 tests/target/configs/indent_style/visual_trailing_comma.rs create mode 100644 tests/target/configs/indent_style/visual_where_pred.rs create mode 100644 tests/target/configs/match_arm_blocks/false.rs create mode 100644 tests/target/configs/match_arm_blocks/true.rs create mode 100644 tests/target/configs/match_arm_leading_pipes/always.rs create mode 100644 tests/target/configs/match_arm_leading_pipes/never.rs create mode 100644 tests/target/configs/match_arm_leading_pipes/preserve.rs create mode 100644 tests/target/configs/match_block_trailing_comma/false.rs create mode 100644 tests/target/configs/match_block_trailing_comma/true.rs create mode 100644 tests/target/configs/merge_derives/true.rs create mode 100644 tests/target/configs/normalize_comments/false.rs create mode 100644 tests/target/configs/normalize_comments/true.rs create mode 100644 tests/target/configs/normalize_doc_attributes/false.rs create mode 100644 tests/target/configs/normalize_doc_attributes/true.rs create mode 100644 tests/target/configs/remove_nested_parens/remove_nested_parens.rs create mode 100644 tests/target/configs/reorder_impl_items/false.rs create mode 100644 tests/target/configs/reorder_impl_items/true.rs create mode 100644 tests/target/configs/reorder_imports/false.rs create mode 100644 tests/target/configs/reorder_imports/true.rs create mode 100644 tests/target/configs/reorder_modules/dolor/mod.rs create mode 100644 tests/target/configs/reorder_modules/false.rs create mode 100644 tests/target/configs/reorder_modules/ipsum/mod.rs create mode 100644 tests/target/configs/reorder_modules/lorem/mod.rs create mode 100644 tests/target/configs/reorder_modules/sit/mod.rs create mode 100644 tests/target/configs/reorder_modules/true.rs create mode 100644 tests/target/configs/short_array_element_width_threshold/10.rs create mode 100644 tests/target/configs/short_array_element_width_threshold/20.rs create mode 100644 tests/target/configs/short_array_element_width_threshold/greater_than_max_width.rs create mode 100644 tests/target/configs/single_line_let_else_max_width/100.rs create mode 100644 tests/target/configs/single_line_let_else_max_width/50.rs create mode 100644 tests/target/configs/single_line_let_else_max_width/zero.rs create mode 100644 tests/target/configs/skip_children/foo/mod.rs create mode 100644 tests/target/configs/skip_children/true.rs create mode 100644 tests/target/configs/space_before_colon/true.rs create mode 100644 tests/target/configs/spaces_around_ranges/false.rs create mode 100644 tests/target/configs/spaces_around_ranges/true.rs create mode 100644 tests/target/configs/struct_field_align_threshold/20.rs create mode 100644 tests/target/configs/struct_lit_single_line/false.rs create mode 100644 tests/target/configs/tab_spaces/2.rs create mode 100644 tests/target/configs/tab_spaces/4.rs create mode 100644 tests/target/configs/trailing_comma/always.rs create mode 100644 tests/target/configs/trailing_comma/never.rs create mode 100644 tests/target/configs/trailing_comma/vertical.rs create mode 100644 tests/target/configs/trailing_semicolon/false.rs create mode 100644 tests/target/configs/trailing_semicolon/true.rs create mode 100644 tests/target/configs/type_punctuation_density/compressed.rs create mode 100644 tests/target/configs/type_punctuation_density/wide.rs create mode 100644 tests/target/configs/use_field_init_shorthand/false.rs create mode 100644 tests/target/configs/use_field_init_shorthand/true.rs create mode 100644 tests/target/configs/use_small_heuristics/default.rs create mode 100644 tests/target/configs/use_small_heuristics/max.rs create mode 100644 tests/target/configs/use_small_heuristics/off.rs create mode 100644 tests/target/configs/use_try_shorthand/false.rs create mode 100644 tests/target/configs/use_try_shorthand/true.rs create mode 100644 tests/target/configs/where_single_line/true-with-brace-style.rs create mode 100644 tests/target/configs/where_single_line/true.rs create mode 100644 tests/target/configs/wrap_comments/false.rs create mode 100644 tests/target/configs/wrap_comments/true.rs create mode 100644 tests/target/const_generics.rs create mode 100644 tests/target/control-brace-style-always-next-line.rs create mode 100644 tests/target/control-brace-style-always-same-line.rs create mode 100644 tests/target/doc-attrib.rs create mode 100644 tests/target/doc-comment-with-example.rs create mode 100644 tests/target/doc-of-generic-item.rs create mode 100644 tests/target/doc.rs create mode 100644 tests/target/dyn_trait.rs create mode 100644 tests/target/else-if-brace-style-always-next-line.rs create mode 100644 tests/target/else-if-brace-style-always-same-line.rs create mode 100644 tests/target/else-if-brace-style-closing-next-line.rs create mode 100644 tests/target/empty-item-single-line-false.rs create mode 100644 tests/target/empty-tuple-no-conversion-to-unit-struct.rs create mode 100644 tests/target/empty_file.rs create mode 100644 tests/target/enum.rs create mode 100644 tests/target/existential_type.rs create mode 100644 tests/target/expr-block.rs create mode 100644 tests/target/expr-overflow-delimited.rs create mode 100644 tests/target/expr.rs create mode 100644 tests/target/extern.rs create mode 100644 tests/target/extern_not_explicit.rs create mode 100644 tests/target/file-lines-1.rs create mode 100644 tests/target/file-lines-2.rs create mode 100644 tests/target/file-lines-3.rs create mode 100644 tests/target/file-lines-4.rs create mode 100644 tests/target/file-lines-5.rs create mode 100644 tests/target/file-lines-6.rs create mode 100644 tests/target/file-lines-7.rs create mode 100644 tests/target/file-lines-item.rs create mode 100644 tests/target/fn-args-with-last-line-comment.rs create mode 100644 tests/target/fn-custom-2.rs create mode 100644 tests/target/fn-custom-3.rs create mode 100644 tests/target/fn-custom-4.rs create mode 100644 tests/target/fn-custom-6.rs create mode 100644 tests/target/fn-custom-7.rs create mode 100644 tests/target/fn-custom-8.rs create mode 100644 tests/target/fn-custom.rs create mode 100644 tests/target/fn-param-attributes.rs create mode 100644 tests/target/fn-simple.rs create mode 100644 tests/target/fn-single-line/version_one.rs create mode 100644 tests/target/fn-single-line/version_two.rs create mode 100644 tests/target/fn-ty.rs create mode 100644 tests/target/fn.rs create mode 100644 tests/target/fn_args_indent-block.rs create mode 100644 tests/target/fn_args_layout-vertical.rs create mode 100644 tests/target/fn_once.rs create mode 100644 tests/target/format_strings/issue-202.rs create mode 100644 tests/target/format_strings/issue-2833.rs create mode 100644 tests/target/format_strings/issue-3263.rs create mode 100644 tests/target/format_strings/issue-687.rs create mode 100644 tests/target/format_strings/issue564.rs create mode 100644 tests/target/hard-tabs.rs create mode 100644 tests/target/hello.rs create mode 100644 tests/target/hex_literal_lower.rs create mode 100644 tests/target/hex_literal_preserve.rs create mode 100644 tests/target/hex_literal_upper.rs create mode 100644 tests/target/if_while_or_patterns.rs create mode 100644 tests/target/immovable_generators.rs create mode 100644 tests/target/impl.rs create mode 100644 tests/target/impls.rs create mode 100644 tests/target/imports/import-fencepost-length.rs create mode 100644 tests/target/imports/imports-impl-only-use.rs create mode 100644 tests/target/imports/imports-reorder-lines-and-items.rs create mode 100644 tests/target/imports/imports-reorder-lines.rs create mode 100644 tests/target/imports/imports-reorder.rs create mode 100644 tests/target/imports/imports.rs create mode 100644 tests/target/imports/imports_2021_edition.rs create mode 100644 tests/target/imports/imports_block_indent.rs create mode 100644 tests/target/imports/imports_granularity_crate.rs create mode 100644 tests/target/imports/imports_granularity_default-with-dups.rs create mode 100644 tests/target/imports/imports_granularity_item-with-dups-StdExternalCrate-no-reorder.rs create mode 100644 tests/target/imports/imports_granularity_item-with-dups.rs create mode 100644 tests/target/imports/imports_granularity_item.rs create mode 100644 tests/target/imports/imports_granularity_module.rs create mode 100644 tests/target/imports_granularity_one.rs create mode 100644 tests/target/imports_raw_identifiers/version_One.rs create mode 100644 tests/target/imports_raw_identifiers/version_Two.rs create mode 100644 tests/target/indented-impl.rs create mode 100644 tests/target/inner-module-path/b.rs create mode 100644 tests/target/inner-module-path/c/d.rs create mode 100644 tests/target/inner-module-path/lib.rs create mode 100644 tests/target/invalid-rust-code-in-doc-comment.rs create mode 100644 tests/target/issue-1021.rs create mode 100644 tests/target/issue-1049.rs create mode 100644 tests/target/issue-1055.rs create mode 100644 tests/target/issue-1096.rs create mode 100644 tests/target/issue-1111.rs create mode 100644 tests/target/issue-1113.rs create mode 100644 tests/target/issue-1120.rs create mode 100644 tests/target/issue-1124.rs create mode 100644 tests/target/issue-1127.rs create mode 100644 tests/target/issue-1158.rs create mode 100644 tests/target/issue-1177.rs create mode 100644 tests/target/issue-1192.rs create mode 100644 tests/target/issue-1210/a.rs create mode 100644 tests/target/issue-1210/b.rs create mode 100644 tests/target/issue-1210/c.rs create mode 100644 tests/target/issue-1210/d.rs create mode 100644 tests/target/issue-1210/e.rs create mode 100644 tests/target/issue-1211.rs create mode 100644 tests/target/issue-1214.rs create mode 100644 tests/target/issue-1216.rs create mode 100644 tests/target/issue-1239.rs create mode 100644 tests/target/issue-1247.rs create mode 100644 tests/target/issue-1255.rs create mode 100644 tests/target/issue-1278.rs create mode 100644 tests/target/issue-1350.rs create mode 100644 tests/target/issue-1366.rs create mode 100644 tests/target/issue-1397.rs create mode 100644 tests/target/issue-1468.rs create mode 100644 tests/target/issue-1598.rs create mode 100644 tests/target/issue-1624.rs create mode 100644 tests/target/issue-1681.rs create mode 100644 tests/target/issue-1693.rs create mode 100644 tests/target/issue-1703.rs create mode 100644 tests/target/issue-1800.rs create mode 100644 tests/target/issue-1802.rs create mode 100644 tests/target/issue-1824.rs create mode 100644 tests/target/issue-1914.rs create mode 100644 tests/target/issue-2025.rs create mode 100644 tests/target/issue-2103.rs create mode 100644 tests/target/issue-2111.rs create mode 100644 tests/target/issue-2123.rs create mode 100644 tests/target/issue-2164.rs create mode 100644 tests/target/issue-2179/one.rs create mode 100644 tests/target/issue-2179/two.rs create mode 100644 tests/target/issue-2197.rs create mode 100644 tests/target/issue-2256.rs create mode 100644 tests/target/issue-2324.rs create mode 100644 tests/target/issue-2329.rs create mode 100644 tests/target/issue-2342.rs create mode 100644 tests/target/issue-2346.rs create mode 100644 tests/target/issue-2401.rs create mode 100644 tests/target/issue-2445.rs create mode 100644 tests/target/issue-2446.rs create mode 100644 tests/target/issue-2479.rs create mode 100644 tests/target/issue-2482/a.rs create mode 100644 tests/target/issue-2482/b.rs create mode 100644 tests/target/issue-2482/c.rs create mode 100644 tests/target/issue-2496.rs create mode 100644 tests/target/issue-2520.rs create mode 100644 tests/target/issue-2523.rs create mode 100644 tests/target/issue-2526.rs create mode 100644 tests/target/issue-2534/format_macro_matchers_false.rs create mode 100644 tests/target/issue-2534/format_macro_matchers_true.rs create mode 100644 tests/target/issue-2551.rs create mode 100644 tests/target/issue-2554.rs create mode 100644 tests/target/issue-2582.rs create mode 100644 tests/target/issue-2641.rs create mode 100644 tests/target/issue-2644.rs create mode 100644 tests/target/issue-2673-nonmodrs-mods/foo.rs create mode 100644 tests/target/issue-2673-nonmodrs-mods/foo/bar.rs create mode 100644 tests/target/issue-2673-nonmodrs-mods/lib.rs create mode 100644 tests/target/issue-2728.rs create mode 100644 tests/target/issue-2759.rs create mode 100644 tests/target/issue-2761.rs create mode 100644 tests/target/issue-2781.rs create mode 100644 tests/target/issue-2794.rs create mode 100644 tests/target/issue-2810.rs create mode 100644 tests/target/issue-2835.rs create mode 100644 tests/target/issue-2863.rs create mode 100644 tests/target/issue-2869.rs create mode 100644 tests/target/issue-2896.rs create mode 100644 tests/target/issue-2916.rs create mode 100644 tests/target/issue-2917/minimal.rs create mode 100644 tests/target/issue-2917/packed_simd.rs create mode 100644 tests/target/issue-2922.rs create mode 100644 tests/target/issue-2927-2.rs create mode 100644 tests/target/issue-2927.rs create mode 100644 tests/target/issue-2930.rs create mode 100644 tests/target/issue-2936.rs create mode 100644 tests/target/issue-2941.rs create mode 100644 tests/target/issue-2955.rs create mode 100644 tests/target/issue-2973.rs create mode 100644 tests/target/issue-2976.rs create mode 100644 tests/target/issue-2977/block.rs create mode 100644 tests/target/issue-2977/impl.rs create mode 100644 tests/target/issue-2977/item.rs create mode 100644 tests/target/issue-2977/trait.rs create mode 100644 tests/target/issue-2985.rs create mode 100644 tests/target/issue-2995.rs create mode 100644 tests/target/issue-3029.rs create mode 100644 tests/target/issue-3032.rs create mode 100644 tests/target/issue-3038.rs create mode 100644 tests/target/issue-3043.rs create mode 100644 tests/target/issue-3049.rs create mode 100644 tests/target/issue-3055/backtick.rs create mode 100644 tests/target/issue-3055/empty-code-block.rs create mode 100644 tests/target/issue-3055/original.rs create mode 100644 tests/target/issue-3059.rs create mode 100644 tests/target/issue-3066.rs create mode 100644 tests/target/issue-3105.rs create mode 100644 tests/target/issue-3118.rs create mode 100644 tests/target/issue-3124.rs create mode 100644 tests/target/issue-3131.rs create mode 100644 tests/target/issue-3132.rs create mode 100644 tests/target/issue-3153.rs create mode 100644 tests/target/issue-3158.rs create mode 100644 tests/target/issue-3182.rs create mode 100644 tests/target/issue-3184.rs create mode 100644 tests/target/issue-3194.rs create mode 100644 tests/target/issue-3198.rs create mode 100644 tests/target/issue-3213/version_one.rs create mode 100644 tests/target/issue-3213/version_two.rs create mode 100644 tests/target/issue-3217.rs create mode 100644 tests/target/issue-3224.rs create mode 100644 tests/target/issue-3227/one.rs create mode 100644 tests/target/issue-3227/two.rs create mode 100644 tests/target/issue-3234.rs create mode 100644 tests/target/issue-3241.rs create mode 100644 tests/target/issue-3253/bar.rs create mode 100644 tests/target/issue-3253/foo.rs create mode 100644 tests/target/issue-3253/lib.rs create mode 100644 tests/target/issue-3253/paths/bar_foo.rs create mode 100644 tests/target/issue-3253/paths/excluded.rs create mode 100644 tests/target/issue-3253/paths/foo_bar.rs create mode 100644 tests/target/issue-3265.rs create mode 100644 tests/target/issue-3270/one.rs create mode 100644 tests/target/issue-3270/two.rs create mode 100644 tests/target/issue-3270/wrap.rs create mode 100644 tests/target/issue-3272/v1.rs create mode 100644 tests/target/issue-3272/v2.rs create mode 100644 tests/target/issue-3278/version_one.rs create mode 100644 tests/target/issue-3278/version_two.rs create mode 100644 tests/target/issue-3295/two.rs create mode 100644 tests/target/issue-3302.rs create mode 100644 tests/target/issue-3304.rs create mode 100644 tests/target/issue-3314.rs create mode 100644 tests/target/issue-3343.rs create mode 100644 tests/target/issue-3423.rs create mode 100644 tests/target/issue-3434/lib.rs create mode 100644 tests/target/issue-3434/no_entry.rs create mode 100644 tests/target/issue-3434/not_skip_macro.rs create mode 100644 tests/target/issue-3442.rs create mode 100644 tests/target/issue-3465.rs create mode 100644 tests/target/issue-3494/crlf.rs create mode 100644 tests/target/issue-3494/lf.rs create mode 100644 tests/target/issue-3499.rs create mode 100644 tests/target/issue-3508.rs create mode 100644 tests/target/issue-3515.rs create mode 100644 tests/target/issue-3532.rs create mode 100644 tests/target/issue-3539.rs create mode 100644 tests/target/issue-3554.rs create mode 100644 tests/target/issue-3567.rs create mode 100644 tests/target/issue-3568.rs create mode 100644 tests/target/issue-3585/extern_crate.rs create mode 100644 tests/target/issue-3585/reorder_imports_disabled.rs create mode 100644 tests/target/issue-3585/reorder_imports_enabled.rs create mode 100644 tests/target/issue-3585/use.rs create mode 100644 tests/target/issue-3595.rs create mode 100644 tests/target/issue-3601.rs create mode 100644 tests/target/issue-3614/version_one.rs create mode 100644 tests/target/issue-3614/version_two.rs create mode 100644 tests/target/issue-3636.rs create mode 100644 tests/target/issue-3639.rs create mode 100644 tests/target/issue-3645.rs create mode 100644 tests/target/issue-3651.rs create mode 100644 tests/target/issue-3665/lib.rs create mode 100644 tests/target/issue-3665/not_skip_attribute.rs create mode 100644 tests/target/issue-3665/sub_mod.rs create mode 100644 tests/target/issue-3672.rs create mode 100644 tests/target/issue-3675.rs create mode 100644 tests/target/issue-3701/one.rs create mode 100644 tests/target/issue-3701/two.rs create mode 100644 tests/target/issue-3709.rs create mode 100644 tests/target/issue-3711.rs create mode 100644 tests/target/issue-3717.rs create mode 100644 tests/target/issue-3718.rs create mode 100644 tests/target/issue-3740.rs create mode 100644 tests/target/issue-3741.rs create mode 100644 tests/target/issue-3750.rs create mode 100644 tests/target/issue-3751.rs create mode 100644 tests/target/issue-3759.rs create mode 100644 tests/target/issue-3779/ice.rs create mode 100644 tests/target/issue-3779/lib.rs create mode 100644 tests/target/issue-3786.rs create mode 100644 tests/target/issue-3787.rs create mode 100644 tests/target/issue-3815.rs create mode 100644 tests/target/issue-3840/version-one_hard-tabs.rs create mode 100644 tests/target/issue-3840/version-one_soft-tabs.rs create mode 100644 tests/target/issue-3840/version-two_hard-tabs.rs create mode 100644 tests/target/issue-3840/version-two_soft-tabs.rs create mode 100644 tests/target/issue-3845.rs create mode 100644 tests/target/issue-3882.rs create mode 100644 tests/target/issue-3974.rs create mode 100644 tests/target/issue-3987/format_macro_bodies_false.rs create mode 100644 tests/target/issue-3987/format_macro_bodies_true.rs create mode 100644 tests/target/issue-4018.rs create mode 100644 tests/target/issue-4020.rs create mode 100644 tests/target/issue-4029.rs create mode 100644 tests/target/issue-4036/one.rs create mode 100644 tests/target/issue-4036/three.rs create mode 100644 tests/target/issue-4036/two.rs create mode 100644 tests/target/issue-4041.rs create mode 100644 tests/target/issue-4068.rs create mode 100644 tests/target/issue-4079.rs create mode 100644 tests/target/issue-4115.rs create mode 100644 tests/target/issue-4120.rs create mode 100644 tests/target/issue-4152.rs create mode 100644 tests/target/issue-4159.rs create mode 100644 tests/target/issue-4210-disabled.rs create mode 100644 tests/target/issue-4210.rs create mode 100644 tests/target/issue-4243.rs create mode 100644 tests/target/issue-4244.rs create mode 100644 tests/target/issue-4245.rs create mode 100644 tests/target/issue-4310.rs create mode 100644 tests/target/issue-4312.rs create mode 100644 tests/target/issue-4313.rs create mode 100644 tests/target/issue-4382.rs create mode 100644 tests/target/issue-4398.rs create mode 100644 tests/target/issue-4427.rs create mode 100644 tests/target/issue-447.rs create mode 100644 tests/target/issue-4530.rs create mode 100644 tests/target/issue-4577.rs create mode 100644 tests/target/issue-4603.rs create mode 100644 tests/target/issue-4615/minimum_example.rs create mode 100644 tests/target/issue-4643.rs create mode 100644 tests/target/issue-4646.rs create mode 100644 tests/target/issue-4656/format_me_please.rs create mode 100644 tests/target/issue-4656/lib.rs create mode 100644 tests/target/issue-4656/lib2.rs create mode 100644 tests/target/issue-4689/one.rs create mode 100644 tests/target/issue-4689/two.rs create mode 100644 tests/target/issue-4791/buggy.rs create mode 100644 tests/target/issue-4791/issue_4928.rs create mode 100644 tests/target/issue-4791/no_trailing_comma.rs create mode 100644 tests/target/issue-4791/trailing_comma.rs create mode 100644 tests/target/issue-4816/lib.rs create mode 100644 tests/target/issue-4908-2.rs create mode 100644 tests/target/issue-4908.rs create mode 100644 tests/target/issue-4926/deeply_nested_struct.rs create mode 100644 tests/target/issue-4926/deeply_nested_struct_with_long_field_names.rs create mode 100644 tests/target/issue-4926/deeply_nested_struct_with_many_fields.rs create mode 100644 tests/target/issue-4926/enum_struct_field.rs create mode 100644 tests/target/issue-4926/minimum_example.rs create mode 100644 tests/target/issue-4926/struct_with_long_field_names.rs create mode 100644 tests/target/issue-4926/struct_with_many_fields.rs create mode 100644 tests/target/issue-4984/minimum_example.rs create mode 100644 tests/target/issue-4984/multi_line_derive.rs create mode 100644 tests/target/issue-4984/multiple_comments_within.rs create mode 100644 tests/target/issue-4984/should_not_change.rs create mode 100644 tests/target/issue-5005/minimum_example.rs create mode 100644 tests/target/issue-5009/1_minimum_example.rs create mode 100644 tests/target/issue-5009/2_many_in_connectors_in_pattern.rs create mode 100644 tests/target/issue-5009/3_nested_for_loop_with_connector_in_pattern.rs create mode 100644 tests/target/issue-5009/4_nested_for_loop_with_if_elseif_else.rs create mode 100644 tests/target/issue-5009/5_nested_for_loop_with_connector_in_if_elseif_else.rs create mode 100644 tests/target/issue-5009/6_deeply_nested_for_loop_with_connector_in_pattern.rs create mode 100644 tests/target/issue-5011.rs create mode 100644 tests/target/issue-5012/trailing_comma_always.rs create mode 100644 tests/target/issue-5012/trailing_comma_never.rs create mode 100644 tests/target/issue-5023.rs create mode 100644 tests/target/issue-5030.rs create mode 100644 tests/target/issue-5033/minimum_example.rs create mode 100644 tests/target/issue-5033/nested_modules.rs create mode 100644 tests/target/issue-5042/multi-line_comment_with_trailing_comma.rs create mode 100644 tests/target/issue-5042/multi-line_comment_without_trailing_comma.rs create mode 100644 tests/target/issue-5042/single-line_comment_with_trailing_comma.rs create mode 100644 tests/target/issue-5042/single-line_comment_without_trailing_comma.rs create mode 100644 tests/target/issue-5066/multi_line_struct_trailing_comma_always_struct_lit_width_0.rs create mode 100644 tests/target/issue-5066/multi_line_struct_trailing_comma_never_struct_lit_width_0.rs create mode 100644 tests/target/issue-5066/multi_line_struct_with_trailing_comma_always.rs create mode 100644 tests/target/issue-5066/multi_line_struct_with_trailing_comma_never.rs create mode 100644 tests/target/issue-5066/with_trailing_comma_always.rs create mode 100644 tests/target/issue-5066/with_trailing_comma_never.rs create mode 100644 tests/target/issue-5088/deeply_nested_long_comment_wrap_comments_false.rs create mode 100644 tests/target/issue-5088/deeply_nested_long_comment_wrap_comments_true.rs create mode 100644 tests/target/issue-5088/multi_line_itemized_block_wrap_comments_false.rs create mode 100644 tests/target/issue-5088/multi_line_itemized_block_wrap_comments_true.rs create mode 100644 tests/target/issue-5088/multi_line_text_with_itemized_block_wrap_comments_false.rs create mode 100644 tests/target/issue-5088/multi_line_text_with_itemized_block_wrap_comments_true.rs create mode 100644 tests/target/issue-5088/single_line_itemized_block_wrap_comments_false.rs create mode 100644 tests/target/issue-5088/single_line_itemized_block_wrap_comments_true.rs create mode 100644 tests/target/issue-5088/start_with_empty_comment_very_long_itemized_block_wrap_comments_false.rs create mode 100644 tests/target/issue-5088/start_with_empty_comment_very_long_itemized_block_wrap_comments_true.rs create mode 100644 tests/target/issue-5088/start_with_empty_comment_wrap_comments_false.rs create mode 100644 tests/target/issue-5088/start_with_empty_comment_wrap_comments_true.rs create mode 100644 tests/target/issue-5088/very_long_comment_wrap_comments_false.rs create mode 100644 tests/target/issue-5088/very_long_comment_wrap_comments_true.rs create mode 100644 tests/target/issue-5095.rs create mode 100644 tests/target/issue-510.rs create mode 100644 tests/target/issue-5125/attributes_in_formal_fuction_parameter.rs create mode 100644 tests/target/issue-5125/long_parameter_in_different_positions.rs create mode 100644 tests/target/issue-5125/minimum_example.rs create mode 100644 tests/target/issue-5125/with_leading_and_inline_comments.rs create mode 100644 tests/target/issue-5151/minimum_example.rs create mode 100644 tests/target/issue-5157/indented_itemized_markdown_blockquote.rs create mode 100644 tests/target/issue-5157/nested_itemized_markdown_blockquote.rs create mode 100644 tests/target/issue-5157/support_itemized_markdown_blockquote.rs create mode 100644 tests/target/issue-5234.rs create mode 100644 tests/target/issue-5238/markdown_header_wrap_comments_false.rs create mode 100644 tests/target/issue-5238/markdown_header_wrap_comments_true.rs create mode 100644 tests/target/issue-5260.rs create mode 100644 tests/target/issue-5270/merge_derives_false.rs create mode 100644 tests/target/issue-5270/merge_derives_true.rs create mode 100644 tests/target/issue-5358.rs create mode 100644 tests/target/issue-539.rs create mode 100644 tests/target/issue-5488.rs create mode 100644 tests/target/issue-5586.rs create mode 100644 tests/target/issue-64.rs create mode 100644 tests/target/issue-683.rs create mode 100644 tests/target/issue-691.rs create mode 100644 tests/target/issue-770.rs create mode 100644 tests/target/issue-811.rs create mode 100644 tests/target/issue-831.rs create mode 100644 tests/target/issue-850.rs create mode 100644 tests/target/issue-855.rs create mode 100644 tests/target/issue-913.rs create mode 100644 tests/target/issue-945.rs create mode 100644 tests/target/issue-977.rs create mode 100644 tests/target/issue_1306.rs create mode 100644 tests/target/issue_3033.rs create mode 100644 tests/target/issue_3245.rs create mode 100644 tests/target/issue_3561.rs create mode 100644 tests/target/issue_3839.rs create mode 100644 tests/target/issue_3844.rs create mode 100644 tests/target/issue_3853.rs create mode 100644 tests/target/issue_3854.rs create mode 100644 tests/target/issue_3868.rs create mode 100644 tests/target/issue_3934.rs create mode 100644 tests/target/issue_3937.rs create mode 100644 tests/target/issue_4031.rs create mode 100644 tests/target/issue_4032.rs create mode 100644 tests/target/issue_4049.rs create mode 100644 tests/target/issue_4057.rs create mode 100644 tests/target/issue_4086.rs create mode 100644 tests/target/issue_4110.rs create mode 100644 tests/target/issue_4257.rs create mode 100644 tests/target/issue_4322.rs create mode 100644 tests/target/issue_4350.rs create mode 100644 tests/target/issue_4374.rs create mode 100644 tests/target/issue_4467.rs create mode 100644 tests/target/issue_4475.rs create mode 100644 tests/target/issue_4522.rs create mode 100644 tests/target/issue_4528.rs create mode 100644 tests/target/issue_4545.rs create mode 100644 tests/target/issue_4573.rs create mode 100644 tests/target/issue_4579.rs create mode 100644 tests/target/issue_4584.rs create mode 100644 tests/target/issue_4636.rs create mode 100644 tests/target/issue_4675.rs create mode 100644 tests/target/issue_4823.rs create mode 100644 tests/target/issue_4850.rs create mode 100644 tests/target/issue_4854.rs create mode 100644 tests/target/issue_4868.rs create mode 100644 tests/target/issue_4911.rs create mode 100644 tests/target/issue_4936.rs create mode 100644 tests/target/issue_4943.rs create mode 100644 tests/target/issue_4954.rs create mode 100644 tests/target/issue_4963.rs create mode 100644 tests/target/issue_5027.rs create mode 100644 tests/target/issue_5086.rs create mode 100644 tests/target/issue_5273.rs create mode 100644 tests/target/issue_5399.rs create mode 100644 tests/target/issue_5668.rs create mode 100644 tests/target/issue_5686.rs create mode 100644 tests/target/issue_5691.rs create mode 100644 tests/target/issue_5728.rs create mode 100644 tests/target/issue_5729.rs create mode 100644 tests/target/item-brace-style-always-next-line.rs create mode 100644 tests/target/item-brace-style-prefer-same-line.rs create mode 100644 tests/target/item-brace-style-same-line-where.rs create mode 100644 tests/target/itemized-blocks/no_wrap.rs create mode 100644 tests/target/itemized-blocks/rewrite_fail.rs create mode 100644 tests/target/itemized-blocks/urls.rs create mode 100644 tests/target/itemized-blocks/wrap.rs create mode 100644 tests/target/label_break.rs create mode 100644 tests/target/large-block.rs create mode 100644 tests/target/large_vec.rs create mode 100644 tests/target/lazy_static.rs create mode 100644 tests/target/let_else.rs create mode 100644 tests/target/long-fn-1/version_one.rs create mode 100644 tests/target/long-fn-1/version_two.rs create mode 100644 tests/target/long-match-arms-brace-newline.rs create mode 100644 tests/target/long-use-statement-issue-3154.rs create mode 100644 tests/target/long_field_access.rs create mode 100644 tests/target/loop.rs create mode 100644 tests/target/macro_not_expr.rs create mode 100644 tests/target/macro_rules.rs create mode 100644 tests/target/macro_rules_semi.rs create mode 100644 tests/target/macros.rs create mode 100644 tests/target/markdown-comment-with-options.rs create mode 100644 tests/target/markdown-comment.rs create mode 100644 tests/target/match-block-trailing-comma.rs create mode 100644 tests/target/match-flattening.rs create mode 100644 tests/target/match-nowrap-trailing-comma.rs create mode 100644 tests/target/match-nowrap.rs create mode 100644 tests/target/match.rs create mode 100644 tests/target/match_overflow_expr.rs create mode 100644 tests/target/max-line-length-in-chars.rs create mode 100644 tests/target/merge_imports_true_compat.rs create mode 100644 tests/target/mod-1.rs create mode 100644 tests/target/mod-2.rs create mode 100644 tests/target/mod_skip_child.rs create mode 100644 tests/target/mulit-file.rs create mode 100644 tests/target/multiline_string_in_macro_def.rs create mode 100644 tests/target/multiple.rs create mode 100644 tests/target/negative-bounds.rs create mode 100644 tests/target/negative-impl.rs create mode 100644 tests/target/nested-if-else.rs create mode 100644 tests/target/nested-visual-block.rs create mode 100644 tests/target/nested_skipped/mod.rs create mode 100644 tests/target/nestedmod/mod.rs create mode 100644 tests/target/nestedmod/mod2a.rs create mode 100644 tests/target/nestedmod/mod2b.rs create mode 100644 tests/target/nestedmod/mod2c.rs create mode 100644 tests/target/nestedmod/mymod1/mod3a.rs create mode 100644 tests/target/nestedmod/submod2/a.rs create mode 100644 tests/target/nestedmod/submod2/mod.rs create mode 100644 tests/target/no_arg_with_commnet.rs create mode 100644 tests/target/no_new_line_beginning.rs create mode 100644 tests/target/normalize_doc_attributes_should_not_imply_format_doc_comments.rs create mode 100644 tests/target/normalize_multiline_doc_attribute.rs create mode 100644 tests/target/obsolete_in_place.rs create mode 100644 tests/target/one_line_if_v1.rs create mode 100644 tests/target/one_line_if_v2.rs create mode 100644 tests/target/other.rs create mode 100644 tests/target/paren.rs create mode 100644 tests/target/path_clarity/foo.rs create mode 100644 tests/target/path_clarity/foo/bar.rs create mode 100644 tests/target/paths.rs create mode 100644 tests/target/pattern-condense-wildcards.rs create mode 100644 tests/target/pattern.rs create mode 100644 tests/target/preserves_carriage_return_for_unix.rs create mode 100644 tests/target/preserves_carriage_return_for_windows.rs create mode 100644 tests/target/pub-restricted.rs create mode 100644 tests/target/raw_identifiers.rs create mode 100644 tests/target/remove_blank_lines.rs create mode 100644 tests/target/reorder-impl-items.rs create mode 100644 tests/target/should_not_format_string_when_format_strings_is_not_set.rs create mode 100644 tests/target/single-line-if-else.rs create mode 100644 tests/target/single-line-macro/v1.rs create mode 100644 tests/target/single-line-macro/v2.rs create mode 100644 tests/target/skip.rs create mode 100644 tests/target/skip/foo.rs create mode 100644 tests/target/skip/main.rs create mode 100644 tests/target/skip/preserve_trailing_comment.rs create mode 100644 tests/target/skip_macro_invocations/all.rs create mode 100644 tests/target/skip_macro_invocations/all_and_name.rs create mode 100644 tests/target/skip_macro_invocations/empty.rs create mode 100644 tests/target/skip_macro_invocations/name.rs create mode 100644 tests/target/skip_macro_invocations/name_unknown.rs create mode 100644 tests/target/skip_macro_invocations/names.rs create mode 100644 tests/target/skip_macro_invocations/path_qualified_invocation_mismatch.rs create mode 100644 tests/target/skip_macro_invocations/path_qualified_match.rs create mode 100644 tests/target/skip_macro_invocations/path_qualified_name_mismatch.rs create mode 100644 tests/target/skip_macro_invocations/use_alias_examples.rs create mode 100644 tests/target/skip_mod.rs create mode 100644 tests/target/soft-wrapping.rs create mode 100644 tests/target/space-not-before-newline.rs create mode 100644 tests/target/spaces-around-ranges.rs create mode 100644 tests/target/statements.rs create mode 100644 tests/target/static.rs create mode 100644 tests/target/string-lit-2.rs create mode 100644 tests/target/string-lit-custom.rs create mode 100644 tests/target/string-lit.rs create mode 100644 tests/target/string_punctuation.rs create mode 100644 tests/target/struct-field-attributes.rs create mode 100644 tests/target/struct_field_doc_comment.rs create mode 100644 tests/target/struct_lits.rs create mode 100644 tests/target/struct_lits_multiline.rs create mode 100644 tests/target/struct_lits_visual.rs create mode 100644 tests/target/struct_lits_visual_multiline.rs create mode 100644 tests/target/struct_tuple_visual.rs create mode 100644 tests/target/structs.rs create mode 100644 tests/target/trailing-comma-never.rs create mode 100644 tests/target/trailing_commas.rs create mode 100644 tests/target/trailing_comments/hard_tabs.rs create mode 100644 tests/target/trailing_comments/soft_tabs.rs create mode 100644 tests/target/trait.rs create mode 100644 tests/target/try-conversion.rs create mode 100644 tests/target/try_block.rs create mode 100644 tests/target/tuple.rs create mode 100644 tests/target/tuple_v2.rs create mode 100644 tests/target/type.rs create mode 100644 tests/target/type_alias.rs create mode 100644 tests/target/unicode.rs create mode 100644 tests/target/unindent_if_else_cond_comment.rs create mode 100644 tests/target/unions.rs create mode 100644 tests/target/unsafe-mod.rs create mode 100644 tests/target/visibility.rs create mode 100644 tests/target/visual-fn-type.rs create mode 100644 tests/target/where-clause-rfc.rs create mode 100644 tests/target/where-clause.rs create mode 100644 tests/target/width-heuristics.rs create mode 100644 tests/target/wrap_comments_should_not_imply_format_doc_comments.rs create mode 100644 tests/writemode/source/fn-single-line.rs create mode 100644 tests/writemode/source/json.rs create mode 100644 tests/writemode/source/modified.rs create mode 100644 tests/writemode/source/stdin.rs create mode 100644 tests/writemode/target/checkstyle.xml create mode 100644 tests/writemode/target/modified.txt create mode 100644 tests/writemode/target/output.json create mode 100644 tests/writemode/target/stdin.json create mode 100644 tests/writemode/target/stdin.xml create mode 100644 triagebot.toml diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000000..ed6894e5c273 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,23 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false + +[*.rs] +indent_size = 4 + +[tests/**/*.rs] +charset = utf-8 +end_of_line = unset +indent_size = unset +indent_style = unset +trim_trailing_whitespace = unset +insert_final_newline = unset diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000000..91df4f0eb18d --- /dev/null +++ b/.gitattributes @@ -0,0 +1,7 @@ +* text=auto eol=lf +tests/source/issue-3494/crlf.rs -text +tests/source/comment_crlf_newline.rs -text +tests/source/configs/enum_discrim_align_threshold/40.rs -text +tests/target/issue-3494/crlf.rs -text +tests/target/comment_crlf_newline.rs -text +tests/target/configs/enum_discrim_align_threshold/40.rs -text diff --git a/.github/workflows/check_diff.yml b/.github/workflows/check_diff.yml new file mode 100644 index 000000000000..8bfb5834519c --- /dev/null +++ b/.github/workflows/check_diff.yml @@ -0,0 +1,33 @@ +name: Diff Check +on: + workflow_dispatch: + inputs: + clone_url: + description: 'Git url of a rustfmt fork to compare against the latest master rustfmt' + required: true + branch_name: + description: 'Name of the feature branch on the forked repo' + required: true + commit_hash: + description: 'Optional commit hash from the feature branch' + required: false + rustfmt_configs: + description: 'Optional comma separated list of rustfmt config options to pass when running the feature branch' + required: false + +jobs: + diff_check: + runs-on: ubuntu-latest + + steps: + - name: checkout + uses: actions/checkout@v3 + + - name: install rustup + run: | + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > rustup-init.sh + sh rustup-init.sh -y --default-toolchain none + rustup target add x86_64-unknown-linux-gnu + + - name: check diff + run: bash ${GITHUB_WORKSPACE}/ci/check_diff.sh ${{ github.event.inputs.clone_url }} ${{ github.event.inputs.branch_name }} ${{ github.event.inputs.commit_hash }} ${{ github.event.inputs.rustfmt_configs }} diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml new file mode 100644 index 000000000000..314ce0e84c61 --- /dev/null +++ b/.github/workflows/integration.yml @@ -0,0 +1,81 @@ +name: integration +on: + push: + branches: + - master + pull_request: + +jobs: + integration-tests: + runs-on: ubuntu-latest + name: ${{ matrix.integration }} + strategy: + # https://help.github.com/en/actions/getting-started-with-github-actions/about-github-actions#usage-limits + # There's a limit of 60 concurrent jobs across all repos in the rust-lang organization. + # In order to prevent overusing too much of that 60 limit, we throttle the + # number of rustfmt jobs that will run concurrently. + max-parallel: 4 + fail-fast: false + matrix: + integration: [ + bitflags, + error-chain, + log, + mdbook, + packed_simd, + rust-semverver, + tempdir, + futures-rs, + rust-clippy, + ] + include: + # Allowed Failures + # Actions doesn't yet support explicitly marking matrix legs as allowed failures + # https://github.community/t5/GitHub-Actions/continue-on-error-allow-failure-UI-indication/td-p/37033 + # https://github.community/t5/GitHub-Actions/Why-a-matrix-step-will-be-canceled-if-another-one-failed/td-p/30920 + # Instead, leverage `continue-on-error` + # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions#jobsjob_idstepscontinue-on-error + # + # Failing due to breaking changes in rustfmt 2.0 where empty + # match blocks have trailing commas removed + # https://github.com/rust-lang/rustfmt/pull/4226 + - integration: chalk + allow-failure: true + - integration: crater + allow-failure: true + - integration: glob + allow-failure: true + - integration: stdsimd + allow-failure: true + # Using old rustfmt configuration option + - integration: rand + allow-failure: true + # Keep this as an allowed failure as it's fragile to breaking changes of rustc. + - integration: rust-clippy + allow-failure: true + # Using old rustfmt configuration option + - integration: packed_simd + allow-failure: true + # calebcartwright (2019-12-24) + # Keeping this as an allowed failure since it was flagged as such in the TravisCI config, even though + # it appears to have been passing for quite some time. + # Original comment was: temporal build failure due to breaking changes in the nightly compiler + - integration: rust-semverver + allow-failure: true + + steps: + - name: checkout + uses: actions/checkout@v3 + + # Run build + - name: install rustup + run: | + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > rustup-init.sh + sh rustup-init.sh -y --default-toolchain none + + - name: run integration tests + env: + INTEGRATION: ${{ matrix.integration }} + TARGET: x86_64-unknown-linux-gnu + run: ./ci/integration.sh + continue-on-error: ${{ matrix.allow-failure == true }} diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml new file mode 100644 index 000000000000..bce9b0c8d5a9 --- /dev/null +++ b/.github/workflows/linux.yml @@ -0,0 +1,39 @@ +name: linux +on: + push: + branches: + - master + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + name: (${{ matrix.target }}, ${{ matrix.cfg_release_channel }}) + env: + CFG_RELEASE_CHANNEL: ${{ matrix.cfg_release_channel }} + strategy: + # https://help.github.com/en/actions/getting-started-with-github-actions/about-github-actions#usage-limits + # There's a limit of 60 concurrent jobs across all repos in the rust-lang organization. + # In order to prevent overusing too much of that 60 limit, we throttle the + # number of rustfmt jobs that will run concurrently. + max-parallel: 1 + fail-fast: false + matrix: + target: [ + x86_64-unknown-linux-gnu, + ] + cfg_release_channel: [nightly, stable] + + steps: + - name: checkout + uses: actions/checkout@v3 + + # Run build + - name: install rustup + run: | + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > rustup-init.sh + sh rustup-init.sh -y --default-toolchain none + rustup target add ${{ matrix.target }} + + - name: Build and Test + run: ./ci/build_and_test.sh diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml new file mode 100644 index 000000000000..89a980c42c5a --- /dev/null +++ b/.github/workflows/mac.yml @@ -0,0 +1,36 @@ +name: mac +on: + push: + branches: + - master + pull_request: + +jobs: + test: + # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/virtual-environments-for-github-hosted-runners#supported-runners-and-hardware-resources + # macOS Catalina 10.15 + runs-on: macos-latest + name: (${{ matrix.target }}, ${{ matrix.cfg_release_channel }}) + env: + CFG_RELEASE_CHANNEL: ${{ matrix.cfg_release_channel }} + strategy: + fail-fast: false + matrix: + target: [ + x86_64-apple-darwin, + ] + cfg_release_channel: [nightly, stable] + + steps: + - name: checkout + uses: actions/checkout@v3 + + # Run build + - name: install rustup + run: | + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > rustup-init.sh + sh rustup-init.sh -y --default-toolchain none + rustup target add ${{ matrix.target }} + + - name: Build and Test + run: ./ci/build_and_test.sh diff --git a/.github/workflows/rustdoc_check.yml b/.github/workflows/rustdoc_check.yml new file mode 100644 index 000000000000..cd0c3218971e --- /dev/null +++ b/.github/workflows/rustdoc_check.yml @@ -0,0 +1,25 @@ +name: rustdoc check +on: + push: + branches: + - master + pull_request: + +jobs: + rustdoc_check: + runs-on: ubuntu-latest + name: rustdoc check + steps: + - name: checkout + uses: actions/checkout@v3 + + - name: install rustup + run: | + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > rustup-init.sh + sh rustup-init.sh -y --default-toolchain none + rustup target add x86_64-unknown-linux-gnu + + - name: document rustfmt + env: + RUSTDOCFLAGS: --document-private-items --enable-index-page --show-type-layout --generate-link-to-definition -Zunstable-options -Dwarnings + run: cargo doc -Zskip-rustdoc-fingerprint --no-deps -p rustfmt-nightly -p rustfmt-config_proc_macro diff --git a/.github/workflows/upload-assets.yml b/.github/workflows/upload-assets.yml new file mode 100644 index 000000000000..7dfaa4b9204c --- /dev/null +++ b/.github/workflows/upload-assets.yml @@ -0,0 +1,78 @@ +name: upload + +on: + push: + release: + types: [created] + workflow_dispatch: + +jobs: + build-release: + name: build-release + strategy: + matrix: + build: [linux-x86_64, macos-x86_64, windows-x86_64-gnu, windows-x86_64-msvc] + include: + - build: linux-x86_64 + os: ubuntu-latest + rust: nightly + target: x86_64-unknown-linux-gnu + - build: macos-x86_64 + os: macos-latest + rust: nightly + target: x86_64-apple-darwin + - build: windows-x86_64-gnu + os: windows-latest + rust: nightly-x86_64-gnu + target: x86_64-pc-windows-gnu + - build: windows-x86_64-msvc + os: windows-latest + rust: nightly-x86_64-msvc + target: x86_64-pc-windows-msvc + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v3 + + # Run build + - name: install rustup + run: | + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > rustup-init.sh + sh rustup-init.sh -y --default-toolchain none + rustup target add ${{ matrix.target }} + + - name: Add mingw64 to path for x86_64-gnu + run: echo "C:\msys64\mingw64\bin" >> $GITHUB_PATH + if: matrix.rust == 'nightly-x86_64-gnu' + shell: bash + + - name: Build release binaries + run: cargo build --release + + - name: Build archive + shell: bash + run: | + staging="rustfmt_${{ matrix.build }}_${{ github.event.release.tag_name }}" + mkdir -p "$staging" + + cp {README.md,Configurations.md,CHANGELOG.md,LICENSE-MIT,LICENSE-APACHE} "$staging/" + + if [ "${{ matrix.os }}" = "windows-latest" ]; then + cp target/release/{rustfmt.exe,cargo-fmt.exe,rustfmt-format-diff.exe,git-rustfmt.exe} "$staging/" + 7z a "$staging.zip" "$staging" + echo "ASSET=$staging.zip" >> $GITHUB_ENV + else + cp target/release/{rustfmt,cargo-fmt,rustfmt-format-diff,git-rustfmt} "$staging/" + tar czf "$staging.tar.gz" "$staging" + echo "ASSET=$staging.tar.gz" >> $GITHUB_ENV + fi + + - name: Upload Release Asset + if: github.event_name == 'release' + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ github.event.release.upload_url }} + asset_path: ${{ env.ASSET }} + asset_name: ${{ env.ASSET }} + asset_content_type: application/octet-stream diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml new file mode 100644 index 000000000000..ec37c714b085 --- /dev/null +++ b/.github/workflows/windows.yml @@ -0,0 +1,62 @@ +name: windows +on: + push: + branches: + - master + pull_request: + +jobs: + test: + runs-on: windows-latest + name: (${{ matrix.target }}, ${{ matrix.cfg_release_channel }}) + env: + CFG_RELEASE_CHANNEL: ${{ matrix.cfg_release_channel }} + strategy: + # https://help.github.com/en/actions/getting-started-with-github-actions/about-github-actions#usage-limits + # There's a limit of 60 concurrent jobs across all repos in the rust-lang organization. + # In order to prevent overusing too much of that 60 limit, we throttle the + # number of rustfmt jobs that will run concurrently. + max-parallel: 2 + fail-fast: false + matrix: + target: [ + i686-pc-windows-gnu, + i686-pc-windows-msvc, + x86_64-pc-windows-gnu, + x86_64-pc-windows-msvc, + ] + cfg_release_channel: [nightly, stable] + + steps: + # The Windows runners have autocrlf enabled by default + # which causes failures for some of rustfmt's line-ending sensitive tests + - name: disable git eol translation + run: git config --global core.autocrlf false + - name: checkout + uses: actions/checkout@v3 + + # Run build + - name: Install Rustup using win.rustup.rs + run: | + # Disable the download progress bar which can cause perf issues + $ProgressPreference = "SilentlyContinue" + Invoke-WebRequest https://win.rustup.rs/ -OutFile rustup-init.exe + .\rustup-init.exe -y --default-host=x86_64-pc-windows-msvc --default-toolchain=none + del rustup-init.exe + rustup target add ${{ matrix.target }} + shell: powershell + + - name: Add mingw32 to path for i686-gnu + run: | + echo "C:\msys64\mingw32\bin" >> $GITHUB_PATH + if: matrix.target == 'i686-pc-windows-gnu' && matrix.channel == 'nightly' + shell: bash + + - name: Add mingw64 to path for x86_64-gnu + run: echo "C:\msys64\mingw64\bin" >> $GITHUB_PATH + if: matrix.target == 'x86_64-pc-windows-gnu' && matrix.channel == 'nightly' + shell: bash + + - name: Build and Test + shell: cmd + run: ci\build_and_test.bat diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000000..71cf88f79e67 --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ + +# Created by https://www.gitignore.io/api/rust + +### Rust ### +# Generated by Cargo +# will have compiled files and executables +/target +tests/cargo-fmt/**/target + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +# Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# End of https://www.gitignore.io/api/rust + +# Used by macOS' file system to track custom attributes of containing folder +.DS_Store + +# Editors' specific files +.idea/ +.vscode/ +*~ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000000..fbcd0a57f4e5 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,1415 @@ +# Changelog + +## [Unreleased] + + +## [1.6.0] 2023-07-02 + +### Added + +- Support for formatting let-else statements [#5690] +- New config option, `single_line_let_else_max_width`, that allows users to configure the maximum length of single line `let-else` statements. `let-else` statements that otherwise meet the requirements to be formatted on a single line will have their divergent`else` block formatted over multiple lines if they exceed this length [#5684] + +[#5690]: (https://github.com/rust-lang/rustfmt/pulls/5690) +[#5684]: https://github.com/rust-lang/rustfmt/issues/5684 + +## [1.5.3] 2023-06-20 + +### Fixed + +- When formatting doc comments with `wrap_comments = true` rustfmt will no longer wrap markdown tables [#4210](https://github.com/rust-lang/rustfmt/issues/4210) +- Properly handle wrapping comments that include a numbered list in markdown [#5416](https://github.com/rust-lang/rustfmt/issues/5416) +- Properly handle markdown sublists that utilize a `+` [#4041](https://github.com/rust-lang/rustfmt/issues/4210) +- rustfmt will no longer use shorthand initialization when rewriting a tuple struct even when `use_field_init_shorthand = true` as this leads to code that could no longer compile. + Take the following struct as an example `struct MyStruct(u64);`. rustfmt will no longer format `MyStruct { 0: 0 }` as `MyStruct { 0 }` [#5488](https://github.com/rust-lang/rustfmt/issues/5488) +- rustfmt no longer panics when formatting an empty code block in a doc comment with `format_code_in_doc_comments = true` [#5234](https://github.com/rust-lang/rustfmt/issues/5234). For example: + ```rust + /// ``` + /// + /// ``` + fn main() {} + ``` +- rustfmt no longer incorrectly duplicates the where clause bounds when using const expression in where clause bounds with feature `#![feature(generic_const_exprs)]` [#5691](https://github.com/rust-lang/rustfmt/issues/5691). e.g.: + ```rust + struct S + where + [(); { num_slots!(C) }]:, { + // code ... + } + ``` +- Prevent ICE when parsing invalid attributes in `cfg_if!` macros [#5728](https://github.com/rust-lang/rustfmt/issues/5728), [#5729](https://github.com/rust-lang/rustfmt/issues/5729) +- rustfmt no longer loses comments placed between a doc comment and generic params [#5320](https://github.com/rust-lang/rustfmt/issues/5320) +- Handle explicit discriminants in enums with comments present [#5686](https://github.com/rust-lang/rustfmt/issues/5686) + +### Changed + +- Users can now control whether rustc parser errors are displayed with color using rustfmt's `--color` option. To disable colored errors pass `--color=Never` to rustfmt [#5717](https://github.com/rust-lang/rustfmt/issues/5717) + + +### Added + +- rustfmt now recognises `+` as the start of a markdown list, and won't incorrectly wrap sublists that begin with `+` when formatting doc comments with `wrap_comments = true` [#5560](https://github.com/rust-lang/rustfmt/pull/5560) + +### Misc + +- Update various dependencies, including `syn`, `cargo_metadata`, `env_logger`, and `toml` + +## [1.5.2] 2023-01-24 + +### Fixed + +- Resolve issue when comments are found within const generic defaults in unit structs [#5668](https://github.com/rust-lang/rustfmt/issues/5668) +- Resolve issue when block comments are found within trait generics [#5358](https://github.com/rust-lang/rustfmt/issues/5358) +- Correctly handle alignment of comments containing unicode characters [#5504](https://github.com/rust-lang/rustfmt/issues/5504) +- Properly indent a single generic bound that requires being written across multiple lines [#4689](https://github.com/rust-lang/rustfmt/issues/4689) (n.b. this change is version gated and will only appear when the `version` configuration option is set to `Two`) + +### Changed + +- Renamed `fn_args_layout` configuration option to `fn_params_layout` [#4149](https://github.com/rust-lang/rustfmt/issues/4149). Note that `fn_args_layout` has only been soft deprecated: `fn_args_layout` will continue to work without issue, but rustfmt will display a warning to encourage users to switch to the new name + +### Added + +- New configuration option (`skip_macro_invocations`)[https://rust-lang.github.io/rustfmt/?version=master&search=#skip_macro_invocations] [#5347](https://github.com/rust-lang/rustfmt/pull/5347) that can be used to globally define a single enumerated list of macro calls that rustfmt should skip formatting. rustfmt [currently also supports this via a custom tool attribute](https://github.com/rust-lang/rustfmt#tips), however, these cannot be used in all contexts because [custom inner attributes are unstable](https://github.com/rust-lang/rust/issues/54726) + +### Misc + +- rustfmt now internally supports the ability to have both stable and unstable variants of a configuration option [#5378](https://github.com/rust-lang/rustfmt/issues/5378). This ability will allow the rustfmt team to make certain configuration options available on stable toolchains more quickly because we no longer have to wait for _every_ variant to be stable-ready before stabilizing _any_ variant. + +### Install/Download Options +- **rustup (nightly)** - nightly-2023-01-24 +- **GitHub Release Binaries** - [Release v1.5.2](https://github.com/rust-lang/rustfmt/releases/tag/v1.5.2) +- **Build from source** - [Tag v1.5.2](https://github.com/rust-lang/rustfmt/tree/v1.5.2), see instructions for how to [install rustfmt from source][install-from-source] + +## [1.5.1] 2022-06-24 + +**N.B** A bug was introduced in v1.5.0/nightly-2022-06-15 which modified formatting. If you happened to run rustfmt over your code with one of those ~10 nightlies it's possible you may have seen formatting changes, and you may see additional changes after this fix since that bug has now been reverted. + +### Fixed + +- Correct an issue introduced in v1.5.0 where formatting changes were unintentionally introduced in a few cases with a large/long construct in a right hand side position (e.g. a large chain on the RHS of a local/assignment statement) +- `cargo fmt --version` properly displays the version value again [#5395](https://github.com/rust-lang/rustfmt/issues/5395) + +### Changed + +- Properly sort imports containing raw identifiers [#3791](https://github.com/rust-lang/rustfmt/issues/3791) (note this is change version gated, and not applied by default) + +### Added + +- Add new configuration option, `doc_comment_code_block_width`, which allows for setting a shorter width limit to use for formatting code snippets in doc comments [#5384](https://github.com/rust-lang/rustfmt/issues/5384) + +### Install/Download Options +- **rustup (nightly)** - nightly-2022-06-24 +- **GitHub Release Binaries** - [Release v1.5.1](https://github.com/rust-lang/rustfmt/releases/tag/v1.5.0) +- **Build from source** - [Tag v1.5.1](https://github.com/rust-lang/rustfmt/tree/v1.5.1), see instructions for how to [install rustfmt from source][install-from-source] + +## [1.5.0] 2022-06-14 + +### Changed + +- Simplify the rustfmt help text by eliding the full path to the rustfmt binary path from the usage string when running `rustfmt --help` [#5214](https://github.com/rust-lang/rustfmt/issues/5214) + +- Bumped the version for serveral dependencies. Most notably `dirs` `v2.0.1` -> `v4.0.0`. This changed the global user config directory on macOS from `$HOME/Library/Preferences` to `$HOME/Library/Application Support` [#5237](https://github.com/rust-lang/rustfmt/pull/5237) + +### Fixed + +- Remove duplicate imports when `imports_granularity` is set to `Item` [#4725](https://github.com/rust-lang/rustfmt/issues/4725) +- Properly handle stdin input containing an inner skip attribute [#5368](https://github.com/rust-lang/rustfmt/issues/5368) +- Maintain attributes on imports when `imports_granularity` is set to `Item` [#5030](https://github.com/rust-lang/rustfmt/issues/5030) +- Format empty trait definitions as a single line when both `empty_item_single_line` is enabled and `brace_style` is set to `AlwaysNextLine` [#5047](https://github.com/rust-lang/rustfmt/issues/5047) +- Don't change granularity of imports containing comments with `imports_granularity` if doing so could lose or misplace those comments [#5311](https://github.com/rust-lang/rustfmt/pull/5311) +- Prevent rustfmt from removing trailing comments at the end of files annotated with inner `#![rustfmt::skip]` attributes [#5033](https://github.com/rust-lang/rustfmt/issues/5033) +- Fixed various `error[internal]: left behind trailing whitespace"` issues: + - Remove trailing whitespace when formatting a where clause who's bounds have an empty right hand side [#5012](https://github.com/rust-lang/rustfmt/issues/5012) [#4850](https://github.com/rust-lang/rustfmt/issues/4850) + - Prevent rustfmt from adding an empty comment line when rewriting markdown lists at the start of doc comments. This issue was triggered when `wrap_comments=true` [#5088](https://github.com/rust-lang/rustfmt/issues/5088) +- Prevent adding a block indented newline before a function parameter with a complex type that was formatted over multiple lines [#5125](https://github.com/rust-lang/rustfmt/issues/5125) +- Fix various module resolution issues preventing rustfmt from finding modules that should be formatted: + - Handle external mods imported via external->inline load hierarchy [#5063](https://github.com/rust-lang/rustfmt/issues/5063) + - Resolve sub modules of integration tests [#5119](https://github.com/rust-lang/rustfmt/issues/5119) + - Module resolution will fallback to the current search directory if a relative directory search results in a `FileNotFound` error [#5198](https://github.com/rust-lang/rustfmt/issues/5198) +- Give users a clearer error message when resolving a module who's file path is ambiguous (e.g `x.rs` and `x/mod.rs`). Before users were given a `File not found` error message which was confusing [#5167](https://github.com/rust-lang/rustfmt/issues/5167) +- Fix various issues related to type aliases: + - Prevent rustfmt from adding `= impl` to associated types defined in macro bodies [#4823](https://github.com/rust-lang/rustfmt/issues/4823) + - Properly block indent type alias impl traits (TAITs) that wrap to the next line when `version=Two` is set. Before any trait bounds that wrapped to the next line would not be indented [#5027](https://github.com/rust-lang/rustfmt/issues/5027) + - Prevent rustfmt from adding an `impl Trait` definition into types [#5086](https://github.com/rust-lang/rustfmt/issues/5086) +- Fix cases where `normalize_comments=true` would de-normalizes some comments by changing inline comments into block comments [#4909](https://github.com/rust-lang/rustfmt/issues/4909) +- Prevent rustfmt from wrapping the content of markdown [reference-style links](https://www.markdownguide.org/basic-syntax/#reference-style-links) in doc comments [#5095](https://github.com/rust-lang/rustfmt/issues/5095) [#4933](https://github.com/rust-lang/rustfmt/issues/4933) +- Don't format files annotated with inner `#![rustfmt::skip]` attribute [PR #5094](https://github.com/rust-lang/rustfmt/pull/5094) +- Prevent duplicate comma when struct pattern ends with `..` and `trailing_comma=Always`. For example, `let Foo { a, .. } = b;` would become `let Foo { a,, .. } = b;` [#5066](https://github.com/rust-lang/rustfmt/issues/5066) +- Fix the order of `static` and `async` keywords when rewriting static async closures. The correct order is `static` and then `async` (e.g `static async || {}`) [#5149](https://github.com/rust-lang/rustfmt/issues/5149) +- Retain the fully qualified path segment when rewriting struct literals in expression position. Now `::Type` is not rewritten as `Trait::Type` [#5151](https://github.com/rust-lang/rustfmt/issues/5151) +- Do not remove match arm braces from a match arm with a single `ast::ExprKind::Block` that has leading attributes. Removing the braces could lead to code that does not compile. Now rustfmt will leave the outer `{}` in place when formatting `=> {#[allow(unsafe_code)]unsafe {}}` [#4109](https://github.com/rust-lang/rustfmt/issues/4109) +- Backport json emitter and stdin changes [PR #5054](https://github.com/rust-lang/rustfmt/pull/5054) + - Make `--check` work when running rustfmt with input from stdin [PR #3896](https://github.com/rust-lang/rustfmt/pull/3896) + - Fix `--check` with the `--files-with-diff` flag [PR #3910](https://github.com/rust-lang/rustfmt/pull/3910) + - Produce valid JSON when using the JSON emitter [PR #3953](https://github.com/rust-lang/rustfmt/pull/3953) + - Fix newlines in JSON output [PR #4262](https://github.com/rust-lang/rustfmt/pull/4262) + - Use `` when emitting stdin as filename [PR #4298](https://github.com/rust-lang/rustfmt/pull/4298) +- Always generate some output when formatting `@generated` files via stdin even when `format_generated_files=false`. Not producing output caused rust-analyzer to delete the file content [rust-lang/rust-analyzer](https://github.com/rust-lang/rust-analyzer/issues/11285) [#5172](https://github.com/rust-lang/rustfmt/issues/5172) +- Properly block indent multi-line comments in empty struct definitions. Previously, only the first comment line would be block indented. All other comment lines would be aligned with the struct definition [#4854](https://github.com/rust-lang/rustfmt/issues/4854) +- Prevent rustfmt from wrapping a comment at a byte position inside a non-ascii character when `wrap_comments=true`. This prevents rustfmt from panicking when breaking on the invalid position [#5023](https://github.com/rust-lang/rustfmt/issues/5023) +- Prevent rustfmt from removing commented out trailing separators (e.g commas) when rewriting lists. For example, remove the comma from a comment like this `// ...,` would lead to a scenario where the entire list could not be rewritten because the content of the comment changed [#5042](https://github.com/rust-lang/rustfmt/issues/5042) +- Fix panic when `import_granularity` was set to `Module`, `One`, or `Crate` and the import use declaration contained an alias `use crate a::b as b1` [#5131](https://github.com/rust-lang/rustfmt/issues/5131) +- Add a newline between generic parameters and their doc comments to prevent the generic parameters from being merged into their doc comments [#5122](https://github.com/rust-lang/rustfmt/issues/5122) +- Fixes indentation issue where string literals manually broken with line continuation characters (`\`) would be incorrectly indented in macro definitions when setting `format_strings=true`[#4036](https://github.com/rust-lang/rustfmt/issues/4036) +- Properly wrap and format long markdown block quotes when `wrap_comments=true` [#5157](https://github.com/rust-lang/rustfmt/issues/5157) +- Prevent rustfmt from wrapping markdown headers even when `wrap_comments=true`. Wrapping the markdown headers would prevent them from being properly rendered with rustdoc [#5238](https://github.com/rust-lang/rustfmt/issues/5238) +- Prevent rustfmt from removing commas between struct fields when those fields were also separated by an empty line [#4791](https://github.com/rust-lang/rustfmt/issues/4791) [#4928](https://github.com/rust-lang/rustfmt/issues/4928) +- Fix compiler error caused when formatting imports with `imports_granularity=Module` and a path containing `self`. Given the following import `use crate::lexer::{self, tokens::TokenData};`, rustfmt would transform the `self` import into `use crate::lexer::self;`. Now rustfmt produces `use crate::lexer::{self};` [#4681](https://github.com/rust-lang/rustfmt/issues/4681) +- Prevent rustfmt from breaking long type links in doc comments on namespace qualifiers (`::`) when `wrap_comments=true`. Breaking these long type links over multiple lines prevented them from being properly rendered in rustdoc [#5260](https://github.com/rust-lang/rustfmt/issues/5260) +- Correctly find the start of struct bodies after any generic `const` parameters. Naively searching for an opening `{` lead to issues since generic `const` parameters are also defined with `{}` (e.g. `struct Example {}`) [#5273](https://github.com/rust-lang/rustfmt/issues/5273) +- Prevent rustfmt from merging derives when using inner or outer `rustfmt::skip::attributes` attributes. For example, `#[rustfmt::skip::attributes(derive)]` [#5270](https://github.com/rust-lang/rustfmt/issues/5270) +- Retain trailing `;` when rewriting macro calls in extern blocks. For example, `extern "C" { x!(-); }`[#5281](https://github.com/rust-lang/rustfmt/issues/5281) +- Add a newline when formatting struct fields preceded by both doc comments and inline comments to prevent the field from being merged into the inline comment. This was not an issue when a struct was preceded by just a doc comment or just an inline comment [#5215](https://github.com/rust-lang/rustfmt/issues/5215) + +### Added + +- Added `One` as a new [group_imports](https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#group_imports) option to create a single group for all imports [PR #4966](https://github.com/rust-lang/rustfmt/pull/4966) +- Add [short_array_element_width_threshold](https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#short_array_element_width_threshold) config option to give users more control over when `Mixed` list formatting is used [PR #5228](https://github.com/rust-lang/rustfmt/pull/5228) + +### Removed + +- Removed unstable, nightly-only config option `report_todo` [#5101](https://github.com/rust-lang/rustfmt/issues/5101) +- Removed unstable, nightly-only config option `report_fixme` [#5102](https://github.com/rust-lang/rustfmt/issues/5102) +- Removed unstable, nightly-only config option `license_template_path` [#5103](https://github.com/rust-lang/rustfmt/issues/5103) + +### Misc + +- Improved performance when formatting large and deeply nested expression trees, often found in generated code, which have many expressions that exceed `max_width` [#5128](https://github.com/rust-lang/rustfmt/issues/5128), [#4867](https://github.com/rust-lang/rustfmt/issues/4867), [#4476](https://github.com/rust-lang/rustfmt/issues/4476), [#5139](https://github.com/rust-lang/rustfmt/pull/5139) + +### Install/Download Options +- **rustup (nightly)** - nightly-2022-06-15 +- **GitHub Release Binaries** - [Release v1.5.0](https://github.com/rust-lang/rustfmt/releases/tag/v1.5.0) +- **Build from source** - [Tag v1.5.0](https://github.com/rust-lang/rustfmt/tree/v1.5.0), see instructions for how to [install rustfmt from source][install-from-source] + +## [1.4.38] 2021-10-20 + +### Changed + +- Switched from `rustc-ap-*` crates to `rustc_private` for consumption model of rustc internals +- `annotate-snippets` updated to v0.8 [PR #4762](https://github.com/rust-lang/rustfmt/pull/4762) +- Greatly improved the performance of `cargo fmt` in large workspaces utilizing the `--all` flag by updating to a newer version of `cargo_metadata` that leverages updated `cargo` output from v1.51+ [PR #4997](https://github.com/rust-lang/rustfmt/pull/4997) +- Improved formatting of long slice patterns [#4530](https://github.com/rust-lang/rustfmt/issues/4530) + - **Note you must have `version = Two` in your configuration to take advantage of the new formatting** +- Stabilized `match_block_trailing_comma` configuration option [#3380](https://github.com/rust-lang/rustfmt/issues/3380) - [https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#match_block_trailing_comma](https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#match_block_trailing_comma) +- Stabilized `disable_all_formatting` configuration option [#5026](https://github.com/rust-lang/rustfmt/pull/5026) - [https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#disable_all_formatting](https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#disable_all_formatting) +- Various improvements to the configuration documentation website [https://rust-lang.github.io/rustfmt/?version=v1.4.38]([https://rust-lang.github.io/rustfmt/?version=v1.4.38]) +- Addressed various clippy and rustc warnings + + +### Fixed + +- Resolved issue where specious whitespace would be inserted when a block style comment was terminated within string literal processing [#4312](https://github.com/rust-lang/rustfmt/issues/4312) +- Nested out-of-line mods are again parsed and formatted [#4874](https://github.com/rust-lang/rustfmt/issues/4874) +- Accepts `2021` for edition value from rustfmt command line [PR #4847](https://github.com/rust-lang/rustfmt/pull/4847) +- Unstable command line options are no longer displayed in `--help` text on stable [PR #4798](https://github.com/rust-lang/rustfmt/issues/4798) +- Stopped panicking on patterns in match arms which start with non-ascii characters [#4868](https://github.com/rust-lang/rustfmt/issues/4868) +- Stopped stripping defaults on const params [#4816](https://github.com/rust-lang/rustfmt/issues/4816) +- Fixed issue with dropped content with GAT aliases with self bounds in impls [#4911](https://github.com/rust-lang/rustfmt/issues/4911) +- Stopped removing generic args on associated type constraints [#4943](https://github.com/rust-lang/rustfmt/issues/4943) +- Stopped dropping visibility on certain trait and impl items [#4960](https://github.com/rust-lang/rustfmt/issues/4960) +- Fixed dropping of qualified paths in struct patterns [#4908](https://github.com/rust-lang/rustfmt/issues/4908) and [#5005](https://github.com/rust-lang/rustfmt/issues/5005) +- Fixed bug in line width calculation that was causing specious formatting of certain patterns [#4031](https://github.com/rust-lang/rustfmt/issues/4031) + - **Note that this bug fix may cause observable formatting changes in cases where code had been formatted with prior versions of rustfmt that contained the bug** +- Fixed bug where rustfmt would drop parameter attributes if they were too long in certain cases [#4579](https://github.com/rust-lang/rustfmt/issues/4579) +- Resolved idempotency issue with extern body elements [#4963](https://github.com/rust-lang/rustfmt/issues/4963) +- rustfmt will now handle doc-style comments on function parameters, since they could appear with certain macro usage patterns even though it's generally invalid syntax [#4936](https://github.com/rust-lang/rustfmt/issues/4936) +- Fixed bug in `match_block_trailing_comma` where commas were not added to the blocks of bodies whose arm had a guard that did not fit on the same line as the pattern [#4998](https://github.com/rust-lang/rustfmt/pull/4998) +- Fixed bug in cases where derive attributes started with a block style comment [#4984](https://github.com/rust-lang/rustfmt/issues/4984) +- Fixed issue where the struct rest could be lost when `struct_field_align_threshold` was enabled [#4926](https://github.com/rust-lang/rustfmt/issues/4926) +- Handles cases where certain control flow type expressions have comments between patterns/keywords and the pattern ident contains the keyword [#5009](https://github.com/rust-lang/rustfmt/issues/5009) +- Handles tuple structs that have explicit visibilities and start with a block style comment [#5011](https://github.com/rust-lang/rustfmt/issues/5011) +- Handles leading line-style comments in certain types of macro calls [#4615](https://github.com/rust-lang/rustfmt/issues/4615) + + +### Added +- Granular width heuristic options made available for user control [PR #4782](https://github.com/rust-lang/rustfmt/pull/4782). This includes the following: + - [`array_width`](https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#array_width) + - [`attr_fn_like_width`](https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#attr_fn_like_width) + - [`chain_width`](https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#chain_width) + - [`fn_call_width`](https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#fn_call_width) + - [`single_line_if_else_max_width`](https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#single_line_if_else_max_width) + - [`struct_lit_width`](https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#struct_lit_width) + - [`struct_variant_width`](https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#struct_variant_width) + +Note this hit the rustup distributions prior to the v1.4.38 release as part of an out-of-cycle updates, but is listed in this version because the feature was not in the other v1.4.37 releases. See also the `use_small_heuristics` section on the configuration site for more information +[https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#use_small_heuristics](https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#use_small_heuristics) + +- 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 +https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#hex_literal_case + +- `cargo fmt` now directly supports the `--check` flag, which means it's now possible to run `cargo fmt --check` instead of the more verbose `cargo fmt -- --check` [#3888](https://github.com/rust-lang/rustfmt/issues/3888) + +### Install/Download Options +- **rustup (nightly)** - *pending* +- **GitHub Release Binaries** - [Release v1.4.38](https://github.com/rust-lang/rustfmt/releases/tag/v1.4.38) +- **Build from source** - [Tag v1.4.38](https://github.com/rust-lang/rustfmt/tree/v1.4.38), see instructions for how to [install rustfmt from source][install-from-source] + +## [1.4.37] 2021-04-03 + +### Changed + +- `rustc-ap-*` crates updated to v712.0.0 + +### Fixed +- Resolve idempotence issue related to indentation of macro defs that contain or-patterns with inner comments ([#4603](https://github.com/rust-lang/rustfmt/issues/4603)) +- Addressed various clippy and rustc warnings + +### Install/Download Options +- **crates.io package** - *pending* +- **rustup (nightly)** - *pending* +- **GitHub Release Binaries** - [Release v1.4.37](https://github.com/rust-lang/rustfmt/releases/tag/v1.4.37) +- **Build from source** - [Tag v1.4.37](https://github.com/rust-lang/rustfmt/tree/v1.4.37), see instructions for how to [install rustfmt from source][install-from-source] + +## [1.4.36] 2021-02-07 + +### Changed + +- `rustc-ap-*` crates updated to v705.0.0 + +### Install/Download Options +- **crates.io package** - *pending* +- **rustup (nightly)** - *pending* +- **GitHub Release Binaries** - [Release v1.4.36](https://github.com/rust-lang/rustfmt/releases/tag/v1.4.36) +- **Build from source** - [Tag v1.4.36](https://github.com/rust-lang/rustfmt/tree/v1.4.36), see instructions for how to [install rustfmt from source][install-from-source] + +## [1.4.35] 2021-02-03 + +### Changed + +- `rustc-ap-*` crates updated to v702.0.0 + +### Install/Download Options +- **crates.io package** - *pending* +- **rustup (nightly)** - *n/a (superseded by [v1.4.36](#1436-2021-02-07)) +- **GitHub Release Binaries** - [Release v1.4.35](https://github.com/rust-lang/rustfmt/releases/tag/v1.4.35) +- **Build from source** - [Tag v1.4.35](https://github.com/rust-lang/rustfmt/tree/v1.4.35), see instructions for how to [install rustfmt from source][install-from-source] + +## [1.4.34] 2021-01-28 + +### Fixed +- Don't insert trailing comma on (base-less) rest in struct literals within macros ([#4675](https://github.com/rust-lang/rustfmt/issues/4675)) + +### Install/Download Options +- **crates.io package** - *pending* +- **rustup (nightly)** - Starting in `2021-01-31` +- **GitHub Release Binaries** - [Release v1.4.34](https://github.com/rust-lang/rustfmt/releases/tag/v1.4.34) +- **Build from source** - [Tag v1.4.34](https://github.com/rust-lang/rustfmt/tree/v1.4.34), see instructions for how to [install rustfmt from source][install-from-source] + +## [1.4.33] 2021-01-27 + +### Changed +- `merge_imports` configuration has been deprecated in favor of the new `imports_granularity` option. Any existing usage of `merge_imports` will be automatically mapped to the corresponding value on `imports_granularity` with a warning message printed to encourage users to update their config files. + +### Added +- New `imports_granularity` option has been added which succeeds `merge_imports`. This new option supports several additional variants which allow users to merge imports at different levels (crate or module), and even flatten imports to have a single use statement per item. ([PR #4634](https://github.com/rust-lang/rustfmt/pull/4634), [PR #4639](https://github.com/rust-lang/rustfmt/pull/4639)) + +See the section on the configuration site for more information +https://rust-lang.github.io/rustfmt/?version=v1.4.33&search=#imports_granularity + +### Fixed +- Fix erroneous removal of `const` keyword on const trait impl ([#4084](https://github.com/rust-lang/rustfmt/issues/4084)) +- Fix incorrect span usage wit const generics in supertraits ([#4204](https://github.com/rust-lang/rustfmt/issues/4204)) +- Use correct span for const generic params ([#4263](https://github.com/rust-lang/rustfmt/issues/4263)) +- Correct span on const generics to include type bounds ([#4310](https://github.com/rust-lang/rustfmt/issues/4310)) +- Idempotence issue on blocks containing only empty statements ([#4627](https://github.com/rust-lang/rustfmt/issues/4627) and [#3868](https://github.com/rust-lang/rustfmt/issues/3868)) +- Fix issue with semicolon placement on required functions that have a trailing comment that ends in a line-style comment before the semicolon ([#4646](https://github.com/rust-lang/rustfmt/issues/4646)) +- Avoid shared interned cfg_if symbol since rustfmt can re-initialize the rustc_ast globals on multiple inputs ([#4656](https://github.com/rust-lang/rustfmt/issues/4656)) + +### Install/Download Options +- **crates.io package** - *pending* +- **rustup (nightly)** - n/a (superseded by [v1.4.34](#1434-2021-01-28)) +- **GitHub Release Binaries** - [Release v1.4.33](https://github.com/rust-lang/rustfmt/releases/tag/v1.4.33) +- **Build from source** - [Tag v1.4.33](https://github.com/rust-lang/rustfmt/tree/v1.4.33), see instructions for how to [install rustfmt from source][install-from-source] + +## [1.4.32] 2021-01-16 + +### Fixed +- Indentation now correct on first bound in cases where the generic bounds are multiline formatted and the first bound itself is multiline formatted ([#4636](https://github.com/rust-lang/rustfmt/issues/4636)) + +### Install/Download Options +- **crates.io package** - *pending* +- **rustup (nightly)** - Starting in `2021-01-18` +- **GitHub Release Binaries** - [Release v1.4.32](https://github.com/rust-lang/rustfmt/releases/tag/v1.4.32) +- **Build from source** - [Tag v1.4.32](https://github.com/rust-lang/rustfmt/tree/v1.4.32), see instructions for how to [install rustfmt from source][install-from-source] + +## [1.4.31] 2021-01-09 + +### Changed + +- `rustc-ap-*` crates updated to v697.0.0 + +### Added +- Support for 2021 Edition [#4618](https://github.com/rust-lang/rustfmt/pull/4618)) + +### Install/Download Options +- **crates.io package** - *pending* +- **rustup (nightly)** - Starting in `2021-01-16` +- **GitHub Release Binaries** - [Release v1.4.31](https://github.com/rust-lang/rustfmt/releases/tag/v1.4.31) +- **Build from source** - [Tag v1.4.31](https://github.com/rust-lang/rustfmt/tree/v1.4.31), see instructions for how to [install rustfmt from source][install-from-source] + +## [1.4.30] 2020-12-20 + +### Fixed +- Last character in derive no longer erroneously stripped when `indent_style` is overridden to `Visual`. ([#4584](https://github.com/rust-lang/rustfmt/issues/4584)) +- Brace wrapping of closure bodies maintained in cases where the closure has an explicit return type and the body consists of a single expression statement. ([#4577](https://github.com/rust-lang/rustfmt/issues/4577)) +- No more panics on invalid code with `err` and `typeof` types ([#4357](https://github.com/rust-lang/rustfmt/issues/4357), [#4586](https://github.com/rust-lang/rustfmt/issues/4586)) + +### Install/Download Options +- **crates.io package** - *pending* +- **rustup (nightly)** - Starting in `2020-12-25` +- **GitHub Release Binaries** - [Release v1.4.30](https://github.com/rust-lang/rustfmt/releases/tag/v1.4.30) +- **Build from source** - [Tag v1.4.30](https://github.com/rust-lang/rustfmt/tree/v1.4.30), see instructions for how to [install rustfmt from source][install-from-source] + +## [1.4.29] 2020-12-04 + +### Fixed +- Negative polarity on non-trait impl now preserved. ([#4566](https://github.com/rust-lang/rustfmt/issues/4566)) + +### Install/Download Options +- **crates.io package** - *pending* +- **rustup (nightly)** - Starting in `2020-12-07` +- **GitHub Release Binaries** - [Release v1.4.29](https://github.com/rust-lang/rustfmt/releases/tag/v1.4.29) +- **Build from source** - [Tag v1.4.29](https://github.com/rust-lang/rustfmt/tree/v1.4.29), see instructions for how to [install rustfmt from source][install-from-source] + +## [1.4.28] 2020-11-29 + +### Changed + +- `rustc-ap-*` crates updated to v691.0.0 +- In the event of an invalid inner attribute on a `cfg_if` condition, rustfmt will now attempt to continue and format the imported modules. Previously rustfmt would emit the parser error about an inner attribute being invalid in this position, but for rustfmt's purposes the invalid attribute doesn't prevent nor impact module formatting. + +### Added + +- [`group_imports`][group-imports-config-docs] - a new configuration option that allows users to control the strategy used for grouping imports ([#4107](https://github.com/rust-lang/rustfmt/issues/4107)) + +[group-imports-config-docs]: https://github.com/rust-lang/rustfmt/blob/v1.4.28/Configurations.md#group_imports + +### Fixed +- Formatting of malformed derived attributes is no longer butchered. ([#3898](https://github.com/rust-lang/rustfmt/issues/3898), [#4029](https://github.com/rust-lang/rustfmt/issues/4029), [#4115](https://github.com/rust-lang/rustfmt/issues/4115), [#4545](https://github.com/rust-lang/rustfmt/issues/4545)) +- Correct indentation used in macro branches when `hard_tabs` is enabled. ([#4152](https://github.com/rust-lang/rustfmt/issues/4152)) +- Comments between the visibility modifier and item name are no longer dropped. ([#2781](https://github.com/rust-lang/rustfmt/issues/2781)) +- Comments preceding the assignment operator in type aliases are no longer dropped. ([#4244](https://github.com/rust-lang/rustfmt/issues/4244)) +- Comments between {`&` operator, lifetime, `mut` kw, type} are no longer dropped. ([#4245](https://github.com/rust-lang/rustfmt/issues/4245)) +- Comments between type bounds are no longer dropped. ([#4243](https://github.com/rust-lang/rustfmt/issues/4243)) +- Function headers are no longer dropped on foreign function items. ([#4288](https://github.com/rust-lang/rustfmt/issues/4288)) +- Foreign function blocks are no longer dropped. ([#4313](https://github.com/rust-lang/rustfmt/issues/4313)) +- `where_single_line` is no longer incorrectly applied to multiline function signatures that have no `where` clause. ([#4547](https://github.com/rust-lang/rustfmt/issues/4547)) +- `matches!` expressions with multiple patterns and a destructure pattern are now able to be formatted. ([#4512](https://github.com/rust-lang/rustfmt/issues/4512)) + +### Install/Download Options +- **crates.io package** - *pending* +- **rustup (nightly)** - n/a (superseded by [v1.4.29](#1429-2020-12-04)) +- **GitHub Release Binaries** - [Release v1.4.28](https://github.com/rust-lang/rustfmt/releases/tag/v1.4.28) +- **Build from source** - [Tag v1.4.28](https://github.com/rust-lang/rustfmt/tree/v1.4.28), see instructions for how to [install rustfmt from source][install-from-source] + +## [1.4.27] 2020-11-16 + +### Fixed + +- Leading comments in an extern block are no longer dropped (a bug that exists in v1.4.26). ([#4528](https://github.com/rust-lang/rustfmt/issues/4528)) + +### Install/Download Options +- **crates.io package** - *pending* +- **rustup (nightly)** - Starting in `2020-11-18` +- **GitHub Release Binaries** - [Release v1.4.27](https://github.com/rust-lang/rustfmt/releases/tag/v1.4.27) +- **Build from source** - [Tag v1.4.27](https://github.com/rust-lang/rustfmt/tree/v1.4.27), see instructions for how to [install rustfmt from source][install-from-source] + +## [1.4.26] 2020-11-14 + +### Changed + +- Original comment indentation for trailing comments within an `if` is now taken into account when determining the indentation level to use for the trailing comment in formatted code. This does not modify any existing code formatted with rustfmt; it simply gives the programmer discretion to specify whether the comment is associated to the `else` block, or if the trailing comment is just a member of the `if` block. ([#1575](https://github.com/rust-lang/rustfmt/issues/1575), [#4120](https://github.com/rust-lang/rustfmt/issues/4120), [#4506](https://github.com/rust-lang/rustfmt/issues/4506)) + +In this example the `// else comment` refers to the `else`: +```rust +// if comment +if cond { + "if" +// else comment +} else { + "else" +} +``` + +Whereas in this case the `// continue` comments are members of their respective blocks and do not refer to the `else` below. +```rust +if toks.eat_token(Token::Word("modify"))? && toks.eat_token(Token::Word("labels"))? { + if toks.eat_token(Token::Colon)? { + // ate the token + } else if toks.eat_token(Token::Word("to"))? { + // optionally eat the colon after to, e.g.: + // @rustbot modify labels to: -S-waiting-on-author, +S-waiting-on-review + toks.eat_token(Token::Colon)?; + } else { + // It's okay if there's no to or colon, we can just eat labels + // afterwards. + } + 1 + 2; + // continue +} else if toks.eat_token(Token::Word("label"))? { + // continue +} else { + return Ok(None); +} +``` + +### Fixed +- Formatting of empty blocks with attributes which only contained comments is no longer butchered.([#4475](https://github.com/rust-lang/rustfmt/issues/4475), [#4467](https://github.com/rust-lang/rustfmt/issues/4467), [#4452](https://github.com/rust-lang/rustfmt/issues/4452#issuecomment-705886282), [#4522](https://github.com/rust-lang/rustfmt/issues/4522)) +- Indentation of trailing comments in non-empty extern blocks is now correct. ([#4120](https://github.com/rust-lang/rustfmt/issues/4120#issuecomment-696491872)) + +### Install/Download Options +- **crates.io package** - *pending* +- **rustup (nightly)** - Starting in `2020-11-16` +- **GitHub Release Binaries** - [Release v1.4.26](https://github.com/rust-lang/rustfmt/releases/tag/v1.4.26) +- **Build from source** - [Tag v1.4.26](https://github.com/rust-lang/rustfmt/tree/v1.4.26), see instructions for how to [install rustfmt from source][install-from-source] + +## [1.4.25] 2020-11-10 + +### Changed + +- Semicolons are no longer automatically inserted on trailing expressions in macro definition arms ([#4507](https://github.com/rust-lang/rustfmt/pull/4507)). This gives the programmer control and discretion over whether there should be semicolons in these scenarios so that potential expansion issues can be avoided. + +### Install/Download Options +- **crates.io package** - *pending* +- **rustup (nightly)** - Starting in `2020-11-14` +- **GitHub Release Binaries** - [Release v1.4.25](https://github.com/rust-lang/rustfmt/releases/tag/v1.4.25) +- **Build from source** - [Tag v1.4.25](https://github.com/rust-lang/rustfmt/tree/v1.4.25), see instructions for how to [install rustfmt from source][install-from-source] + +## [1.4.24] 2020-11-05 + +### Changed + +- Block wrapped match arm bodies containing a single macro call expression are no longer flattened ([#4496](https://github.com/rust-lang/rustfmt/pull/4496)). This allows programmer discretion so that the block wrapping can be preserved in cases where needed to prevent issues in expansion, such as with trailing semicolons, and aligns with updated [Style Guide guidance](https://github.com/rust-dev-tools/fmt-rfcs/blob/master/guide/expressions.md#macro-call-expressions) for such scenarios. + +### Fixed +- Remove useless `deprecated` attribute on a trait impl block in the rustfmt lib, as these now trigger errors ([rust-lang/rust/#78626](https://github.com/rust-lang/rust/pull/78626)) + +### Install/Download Options +- **crates.io package** - *pending* +- **rustup (nightly)** - Starting in `2020-11-09` +- **GitHub Release Binaries** - [Release v1.4.24](https://github.com/rust-lang/rustfmt/releases/tag/v1.4.24) +- **Build from source** - [Tag v1.4.24](https://github.com/rust-lang/rustfmt/tree/v1.4.24), see instructions for how to [install rustfmt from source][install-from-source] + +## [1.4.23] 2020-10-30 + +### Changed + +- Update `rustc-ap-*` crates to v686.0.0 + +### Added +- Initial support for formatting new ConstBlock syntax ([#4478](https://github.com/rust-lang/rustfmt/pull/4478)) + +### Fixed +- Handling of unclosed delimiter-only parsing errors in input files ([#4466](https://github.com/rust-lang/rustfmt/issues/4466)) +- Misc. minor parser bugs ([#4418](https://github.com/rust-lang/rustfmt/issues/4418) and [#4431](https://github.com/rust-lang/rustfmt/issues/4431)) +- Panic on nested tuple access ([#4355](https://github.com/rust-lang/rustfmt/issues/4355)) +- Unable to disable license template path via cli override ([#4487](https://github.com/rust-lang/rustfmt/issues/4487)) +- Preserve comments in empty statements [#4018](https://github.com/rust-lang/rustfmt/issues/4018)) +- Indentation on skipped code [#4398](https://github.com/rust-lang/rustfmt/issues/4398)) + +### Install/Download Options +- **crates.io package** - *pending* +- **rustup (nightly)** - n/a (superseded by [v1.4.24](#1424-2020-11-05)) +- **GitHub Release Binaries** - [Release v1.4.23](https://github.com/rust-lang/rustfmt/releases/tag/v1.4.23) +- **Build from source** - [Tag v1.4.23](https://github.com/rust-lang/rustfmt/tree/v1.4.23), see instructions for how to [install rustfmt from source][install-from-source] + + + +## [1.4.22] 2020-10-04 + +### Changed + +- Update `rustc-ap-*` crates to v679.0.0 +- Add config option to allow control of leading match arm pipes +- Support `RUSTFMT` environment variable in `cargo fmt` to run specified `rustfmt` instance + +### Fixed + +- Fix preservation of type aliases within extern blocks + + +## [1.4.9] 2019-10-07 + +### Changed + +- Update `rustc-ap-*` crates to 606.0.0. + +### Fixed + +- Fix aligning comments of different group +- Fix flattening imports with a single `self`. +- Fix removing attributes on function parameters. +- Fix removing `impl` keyword from opaque type. + +## [1.4.8] 2019-09-08 + +### Changed + +- Update `rustc-ap-*` crates to 583.0.0. + +## [1.4.7] 2019-09-06 + +### Added + +- Add `--config` command line option. + +### Changed + +- Update `rustc-ap-*` crates to 581.0.0. +- rustfmt now do not warn against trailing whitespaces inside macro calls. + +### Fixed + +- Fix `merge_imports` generating invalid code. +- Fix removing discriminant values on enum variants. +- Fix modules defined inside `cfg_if!` not being formatted. +- Fix minor formatting issues. + +## [1.4.6] 2019-08-28 + +### Added + +- Add `--message-format` command line option to `cargo-fmt`. +- Add `-l,--files-with-diff` command line option to `rustfmt`. +- Add `json` emit mode. + +### Fixed + +- Fix removing attributes on struct pattern's fields. +- Fix non-idempotent formatting of match arm. +- Fix `merge_imports` generating invalid code. +- Fix imports with `#![macro_use]` getting reordered with `reorder_imports`. +- Fix calculation of line numbers in checkstyle output. +- Fix poor formatting of complex fn type. + +## [1.4.5] 2019-08-13 + +### Fixed + +- Fix generating invalid code when formatting an impl block with const generics inside a where clause. +- Fix adding a trailing space after a `dyn` keyword which is used as a macro argument by itself. + +## [1.4.4] 2019-08-06 + +### Fixed + +- Fix `cargo fmt` incorrectly formatting crates that is not part of the workspace or the path dependencies. +- Fix removing a trailing comma from a tuple pattern. + +## [1.4.3] 2019-08-02 + +### Changed + +- Update `rustc-ap-*` crates to 546.0.0. + +### Fixed + +- Fix an underscore pattern getting removed. + +## [1.4.2] 2019-07-31 + +### Changed + +- Explicitly require the version of `rustfmt-config_proc_macro` to be 0.1.2 or later. + +## [1.4.1] 2019-07-30 + +### Changed + +- Update `rustc-ap-*` crates to 542.0.0. + +## [1.4.0] 2019-07-29 + +### Added + +- Add new attribute `rustfmt::skip::attributes` to prevent rustfmt +from formatting an attribute #3665 + +### Changed + +- Update `rustc-ap-*` crates to 541.0.0. +- Remove multiple semicolons. + +## [1.3.3] 2019-07-15 + +### Added + +- Add `--manifest-path` support to `cargo fmt` (#3683). + +### Fixed + +- Fix `cargo fmt -- --help` printing nothing (#3620). +- Fix inserting an extra comma (#3677). +- Fix incorrect handling of CRLF with `file-lines` (#3684). +- Fix `print-config=minimal` option (#3687). + +## [1.3.2] 2019-07-06 + +### Fixed + +- Fix rustfmt crashing when `await!` macro call is used in a method chain. +- Fix rustfmt not recognizing a package whose name differs from its directory's name. + +## [1.3.1] 2019-06-30 + +### Added + +- Implement the `Display` trait on the types of `Config`. + +### Changed + +- `ignore` configuration option now only supports paths separated by `/`. Windows-style paths are not supported. +- Running `cargo fmt` in a sub-directory of a project is now supported. + +### Fixed + +- Fix bugs that may cause rustfmt to crash. + +## [1.3.0] 2019-06-09 + +### Added + +- Format modules defined inside `cfg_if` macro calls #3600 + +### Changed + +- Change option `format_doc_comment` to `format_code_in_doc_comment`. +- `use_small_heuristics` changed to be an enum and stabilised. Configuration + options are now ready for 1.0. +- Stabilise `fn_args_density` configuration option and rename it to `fn_args_layout` #3581 +- Update `rustc-ap-*` crates to 486.0.0 +- Ignore sub-modules when skip-children is used #3607 +- Removed bitrig support #3608 + +### Fixed + +- `wrap_comments` should not imply `format_doc_comments` #3535 +- Incorrect handling of const generics #3555 +- Add the handling for `vec!` with paren inside macro #3576 +- Format trait aliases with where clauses #3586 +- Catch panics from the parser while rewriting macro calls #3589 +- Fix erasing inner attributes in struct #3593 +- Inline the attribute with its item even with the `macro_use` attribute or when `reorder_imports` is disabled #3598 +- Fix the bug add unwanted code to impl #3602 + +## [1.2.2] 2019-04-24 + +### Fixed + +- Fix processing of `ignore` paths #3522 +- Attempt to format attributes if only they exist #3523 + +## [1.2.1] 2019-04-18 + +### Added + +- Add `--print-config current` CLI option b473e65 +- Create GitHub [page](https://rust-lang.github.io/rustfmt/) for Configuration.md #3485 + +### Fixed + +- Keep comment appearing between parameter's name and its type #3491 +- Do not delete semicolon after macro call with square brackets #3500 +- Fix `--version` CLI option #3506 +- Fix duplication of attributes on a match arm's body #3510 +- Avoid overflowing item with attributes #3511 + +## [1.2.0] 2019-03-27 + +### Added + +- Add new attribute `rustfmt::skip::macros` to prevent rustfmt from formatting a macro #3454 + +### Changed + +- Discard error report in silent_emitter #3466 + +### Fixed + +- Fix bad performance on deeply nested binary expressions #3467 +- Use BTreeMap to guarantee consistent ordering b4d4b57 + +## [1.1.1] 2019-03-21 + +### Fixed + +- Avoid panic on macro inside deeply nested block c9479de +- Fix line numbering in missed spans and handle file_lines in edge cases cdd08da +- Fix formatting of async blocks 1fa06ec +- Avoid duplication on the presence of spaces between macro name and `!` #3464 + +## [1.1.0] 2019-03-17 + +### Added + +- Add `inline_attribute_width` configuration option to write an item and its attribute on the same line if their combined width is below a threshold #3409 +- Support `const` generics f0c861b +- Support path clarity module #3448 + +### Changed + +- Align loop and while formatting 7d9a2ef +- Support `EmitMode::ModifiedLines` with stdin input #3424 +- Update `rustc-ap-*` crates to 407.0.0 +- Remove trailing whitespaces in missing spans 2d5bc69 + +### Fixed + +- Do not remove comment in the case of no arg 8e3ef3e +- Fix `Ident of macro+ident gets duplicated` error 40ff078 +- Format the if expression at the end of the block in a single line 5f3dfe6 + +## [1.0.3] 2019-02-14 + +### Added + +- Point unstable options to tracking issues 412dcc7 + +### Changed + +- Update `rustc-ap-*` crates to 373.0.0 + +## [1.0.2] 2019-02-12 + +### Added + +- Add a [section](https://github.com/rust-lang/rustfmt/blob/ae331be/Contributing.md#version-gate-formatting-changes) to the Contributing.md file about version-gating formatting changes 36e2cb0 +- Allow specifying package with `-p` CLI option a8d2591 +- Support `rustfmt::skip` on imports #3289 +- Support global `rustfmt.toml` to be written in user config directory #3280 +- Format visibility on trait alias 96a3df3 + +### Changed + +- Do not modify original source code inside macro call #3260 +- Recognize strings inside comments in order to avoid indenting them baa62c6 +- Use Unicode-standard char width to wrap comments or strings a01990c +- Change new line point in the case of no args #3294 +- Use the same formatting rule between functions and macros #3298 +- Update rustc-ap-rustc_target to 366.0.0, rustc-ap-syntax to 366.0.0, and rustc-ap-syntax_pos to 366.0.0 + +### Fixed + +- rewrite_comment: fix block fallback when failing to rewrite an itemized block ab7f4e1 +- Catch possible tokenizer panics #3240 +- Fix macro indentation on Windows #3266 +- Fix shape when formatting return or break expr on statement position #3259 +- rewrite_comment: fix block fallback when failing to rewrite an itemized block +- Keep leading double-colon to respect the 2018 edition of rust's paths a2bfc02 +- Fix glob and nested global imports 2125ad2 +- Do not force trailing comma when using mixed layout #3306 +- Prioritize `single_line_fn` and `empty_item_single_line` over `brace_style` #3308 +- Fix `internal error: left behind trailing whitespace` with long lines c2534f5 +- Fix attribute duplication #3325 +- Fix formatting of strings within a macro 813aa79 +- Handle a macro argument with a single keyword 9a7ea6a + +## [1.0.1] 2018-12-09 + +### Added + +- Add a `version` option 378994b + +### Changed + +- End expressions like return/continue/break with a semicolon #3223 +- Update rustc-ap-rustc_target to 306.0.0, rustc-ap-syntax to 306.0.0, and rustc-ap-syntax_pos to 306.0.0 + +### Fixed + +- Allow to run a rustfmt command from cargo-fmt even when there is no target a2da636 +- Fix `un-closed delimiter` errors when formatting break labels 40174e9 + +## [1.0.0] 2018-11-19 + +### Changed + +- Preserve possibly one whitespace for brace macros 1a3bc79 +- Prefer to break arguments over putting output type on the next line 1dd54e6 + +## [0.99.9] 2018-11-15 + +### Changed + +- Update rustc-ap-rustc_target to 297.0.0, rustc-ap-syntax to 297.0.0, to rustc-ap-syntax_pos to 297.0.0 +- Don't align comments on `extern crate`s dd7add7 + +## [0.99.8] 2018-11-14 + +### Added + +- Add `overflow_delimited_expr` config option to more aggressively allow overflow #3175 + +### Fixed + +- Fix the logic for retaining a comment before the arrow in a match #3181 +- Do not wrap comments in doctest to avoid failing doctest runs #3183 +- Fix comment rewriting that was wrapping code into a line comment #3188 +- Fix formatting of unit-struct with `where`-clause #3200 + +## [0.99.7] 2018-11-07 + +### Changed + +- Force a newline after the `if` condition if there is a different indentation level #3109 +- Use correct width when formatting type on local statement #3126 +- Treat crates non-alphabetically when ordering 799005f +- Fix formatting of code that is annotated with rustfmt::skip #3113 +- Stabilize `edition` configuration option 9c3ae2d +- cargo-fmt: detect Rust edition in use #3129 +- Trim the indentation on macros which heuristically appear to use block-style indentation #3178 + +### Fixed + +- Do not remove path disambiugator inside macro #3142 +- Improve handling of Windows newlines #3141 +- Fix alignment of a struct's fields (`struct_field_align_threshold` option) with the Visual `indent_style` #3165 +- Fix a bug in formatting markdown lists within comments #3172 + +## [0.99.6] 2018-10-18 + +### Added + +- Add `enum_discrim_align_threshold` option to vertically align enum discriminants cc22869 +- Add `println!`-like heuristic to the `fail` attribute #3067 +- Handle itemized items inside comments #3083 +- Add `format_doc_comments` configuration option to control the formatting of code snippets inside comments #3089 + +### Changed + +- Makes brace behavior consistent with empty bodies for traits and impls 2727d41 +- Consider a multi-lined array as a block-like expression #3969 +- Improve formatting of strings #3073 +- Get rid of extra commas in Visual struct literal formatting #3077 +- Update rustc-ap-rustc_target to 274.0.0, rustc-ap-syntax to 274.0.0, and rustc-ap-syntax_pos to 274.0.0 +- Format macro calls with item-like arguments #3080 +- Avoid control flow expressions conditions to go multi line ef59b34 +- Simplify multi-lining binop expressions #3101 + +### Fixed + +- Do not format a code block in documentation if it is annotated with ignore or text 2bcc3a9 +- Fix inconsistent overflow behavior in Visual style #3078 +- Fix corner cases of the string formatting implementation #3083 +- Do not add parens around lifetimes 0ac68c9 +- Catch parser panic in format_snippet 8c4e92a + +## [0.99.5] 2018-09-25 + +### Added + +- Handle leading module separator for 2018 Edition #2952 +- Add configuration option `normalize_doc_attributes`: convert doc attributes to comments #3002 + +### Changed + +- Accept 2015 and 2018 instead of Edition2015 and Edition2018 for edition option eec7436 +- Support platforms without a timer 46e2a2e +- Update rustc-ap-rustc_target to 263.0.0, rustc-ap-syntax to 263.0.0, and rustc-ap-syntax_pos to 263.0.0 + +### Fixed + +- Format of attributes with commas #2971 +- Fix optional arg condensing #2972 +- Improve formatting of long function parameters #2981 +- Fix formatting of raw string literals #2983 +- Handle chain with try operators with spaces #2986 +- Use correct shape in Visual tuple rewriting #2987 +- Improve formatting of arguments with `visual_style = "Visual"` option #2988 +- Change `print_diff` to output the correct line number 992b179 +- Propagate errors about failing to rewrite a macro 6f318e3 +- Handle formatting of long function signature #3010 +- Fix indent computation of a macro with braces c3edf6d +- Format generics on associated types #3035 +- Incorrect indentation of multiline block match expression #3042 +- Fix bug in import where two consecutive module separators were possible 98a0ef2 +- Prevent right-shifting of block comments with bare lines 5fdb6db + +## [0.99.4] 2018-08-27 + +### Added + +- Handle formatting of underscore imports #2951 +- Handle formatting of try blocks #2965 + +### Changed + +- Update rustc-ap-rustc_target to 237.0.0, rustc-ap-syntax to 237.0.0, and rustc-ap-syntax_pos to 237.0.0 ca19c9a +- Consider `dev` channel as nightly for unstable features #2948 + +### Fixed + +- Fix formatting of patterns with ellipsis # 2942 + +## [0.99.3] 2018-08-23 + +### Added + +- Use path attribute when searching for modules #2901 +- Expose FileLines JSON representation to allow external libraries to use the file_lines option #2915 + +### Changed + +- Replace '--conifig-help' with '--config=help' cb10e06 +- Improve formatting of slice patterns #2912 + +### Fixed + +- Format chains with comment #2899 +- Fix indentation of formatted macro body #2920 +- Fix indentation of block comments f23e6aa + +## [0.99.2] 2018-08-07 + +### Changed + +- Update rustc-ap-rustc_target to 218.0.0, rustc-ap-syntax to 218.0.0, and rustc-ap-syntax_pos to 218.0.0 5c9a2b6 +- Combine function-like attributes #2900 + +### Fixed + +- Explicitly handle semicolon after the item in statement position d96e3ca +- Fix parsing '#'-hiding of rustdoc 2eca09e + +## [0.99.1] 2018-08-04 + +### Fixed + +- fix use statements ordering when a number is present 1928ae7 + +## [0.99.0] 2018-08-03 + +- 1.0 RC release + +### Changed + +- Clarification in README.md 30fe66b + +## [0.9.0] 2018-08-01 + +### Added + +- Handle raw identifiers 3027c21 +- Format async closure 60ce411 +- Add max_width option for all heuristics c2ae39e +- Add config option `format_macro_matchers` to format the metavariable matching patterns in macros 79c5ee8 +- Add config option `format_macro_bodies` to format the bodies of macros 79c5ee8 +- Format exitential type fc307ff +- Support raw identifiers in struct expressions f121b1a +- Format Async block and async function 0b25f60 + +### Changed + +- Update rustc-ap-rustc_target to 211.0.0, rustc-ap-syntax to 211.0.0, and rustc-ap-syntax_pos to 211.0.0 +- Put each nested import on its own line while putting non-nested imports on the same line as much as possible 42ab258 +- Respect `empty_item_single_line` config option when formatting empty impls. Put the `where` on its own line to improve readability #2771 +- Strip leading `|` in match arm patterns 1d4b988 +- Apply short function call heuristic to attributes 3abebf9 +- Indent a match guard if the pattern is multiline be4d37d +- Change default newline style to `Native` 9d8f381 +- Improve formatting of series of binop expressions a4cdb68 +- Trigger an internal error if we skip formatting due to a lost comment b085113 +- Refactor chain formatting #2838 + +### Fixed + +- Do not insert spaces around braces with empty body or multiple lines 2f65852 +- Allow using mixed layout with comments #2766 +- Handle break labels #2726 +- fix rewrite_string when a line feed is present 472a2ed +- Fix an anomaly with comments and array literals b28a0cd +- Check for comments after the `=>` in a match arm 6899471 + +## [0.8.0,0.8.1,0.8.2] 2018-05-28 + +### Added + +- Use scoped attributes for skip attribute https://github.com/rust-lang/rustfmt/pull/2703 + +### Changed + +- Comment options `wrap_comments` and `normalize_comments` are reverted back to unstable 416bc4c +- Stabilise `reorder_imports` and `reorder_modules` options 7b6d2b4 +- Remove `spaces_within_parens_and_brackets` option d726492 +- Stabilise shorthand options: `use_try_shorthand`, `use_field_init_shorthand`, and `force_explicit_abi` 8afe367 +- Stabilise `remove_nested_parens` and set default to true a70f716 +- Unstabilise `unstable_features` dd9c15a +- Remove `remove_blank_lines_at_start_or_end_of_block` option 2ee8b0e +- Update rustc-ap-syntax to 146.0.0 and rustc-ap-rustc_target to 146.0.0 2c275a2 +- Audit the public API #2639 + +### Fixed + +- Handle code block in doc comment without rust prefix f1974e2 + +## [0.7.0] 2018-05-14 + +### Added + +- Add integration tests against crates in the rust-lang-nursery c79f39a + +### Changed + +- Update rustc-ap-syntax to 128.0.0 and ustc-ap-rustc_target to 128.0.0 195395f +- Put operands on its own line when each fits in a single line f8439ce +- Improve CLI options 55ac062 1869888 798bffb 4d9de48 eca7796 8396da1 5d9f5aa + +### Fixed + +- Use correct line width for list attribute 61a401a +- Avoid flip-flopping impl items when reordering them 37c216c +- Formatting breaks short lines when max_width is less than 100 9b36156 +- Fix variant "Mixed" of imports_layout option 8c8676c +- Improve handling of long lines f885039 +- Fix up lines exceeding max width 51c07f4 +- Fix handling of modules in non_modrs_mods style cf573e8 +- Do not duplicate attributes on use items e59ceaf +- Do not insert an extra brace in macros with native newlines 4c9ef93 + +## [0.6.1] 2018-05-01 + +### Changed + +- Change the default value of imports_indent to IndentStyle::Block https://github.com/rust-lang/rustfmt/pull/2662 + +### Fixed + +- Handle formatting of auto traits 5b5a72c +- Use consistent formatting for empty enum and struct https://github.com/rust-lang/rustfmt/pull/2656 + +## [0.6.0] 2018-04-20 + +### Changed + +- Improve public API 8669004 + +## [0.5.0] 2018-04-20 + +### Added + +- Add `verbose-diff` CLI option 5194984 + +### Changed + +- Update rustc-ap-syntax to 103.0.0 dd807e2 +- Refactor to make a sensible public API ca610d3 + +### Fixed + +- Add spaces between consecutive `..` `..=` 61d29eb + +## [0.4.2] 2018-04-12 + +### Added + +- Handle binary operators and lifetimes 0fd174d +- Add reorder_impl_items config option 94f5a05 +- Add `--unstable-features` CLI option to list unstable options from the `--help` output 8208f8a +- Add merge_imports config option 5dd203e + +### Changed + +- Format macro arguments with vertical layout ec71459 +- Reorder imports by default 164cf7d +- Do not collapse block around expr with condition on match arm 5b9b7d5 +- Use vertical layout for complex attributes c77708f +- Format array using heuristics for function calls 98c6f7b +- Implement stable ordering for impl items with the the following item priority: type, const, macro, then method fa80ddf +- Reorder imports by default 164cf7d +- Group `extern crate` by default 3a138a2 +- Make `error_on_line_overflow` false by default f146711 +- Merge imports with the same prefix into a single nested import 1954513 +- Squash the various 'reorder imports' option into one 911395a + +### Fixed + +- Print version is missing the channel ca6fc67 +- Do not add the beginning vert to the match arm 1e1d9d4 +- Follow indent style config when formatting attributes efd295a +- Do not insert newline when item is empty a8022f3 +- Do not indent or unindent inside string literal ec1907b + +## [0.4.1] 2018-03-16 + +### Added + +- Add `ignore` configuration option. +- Add `license_template_path` configuration option. +- Format `lazy_static!`. + +### Fixed + +- Fix formatting bugs. +- Fix setting `reorder_modules` removing inline modules. +- Format attributes on block expressions. +- Support `dyn trait` syntax. +- Support multiple patterns in `if let` and `while let`. +- Support a pattern with parentheses. + +## [0.4.0] 2018-03-02 + +### Changed + +- Do not print verbose outputs when formatting with stdin. +- Preserve trailing whitespaces in doc comments. +- Scale the values of width heuristics by `max_width`. + +### Fixed + +- Do not reorder items with `#[macro_use]`. +- Fix formatting bugs. +- Support the beginning `|` on a match arm. + +## [0.3.8] 2018-02-04 + +### Added + +- Format (or at least try to format) `macro_rules!`. + +## [0.3.7] 2018-02-01 + +### Added + +- Add `use_field_init_shorthand` config option. +- Add `reorder_modules` configuration option. + +## [0.3.6] 2018-01-18 + +### Fixed + +- Fix panicking on formatting certain macros (#2371). + +## [0.3.5] 2018-01-15 + +### Changed + +- Format code block in comments when `wrap_comments` is set to `true`. +- Remove `same_line_attributes` configuration option. +- Rename `git-fmt` to `git-rustfmt`. + +### Fixed + +- Rustup to `rustc 1.25.0-nightly (e6072a7b3 2018-01-13)`. +- Fix formatting bugs. + +## [0.3.4] 2017-12-23 + +### Added + +- Add `--version` flag to `cargo-fmt`, allow `cargo fmt --version`. + +### Fixed + +- Rustup to `rustc 1.24.0-nightly (5165ee9e2 2017-12-22)`. + +## [0.3.3] 2017-12-22 + +### Added + +- Format trait aliases. + +### Changed + +- `cargo fmt` will format every workspace member. + +### Fixed + +- Rustup to `rustc 1.24.0-nightly (250b49205 2017-12-21)` +- Fix formatting bugs. + +## [0.3.2] 2017-12-15 + +### Changed + +- Warn when unknown configuration option is used. + +### Fixed + +- Rustup to `rustc 1.24.0-nightly (0077d128d 2017-12-14)`. + +## [0.3.1] 2017-12-11 + +### Added + +- Add `error_on_unformatted` configuration option. +- Add `--error-on-unformatted` command line option. + +### Changed + +- Do not report formatting errors on comments or strings by default. +- Rename `error_on_line_overflow_comments` to `error_on_unformatted`. + +### Fixed + +- Fix formatting bugs. +- Fix adding a trailing whitespace inside code block when `wrap_comments = true`. + +## [0.3.0] 2017-12-11 + +### Added + +- Support nested imports. + +### Changed + +- Do not report errors on skipped items. +- Do not format code block inside comments when `wrap_comments = true`. +- Keep vertical spaces between items within range. +- Format `format!` and its variants using compressed style. +- Format `write!` and its variants using compressed style. +- Format **simple** array using compressed style. + +### Fixed + +- Fix `rustfmt --package package_name` not working properly. +- Fix formatting bugs. + +## [0.2.17] 2017-12-03 + +### Added + +- Add `blank_lines_lower_bound` and `blank_lines_upper_bound` configuration options. + +### Changed + +- Combine configuration options related to width heuristic into `width_heuristic`. +- If the match arm's body is `if` expression, force to use block. + +### Fixed + +- Fix `cargo fmt --all` being trapped in an infinite loop. +- Fix many formatting bugs. + +### Removed + +- Remove legacy configuration options. + +## [0.2.16] 2017-11-21 + +### Added + +- Remove empty lines at the beginning of the file. +- Soft wrapping on doc comments. + +### Changed + +- Break before `|` when using multiple lines for match arm patterns. +- Combine `control_style`, `where_style` and `*_indent` config options into `indent_style`. +- Combine `item_brace_style` and `fn_brace_style` config options into `brace_style`. +- Combine config options related spacing around colons into `space_before_colon` and `space_after_colon`. + +### Fixed + +- Fix many bugs. + +## [0.2.15] 2017-11-08 + +### Added + +- Add git-fmt tool +- `where_single_line` configuration option. + +### Changed + +- Rename `chain_one_line_max` to `chain_width`. +- Change the suffix of indent-related configuration options to `_indent`. + +## [0.2.14] 2017-11-06 + +### Fixed + +- Rustup to the latest nightly. + +## [0.2.13] 2017-10-30 + +### Fixed + +- Rustup to the latest nightly. + +## [0.2.12] 2017-10-29 + +### Fixed + +- Fix a bug that `cargo fmt` hangs forever. + +## [0.2.11] 2017-10-29 + +### Fixed + +- Fix a bug that `cargo fmt` crashes. + +## [0.2.10] 2017-10-28 + +## [0.2.9] 2017-10-16 + +## [0.2.8] 2017-09-28 + +## [0.2.7] 2017-09-21 + +### Added + +- `binop_separator` configuration option (#1964). + +### Changed + +- Use horizontal layout for function call with a single argument. + +### Fixed + +- Fix panicking when calling `cargo fmt --all` (#1963). +- Refactorings & faster rustfmt. + +## [0.2.6] 2017-09-14 + +### Fixed + +- Fix a performance issue with nested block (#1940). +- Refactorings & faster rustfmt. + +## [0.2.5] 2017-08-31 + +### Added + +- Format and preserve attributes on statements (#1933). + +### Fixed + +- Use getters to access `Span` fields (#1899). + +## [0.2.4] 2017-08-30 + +### Added + +- Add support for `Yield` (#1928). + +## [0.2.3] 2017-08-30 + +### Added + +- `multiline_closure_forces_block` configuration option (#1898). +- `multiline_match_arm_forces_block` configuration option (#1898). +- `merge_derives` configuration option (#1910). +- `struct_remove_empty_braces` configuration option (#1930). +- Various refactorings. + +### Changed + +- Put single-lined block comments on the same line with list-like structure's item (#1923). +- Preserve blank line between doc comment and attribute (#1925). +- Put the opening and the closing braces of enum and struct on the same line, even when `item_brace_style = "AlwaysNextLine"` (#1930). + +### Fixed + +- Format attributes on `ast::ForeignItem` and take max width into account (#1916). +- Ignore empty lines when calculating the shortest indent width inside macro with braces (#1918). +- Handle tabs properly inside macro with braces (#1918). +- Fix a typo in `compute_budgets_for_args()` (#1924). +- Recover comment between keyword (`impl` and `trait`) and `{` which used to get removed (#1925). + + +[install-from-source]: https://github.com/rust-lang/rustfmt#installing-from-source diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000000..d70b2b52aca1 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,40 @@ +# The Rust Code of Conduct + +A version of this document [can be found online](https://www.rust-lang.org/conduct.html). + +## Conduct + +**Contact**: [rust-mods@rust-lang.org](mailto:rust-mods@rust-lang.org) + +* We are committed to providing a friendly, safe and welcoming environment for all, regardless of level of experience, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, nationality, or other similar characteristic. +* On IRC, please avoid using overtly sexual nicknames or other nicknames that might detract from a friendly, safe and welcoming environment for all. +* Please be kind and courteous. There's no need to be mean or rude. +* Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and numerous costs. There is seldom a right answer. +* Please keep unstructured critique to a minimum. If you have solid ideas you want to experiment with, make a fork and see how it works. +* We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behavior. We interpret the term "harassment" as including the definition in the Citizen Code of Conduct; if you have any lack of clarity about what might be included in that concept, please read their definition. In particular, we don't tolerate behavior that excludes people in socially marginalized groups. +* Private harassment is also unacceptable. No matter who you are, if you feel you have been or are being harassed or made uncomfortable by a community member, please contact one of the channel ops or any of the [Rust moderation team][mod_team] immediately. Whether you're a regular contributor or a newcomer, we care about making this community a safe place for you and we've got your back. +* Likewise any spamming, trolling, flaming, baiting or other attention-stealing behavior is not welcome. + +## Moderation + + +These are the policies for upholding our community's standards of conduct. If you feel that a thread needs moderation, please contact the [Rust moderation team][mod_team]. + +1. Remarks that violate the Rust standards of conduct, including hateful, hurtful, oppressive, or exclusionary remarks, are not allowed. (Cursing is allowed, but never targeting another user, and never in a hateful manner.) +2. Remarks that moderators find inappropriate, whether listed in the code of conduct or not, are also not allowed. +3. Moderators will first respond to such remarks with a warning. +4. If the warning is unheeded, the user will be "kicked," i.e., kicked out of the communication channel to cool off. +5. If the user comes back and continues to make trouble, they will be banned, i.e., indefinitely excluded. +6. Moderators may choose at their discretion to un-ban the user if it was a first offense and they offer the offended party a genuine apology. +7. If a moderator bans someone and you think it was unjustified, please take it up with that moderator, or with a different moderator, **in private**. Complaints about bans in-channel are not allowed. +8. Moderators are held to a higher standard than other community members. If a moderator creates an inappropriate situation, they should expect less leeway than others. + +In the Rust community we strive to go the extra step to look out for each other. Don't just aim to be technically unimpeachable, try to be your best self. In particular, avoid flirting with offensive or sensitive issues, particularly if they're off-topic; this all too often leads to unnecessary fights, hurt feelings, and damaged trust; worse, it can drive people away from the community entirely. + +And if someone takes issue with something you said or did, resist the urge to be defensive. Just stop doing what it was they complained about and apologize. Even if you feel you were misinterpreted or unfairly accused, chances are good there was something you could've communicated better — remember that it's your responsibility to make your fellow Rustaceans comfortable. Everyone wants to get along and we are all here first and foremost because we want to talk about cool technology. You will find that people will be eager to assume good intent and forgive as long as you earn their trust. + +The enforcement policies listed above apply to all official Rust venues; including official IRC channels (#rust, #rust-internals, #rust-tools, #rust-libs, #rustc, #rust-beginners, #rust-docs, #rust-community, #rust-lang, and #cargo); GitHub repositories under rust-lang, rust-lang-nursery, and rust-lang-deprecated; and all forums under rust-lang.org (users.rust-lang.org, internals.rust-lang.org). For other projects adopting the Rust Code of Conduct, please contact the maintainers of those projects for enforcement. If you wish to use this code of conduct for your own project, consider explicitly mentioning your moderation policy or making a copy with your own moderation policy so as to avoid confusion. + +*Adapted from the [Node.js Policy on Trolling](http://blog.izs.me/post/30036893703/policy-on-trolling) as well as the [Contributor Covenant v1.3.0](https://www.contributor-covenant.org/version/1/3/0/).* + +[mod_team]: https://www.rust-lang.org/team.html#Moderation-team diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 000000000000..bd28df7a7573 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,985 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "annotate-snippets" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3b9d411ecbaf79885c6df4d75fff75858d5995ff25385657a28af47e82f9c36" +dependencies = [ + "unicode-width", + "yansi-term", +] + +[[package]] +name = "anstream" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "342258dd14006105c2b75ab1bd7543a03bdf0cfc94383303ac212a04939dff6f" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-wincon", + "concolor-override", + "concolor-query", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23ea9e81bd02e310c216d080f6223c179012256e5151c41db88d12c88a1684d2" + +[[package]] +name = "anstyle-parse" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7d1bb534e9efed14f3e5f44e7dd1a4f709384023a4165199a4241e18dff0116" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-wincon" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3127af6145b149f3287bb9a0d10ad9c5692dba8c53ad48285e5bec4063834fa" +dependencies = [ + "anstyle", + "windows-sys 0.45.0", +] + +[[package]] +name = "anyhow" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bstr" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +dependencies = [ + "memchr", +] + +[[package]] +name = "bytecount" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72feb31ffc86498dacdbd0fcebb56138e7177a8cc5cea4516031d15ae85a742e" +dependencies = [ + "packed_simd_2", +] + +[[package]] +name = "camino" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f3132262930b0522068049f5870a856ab8affc80c70d08b6ecb785771a6fc23" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "4.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046ae530c528f252094e4a77886ee1374437744b2bff1497aa898bbddbbb29b3" +dependencies = [ + "clap_builder", + "clap_derive", + "once_cell", +] + +[[package]] +name = "clap_builder" +version = "4.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "223163f58c9a40c3b0a43e1c4b50a9ce09f007ea2cb1ec258a687945b4b7929f" +dependencies = [ + "anstream", + "anstyle", + "bitflags", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" + +[[package]] +name = "concolor-override" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a855d4a1978dc52fb0536a04d384c2c0c1aa273597f08b77c8c4d3b2eec6037f" + +[[package]] +name = "concolor-query" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d11d52c3d7ca2e6d0040212be9e4dbbcd78b6447f535b6b561f449427944cf" +dependencies = [ + "windows-sys 0.45.0", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" +dependencies = [ + "cfg-if", + "lazy_static", +] + +[[package]] +name = "diff" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499" + +[[package]] +name = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "env_logger" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "getopts" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "getrandom" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "globset" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10463d9ff00a2a068db14231982f5132edebad0d7660cd956a1c30292dbcbfbd" +dependencies = [ + "aho-corasick", + "bstr", + "fnv", + "log", + "regex", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "ignore" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d" +dependencies = [ + "crossbeam-utils", + "globset", + "lazy_static", + "log", + "memchr", + "regex", + "same-file", + "thread_local", + "walkdir", + "winapi-util", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "is-terminal" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" +dependencies = [ + "hermit-abi", + "io-lifetimes", + "rustix", + "windows-sys 0.48.0", +] + +[[package]] +name = "itertools" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.141" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" + +[[package]] +name = "libm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" + +[[package]] +name = "linux-raw-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" + +[[package]] +name = "log" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "once_cell" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "packed_simd_2" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defdcfef86dcc44ad208f71d9ff4ce28df6537a4e0d6b0e8e845cb8ca10059a6" +dependencies = [ + "cfg-if", + "libm", +] + +[[package]] +name = "proc-macro2" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom", + "redox_syscall", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "rustfmt-config_proc_macro" +version = "0.3.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "rustfmt-nightly" +version = "1.6.0" +dependencies = [ + "annotate-snippets", + "anyhow", + "bytecount", + "cargo_metadata", + "clap", + "diff", + "dirs", + "env_logger", + "getopts", + "ignore", + "itertools", + "lazy_static", + "log", + "regex", + "rustfmt-config_proc_macro", + "serde", + "serde_json", + "term", + "thiserror", + "toml", + "unicode-segmentation", + "unicode-width", + "unicode_categories", +] + +[[package]] +name = "rustix" +version = "0.37.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85597d61f83914ddeba6a47b3b8ffe7365107221c2e557ed94426489fefb5f77" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustversion" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f" + +[[package]] +name = "ryu" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "semver" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d65bd28f48be7196d222d95b9243287f48d27aca604e08497513019ff0502cc4" +dependencies = [ + "serde", +] + +[[package]] +name = "serde" +version = "1.0.160" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.160" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93107647184f6027e3b7dcb2e11034cf95ffa1e3a682c67951963ac69c1c007d" +dependencies = [ + "serde", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "2.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcf316d5356ed6847742d036f8a39c3b8435cac10bd528a4bd461928a6ab34d5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "term" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" +dependencies = [ + "dirs-next", + "rustversion", + "winapi", +] + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +dependencies = [ + "once_cell", +] + +[[package]] +name = "toml" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6135d499e69981f9ff0ef2167955a5333c35e36f6937d382974566b3d5b94ec" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a76a9312f5ba4c2dec6b9161fdf25d87ad8a09256ccea5a556fef03c706a10f" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "unicode-ident" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" + +[[package]] +name = "unicode-segmentation" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" + +[[package]] +name = "unicode-width" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" + +[[package]] +name = "unicode_categories" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "winnow" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca0ace3845f0d96209f0375e6d367e3eb87eb65d27d445bdc9f1843a26f39448" +dependencies = [ + "memchr", +] + +[[package]] +name = "yansi-term" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe5c30ade05e61656247b2e334a031dfd0cc466fadef865bdcdea8d537951bf1" +dependencies = [ + "winapi", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 000000000000..8c312f47a28f --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,65 @@ +[package] + +name = "rustfmt-nightly" +version = "1.6.0" +description = "Tool to find and fix Rust formatting issues" +repository = "https://github.com/rust-lang/rustfmt" +readme = "README.md" +license = "Apache-2.0/MIT" +build = "build.rs" +categories = ["development-tools"] +edition = "2021" + +[[bin]] +name = "rustfmt" +path = "src/bin/main.rs" + +[[bin]] +name = "cargo-fmt" +path = "src/cargo-fmt/main.rs" + +[[bin]] +name = "rustfmt-format-diff" +path = "src/format-diff/main.rs" + +[[bin]] +name = "git-rustfmt" +path = "src/git-rustfmt/main.rs" + +[features] +default = ["cargo-fmt", "rustfmt-format-diff"] +cargo-fmt = [] +rustfmt-format-diff = [] +generic-simd = ["bytecount/generic-simd"] + +[dependencies] +annotate-snippets = { version = "0.9", features = ["color"] } +anyhow = "1.0" +bytecount = "0.6" +cargo_metadata = "0.15.4" +clap = { version = "4.2.1", features = ["derive"] } +diff = "0.1" +dirs = "4.0" +env_logger = "0.10.0" +getopts = "0.2" +ignore = "0.4" +itertools = "0.10" +lazy_static = "1.4" +log = "0.4" +regex = "1.5" +serde = { version = "1.0.160", features = ["derive"] } +serde_json = "1.0" +term = "0.7" +thiserror = "1.0.40" +toml = "0.7.4" +unicode-segmentation = "1.9" +unicode-width = "0.1" +unicode_categories = "0.1" + +rustfmt-config_proc_macro = { version = "0.3", path = "config_proc_macro" } + +# Rustc dependencies are loaded from the sysroot, Cargo doesn't know about them. + +[package.metadata.rust-analyzer] +# This package uses #[feature(rustc_private)] +rustc_private = true diff --git a/Configurations.md b/Configurations.md new file mode 100644 index 000000000000..ac5747800b25 --- /dev/null +++ b/Configurations.md @@ -0,0 +1,3135 @@ +# Configuring Rustfmt + +Rustfmt is designed to be very configurable. You can create a TOML file called `rustfmt.toml` or `.rustfmt.toml`, place it in the project or any other parent directory and it will apply the options in that file. If none of these directories contain such a file, both your home directory and a directory called `rustfmt` in your [global config directory](https://docs.rs/dirs/4.0.0/dirs/fn.config_dir.html) (e.g. `.config/rustfmt/`) are checked as well. + +A possible content of `rustfmt.toml` or `.rustfmt.toml` might look like this: + +```toml +indent_style = "Block" +reorder_imports = false +``` + +Each configuration option is either stable or unstable. +Stable options can always be used, while unstable options are only available on a nightly toolchain and must be opted into. +To enable unstable options, set `unstable_features = true` in `rustfmt.toml` or pass `--unstable-features` to rustfmt. + +# Configuration Options + +Below you find a detailed visual guide on all the supported configuration options of rustfmt: + +## `array_width` + +Maximum width of an array literal before falling back to vertical formatting. + +- **Default value**: `60` +- **Possible values**: any positive integer that is less than or equal to the value specified for [`max_width`](#max_width) +- **Stable**: Yes + +By default this option is set as a percentage of [`max_width`](#max_width) provided by [`use_small_heuristics`](#use_small_heuristics), but a value set directly for `array_width` will take precedence. + +See also [`max_width`](#max_width) and [`use_small_heuristics`](#use_small_heuristics) + +## `attr_fn_like_width` + +Maximum width of the args of a function-like attributes before falling back to vertical formatting. + +- **Default value**: `70` +- **Possible values**: any positive integer that is less than or equal to the value specified for [`max_width`](#max_width) +- **Stable**: Yes + +By default this option is set as a percentage of [`max_width`](#max_width) provided by [`use_small_heuristics`](#use_small_heuristics), but a value set directly for `attr_fn_like_width` will take precedence. + +See also [`max_width`](#max_width) and [`use_small_heuristics`](#use_small_heuristics) + +## `binop_separator` + +Where to put a binary operator when a binary expression goes multiline. + +- **Default value**: `"Front"` +- **Possible values**: `"Front"`, `"Back"` +- **Stable**: No (tracking issue: [#3368](https://github.com/rust-lang/rustfmt/issues/3368)) + +#### `"Front"` (default): + +```rust +fn main() { + let or = foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo + || barbarbarbarbarbarbarbarbarbarbarbarbarbarbarbar; + + let sum = 123456789012345678901234567890 + + 123456789012345678901234567890 + + 123456789012345678901234567890; + + let range = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + ..bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb; +} +``` + +#### `"Back"`: + +```rust +fn main() { + let or = foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo || + barbarbarbarbarbarbarbarbarbarbarbarbarbarbarbar; + + let sum = 123456789012345678901234567890 + + 123456789012345678901234567890 + + 123456789012345678901234567890; + + let range = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.. + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb; +} +``` + +## `blank_lines_lower_bound` + +Minimum number of blank lines which must be put between items. If two items have fewer blank lines between +them, additional blank lines are inserted. + +- **Default value**: `0` +- **Possible values**: *unsigned integer* +- **Stable**: No (tracking issue: [#3382](https://github.com/rust-lang/rustfmt/issues/3382)) + +### Example +Original Code (rustfmt will not change it with the default value of `0`): + +```rust +#![rustfmt::skip] + +fn foo() { + println!("a"); +} +fn bar() { + println!("b"); + println!("c"); +} +``` + +#### `1` +```rust +fn foo() { + + println!("a"); +} + +fn bar() { + + println!("b"); + + println!("c"); +} +``` + + +## `blank_lines_upper_bound` + +Maximum number of blank lines which can be put between items. If more than this number of consecutive empty +lines are found, they are trimmed down to match this integer. + +- **Default value**: `1` +- **Possible values**: any non-negative integer +- **Stable**: No (tracking issue: [#3381](https://github.com/rust-lang/rustfmt/issues/3381)) + +### Example +Original Code: + +```rust +#![rustfmt::skip] + +fn foo() { + println!("a"); +} + + + +fn bar() { + println!("b"); + + + println!("c"); +} +``` + +#### `1` (default): +```rust +fn foo() { + println!("a"); +} + +fn bar() { + println!("b"); + + println!("c"); +} +``` + +#### `2`: +```rust +fn foo() { + println!("a"); +} + + +fn bar() { + println!("b"); + + + println!("c"); +} +``` + +See also: [`blank_lines_lower_bound`](#blank_lines_lower_bound) + +## `brace_style` + +Brace style for items + +- **Default value**: `"SameLineWhere"` +- **Possible values**: `"AlwaysNextLine"`, `"PreferSameLine"`, `"SameLineWhere"` +- **Stable**: No (tracking issue: [#3376](https://github.com/rust-lang/rustfmt/issues/3376)) + +### Functions + +#### `"SameLineWhere"` (default): + +```rust +fn lorem() { + // body +} + +fn lorem(ipsum: usize) { + // body +} + +fn lorem(ipsum: T) +where + T: Add + Sub + Mul + Div, +{ + // body +} +``` + +#### `"AlwaysNextLine"`: + +```rust +fn lorem() +{ + // body +} + +fn lorem(ipsum: usize) +{ + // body +} + +fn lorem(ipsum: T) +where + T: Add + Sub + Mul + Div, +{ + // body +} +``` + +#### `"PreferSameLine"`: + +```rust +fn lorem() { + // body +} + +fn lorem(ipsum: usize) { + // body +} + +fn lorem(ipsum: T) +where + T: Add + Sub + Mul + Div, { + // body +} +``` + +### Structs and enums + +#### `"SameLineWhere"` (default): + +```rust +struct Lorem { + ipsum: bool, +} + +struct Dolor +where + T: Eq, +{ + sit: T, +} +``` + +#### `"AlwaysNextLine"`: + +```rust +struct Lorem +{ + ipsum: bool, +} + +struct Dolor +where + T: Eq, +{ + sit: T, +} +``` + +#### `"PreferSameLine"`: + +```rust +struct Lorem { + ipsum: bool, +} + +struct Dolor +where + T: Eq, { + sit: T, +} +``` + +## `chain_width` + +Maximum width of a chain to fit on one line. + +- **Default value**: `60` +- **Possible values**: any positive integer that is less than or equal to the value specified for [`max_width`](#max_width) +- **Stable**: Yes + +By default this option is set as a percentage of [`max_width`](#max_width) provided by [`use_small_heuristics`](#use_small_heuristics), but a value set directly for `chain_width` will take precedence. + +See also [`max_width`](#max_width) and [`use_small_heuristics`](#use_small_heuristics) + +## `color` + +Whether to use colored output or not. + +- **Default value**: `"Auto"` +- **Possible values**: "Auto", "Always", "Never" +- **Stable**: No (tracking issue: [#3385](https://github.com/rust-lang/rustfmt/issues/3385)) + +## `combine_control_expr` + +Combine control expressions with function calls. + +- **Default value**: `true` +- **Possible values**: `true`, `false` +- **Stable**: No (tracking issue: [#3369](https://github.com/rust-lang/rustfmt/issues/3369)) + +#### `true` (default): + +```rust +fn example() { + // If + foo!(if x { + foo(); + } else { + bar(); + }); + + // IfLet + foo!(if let Some(..) = x { + foo(); + } else { + bar(); + }); + + // While + foo!(while x { + foo(); + bar(); + }); + + // WhileLet + foo!(while let Some(..) = x { + foo(); + bar(); + }); + + // ForLoop + foo!(for x in y { + foo(); + bar(); + }); + + // Loop + foo!(loop { + foo(); + bar(); + }); +} +``` + +#### `false`: + +```rust +fn example() { + // If + foo!( + if x { + foo(); + } else { + bar(); + } + ); + + // IfLet + foo!( + if let Some(..) = x { + foo(); + } else { + bar(); + } + ); + + // While + foo!( + while x { + foo(); + bar(); + } + ); + + // WhileLet + foo!( + while let Some(..) = x { + foo(); + bar(); + } + ); + + // ForLoop + foo!( + for x in y { + foo(); + bar(); + } + ); + + // Loop + foo!( + loop { + foo(); + bar(); + } + ); +} +``` + +## `comment_width` + +Maximum length of comments. No effect unless `wrap_comments = true`. + +- **Default value**: `80` +- **Possible values**: any positive integer +- **Stable**: No (tracking issue: [#3349](https://github.com/rust-lang/rustfmt/issues/3349)) + +**Note:** A value of `0` results in [`wrap_comments`](#wrap_comments) being applied regardless of a line's width. + +#### `80` (default; comments shorter than `comment_width`): +```rust +// Lorem ipsum dolor sit amet, consectetur adipiscing elit. +``` + +#### `60` (comments longer than `comment_width`): +```rust +// Lorem ipsum dolor sit amet, +// consectetur adipiscing elit. +``` + +See also [`wrap_comments`](#wrap_comments). + +## `condense_wildcard_suffixes` + +Replace strings of _ wildcards by a single .. in tuple patterns + +- **Default value**: `false` +- **Possible values**: `true`, `false` +- **Stable**: No (tracking issue: [#3384](https://github.com/rust-lang/rustfmt/issues/3384)) + +#### `false` (default): + +```rust +fn main() { + let (lorem, ipsum, _, _) = (1, 2, 3, 4); + let (lorem, ipsum, ..) = (1, 2, 3, 4); +} +``` + +#### `true`: + +```rust +fn main() { + let (lorem, ipsum, ..) = (1, 2, 3, 4); +} +``` + +## `control_brace_style` + +Brace style for control flow constructs + +- **Default value**: `"AlwaysSameLine"` +- **Possible values**: `"AlwaysNextLine"`, `"AlwaysSameLine"`, `"ClosingNextLine"` +- **Stable**: No (tracking issue: [#3377](https://github.com/rust-lang/rustfmt/issues/3377)) + +#### `"AlwaysSameLine"` (default): + +```rust +fn main() { + if lorem { + println!("ipsum!"); + } else { + println!("dolor!"); + } +} +``` + +#### `"AlwaysNextLine"`: + +```rust +fn main() { + if lorem + { + println!("ipsum!"); + } + else + { + println!("dolor!"); + } +} +``` + +#### `"ClosingNextLine"`: + +```rust +fn main() { + if lorem { + println!("ipsum!"); + } + else { + println!("dolor!"); + } +} +``` + +## `disable_all_formatting` + +Don't reformat anything. + +Note that this option may be soft-deprecated in the future once the [ignore](#ignore) option is stabilized. Nightly toolchain users are encouraged to use [ignore](#ignore) instead when possible. + +- **Default value**: `false` +- **Possible values**: `true`, `false` +- **Stable**: Yes + +## `edition` + +Specifies which edition is used by the parser. + +- **Default value**: `"2015"` +- **Possible values**: `"2015"`, `"2018"`, `"2021"` +- **Stable**: Yes + +Rustfmt is able to pick up the edition used by reading the `Cargo.toml` file if executed +through the Cargo's formatting tool `cargo fmt`. Otherwise, the edition needs to be specified +in your config file: + +```toml +edition = "2018" +``` + +## `empty_item_single_line` + +Put empty-body functions and impls on a single line + +- **Default value**: `true` +- **Possible values**: `true`, `false` +- **Stable**: No (tracking issue: [#3356](https://github.com/rust-lang/rustfmt/issues/3356)) + +#### `true` (default): + +```rust +fn lorem() {} + +impl Lorem {} +``` + +#### `false`: + +```rust +fn lorem() { +} + +impl Lorem { +} +``` + +See also [`brace_style`](#brace_style), [`control_brace_style`](#control_brace_style). + + +## `enum_discrim_align_threshold` + +The maximum length of enum variant having discriminant, that gets vertically aligned with others. +Variants without discriminants would be ignored for the purpose of alignment. + +Note that this is not how much whitespace is inserted, but instead the longest variant name that +doesn't get ignored when aligning. + +- **Default value** : 0 +- **Possible values**: any positive integer +- **Stable**: No (tracking issue: [#3372](https://github.com/rust-lang/rustfmt/issues/3372)) + +#### `0` (default): + +```rust +enum Foo { + A = 0, + Bb = 1, + RandomLongVariantGoesHere = 10, + Ccc = 71, +} + +enum Bar { + VeryLongVariantNameHereA = 0, + VeryLongVariantNameHereBb = 1, + VeryLongVariantNameHereCcc = 2, +} +``` + +#### `20`: + +```rust +enum Foo { + A = 0, + Bb = 1, + RandomLongVariantGoesHere = 10, + Ccc = 2, +} + +enum Bar { + VeryLongVariantNameHereA = 0, + VeryLongVariantNameHereBb = 1, + VeryLongVariantNameHereCcc = 2, +} +``` + + +## `error_on_line_overflow` + +Error if Rustfmt is unable to get all lines within `max_width`, except for comments and string +literals. If this happens, then it is a bug in Rustfmt. You might be able to work around the bug by +refactoring your code to avoid long/complex expressions, usually by extracting a local variable or +using a shorter name. + +- **Default value**: `false` +- **Possible values**: `true`, `false` +- **Stable**: No (tracking issue: [#3391](https://github.com/rust-lang/rustfmt/issues/3391)) + +See also [`max_width`](#max_width). + +## `error_on_unformatted` + +Error if unable to get comments or string literals within `max_width`, or they are left with +trailing whitespaces. + +- **Default value**: `false` +- **Possible values**: `true`, `false` +- **Stable**: No (tracking issue: [#3392](https://github.com/rust-lang/rustfmt/issues/3392)) + +## `fn_args_layout` + +This option is deprecated and has been renamed to `fn_params_layout` to better communicate that +it affects the layout of parameters in function signatures. + +- **Default value**: `"Tall"` +- **Possible values**: `"Compressed"`, `"Tall"`, `"Vertical"` +- **Stable**: Yes + +#### `"Tall"` (default): + +```rust +trait Lorem { + fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet); + + fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet) { + // body + } + + fn lorem( + ipsum: Ipsum, + dolor: Dolor, + sit: Sit, + amet: Amet, + consectetur: Consectetur, + adipiscing: Adipiscing, + elit: Elit, + ); + + fn lorem( + ipsum: Ipsum, + dolor: Dolor, + sit: Sit, + amet: Amet, + consectetur: Consectetur, + adipiscing: Adipiscing, + elit: Elit, + ) { + // body + } +} +``` + +#### `"Compressed"`: + +```rust +trait Lorem { + fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet); + + fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet) { + // body + } + + fn lorem( + ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: Consectetur, + adipiscing: Adipiscing, elit: Elit, + ); + + fn lorem( + ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: Consectetur, + adipiscing: Adipiscing, elit: Elit, + ) { + // body + } +} +``` + +#### `"Vertical"`: + +```rust +trait Lorem { + fn lorem( + ipsum: Ipsum, + dolor: Dolor, + sit: Sit, + amet: Amet, + ); + + fn lorem( + ipsum: Ipsum, + dolor: Dolor, + sit: Sit, + amet: Amet, + ) { + // body + } + + fn lorem( + ipsum: Ipsum, + dolor: Dolor, + sit: Sit, + amet: Amet, + consectetur: Consectetur, + adipiscing: Adipiscing, + elit: Elit, + ); + + fn lorem( + ipsum: Ipsum, + dolor: Dolor, + sit: Sit, + amet: Amet, + consectetur: Consectetur, + adipiscing: Adipiscing, + elit: Elit, + ) { + // body + } +} +``` + +See also [`fn_params_layout`](#fn_params_layout) + +## `fn_call_width` + +Maximum width of the args of a function call before falling back to vertical formatting. + +- **Default value**: `60` +- **Possible values**: any positive integer that is less than or equal to the value specified for [`max_width`](#max_width) +- **Stable**: Yes + +By default this option is set as a percentage of [`max_width`](#max_width) provided by [`use_small_heuristics`](#use_small_heuristics), but a value set directly for `fn_call_width` will take precedence. + +See also [`max_width`](#max_width) and [`use_small_heuristics`](#use_small_heuristics) + +## `fn_params_layout` + +Control the layout of parameters in function signatures. + +- **Default value**: `"Tall"` +- **Possible values**: `"Compressed"`, `"Tall"`, `"Vertical"` +- **Stable**: Yes + +#### `"Tall"` (default): + +```rust +trait Lorem { + fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet); + + fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet) { + // body + } + + fn lorem( + ipsum: Ipsum, + dolor: Dolor, + sit: Sit, + amet: Amet, + consectetur: Consectetur, + adipiscing: Adipiscing, + elit: Elit, + ); + + fn lorem( + ipsum: Ipsum, + dolor: Dolor, + sit: Sit, + amet: Amet, + consectetur: Consectetur, + adipiscing: Adipiscing, + elit: Elit, + ) { + // body + } +} +``` + +#### `"Compressed"`: + +```rust +trait Lorem { + fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet); + + fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet) { + // body + } + + fn lorem( + ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: Consectetur, + adipiscing: Adipiscing, elit: Elit, + ); + + fn lorem( + ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: Consectetur, + adipiscing: Adipiscing, elit: Elit, + ) { + // body + } +} +``` + +#### `"Vertical"`: + +```rust +trait Lorem { + fn lorem( + ipsum: Ipsum, + dolor: Dolor, + sit: Sit, + amet: Amet, + ); + + fn lorem( + ipsum: Ipsum, + dolor: Dolor, + sit: Sit, + amet: Amet, + ) { + // body + } + + fn lorem( + ipsum: Ipsum, + dolor: Dolor, + sit: Sit, + amet: Amet, + consectetur: Consectetur, + adipiscing: Adipiscing, + elit: Elit, + ); + + fn lorem( + ipsum: Ipsum, + dolor: Dolor, + sit: Sit, + amet: Amet, + consectetur: Consectetur, + adipiscing: Adipiscing, + elit: Elit, + ) { + // body + } +} +``` + + +## `fn_single_line` + +Put single-expression functions on a single line + +- **Default value**: `false` +- **Possible values**: `true`, `false` +- **Stable**: No (tracking issue: [#3358](https://github.com/rust-lang/rustfmt/issues/3358)) + +#### `false` (default): + +```rust +fn lorem() -> usize { + 42 +} + +fn lorem() -> usize { + let ipsum = 42; + ipsum +} +``` + +#### `true`: + +```rust +fn lorem() -> usize { 42 } + +fn lorem() -> usize { + let ipsum = 42; + ipsum +} +``` + +See also [`control_brace_style`](#control_brace_style). + + +## `force_explicit_abi` + +Always print the abi for extern items + +- **Default value**: `true` +- **Possible values**: `true`, `false` +- **Stable**: Yes + +**Note:** Non-"C" ABIs are always printed. If `false` then "C" is removed. + +#### `true` (default): + +```rust +extern "C" { + pub static lorem: c_int; +} +``` + +#### `false`: + +```rust +extern { + pub static lorem: c_int; +} +``` + +## `force_multiline_blocks` + +Force multiline closure and match arm bodies to be wrapped in a block + +- **Default value**: `false` +- **Possible values**: `false`, `true` +- **Stable**: No (tracking issue: [#3374](https://github.com/rust-lang/rustfmt/issues/3374)) + +#### `false` (default): + +```rust +fn main() { + result.and_then(|maybe_value| match maybe_value { + None => foo(), + Some(value) => bar(), + }); + + match lorem { + None => |ipsum| { + println!("Hello World"); + }, + Some(dolor) => foo(), + } +} +``` + +#### `true`: + +```rust +fn main() { + result.and_then(|maybe_value| { + match maybe_value { + None => foo(), + Some(value) => bar(), + } + }); + + match lorem { + None => { + |ipsum| { + println!("Hello World"); + } + } + Some(dolor) => foo(), + } +} +``` + + +## `format_code_in_doc_comments` + +Format code snippet included in doc comments. + +- **Default value**: `false` +- **Possible values**: `true`, `false` +- **Stable**: No (tracking issue: [#3348](https://github.com/rust-lang/rustfmt/issues/3348)) + +#### `false` (default): + +```rust +/// Adds one to the number given. +/// +/// # Examples +/// +/// ```rust +/// let five=5; +/// +/// assert_eq!( +/// 6, +/// add_one(5) +/// ); +/// # fn add_one(x: i32) -> i32 { +/// # x + 1 +/// # } +/// ``` +fn add_one(x: i32) -> i32 { + x + 1 +} +``` + +#### `true` + +```rust +/// Adds one to the number given. +/// +/// # Examples +/// +/// ```rust +/// let five = 5; +/// +/// assert_eq!(6, add_one(5)); +/// # fn add_one(x: i32) -> i32 { +/// # x + 1 +/// # } +/// ``` +fn add_one(x: i32) -> i32 { + x + 1 +} +``` + +## `doc_comment_code_block_width` + +Max width for code snippets included in doc comments. Only used if [`format_code_in_doc_comments`](#format_code_in_doc_comments) is true. + +- **Default value**: `100` +- **Possible values**: any positive integer that is less than or equal to the value specified for [`max_width`](#max_width) +- **Stable**: No (tracking issue: [#5359](https://github.com/rust-lang/rustfmt/issues/5359)) + +## `format_generated_files` + +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` +- **Stable**: No (tracking issue: [#5080](https://github.com/rust-lang/rustfmt/issues/5080)) + +## `format_macro_matchers` + +Format the metavariable matching patterns in macros. + +- **Default value**: `false` +- **Possible values**: `true`, `false` +- **Stable**: No (tracking issue: [#3354](https://github.com/rust-lang/rustfmt/issues/3354)) + +#### `false` (default): + +```rust +macro_rules! foo { + ($a: ident : $b: ty) => { + $a(42): $b; + }; + ($a: ident $b: ident $c: ident) => { + $a = $b + $c; + }; +} +``` + +#### `true`: + +```rust +macro_rules! foo { + ($a:ident : $b:ty) => { + $a(42): $b; + }; + ($a:ident $b:ident $c:ident) => { + $a = $b + $c; + }; +} +``` + +See also [`format_macro_bodies`](#format_macro_bodies). + + +## `format_macro_bodies` + +Format the bodies of macros. + +- **Default value**: `true` +- **Possible values**: `true`, `false` +- **Stable**: No (tracking issue: [#3355](https://github.com/rust-lang/rustfmt/issues/3355)) + +#### `true` (default): + +```rust +macro_rules! foo { + ($a: ident : $b: ty) => { + $a(42): $b; + }; + ($a: ident $b: ident $c: ident) => { + $a = $b + $c; + }; +} +``` + +#### `false`: + +```rust +macro_rules! foo { + ($a: ident : $b: ty) => { $a(42): $b; }; + ($a: ident $b: ident $c: ident) => { $a=$b+$c; }; +} +``` + +See also [`format_macro_matchers`](#format_macro_matchers). + +## `skip_macro_invocations` + +Skip formatting the bodies of macro invocations with the following names. + +rustfmt will not format any macro invocation for macros with names set in this list. +Including the special value "*" will prevent any macro invocations from being formatted. + +Note: This option does not have any impact on how rustfmt formats macro definitions. + +- **Default value**: `[]` +- **Possible values**: a list of macro name idents, `["name_0", "name_1", ..., "*"]` +- **Stable**: No (tracking issue: [#5346](https://github.com/rust-lang/rustfmt/issues/5346)) + +#### `[]` (default): + +rustfmt will follow its standard approach to formatting macro invocations. + +No macro invocations will be skipped based on their name. More information about rustfmt's standard macro invocation formatting behavior can be found in [#5437](https://github.com/rust-lang/rustfmt/discussions/5437). + +```rust +lorem!( + const _: u8 = 0; +); + +ipsum!( + const _: u8 = 0; +); +``` + +#### `["lorem"]`: + +The named macro invocations will be skipped. + +```rust +lorem!( + const _: u8 = 0; +); + +ipsum!( + const _: u8 = 0; +); +``` + +#### `["*"]`: + +The special selector `*` will skip all macro invocations. + +```rust +lorem!( + const _: u8 = 0; +); + +ipsum!( + const _: u8 = 0; +); +``` + +## `format_strings` + +Format string literals where necessary + +- **Default value**: `false` +- **Possible values**: `true`, `false` +- **Stable**: No (tracking issue: [#3353](https://github.com/rust-lang/rustfmt/issues/3353)) + +#### `false` (default): + +```rust +fn main() { + let lorem = "ipsum dolor sit amet consectetur adipiscing elit lorem ipsum dolor sit amet consectetur adipiscing"; +} +``` + +#### `true`: + +```rust +fn main() { + let lorem = "ipsum dolor sit amet consectetur adipiscing elit lorem ipsum dolor sit amet \ + consectetur adipiscing"; +} +``` + +See also [`max_width`](#max_width). + +## `hard_tabs` + +Use tab characters for indentation, spaces for alignment + +- **Default value**: `false` +- **Possible values**: `true`, `false` +- **Stable**: Yes + +#### `false` (default): + +```rust +fn lorem() -> usize { + 42 // spaces before 42 +} +``` + +#### `true`: + +```rust +fn lorem() -> usize { + 42 // tabs before 42 +} +``` + +See also: [`tab_spaces`](#tab_spaces). + +## `hex_literal_case` + +Control the case of the letters in hexadecimal literal values + +- **Default value**: `Preserve` +- **Possible values**: `Preserve`, `Upper`, `Lower` +- **Stable**: No (tracking issue: [#5081](https://github.com/rust-lang/rustfmt/issues/5081)) + +## `hide_parse_errors` + +Do not show parse errors if the parser failed to parse files. + +- **Default value**: `false` +- **Possible values**: `true`, `false` +- **Stable**: No (tracking issue: [#3390](https://github.com/rust-lang/rustfmt/issues/3390)) + +## `ignore` + +Skip formatting files and directories that match the specified pattern. +The pattern format is the same as [.gitignore](https://git-scm.com/docs/gitignore#_pattern_format). Be sure to use Unix/forwardslash `/` style paths. This path style will work on all platforms. Windows style paths with backslashes `\` are not supported. + +- **Default value**: format every file +- **Possible values**: See an example below +- **Stable**: No (tracking issue: [#3395](https://github.com/rust-lang/rustfmt/issues/3395)) + +### Example + +If you want to ignore specific files, put the following to your config file: + +```toml +ignore = [ + "src/types.rs", + "src/foo/bar.rs", +] +``` + +If you want to ignore every file under `examples/`, put the following to your config file: + +```toml +ignore = [ + "examples", +] +``` + +If you want to ignore every file under the directory where you put your rustfmt.toml: + +```toml +ignore = ["/"] +``` + +## `imports_indent` + +Indent style of imports + +- **Default Value**: `"Block"` +- **Possible values**: `"Block"`, `"Visual"` +- **Stable**: No (tracking issue: [#3360](https://github.com/rust-lang/rustfmt/issues/3360)) + +#### `"Block"` (default): + +```rust +use foo::{ + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy, + zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz, +}; +``` + +#### `"Visual"`: + +```rust +use foo::{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy, + zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz}; +``` + +See also: [`imports_layout`](#imports_layout). + +## `imports_layout` + +Item layout inside a imports block + +- **Default value**: "Mixed" +- **Possible values**: "Horizontal", "HorizontalVertical", "Mixed", "Vertical" +- **Stable**: No (tracking issue: [#3361](https://github.com/rust-lang/rustfmt/issues/3361)) + +#### `"Mixed"` (default): + +```rust +use foo::{xxxxxxxxxxxxxxxxxx, yyyyyyyyyyyyyyyyyy, zzzzzzzzzzzzzzzzzz}; + +use foo::{ + aaaaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbbbbb, cccccccccccccccccc, dddddddddddddddddd, + eeeeeeeeeeeeeeeeee, ffffffffffffffffff, +}; +``` + +#### `"Horizontal"`: + +**Note**: This option forces all imports onto one line and may exceed `max_width`. + +```rust +use foo::{xxx, yyy, zzz}; + +use foo::{aaa, bbb, ccc, ddd, eee, fff}; +``` + +#### `"HorizontalVertical"`: + +```rust +use foo::{xxxxxxxxxxxxxxxxxx, yyyyyyyyyyyyyyyyyy, zzzzzzzzzzzzzzzzzz}; + +use foo::{ + aaaaaaaaaaaaaaaaaa, + bbbbbbbbbbbbbbbbbb, + cccccccccccccccccc, + dddddddddddddddddd, + eeeeeeeeeeeeeeeeee, + ffffffffffffffffff, +}; +``` + +#### `"Vertical"`: + +```rust +use foo::{ + xxx, + yyy, + zzz, +}; + +use foo::{ + aaa, + bbb, + ccc, + ddd, + eee, + fff, +}; +``` + +## `indent_style` + +Indent on expressions or items. + +- **Default value**: `"Block"` +- **Possible values**: `"Block"`, `"Visual"` +- **Stable**: No (tracking issue: [#3346](https://github.com/rust-lang/rustfmt/issues/3346)) + +### Array + +#### `"Block"` (default): + +```rust +fn main() { + let lorem = vec![ + "ipsum", + "dolor", + "sit", + "amet", + "consectetur", + "adipiscing", + "elit", + ]; +} +``` + +#### `"Visual"`: + +```rust +fn main() { + let lorem = vec!["ipsum", + "dolor", + "sit", + "amet", + "consectetur", + "adipiscing", + "elit"]; +} +``` + +### Control flow + +#### `"Block"` (default): + +```rust +fn main() { + if lorem_ipsum + && dolor_sit + && amet_consectetur + && lorem_sit + && dolor_consectetur + && amet_ipsum + && lorem_consectetur + { + // ... + } +} +``` + +#### `"Visual"`: + +```rust +fn main() { + if lorem_ipsum + && dolor_sit + && amet_consectetur + && lorem_sit + && dolor_consectetur + && amet_ipsum + && lorem_consectetur + { + // ... + } +} +``` + +See also: [`control_brace_style`](#control_brace_style). + +### Function arguments + +#### `"Block"` (default): + +```rust +fn lorem() {} + +fn lorem(ipsum: usize) {} + +fn lorem( + ipsum: usize, + dolor: usize, + sit: usize, + amet: usize, + consectetur: usize, + adipiscing: usize, + elit: usize, +) { + // body +} +``` + +#### `"Visual"`: + +```rust +fn lorem() {} + +fn lorem(ipsum: usize) {} + +fn lorem(ipsum: usize, + dolor: usize, + sit: usize, + amet: usize, + consectetur: usize, + adipiscing: usize, + elit: usize) { + // body +} +``` + +### Function calls + +#### `"Block"` (default): + +```rust +fn main() { + lorem( + "lorem", + "ipsum", + "dolor", + "sit", + "amet", + "consectetur", + "adipiscing", + "elit", + ); +} +``` + +#### `"Visual"`: + +```rust +fn main() { + lorem("lorem", + "ipsum", + "dolor", + "sit", + "amet", + "consectetur", + "adipiscing", + "elit"); +} +``` + +### Generics + +#### `"Block"` (default): + +```rust +fn lorem< + Ipsum: Eq = usize, + Dolor: Eq = usize, + Sit: Eq = usize, + Amet: Eq = usize, + Adipiscing: Eq = usize, + Consectetur: Eq = usize, + Elit: Eq = usize, +>( + ipsum: Ipsum, + dolor: Dolor, + sit: Sit, + amet: Amet, + adipiscing: Adipiscing, + consectetur: Consectetur, + elit: Elit, +) -> T { + // body +} +``` + +#### `"Visual"`: + +```rust +fn lorem( + ipsum: Ipsum, + dolor: Dolor, + sit: Sit, + amet: Amet, + adipiscing: Adipiscing, + consectetur: Consectetur, + elit: Elit) + -> T { + // body +} +``` + +#### Struct + +#### `"Block"` (default): + +```rust +fn main() { + let lorem = Lorem { + ipsum: dolor, + sit: amet, + }; +} +``` + +#### `"Visual"`: + +```rust +fn main() { + let lorem = Lorem { ipsum: dolor, + sit: amet }; +} +``` + +See also: [`struct_lit_single_line`](#struct_lit_single_line), [`indent_style`](#indent_style). + +### Where predicates + +#### `"Block"` (default): + +```rust +fn lorem() -> T +where + Ipsum: Eq, + Dolor: Eq, + Sit: Eq, + Amet: Eq, +{ + // body +} +``` + +#### `"Visual"`: + +```rust +fn lorem() -> T + where Ipsum: Eq, + Dolor: Eq, + Sit: Eq, + Amet: Eq +{ + // body +} +``` + +## `inline_attribute_width` + +Write an item and its attribute on the same line if their combined width is below a threshold + +- **Default value**: 0 +- **Possible values**: any positive integer +- **Stable**: No (tracking issue: [#3343](https://github.com/rust-lang/rustfmt/issues/3343)) + +### Example + +#### `0` (default): +```rust +#[cfg(feature = "alloc")] +use core::slice; +``` + +#### `50`: +```rust +#[cfg(feature = "alloc")] use core::slice; +``` + +## `match_arm_blocks` + +Controls whether arm bodies are wrapped in cases where the first line of the body cannot fit on the same line as the `=>` operator. + +The Style Guide requires that bodies are block wrapped by default if a line break is required after the `=>`, but this option can be used to disable that behavior to prevent wrapping arm bodies in that event, so long as the body does not contain multiple statements nor line comments. + +- **Default value**: `true` +- **Possible values**: `true`, `false` +- **Stable**: No (tracking issue: [#3373](https://github.com/rust-lang/rustfmt/issues/3373)) + +#### `true` (default): + +```rust +fn main() { + match lorem { + ipsum => { + foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo(x) + } + dolor => println!("{}", sit), + sit => foo( + "foooooooooooooooooooooooo", + "baaaaaaaaaaaaaaaaaaaaaaaarr", + "baaaaaaaaaaaaaaaaaaaazzzzzzzzzzzzz", + "qqqqqqqqquuuuuuuuuuuuuuuuuuuuuuuuuuxxx", + ), + } +} +``` + +#### `false`: + +```rust +fn main() { + match lorem { + lorem => + foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo(x), + ipsum => println!("{}", sit), + sit => foo( + "foooooooooooooooooooooooo", + "baaaaaaaaaaaaaaaaaaaaaaaarr", + "baaaaaaaaaaaaaaaaaaaazzzzzzzzzzzzz", + "qqqqqqqqquuuuuuuuuuuuuuuuuuuuuuuuuuxxx", + ), + } +} +``` + +See also: [`match_block_trailing_comma`](#match_block_trailing_comma). + +## `match_arm_leading_pipes` + +Controls whether to include a leading pipe on match arms + +- **Default value**: `Never` +- **Possible values**: `Always`, `Never`, `Preserve` +- **Stable**: Yes + +#### `Never` (default): +```rust +// Leading pipes are removed from this: +// fn foo() { +// match foo { +// | "foo" | "bar" => {} +// | "baz" +// | "something relatively long" +// | "something really really really realllllllllllllly long" => println!("x"), +// | "qux" => println!("y"), +// _ => {} +// } +// } + +// Becomes +fn foo() { + match foo { + "foo" | "bar" => {} + "baz" + | "something relatively long" + | "something really really really realllllllllllllly long" => println!("x"), + "qux" => println!("y"), + _ => {} + } +} +``` + +#### `Always`: +```rust +// Leading pipes are emitted on all arms of this: +// fn foo() { +// match foo { +// "foo" | "bar" => {} +// "baz" +// | "something relatively long" +// | "something really really really realllllllllllllly long" => println!("x"), +// "qux" => println!("y"), +// _ => {} +// } +// } + +// Becomes: +fn foo() { + match foo { + | "foo" | "bar" => {} + | "baz" + | "something relatively long" + | "something really really really realllllllllllllly long" => println!("x"), + | "qux" => println!("y"), + | _ => {} + } +} +``` + +#### `Preserve`: +```rust +fn foo() { + match foo { + | "foo" | "bar" => {} + | "baz" + | "something relatively long" + | "something really really really realllllllllllllly long" => println!("x"), + | "qux" => println!("y"), + _ => {} + } + + match baz { + "qux" => {} + "foo" | "bar" => {} + _ => {} + } +} +``` + +## `match_block_trailing_comma` + +Put a trailing comma after a block based match arm (non-block arms are not affected) + +- **Default value**: `false` +- **Possible values**: `true`, `false` +- **Stable**: Yes + +#### `false` (default): + +```rust +fn main() { + match lorem { + Lorem::Ipsum => { + println!("ipsum"); + } + Lorem::Dolor => println!("dolor"), + } +} +``` + +#### `true`: + +```rust +fn main() { + match lorem { + Lorem::Ipsum => { + println!("ipsum"); + }, + Lorem::Dolor => println!("dolor"), + } +} +``` + +See also: [`trailing_comma`](#trailing_comma), [`match_arm_blocks`](#match_arm_blocks). + +## `max_width` + +Maximum width of each line + +- **Default value**: `100` +- **Possible values**: any positive integer +- **Stable**: Yes + +See also [`error_on_line_overflow`](#error_on_line_overflow). + +## `merge_derives` + +Merge multiple derives into a single one. + +- **Default value**: `true` +- **Possible values**: `true`, `false` +- **Stable**: Yes + +#### `true` (default): + +```rust +#[derive(Eq, PartialEq, Debug, Copy, Clone)] +pub enum Foo {} +``` + +#### `false`: + +```rust +#[derive(Eq, PartialEq, Debug, Copy, Clone)] +pub enum Bar {} + +#[derive(Eq, PartialEq)] +#[derive(Debug)] +#[derive(Copy, Clone)] +pub enum Foo {} +``` + +## `imports_granularity` + +Controls how imports are structured in `use` statements. Imports will be merged or split to the configured level of granularity. + +Similar to other `import` related configuration options, this option operates within the bounds of user-defined groups of imports. See [`group_imports`](#group_imports) for more information on import groups. + +Note that rustfmt will not modify the granularity of imports containing comments if doing so could potentially lose or misplace said comments. + +- **Default value**: `Preserve` +- **Possible values**: `Preserve`, `Crate`, `Module`, `Item`, `One` +- **Stable**: No (tracking issue: [#4991](https://github.com/rust-lang/rustfmt/issues/4991)) + + +#### `Preserve` (default): + +Do not change the granularity of any imports and preserve the original structure written by the developer. + +```rust +use foo::b; +use foo::b::{f, g}; +use foo::{a, c, d::e}; +use qux::{h, i}; +``` + +#### `Crate`: + +Merge imports from the same crate into a single `use` statement. Conversely, imports from different crates are split into separate statements. + +```rust +use foo::{ + a, b, + b::{f, g}, + c, + d::e, +}; +use qux::{h, i}; +``` + +#### `Module`: + +Merge imports from the same module into a single `use` statement. Conversely, imports from different modules are split into separate statements. + +```rust +use foo::b::{f, g}; +use foo::d::e; +use foo::{a, b, c}; +use qux::{h, i}; +``` + +#### `Item`: + +Flatten imports so that each has its own `use` statement. + +```rust +use foo::a; +use foo::b; +use foo::b::f; +use foo::b::g; +use foo::c; +use foo::d::e; +use qux::h; +use qux::i; +``` + +#### `One`: + +Merge all imports into a single `use` statement as long as they have the same visibility. + +```rust +pub use foo::{x, y}; +use { + bar::{ + a, + b::{self, f, g}, + c, + d::e, + }, + qux::{h, i}, +}; +``` + +## `merge_imports` + +This option is deprecated. Use `imports_granularity = "Crate"` instead. + +- **Default value**: `false` +- **Possible values**: `true`, `false` + +#### `false` (default): + +```rust +use foo::{a, c, d}; +use foo::{b, g}; +use foo::{e, f}; +``` + +#### `true`: + +```rust +use foo::{a, b, c, d, e, f, g}; +``` + + +## `newline_style` + +Unix or Windows line endings + +- **Default value**: `"Auto"` +- **Possible values**: `"Auto"`, `"Native"`, `"Unix"`, `"Windows"` +- **Stable**: Yes + +#### `Auto` (default): + +The newline style is detected automatically on a per-file basis. Files +with mixed line endings will be converted to the first detected line +ending style. + +#### `Native` + +Line endings will be converted to `\r\n` on Windows and `\n` on all +other platforms. + +#### `Unix` + +Line endings will be converted to `\n`. + +#### `Windows` + +Line endings will be converted to `\r\n`. + +## `normalize_comments` + +Convert /* */ comments to // comments where possible + +- **Default value**: `false` +- **Possible values**: `true`, `false` +- **Stable**: No (tracking issue: [#3350](https://github.com/rust-lang/rustfmt/issues/3350)) + +#### `false` (default): + +```rust +// Lorem ipsum: +fn dolor() -> usize {} + +/* sit amet: */ +fn adipiscing() -> usize {} +``` + +#### `true`: + +```rust +// Lorem ipsum: +fn dolor() -> usize {} + +// sit amet: +fn adipiscing() -> usize {} +``` + +## `normalize_doc_attributes` + +Convert `#![doc]` and `#[doc]` attributes to `//!` and `///` doc comments. + +- **Default value**: `false` +- **Possible values**: `true`, `false` +- **Stable**: No (tracking issue: [#3351](https://github.com/rust-lang/rustfmt/issues/3351)) + +#### `false` (default): + +```rust +#![doc = "Example documentation"] + +#[doc = "Example item documentation"] +pub enum Bar {} + +/// Example item documentation +pub enum Foo {} +``` + +#### `true`: + +```rust +//! Example documentation + +/// Example item documentation +pub enum Foo {} +``` + +## `overflow_delimited_expr` + +When structs, slices, arrays, and block/array-like macros are used as the last +argument in an expression list, allow them to overflow (like blocks/closures) +instead of being indented on a new line. + +- **Default value**: `false` +- **Possible values**: `true`, `false` +- **Stable**: No (tracking issue: [#3370](https://github.com/rust-lang/rustfmt/issues/3370)) + +#### `false` (default): + +```rust +fn example() { + foo(ctx, |param| { + action(); + foo(param) + }); + + foo( + ctx, + Bar { + x: value, + y: value2, + }, + ); + + foo( + ctx, + &[ + MAROON_TOMATOES, + PURPLE_POTATOES, + ORGANE_ORANGES, + GREEN_PEARS, + RED_APPLES, + ], + ); + + foo( + ctx, + vec![ + MAROON_TOMATOES, + PURPLE_POTATOES, + ORGANE_ORANGES, + GREEN_PEARS, + RED_APPLES, + ], + ); +} +``` + +#### `true`: + +```rust +fn example() { + foo(ctx, |param| { + action(); + foo(param) + }); + + foo(ctx, Bar { + x: value, + y: value2, + }); + + foo(ctx, &[ + MAROON_TOMATOES, + PURPLE_POTATOES, + ORGANE_ORANGES, + GREEN_PEARS, + RED_APPLES, + ]); + + foo(ctx, vec![ + MAROON_TOMATOES, + PURPLE_POTATOES, + ORGANE_ORANGES, + GREEN_PEARS, + RED_APPLES, + ]); +} +``` + +## `remove_nested_parens` + +Remove nested parens. + +- **Default value**: `true`, +- **Possible values**: `true`, `false` +- **Stable**: Yes + + +#### `true` (default): +```rust +fn main() { + (foo()); +} +``` + +#### `false`: +```rust +fn main() { + (foo()); + + ((((foo())))); +} +``` + + +## `reorder_impl_items` + +Reorder impl items. `type` and `const` are put first, then macros and methods. + +- **Default value**: `false` +- **Possible values**: `true`, `false` +- **Stable**: No (tracking issue: [#3363](https://github.com/rust-lang/rustfmt/issues/3363)) + +#### `false` (default) + +```rust +struct Dummy; + +impl Iterator for Dummy { + fn next(&mut self) -> Option { + None + } + + type Item = i32; +} + +impl Iterator for Dummy { + type Item = i32; + + fn next(&mut self) -> Option { + None + } +} +``` + +#### `true` + +```rust +struct Dummy; + +impl Iterator for Dummy { + type Item = i32; + + fn next(&mut self) -> Option { + None + } +} +``` + +## `reorder_imports` + +Reorder import and extern crate statements alphabetically in groups (a group is +separated by a newline). + +- **Default value**: `true` +- **Possible values**: `true`, `false` +- **Stable**: Yes + +#### `true` (default): + +```rust +use dolor; +use ipsum; +use lorem; +use sit; +``` + +#### `false`: + +```rust +use lorem; +use ipsum; +use dolor; +use sit; +``` + +## `group_imports` + +Controls the strategy for how consecutive imports are grouped together. + +Controls the strategy for grouping sets of consecutive imports. Imports may contain newlines between imports and still be grouped together as a single set, but other statements between imports will result in different grouping sets. + +- **Default value**: `Preserve` +- **Possible values**: `Preserve`, `StdExternalCrate`, `One` +- **Stable**: No (tracking issue: [#5083](https://github.com/rust-lang/rustfmt/issues/5083)) + +Each set of imports (one or more `use` statements, optionally separated by newlines) will be formatted independently. Other statements such as `mod ...` or `extern crate ...` will cause imports to not be grouped together. + +#### `Preserve` (default): + +Preserve the source file's import groups. + +```rust +use super::update::convert_publish_payload; +use chrono::Utc; + +use alloc::alloc::Layout; +use juniper::{FieldError, FieldResult}; +use uuid::Uuid; + +use std::sync::Arc; + +use broker::database::PooledConnection; + +use super::schema::{Context, Payload}; +use crate::models::Event; +use core::f32; +``` + +#### `StdExternalCrate`: + +Discard existing import groups, and create three groups for: +1. `std`, `core` and `alloc`, +2. external crates, +3. `self`, `super` and `crate` imports. + +```rust +use alloc::alloc::Layout; +use core::f32; +use std::sync::Arc; + +use broker::database::PooledConnection; +use chrono::Utc; +use juniper::{FieldError, FieldResult}; +use uuid::Uuid; + +use super::schema::{Context, Payload}; +use super::update::convert_publish_payload; +use crate::models::Event; +``` + +#### `One`: + +Discard existing import groups, and create a single group for everything + +```rust +use super::schema::{Context, Payload}; +use super::update::convert_publish_payload; +use crate::models::Event; +use alloc::alloc::Layout; +use broker::database::PooledConnection; +use chrono::Utc; +use core::f32; +use juniper::{FieldError, FieldResult}; +use std::sync::Arc; +use uuid::Uuid; +``` + +## `reorder_modules` + +Reorder `mod` declarations alphabetically in group. + +- **Default value**: `true` +- **Possible values**: `true`, `false` +- **Stable**: Yes + +#### `true` (default) + +```rust +mod a; +mod b; + +mod dolor; +mod ipsum; +mod lorem; +mod sit; +``` + +#### `false` + +```rust +mod b; +mod a; + +mod lorem; +mod ipsum; +mod dolor; +mod sit; +``` + +**Note** `mod` with `#[macro_export]` will not be reordered since that could change the semantics +of the original source code. + +## `required_version` + +Require a specific version of rustfmt. If you want to make sure that the +specific version of rustfmt is used in your CI, use this option. + +- **Default value**: `CARGO_PKG_VERSION` +- **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/3389)) + +## `single_line_if_else_max_width` + +Maximum line length for single line if-else expressions. A value of `0` (zero) results in if-else expressions always being broken into multiple lines. Note this occurs when `use_small_heuristics` is set to `Off`. + +- **Default value**: `50` +- **Possible values**: any positive integer that is less than or equal to the value specified for [`max_width`](#max_width) +- **Stable**: Yes + +By default this option is set as a percentage of [`max_width`](#max_width) provided by [`use_small_heuristics`](#use_small_heuristics), but a value set directly for `single_line_if_else_max_width` will take precedence. + +See also [`max_width`](#max_width) and [`use_small_heuristics`](#use_small_heuristics) + +## `single_line_let_else_max_width` + +Maximum line length for single line let-else statements. +See the [let-else statement section of the Rust Style Guide](https://github.com/rust-lang/rust/blob/master/src/doc/style-guide/src/statements.md#else-blocks-let-else-statements) for more details on when a let-else statement may be written on a single line. +A value of `0` (zero) means the divergent `else` block will always be formatted over multiple lines. +Note this occurs when `use_small_heuristics` is set to `Off`. + +By default this option is set as a percentage of [`max_width`](#max_width) provided by [`use_small_heuristics`](#use_small_heuristics), but a value set directly for `single_line_let_else_max_width` will take precedence. + +- **Default value**: `50` +- **Possible values**: any positive integer that is less than or equal to the value specified for [`max_width`](#max_width) +- **Stable**: Yes + +#### `50` (default): + +```rust +fn main() { + let Some(w) = opt else { return Ok(()) }; + + let Some(x) = opt else { return }; + + let Some(y) = opt else { + return; + }; + + let Some(z) = some_very_very_very_very_long_name else { + return; + }; +} +``` + +#### `0`: + +```rust +fn main() { + let Some(w) = opt else { + return Ok(()); + }; + + let Some(x) = opt else { + return; + }; + + let Some(y) = opt else { + return; + }; + + let Some(z) = some_very_very_very_very_long_name else { + return; + }; +} +``` + +#### `100`: + +```rust +fn main() { + let Some(w) = opt else { return Ok(()) }; + + let Some(x) = opt else { return }; + + let Some(y) = opt else { + return; + }; + + let Some(z) = some_very_very_very_very_long_name else { return }; +} +``` + +See also [`max_width`](#max_width) and [`use_small_heuristics`](#use_small_heuristics) + + +## `space_after_colon` + +Leave a space after the colon. + +- **Default value**: `true` +- **Possible values**: `true`, `false` +- **Stable**: No (tracking issue: [#3366](https://github.com/rust-lang/rustfmt/issues/3366)) + +#### `true` (default): + +```rust +fn lorem(t: T) { + let lorem: Dolor = Lorem { + ipsum: dolor, + sit: amet, + }; +} +``` + +#### `false`: + +```rust +fn lorem(t:T) { + let lorem:Dolor = Lorem { + ipsum:dolor, + sit:amet, + }; +} +``` + +See also: [`space_before_colon`](#space_before_colon). + +## `space_before_colon` + +Leave a space before the colon. + +- **Default value**: `false` +- **Possible values**: `true`, `false` +- **Stable**: No (tracking issue: [#3365](https://github.com/rust-lang/rustfmt/issues/3365)) + +#### `false` (default): + +```rust +fn lorem(t: T) { + let lorem: Dolor = Lorem { + ipsum: dolor, + sit: amet, + }; +} +``` + +#### `true`: + +```rust +fn lorem(t : T) { + let lorem : Dolor = Lorem { + ipsum : dolor, + sit : amet, + }; +} +``` + +See also: [`space_after_colon`](#space_after_colon). + +## `spaces_around_ranges` + +Put spaces around the .., ..=, and ... range operators + +- **Default value**: `false` +- **Possible values**: `true`, `false` +- **Stable**: No (tracking issue: [#3367](https://github.com/rust-lang/rustfmt/issues/3367)) + +#### `false` (default): + +```rust +fn main() { + let lorem = 0..10; + let ipsum = 0..=10; + + match lorem { + 1..5 => foo(), + _ => bar, + } + + match lorem { + 1..=5 => foo(), + _ => bar, + } + + match lorem { + 1...5 => foo(), + _ => bar, + } +} +``` + +#### `true`: + +```rust +fn main() { + let lorem = 0 .. 10; + let ipsum = 0 ..= 10; + + match lorem { + 1 .. 5 => foo(), + _ => bar, + } + + match lorem { + 1 ..= 5 => foo(), + _ => bar, + } + + match lorem { + 1 ... 5 => foo(), + _ => bar, + } +} +``` + +## `struct_field_align_threshold` + +The maximum diff of width between struct fields to be aligned with each other. + +- **Default value** : 0 +- **Possible values**: any non-negative integer +- **Stable**: No (tracking issue: [#3371](https://github.com/rust-lang/rustfmt/issues/3371)) + +#### `0` (default): + +```rust +struct Foo { + x: u32, + yy: u32, + zzz: u32, +} +``` + +#### `20`: + +```rust +struct Foo { + x: u32, + yy: u32, + zzz: u32, +} +``` + +## `struct_lit_single_line` + +Put small struct literals on a single line + +- **Default value**: `true` +- **Possible values**: `true`, `false` +- **Stable**: No (tracking issue: [#3357](https://github.com/rust-lang/rustfmt/issues/3357)) + +#### `true` (default): + +```rust +fn main() { + let lorem = Lorem { foo: bar, baz: ofo }; +} +``` + +#### `false`: + +```rust +fn main() { + let lorem = Lorem { + foo: bar, + baz: ofo, + }; +} +``` + +See also: [`indent_style`](#indent_style). + +## `struct_lit_width` + +Maximum width in the body of a struct literal before falling back to vertical formatting. A value of `0` (zero) results in struct literals always being broken into multiple lines. Note this occurs when `use_small_heuristics` is set to `Off`. + +- **Default value**: `18` +- **Possible values**: any positive integer that is less than or equal to the value specified for [`max_width`](#max_width) +- **Stable**: Yes + +By default this option is set as a percentage of [`max_width`](#max_width) provided by [`use_small_heuristics`](#use_small_heuristics), but a value set directly for `struct_lit_width` will take precedence. + +See also [`max_width`](#max_width), [`use_small_heuristics`](#use_small_heuristics), and [`struct_lit_single_line`](#struct_lit_single_line) + +## `struct_variant_width` + +Maximum width in the body of a struct variant before falling back to vertical formatting. A value of `0` (zero) results in struct literals always being broken into multiple lines. Note this occurs when `use_small_heuristics` is set to `Off`. + +- **Default value**: `35` +- **Possible values**: any positive integer that is less than or equal to the value specified for [`max_width`](#max_width) +- **Stable**: Yes + +By default this option is set as a percentage of [`max_width`](#max_width) provided by [`use_small_heuristics`](#use_small_heuristics), but a value set directly for `struct_variant_width` will take precedence. + +See also [`max_width`](#max_width) and [`use_small_heuristics`](#use_small_heuristics) + +## `tab_spaces` + +Number of spaces per tab + +- **Default value**: `4` +- **Possible values**: any positive integer +- **Stable**: Yes + +#### `4` (default): + +```rust +fn lorem() { + let ipsum = dolor(); + let sit = vec![ + "amet consectetur adipiscing elit amet", + "consectetur adipiscing elit amet consectetur.", + ]; +} +``` + +#### `2`: + +```rust +fn lorem() { + let ipsum = dolor(); + let sit = vec![ + "amet consectetur adipiscing elit amet", + "consectetur adipiscing elit amet consectetur.", + ]; +} +``` + +See also: [`hard_tabs`](#hard_tabs). + + +## `trailing_comma` + +How to handle trailing commas for lists + +- **Default value**: `"Vertical"` +- **Possible values**: `"Always"`, `"Never"`, `"Vertical"` +- **Stable**: No (tracking issue: [#3379](https://github.com/rust-lang/rustfmt/issues/3379)) + +#### `"Vertical"` (default): + +```rust +fn main() { + let Lorem { ipsum, dolor, sit } = amet; + let Lorem { + ipsum, + dolor, + sit, + amet, + consectetur, + adipiscing, + } = elit; +} +``` + +#### `"Always"`: + +```rust +fn main() { + let Lorem { ipsum, dolor, sit, } = amet; + let Lorem { + ipsum, + dolor, + sit, + amet, + consectetur, + adipiscing, + } = elit; +} +``` + +#### `"Never"`: + +```rust +fn main() { + let Lorem { ipsum, dolor, sit } = amet; + let Lorem { + ipsum, + dolor, + sit, + amet, + consectetur, + adipiscing + } = elit; +} +``` + +See also: [`match_block_trailing_comma`](#match_block_trailing_comma). + +## `trailing_semicolon` + +Add trailing semicolon after break, continue and return + +- **Default value**: `true` +- **Possible values**: `true`, `false` +- **Stable**: No (tracking issue: [#3378](https://github.com/rust-lang/rustfmt/issues/3378)) + +#### `true` (default): +```rust +fn foo() -> usize { + return 0; +} +``` + +#### `false`: +```rust +fn foo() -> usize { + return 0 +} +``` + +## `type_punctuation_density` + +Determines if `+` or `=` are wrapped in spaces in the punctuation of types + +- **Default value**: `"Wide"` +- **Possible values**: `"Compressed"`, `"Wide"` +- **Stable**: No (tracking issue: [#3364](https://github.com/rust-lang/rustfmt/issues/3364)) + +#### `"Wide"` (default): + +```rust +fn lorem() { + // body +} +``` + +#### `"Compressed"`: + +```rust +fn lorem() { + // body +} +``` + +## `unstable_features` + +Enable unstable features on the unstable channel. + +- **Default value**: `false` +- **Possible values**: `true`, `false` +- **Stable**: No (tracking issue: [#3387](https://github.com/rust-lang/rustfmt/issues/3387)) + +## `use_field_init_shorthand` + +Use field initialize shorthand if possible. + +- **Default value**: `false` +- **Possible values**: `true`, `false` +- **Stable**: Yes + +#### `false` (default): + +```rust +struct Foo { + x: u32, + y: u32, + z: u32, +} + +fn main() { + let x = 1; + let y = 2; + let z = 3; + let a = Foo { x, y, z }; + let b = Foo { x: x, y: y, z: z }; +} +``` + +#### `true`: + +```rust +struct Foo { + x: u32, + y: u32, + z: u32, +} + +fn main() { + let x = 1; + let y = 2; + let z = 3; + let a = Foo { x, y, z }; +} +``` + +## `use_small_heuristics` + +This option can be used to simplify the management and bulk updates of the granular width configuration settings ([`fn_call_width`](#fn_call_width), [`attr_fn_like_width`](#attr_fn_like_width), [`struct_lit_width`](#struct_lit_width), [`struct_variant_width`](#struct_variant_width), [`array_width`](#array_width), [`chain_width`](#chain_width), [`single_line_if_else_max_width`](#single_line_if_else_max_width)), that respectively control when formatted constructs are multi-lined/vertical based on width. + +Note that explicitly provided values for the width configuration settings take precedence and override the calculated values determined by `use_small_heuristics`. + +- **Default value**: `"Default"` +- **Possible values**: `"Default"`, `"Off"`, `"Max"` +- **Stable**: Yes + +#### `Default` (default): +When `use_small_heuristics` is set to `Default`, the values for the granular width settings are calculated as a ratio of the value for `max_width`. + +The ratios are: +* [`fn_call_width`](#fn_call_width) - `60%` +* [`attr_fn_like_width`](#attr_fn_like_width) - `70%` +* [`struct_lit_width`](#struct_lit_width) - `18%` +* [`struct_variant_width`](#struct_variant_width) - `35%` +* [`array_width`](#array_width) - `60%` +* [`chain_width`](#chain_width) - `60%` +* [`single_line_if_else_max_width`](#single_line_if_else_max_width) - `50%` +* [`single_line_let_else_max_width`](#single_line_let_else_max_width) - `50%` + +For example when `max_width` is set to `100`, the width settings are: +* `fn_call_width=60` +* `attr_fn_like_width=70` +* `struct_lit_width=18` +* `struct_variant_width=35` +* `array_width=60` +* `chain_width=60` +* `single_line_if_else_max_width=50` +* `single_line_let_else_max_width=50` + +and when `max_width` is set to `200`: +* `fn_call_width=120` +* `attr_fn_like_width=140` +* `struct_lit_width=36` +* `struct_variant_width=70` +* `array_width=120` +* `chain_width=120` +* `single_line_if_else_max_width=100` +* `single_line_let_else_max_width=100` + +```rust +enum Lorem { + Ipsum, + Dolor(bool), + Sit { amet: Consectetur, adipiscing: Elit }, +} + +fn main() { + lorem( + "lorem", + "ipsum", + "dolor", + "sit", + "amet", + "consectetur", + "adipiscing", + ); + + let lorem = Lorem { + ipsum: dolor, + sit: amet, + }; + let lorem = Lorem { ipsum: dolor }; + + let lorem = if ipsum { dolor } else { sit }; +} +``` + +#### `Off`: +When `use_small_heuristics` is set to `Off`, the granular width settings are functionally disabled and ignored. See the documentation for the respective width config options for specifics. + +```rust +enum Lorem { + Ipsum, + Dolor(bool), + Sit { + amet: Consectetur, + adipiscing: Elit, + }, +} + +fn main() { + lorem("lorem", "ipsum", "dolor", "sit", "amet", "consectetur", "adipiscing"); + + let lorem = Lorem { + ipsum: dolor, + sit: amet, + }; + + let lorem = if ipsum { + dolor + } else { + sit + }; +} +``` + +#### `Max`: +When `use_small_heuristics` is set to `Max`, then each granular width setting is set to the same value as `max_width`. + +So if `max_width` is set to `200`, then all the width settings are also set to `200`. +* `fn_call_width=200` +* `attr_fn_like_width=200` +* `struct_lit_width=200` +* `struct_variant_width=200` +* `array_width=200` +* `chain_width=200` +* `single_line_if_else_max_width=200` +* `single_line_let_else_max_width=200` + +```rust +enum Lorem { + Ipsum, + Dolor(bool), + Sit { amet: Consectetur, adipiscing: Elit }, +} + +fn main() { + lorem("lorem", "ipsum", "dolor", "sit", "amet", "consectetur", "adipiscing"); + + let lorem = Lorem { ipsum: dolor, sit: amet }; + + let lorem = if ipsum { dolor } else { sit }; +} +``` + + +See also: +* [`max_width`](#max_width) +* [`fn_call_width`](#fn_call_width) +* [`attr_fn_like_width`](#attr_fn_like_width) +* [`struct_lit_width`](#struct_lit_width) +* [`struct_variant_width`](#struct_variant_width) +* [`array_width`](#array_width) +* [`chain_width`](#chain_width) +* [`single_line_if_else_max_width`](#single_line_if_else_max_width) +* [`single_line_let_else_max_width`](#single_line_let_else_max_width) + +## `use_try_shorthand` + +Replace uses of the try! macro by the ? shorthand + +- **Default value**: `false` +- **Possible values**: `true`, `false` +- **Stable**: Yes + +#### `false` (default): + +```rust +fn main() { + let lorem = ipsum.map(|dolor| dolor.sit())?; + + let lorem = try!(ipsum.map(|dolor| dolor.sit())); +} +``` + +#### `true`: + +```rust +fn main() { + let lorem = ipsum.map(|dolor| dolor.sit())?; +} +``` + +## `version` + +Which version of the formatting rules to use. `Version::One` is backwards-compatible +with Rustfmt 1.0. Other versions are only backwards compatible within a major +version number. + +- **Default value**: `One` +- **Possible values**: `One`, `Two` +- **Stable**: No (tracking issue: [#3383](https://github.com/rust-lang/rustfmt/issues/3383)) + +### Example + +```toml +version = "Two" +``` + +## `where_single_line` + +Forces the `where` clause to be laid out on a single line. + +- **Default value**: `false` +- **Possible values**: `true`, `false` +- **Stable**: No (tracking issue: [#3359](https://github.com/rust-lang/rustfmt/issues/3359)) + +#### `false` (default): + +```rust +impl Lorem for T +where + Option: Ipsum, +{ + // body +} +``` + +#### `true`: + +```rust +impl Lorem for T +where Option: Ipsum +{ + // body +} +``` + +See also [`brace_style`](#brace_style), [`control_brace_style`](#control_brace_style). + + +## `wrap_comments` + +Break comments to fit on the line + +Note that no wrapping will happen if: +1. The comment is the start of a markdown header doc comment +2. An URL was found in the comment + +- **Default value**: `false` +- **Possible values**: `true`, `false` +- **Stable**: No (tracking issue: [#3347](https://github.com/rust-lang/rustfmt/issues/3347)) + +#### `false` (default): + +```rust +// 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. + +// 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. + +// Information on the lorem ipsum can be found at the following url: https://en.wikipedia.org/wiki/Lorem_ipsum. Its text is: 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. + +/// # This doc comment is a very long header (it starts with a '#'). Had it not been a header it would have been wrapped. But because it is a header, it will not be. That is because wrapping a markdown header breaks it. +struct Foo {} +``` + +#### `true`: + +```rust +// 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. + +// 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. + +// Information on the lorem ipsum can be found at the following url: https://en.wikipedia.org/wiki/Lorem_ipsum. Its text is: 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. + +/// # This doc comment is a very long header (it starts with a '#'). Had it not been a header it would have been wrapped. But because it is a header, it will not be. That is because wrapping a markdown header breaks it. +struct Foo {} +``` + +# Internal Options + +## `emit_mode` + +Internal option + +## `make_backup` + +Internal option, use `--backup` + +## `print_misformatted_file_names` + +Internal option, use `-l` or `--files-with-diff` diff --git a/Contributing.md b/Contributing.md new file mode 100644 index 000000000000..b986a887c92f --- /dev/null +++ b/Contributing.md @@ -0,0 +1,261 @@ +# Contributing + +There are many ways to contribute to Rustfmt. This document lays out what they +are and has information on how to get started. If you have any questions about +contributing or need help with anything, please ask in the WG-Rustfmt channel +on [Discord](https://discordapp.com/invite/rust-lang). Feel free to also ask questions +on issues, or file new issues specifically to get help. + +All contributors are expected to follow our [Code of +Conduct](CODE_OF_CONDUCT.md). + +## Test and file issues + +It would be really useful to have people use rustfmt on their projects and file +issues where it does something you don't expect. + + +## Create test cases + +Having a strong test suite for a tool like this is essential. It is very easy +to create regressions. Any tests you can add are very much appreciated. + +The tests can be run with `cargo test`. This does a number of things: +* runs the unit tests for a number of internal functions; +* makes sure that rustfmt run on every file in `./tests/source/` is equal to its + associated file in `./tests/target/`; +* runs idempotence tests on the files in `./tests/target/`. These files should + not be changed by rustfmt; +* checks that rustfmt's code is not changed by running on itself. This ensures + that the project bootstraps. + +Creating a test is as easy as creating a new file in `./tests/source/` and an +equally named one in `./tests/target/`. If it is only required that rustfmt +leaves a piece of code unformatted, it may suffice to only create a target file. + +Whenever there's a discrepancy between the expected output when running tests, a +colourised diff will be printed so that the offending line(s) can quickly be +identified. + +Without explicit settings, the tests will be run using rustfmt's default +configuration. It is possible to run a test using non-default settings in several +ways. Firstly, you can include configuration parameters in comments at the top +of the file. For example: to use 3 spaces per tab, start your test with +`// rustfmt-tab_spaces: 3`. Just remember that the comment is part of the input, +so include in both the source and target files! It is also possible to +explicitly specify the name of the expected output file in the target directory. +Use `// rustfmt-target: filename.rs` for this. You can also specify a custom +configuration by using the `rustfmt-config` directive. Rustfmt will then use +that toml file located in `./tests/config/` for its configuration. Including +`// rustfmt-config: small_tabs.toml` will run your test with the configuration +file found at `./tests/config/small_tabs.toml`. The final option is used when the +test source file contains no configuration parameter comments. In this case, the +test harness looks for a configuration file with the same filename as the test +file in the `./tests/config/` directory, so a test source file named `test-indent.rs` +would need a configuration file named `test-indent.toml` in that directory. As an +example, the `issue-1111.rs` test file is configured by the file +`./tests/config/issue-1111.toml`. + +## Debugging + +Some `rewrite_*` methods use the `debug!` macro for printing useful information. +These messages can be printed by using the environment variable `RUSTFMT_LOG=rustfmt=DEBUG`. +These traces can be helpful in understanding which part of the code was used +and get a better grasp on the execution flow. + +## Hack! + +Here are some [good starting issues](https://github.com/rust-lang/rustfmt/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22). + +If you've found areas which need polish and don't have issues, please submit a +PR, don't feel there needs to be an issue. + + +### Guidelines + +Rustfmt bootstraps, that is part of its test suite is running itself on its +source code. So, basically, the only style guideline is that you must pass the +tests. That ensures that the Rustfmt source code adheres to our own conventions. + +Talking of tests, if you add a new feature or fix a bug, please also add a test. +It's really easy, see above for details. Please run `cargo test` before +submitting a PR to ensure your patch passes all tests, it's pretty quick. + +Rustfmt is post-1.0 and within major version releases we strive for backwards +compatibility (at least when using the default options). That means any code +which changes Rustfmt's output must be guarded by either an option or a version +check. The latter is implemented as an option called `option`. See the section on +[configuration](#Configuration) below. + +Please try to avoid leaving `TODO`s in the code. There are a few around, but I +wish there weren't. You can leave `FIXME`s, preferably with an issue number. + + +### Run Rustfmt from source code + +You may want to run a version of rustfmt from source code as part of a test or +hacking on the rustfmt codebase. It's strongly discouraged to install a version +of rustfmt from source. Instead, run it using `cargo run`, and `--manifest-path`. + +``` +cargo run --bin cargo-fmt -- --manifest-path path/to/project/you/want2test/Cargo.toml +``` + +### Version-gate formatting changes + +A change that introduces a different code-formatting should be gated on the +`version` configuration. This is to ensure the formatting of the current major +release is preserved, while allowing fixes to be implemented for the next +release. + +This is done by conditionally guarding the change like so: + +```rust +if config.version() == Version::One { // if the current major release is 1.x + // current formatting +} else { + // new formatting +} +``` + +This allows the user to apply the next formatting explicitly via the +configuration, while being stable by default. + +When the next major release is done, the code block of the previous formatting +can be deleted, e.g., the first block in the example above when going from `1.x` +to `2.x`. + +| Note: Only formatting changes with default options need to be gated. | +| --- | + +### A quick tour of Rustfmt + +Rustfmt is basically a pretty printer - that is, its mode of operation is to +take an AST (abstract syntax tree) and print it in a nice way (including staying +under the maximum permitted width for a line). In order to get that AST, we +first have to parse the source text, we use the Rust compiler's parser to do +that (see [src/lib.rs](src/lib.rs)). We shy away from doing anything too fancy, such as +algebraic approaches to pretty printing, instead relying on an heuristic +approach, 'manually' crafting a string for each AST node. This results in quite +a lot of code, but it is relatively simple. + +The AST is a tree view of source code. It carries all the semantic information +about the code, but not all of the syntax. In particular, we lose white space +and comments (although doc comments are preserved). Rustfmt uses a view of the +AST before macros are expanded, so there are still macro uses in the code. The +arguments to macros are not an AST, but raw tokens - this makes them harder to +format. + +There are different nodes for every kind of item and expression in Rust. For +more details see the source code in the compiler - +[ast.rs](https://github.com/rust-lang/rust/blob/master/compiler/rustc_ast/src/ast.rs) - and/or the +[docs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/index.html). + +Many nodes in the AST (but not all, annoyingly) have a `Span`. A `Span` is a +range in the source code, it can easily be converted to a snippet of source +text. When the AST does not contain enough information for us, we rely heavily +on `Span`s. For example, we can look between spans to try and find comments, or +parse a snippet to see how the user wrote their source code. + +The downside of using the AST is that we miss some information - primarily white +space and comments. White space is sometimes significant, although mostly we +want to ignore it and make our own. We strive to reproduce all comments, but +this is sometimes difficult. The crufty corners of Rustfmt are where we hack +around the absence of comments in the AST and try to recreate them as best we +can. + +Our primary tool here is to look between spans for text we've missed. For +example, in a function call `foo(a, b)`, we have spans for `a` and `b`, in this +case, there is only a comma and a single space between the end of `a` and the +start of `b`, so there is nothing much to do. But if we look at +`foo(a /* a comment */, b)`, then between `a` and `b` we find the comment. + +At a higher level, Rustfmt has machinery so that we account for text between +'top level' items. Then we can reproduce that text pretty much verbatim. We only +count spans we actually reformat, so if we can't format a span it is not missed +completely but is reproduced in the output without being formatted. This is +mostly handled in [src/missed_spans.rs](src/missed_spans.rs). See also `FmtVisitor::last_pos` in +[src/visitor.rs](src/visitor.rs). + + +#### Some important elements + +At the highest level, Rustfmt uses a `Visitor` implementation called `FmtVisitor` +to walk the AST. This is in [src/visitor.rs](src/visitor.rs). This is really just used to walk +items, rather than the bodies of functions. We also cover macros and attributes +here. Most methods of the visitor call out to `Rewrite` implementations that +then walk their own children. + +The `Rewrite` trait is defined in [src/rewrite.rs](src/rewrite.rs). It is implemented for many +things that can be rewritten, mostly AST nodes. It has a single function, +`rewrite`, which is called to rewrite `self` into an `Option`. The +arguments are `width` which is the horizontal space we write into and `offset` +which is how much we are currently indented from the lhs of the page. We also +take a context which contains information used for parsing, the current block +indent, and a configuration (see below). + +##### Rewrite and Indent + +To understand the indents, consider + +``` +impl Foo { + fn foo(...) { + bar(argument_one, + baz()); + } +} +``` + +When formatting the `bar` call we will format the arguments in order, after the +first one we know we are working on multiple lines (imagine it is longer than +written). So, when we come to the second argument, the indent we pass to +`rewrite` is 12, which puts us under the first argument. The current block +indent (stored in the context) is 8. The former is used for visual indenting +(when objects are vertically aligned with some marker), the latter is used for +block indenting (when objects are tabbed in from the lhs). The width available +for `baz()` will be the maximum width, minus the space used for indenting, minus +the space used for the `);`. (Note that actual argument formatting does not +quite work like this, but it's close enough). + +The `rewrite` function returns an `Option` - either we successfully rewrite and +return the rewritten string for the caller to use, or we fail to rewrite and +return `None`. This could be because Rustfmt encounters something it doesn't +know how to reformat, but more often it is because Rustfmt can't fit the item +into the required width. How to handle this is up to the caller. Often the +caller just gives up, ultimately relying on the missed spans system to paste in +the un-formatted source. A better solution (although not performed in many +places) is for the caller to shuffle around some of its other items to make +more width, then call the function again with more space. + +Since it is common for callers to bail out when a callee fails, we often use a +`?` operator to make this pattern more succinct. + +One way we might find out that we don't have enough space is when computing how much +space we have. Something like `available_space = budget - overhead`. Since +widths are unsized integers, this would cause underflow. Therefore we use +checked subtraction: `available_space = budget.checked_sub(overhead)?`. +`checked_sub` returns an `Option`, and if we would underflow `?` returns +`None`, otherwise, we proceed with the computed space. + +##### Rewrite of list-like expressions + +Much of the syntax in Rust is lists: lists of arguments, lists of fields, lists of +array elements, etc. We have some generic code to handle lists, including how to +space them in horizontal and vertical space, indentation, comments between +items, trailing separators, etc. However, since there are so many options, the +code is a bit complex. Look in [src/lists.rs](src/lists.rs). `write_list` is the key function, +and `ListFormatting` the key structure for configuration. You'll need to make a +`ListItems` for input, this is usually done using `itemize_list`. + +##### Configuration + +Rustfmt strives to be highly configurable. Often the first part of a patch is +creating a configuration option for the feature you are implementing. All +handling of configuration options is done in [src/config/mod.rs](src/config/mod.rs). Look for the +`create_config!` macro at the end of the file for all the options. The rest of +the file defines a bunch of enums used for options, and the machinery to produce +the config struct and parse a config file, etc. Checking an option is done by +accessing the correct field on the config struct, e.g., `config.max_width()`. Most +functions have a `Config`, or one can be accessed via a visitor or context of +some kind. diff --git a/Design.md b/Design.md new file mode 100644 index 000000000000..7a4dcf8773b6 --- /dev/null +++ b/Design.md @@ -0,0 +1,184 @@ +# Some thoughts on the design of rustfmt + +## Use cases + +A formatting tool can be used in different ways and the different use cases can +affect the design of the tool. The use cases I'm particularly concerned with are: + +* running on a whole repo before check-in + - in particular, to replace the `make tidy` pass on the Rust distro +* running on code from another project that you are adding to your own +* using for mass changes in code style over a project + +Some valid use cases for a formatting tool which I am explicitly not trying to +address (although it would be nice, if possible): + +* running 'as you type' in an IDE +* running on arbitrary snippets of code +* running on Rust-like code, specifically code which doesn't parse +* use as a pretty printer inside the compiler +* refactoring +* formatting totally unformatted source code + + +## Scope and vision + +I do not subscribe to the notion that a formatting tool should only change +whitespace. I believe that we should semantics preserving, but not necessarily +syntax preserving, i.e., we can change the AST of a program. + +I.e., we might change glob imports to list or single imports, re-order imports, +move bounds to where clauses, combine multiple impls into a single impl, etc. + +However, we will not change the names of variables or make any changes which +*could* change the semantics. To be ever so slightly formal, we might imagine +a compilers high level intermediate representation, we should strive to only +make changes which do not change the HIR, even if they do change the AST. + +I would like to be able to output refactoring scripts for making deeper changes +though. (E.g., renaming variables to satisfy our style guidelines). + +My long term goal is that all style lints can be moved from the compiler to +rustfmt and, as well as warning, can either fix problems or emit refactoring +scripts to do so. + +### Configurability + +I believe reformatting should be configurable to some extent. We should read in +options from a configuration file and reformat accordingly. We should supply at +least a config file which matches the Rust style guidelines. + +There should be multiple modes for running the tool. As well as simply replacing +each file, we should be able to show the user a list of the changes we would +make, or show a list of violations without corrections (the difference being +that there are multiple ways to satisfy a given set of style guidelines, and we +should distinguish violations from deviations from our own model). + + +## Implementation philosophy + +Some details of the philosophy behind the implementation. + + +### Operate on the AST + +A reformatting tool can be based on either the AST or a token stream (in Rust +this is actually a stream of token trees, but it's not a fundamental difference). +There are pros and cons to the two approaches. I have chosen to use the AST +approach. The primary reasons are that it allows us to do more sophisticated +manipulations, rather than just change whitespace, and it gives us more context +when making those changes. + +The advantage of the tokens approach is that you can operate on non-parsable +code. I don't care too much about that, it would be nice, but I think being able +to perform sophisticated transformations is more important. In the future, I hope to +(optionally) be able to use type information for informing reformatting too. One +specific case of unparsable code is macros. Using tokens is certainly easier +here, but I believe it is perfectly solvable with the AST approach. At the limit, +we can operate on just tokens in the macro case. + +I believe that there is not in fact that much difference between the two +approaches. Due to imperfect span information, under the AST approach, we +sometimes are reduced to examining tokens or do some re-lexing of our own. Under +the tokens approach, you need to implement your own (much simpler) parser. I +believe that as the tool gets more sophisticated, you end up doing more at the +token-level, or having an increasingly sophisticated parser, until at the limit +you have the same tool. + +However, I believe starting from the AST gets you more quickly to a usable and +useful tool. + + +### Heuristic rather than algorithmic + +Many formatting tools use a very general algorithmic or even algebraic tool for +pretty printing. This results in very elegant code, but I believe does not give +the best results. I prefer a more ad hoc approach where each expression/item is +formatted using custom rules. We hopefully don't end up with too much code due +to good old fashioned abstraction and code sharing. This will give a bigger code +base, but hopefully a better result. + +It also means that there will be some cases we can't format and we have to give +up. I think that is OK. Hopefully, they are rare enough that manually fixing them +is not painful. Better to have a tool that gives great code in 99% of cases and +fails in 1% than a tool which gives 50% great code and 50% ugly code, but never +fails. + + +### Incremental development + +I want rustfmt to be useful as soon as possible and to always be useful. I +specifically don't want to have to wait for a feature (or worse, the whole tool) +to be perfect before it is useful. The main ways this is achieved is to output +the source code where we can't yet reformat, be able to turn off new features +until they are ready, and the 'do no harm' principle (see next section). + + +### First, do no harm + +Until rustfmt is perfect, there will always be a trade-off between doing more and +doing existing things well. I want to err on the side of the latter. +Specifically, rustfmt should never take OK code and make it look worse. If we +can't make it better, we should leave it as is. That might mean being less +aggressive than we like or using configurability. + + +### Use the source code as guidance + +There are often multiple ways to format code and satisfy standards. Where this +is the case, we should use the source code as a hint for reformatting. +Furthermore, where the code has been formatted in a particular way that satisfies +the coding standard, it should not be changed (this is sometimes not possible or +not worthwhile due to uniformity being desirable, but it is a useful goal). + + +### Architecture details + +We use the AST from [syntex_syntax], an export of rustc's libsyntax. We use +syntex_syntax's visit module to walk the AST to find starting points for +reformatting. Eventually, we should reformat everything and we shouldn't need +the visit module. We keep track of the last formatted position in the code, and +when we reformat the next piece of code we make sure to output the span for all +the code in between (handled by missed_spans.rs). + +[syntex_syntax]: https://crates.io/crates/syntex_syntax + +We read in formatting configuration from a `rustfmt.toml` file if there is one. +The options and their defaults are defined in `config.rs`. A `Config` object is +passed throughout the formatting code, and each formatting routine looks there +for its configuration. + +Our visitor keeps track of the desired current indent due to blocks ( +`block_indent`). Each `visit_*` method reformats code according to this indent, +`config.comment_width()` and `config.max_width()`. Most reformatting that is done +in the `visit_*` methods is a bit hacky and is meant to be temporary until it can +be done properly. + +There are a bunch of methods called `rewrite_*`. They do the bulk of the +reformatting. These take the AST node to be reformatted (this may not literally +be an AST node from syntex_syntax: there might be multiple parameters +describing a logical node), the current indent, and the current width budget. +They return a `String` (or sometimes an `Option`) which formats the +code in the box given by the indent and width budget. If the method fails, it +returns `None` and the calling method then has to fallback in some way to give +the callee more space. + +So, in summary, to format a node, we calculate the width budget and then walk down +the tree from the node. At a leaf, we generate an actual string and then unwind, +combining these strings as we go back up the tree. + +For example, consider a method definition: + +``` + fn foo(a: A, b: B) { + ... + } +``` + +We start at indent 4, the rewrite function for the whole function knows it must +write `fn foo(` before the arguments and `) {` after them, assuming the max width +is 100, it thus asks the rewrite argument list function to rewrite with an indent +of 11 and in a width of 86. Assuming that is possible (obviously in this case), +it returns a string for the arguments and it can make a string for the function +header. If the arguments couldn't be fitted in that space, we might try to +fallback to a hanging indent, so we try again with indent 8 and width 89. diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 000000000000..212ba1f31848 --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright 2016-2021 The Rust Project Developers + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 000000000000..1baa137f605b --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2016-2021 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/Makefile.toml b/Makefile.toml new file mode 100644 index 000000000000..597dd1205643 --- /dev/null +++ b/Makefile.toml @@ -0,0 +1,71 @@ +[env] +CFG_RELEASE = { value = "${CARGO_MAKE_RUST_VERSION}", condition = { env_not_set = ["CFG_RELEASE"] } } +CFG_RELEASE_CHANNEL = { value = "${CARGO_MAKE_RUST_CHANNEL}", condition = { env_not_set = ["CFG_RELEASE_CHANNEL"] } } + +[tasks.build-bin] +command = "cargo" +args = [ + "build", + "--bin", + "rustfmt", + "--bin", + "cargo-fmt", +] + +[tasks.build-bins] +command = "cargo" +args = [ + "build", + "--bins", +] + +[tasks.install] +command = "cargo" +args = [ + "install", + "--path", + ".", + "--force", + "--locked", # Respect Cargo.lock +] + +[tasks.release] +command = "cargo" +args = [ + "build", + "--release", +] + +[tasks.test] +command = "cargo" +args = [ + "test", +] + +[tasks.test-all] +dependencies = ["build-bin"] +run_task = { name = ["test", "test-ignored"] } + +[tasks.test-ignored] +command = "cargo" +args = [ + "test", + "--", + "--ignored", +] + +[tasks.b] +alias = "build" + +[tasks.bb] +alias = "build-bin" + +[tasks.bins] +alias = "build-bins" + +[tasks.c] +alias = "check" + +[tasks.t] +alias = "test" + diff --git a/Processes.md b/Processes.md new file mode 100644 index 000000000000..61abc87eec9f --- /dev/null +++ b/Processes.md @@ -0,0 +1,53 @@ +This document outlines processes regarding management of rustfmt. + +# Stabilising an Option + +In this Section, we describe how to stabilise an option of the rustfmt's configuration. + +## Conditions + +- Is the default value correct ? +- The design and implementation of the option are sound and clean. +- The option is well tested, both in unit tests and, optimally, in real usage. +- There is no open bug about the option that prevents its use. + +## Steps + +Open a pull request that closes the tracking issue. The tracking issue is listed beside the option in `Configurations.md`. + +- Update the `Config` enum marking the option as stable. +- Update the the `Configuration.md` file marking the option as stable. +- Update `CHANGELOG.md` marking the option as stable. + +## After the stabilisation + +The option should remain backward-compatible with previous parameters of the option. For instance, if the option is an enum `enum Foo { Alice, Bob }` and the variant `Foo::Bob` is removed/renamed, existing use of the `Foo::Bob` variant should map to the new logic. Breaking changes can be applied under the condition they are version-gated. + +# Make a Release + +## 0. Update CHANGELOG.md + +## 1. Update Cargo.toml and Cargo.lock + +For example, 1.0.0 -> 1.0.1: + +```diff +-version = "1.0.0" ++version = "1.0.1" +``` + +## 2. Push the commit to the master branch + +E.g., https://github.com/rust-lang/rustfmt/commit/5274b49caa1a7db6ac10c76bf1a3d5710ccef569 + +## 3. Create a release tag + +```sh +git tag -s v1.2.3 -m "Release 1.2.3" +``` + +## 4. Publish to crates.io + +`cargo publish` + +## 5. Create a PR to rust-lang/rust to update the rustfmt submodule diff --git a/README.md b/README.md new file mode 100644 index 000000000000..c05184fbb04b --- /dev/null +++ b/README.md @@ -0,0 +1,232 @@ +# rustfmt [![linux](https://github.com/rust-lang/rustfmt/actions/workflows/linux.yml/badge.svg?event=push)](https://github.com/rust-lang/rustfmt/actions/workflows/linux.yml) [![mac](https://github.com/rust-lang/rustfmt/actions/workflows/mac.yml/badge.svg?event=push)](https://github.com/rust-lang/rustfmt/actions/workflows/mac.yml) [![windows](https://github.com/rust-lang/rustfmt/actions/workflows/windows.yml/badge.svg?event=push)](https://github.com/rust-lang/rustfmt/actions/workflows/windows.yml) [![crates.io](https://img.shields.io/crates/v/rustfmt-nightly.svg)](https://crates.io/crates/rustfmt-nightly) + +A tool for formatting Rust code according to style guidelines. + +If you'd like to help out (and you should, it's a fun project!), see +[Contributing.md](Contributing.md) and our [Code of +Conduct](CODE_OF_CONDUCT.md). + +You can use rustfmt in Travis CI builds. We provide a minimal Travis CI +configuration (see [here](#checking-style-on-a-ci-server)). + +## Quick start + +You can run `rustfmt` with Rust 1.24 and above. + +### On the Stable toolchain + +To install: + +```sh +rustup component add rustfmt +``` + +To run on a cargo project in the current working directory: + +```sh +cargo fmt +``` + +### On the Nightly toolchain + +For the latest and greatest `rustfmt`, nightly is required. + +To install: + +```sh +rustup component add rustfmt --toolchain nightly +``` + +To run on a cargo project in the current working directory: + +```sh +cargo +nightly fmt +``` + +## Limitations + +Rustfmt tries to work on as much Rust code as possible. Sometimes, the code +doesn't even need to compile! In general, we are looking to limit areas of +instability; in particular, post-1.0, the formatting of most code should not +change as Rustfmt improves. However, there are some things that Rustfmt can't +do or can't do well (and thus where formatting might change significantly, +even post-1.0). We would like to reduce the list of limitations over time. + +The following list enumerates areas where Rustfmt does not work or where the +stability guarantees do not apply (we don't make a distinction between the two +because in the future Rustfmt might work on code where it currently does not): + +* a program where any part of the program does not parse (parsing is an early + stage of compilation and in Rust includes macro expansion). +* Macro declarations and uses (current status: some macro declarations and uses + are formatted). +* Comments, including any AST node with a comment 'inside' (Rustfmt does not + currently attempt to format comments, it does format code with comments inside, but that formatting may change in the future). +* Rust code in code blocks in comments. +* Any fragment of a program (i.e., stability guarantees only apply to whole + programs, even where fragments of a program can be formatted today). +* Code containing non-ascii unicode characters (we believe Rustfmt mostly works + here, but do not have the test coverage or experience to be 100% sure). +* Bugs in Rustfmt (like any software, Rustfmt has bugs, we do not consider bug + fixes to break our stability guarantees). + + +## Running + +You can run Rustfmt by just typing `rustfmt filename` if you used `cargo +install`. This runs rustfmt on the given file, if the file includes out of line +modules, then we reformat those too. So to run on a whole module or crate, you +just need to run on the root file (usually mod.rs or lib.rs). Rustfmt can also +read data from stdin. Alternatively, you can use `cargo fmt` to format all +binary and library targets of your crate. + +You can run `rustfmt --help` for information about available arguments. +The easiest way to run rustfmt against a project is with `cargo fmt`. `cargo fmt` works on both +single-crate projects and [cargo workspaces](https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html). +Please see `cargo fmt --help` for usage information. + +You can specify the path to your own `rustfmt` binary for cargo to use by setting the`RUSTFMT` +environment variable. This was added in v1.4.22, so you must have this version or newer to leverage this feature (`cargo fmt --version`) + +### Running `rustfmt` directly + +To format individual files or arbitrary codes from stdin, the `rustfmt` binary should be used. Some +examples follow: + +- `rustfmt lib.rs main.rs` will format "lib.rs" and "main.rs" in place +- `rustfmt` will read a code from stdin and write formatting to stdout + - `echo "fn main() {}" | rustfmt` would emit "fn main() {}". + +For more information, including arguments and emit options, see `rustfmt --help`. + +### Verifying code is formatted + +When running with `--check`, Rustfmt will exit with `0` if Rustfmt would not +make any formatting changes to the input, and `1` if Rustfmt would make changes. +In other modes, Rustfmt will exit with `1` if there was some error during +formatting (for example a parsing or internal error) and `0` if formatting +completed without error (whether or not changes were made). + + + +## Running Rustfmt from your editor + +* [Vim](https://github.com/rust-lang/rust.vim#formatting-with-rustfmt) +* [Emacs](https://github.com/rust-lang/rust-mode) +* [Sublime Text 3](https://packagecontrol.io/packages/RustFmt) +* [Atom](atom.md) +* [Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer) +* [IntelliJ or CLion](intellij.md) + + +## Checking style on a CI server + +To keep your code base consistently formatted, it can be helpful to fail the CI build +when a pull request contains unformatted code. Using `--check` instructs +rustfmt to exit with an error code if the input is not formatted correctly. +It will also print any found differences. (Older versions of Rustfmt don't +support `--check`, use `--write-mode diff`). + +A minimal Travis setup could look like this (requires Rust 1.31.0 or greater): + +```yaml +language: rust +before_script: +- rustup component add rustfmt +script: +- cargo build +- cargo test +- cargo fmt --all -- --check +``` + +See [this blog post](https://medium.com/@ag_dubs/enforcing-style-in-ci-for-rust-projects-18f6b09ec69d) +for more info. + +## How to build and test + +`cargo build` to build. + +`cargo test` to run all tests. + +To run rustfmt after this, use `cargo run --bin rustfmt -- filename`. See the +notes above on running rustfmt. + + +## Configuring Rustfmt + +Rustfmt is designed to be very configurable. You can create a TOML file called +`rustfmt.toml` or `.rustfmt.toml`, place it in the project or any other parent +directory and it will apply the options in that file. See `rustfmt +--help=config` for the options which are available, or if you prefer to see +visual style previews, [GitHub page](https://rust-lang.github.io/rustfmt/). + +By default, Rustfmt uses a style which conforms to the [Rust style guide][style +guide] that has been formalized through the [style RFC +process][fmt rfcs]. + +Configuration options are either stable or unstable. Stable options can always +be used, while unstable ones are only available on a nightly toolchain, and opt-in. +See [GitHub page](https://rust-lang.github.io/rustfmt/) for details. + +### Rust's Editions + +Rustfmt is able to pick up the edition used by reading the `Cargo.toml` file if +executed through the Cargo's formatting tool `cargo fmt`. Otherwise, the edition +needs to be specified in `rustfmt.toml`, e.g., with `edition = "2018"`. + +## Tips + +* For things you do not want rustfmt to mangle, use `#[rustfmt::skip]` +* To prevent rustfmt from formatting a macro or an attribute, + use `#[rustfmt::skip::macros(target_macro_name)]` or + `#[rustfmt::skip::attributes(target_attribute_name)]` + + Example: + + ```rust + #![rustfmt::skip::attributes(custom_attribute)] + + #[custom_attribute(formatting , here , should , be , Skipped)] + #[rustfmt::skip::macros(html)] + fn main() { + let macro_result1 = html! {
+ Hello
+ }.to_string(); + ``` +* When you run rustfmt, place a file named `rustfmt.toml` or `.rustfmt.toml` in + target file directory or its parents to override the default settings of + rustfmt. You can generate a file containing the default configuration with + `rustfmt --print-config default rustfmt.toml` and customize as needed. +* After successful compilation, a `rustfmt` executable can be found in the + target directory. +* If you're having issues compiling Rustfmt (or compile errors when trying to + install), make sure you have the most recent version of Rust installed. + +* You can change the way rustfmt emits the changes with the --emit flag: + + Example: + + ```sh + cargo fmt -- --emit files + ``` + + Options: + + | Flag |Description| Nightly Only | + |:---:|:---:|:---:| + | files | overwrites output to files | No | + | stdout | writes output to stdout | No | + | coverage | displays how much of the input file was processed | Yes | + | checkstyle | emits in a checkstyle format | Yes | + | json | emits diffs in a json format | Yes | + +## License + +Rustfmt is distributed under the terms of both the MIT license and the +Apache License (Version 2.0). + +See [LICENSE-APACHE](LICENSE-APACHE) and [LICENSE-MIT](LICENSE-MIT) for details. + +[rust]: https://github.com/rust-lang/rust +[fmt rfcs]: https://github.com/rust-dev-tools/fmt-rfcs +[style guide]: https://github.com/rust-dev-tools/fmt-rfcs/blob/master/guide/guide.md diff --git a/atom.md b/atom.md new file mode 100644 index 000000000000..c7e3a991a5f0 --- /dev/null +++ b/atom.md @@ -0,0 +1,31 @@ +# Running Rustfmt from Atom + +## rust-analyzer + +Rustfmt can be utilized from [rust-analyzer](https://rust-analyzer.github.io/) which is provided by [ide-rust](https://atom.io/packages/ide-rust). + +`apm install ide-rust` + +Once installed a file is formatted with `ctrl-shift-c` or `cmd-shift-c`, also available in context menu. + +## atom-beautify + +Another way is to install [Beautify](https://atom.io/packages/atom-beautify), you +can do this by running `apm install atom-beautify`. + +There are 2 settings that need to be configured in the atom beautifier configuration. + +- Install rustfmt as per the [readme](README.md). +- Open the atom beautifier settings + + Go to Edit->Preferences. Click the packages on the left side and click on setting for atom-beautifier + +- Set rustfmt as the beautifier + + Find the setting labeled *Language Config - Rust - Default Beautifier* and make sure it is set to rustfmt as shown below. You can also set the beautifier to auto format on save here. +![image](https://cloud.githubusercontent.com/assets/6623285/11147685/c8ade16c-8a3d-11e5-9da5-bd3d998d97f9.png) + +- Set the path to your rustfmt location + + Find the setting labeled *Rust - Rustfmt Path*. This setting is towards the bottom and you will need to scroll a bit. Set it to the path for your rustfmt executable. +![image](https://cloud.githubusercontent.com/assets/6623285/11147718/f4d10224-8a3d-11e5-9f69-9e900cbe0278.png) diff --git a/bootstrap.sh b/bootstrap.sh new file mode 100755 index 000000000000..05ac0ce2f306 --- /dev/null +++ b/bootstrap.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +# Make sure you double check the diffs after running this script - with great +# power comes great responsibility. +# We deliberately avoid reformatting files with rustfmt comment directives. + +cargo build --release + +target/release/rustfmt src/lib.rs +target/release/rustfmt src/bin/main.rs +target/release/rustfmt src/cargo-fmt/main.rs + +for filename in tests/target/*.rs; do + if ! grep -q "rustfmt-" "$filename"; then + target/release/rustfmt $filename + fi +done diff --git a/build.rs b/build.rs new file mode 100644 index 000000000000..e7b1e1b854c0 --- /dev/null +++ b/build.rs @@ -0,0 +1,55 @@ +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::{Path, PathBuf}; +use std::process::Command; + +fn main() { + // Only check .git/HEAD dirty status if it exists - doing so when + // building dependent crates may lead to false positives and rebuilds + if Path::new(".git/HEAD").exists() { + println!("cargo:rerun-if-changed=.git/HEAD"); + } + + println!("cargo:rerun-if-env-changed=CFG_RELEASE_CHANNEL"); + + let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap()); + + File::create(out_dir.join("commit-info.txt")) + .unwrap() + .write_all(commit_info().as_bytes()) + .unwrap(); +} + +// Try to get hash and date of the last commit on a best effort basis. If anything goes wrong +// (git not installed or if this is not a git repository) just return an empty string. +fn commit_info() -> String { + match (channel(), commit_hash(), commit_date()) { + (channel, Some(hash), Some(date)) => format!("{} ({} {})", channel, hash.trim_end(), date), + _ => String::new(), + } +} + +fn channel() -> String { + if let Ok(channel) = env::var("CFG_RELEASE_CHANNEL") { + channel + } else { + "nightly".to_owned() + } +} + +fn commit_hash() -> Option { + Command::new("git") + .args(&["rev-parse", "--short", "HEAD"]) + .output() + .ok() + .and_then(|r| String::from_utf8(r.stdout).ok()) +} + +fn commit_date() -> Option { + Command::new("git") + .args(&["log", "-1", "--date=short", "--pretty=format:%cd"]) + .output() + .ok() + .and_then(|r| String::from_utf8(r.stdout).ok()) +} diff --git a/ci/build_and_test.bat b/ci/build_and_test.bat new file mode 100755 index 000000000000..69dae1fff7b4 --- /dev/null +++ b/ci/build_and_test.bat @@ -0,0 +1,15 @@ +set "RUSTFLAGS=-D warnings" +set "RUSTFMT_CI=1" + +:: Print version information +rustc -Vv || exit /b 1 +cargo -V || exit /b 1 + +:: Build and test main crate +cargo build --locked || exit /b 1 +cargo test || exit /b 1 + +:: Build and test other crates +cd config_proc_macro || exit /b 1 +cargo build --locked || exit /b 1 +cargo test || exit /b 1 diff --git a/ci/build_and_test.sh b/ci/build_and_test.sh new file mode 100755 index 000000000000..94991853263c --- /dev/null +++ b/ci/build_and_test.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +set -euo pipefail + +export RUSTFLAGS="-D warnings" +export RUSTFMT_CI=1 + +# Print version information +rustc -Vv +cargo -V + +# Build and test main crate +cargo build --locked +cargo test + +# Build and test other crates +cd config_proc_macro +cargo build --locked +cargo test diff --git a/ci/check_diff.sh b/ci/check_diff.sh new file mode 100755 index 000000000000..062c2dd8673a --- /dev/null +++ b/ci/check_diff.sh @@ -0,0 +1,199 @@ +#!/bin/bash + +function print_usage() { + echo "usage check_diff REMOTE_REPO FEATURE_BRANCH [COMMIT_HASH] [OPTIONAL_RUSTFMT_CONFIGS]" +} + +if [ $# -le 1 ]; then + print_usage + exit 1 +fi + +REMOTE_REPO=$1 +FEATURE_BRANCH=$2 +OPTIONAL_COMMIT_HASH=$3 +OPTIONAL_RUSTFMT_CONFIGS=$4 + +# OUTPUT array used to collect all the status of running diffs on various repos +STATUSES=() + +# Clone a git repository and cd into it. +# +# Parameters: +# $1: git clone url +# $2: directory where the repo should be cloned +function clone_repo() { + GIT_TERMINAL_PROMPT=0 git clone --quiet $1 --depth 1 $2 && cd $2 +} + +# Initialize Git submoduels for the repo. +# +# Parameters +# $1: list of directories to initialize +function init_submodules() { + git submodule update --init $1 +} + +# Run rusfmt with the --check flag to see if a diff is produced. +# +# Parameters: +# $1: Path to a rustfmt binary +# $2: Output file path for the diff +# $3: Any additional configuration options to pass to rustfmt +# +# Globlas: +# $OPTIONAL_RUSTFMT_CONFIGS: Optional configs passed to the script from $4 +function create_diff() { + local config; + if [ -z "$3" ]; then + config="--config=error_on_line_overflow=false,error_on_unformatted=false" + else + config="--config=error_on_line_overflow=false,error_on_unformatted=false,$OPTIONAL_RUSTFMT_CONFIGS" + fi + + for i in `find . | grep "\.rs$"` + do + $1 --unstable-features --skip-children --check --color=always $config $i >> $2 2>/dev/null + done +} + +# Run the master rustfmt binary and the feature branch binary in the current directory and compare the diffs +# +# Parameters +# $1: Name of the repository (used for logging) +# +# Globlas: +# $RUSFMT_BIN: Path to the rustfmt master binary. Created when running `compile_rustfmt` +# $FEATURE_BIN: Path to the rustfmt feature binary. Created when running `compile_rustfmt` +# $OPTIONAL_RUSTFMT_CONFIGS: Optional configs passed to the script from $4 +function check_diff() { + echo "running rustfmt (master) on $1" + create_diff $RUSFMT_BIN rustfmt_diff.txt + + echo "running rustfmt (feature) on $1" + create_diff $FEATURE_BIN feature_diff.txt $OPTIONAL_RUSTFMT_CONFIGS + + echo "checking diff" + local diff; + # we don't add color to the diff since we added color when running rustfmt --check. + # tail -n + 6 removes the git diff header info + # cut -c 2- removes the leading diff characters("+","-"," ") from running git diff. + # Again, the diff output we care about was already added when we ran rustfmt --check + diff=$( + git --no-pager diff --color=never \ + --unified=0 --no-index rustfmt_diff.txt feature_diff.txt 2>&1 | tail -n +6 | cut -c 2- + ) + + if [ -z "$diff" ]; then + echo "no diff detected between rustfmt and the feture branch" + return 0 + else + echo "$diff" + return 1 + fi +} + +# Compiles and produces two rustfmt binaries. +# One for the current master, and another for the feature branch +# +# Parameters: +# $1: Directory where rustfmt will be cloned +# +# Globlas: +# $REMOTE_REPO: Clone URL to the rustfmt fork that we want to test +# $FEATURE_BRANCH: Name of the feature branch +# $OPTIONAL_COMMIT_HASH: Optional commit hash that will be checked out if provided +function compile_rustfmt() { + RUSTFMT_REPO="https://github.com/rust-lang/rustfmt.git" + clone_repo $RUSTFMT_REPO $1 + git remote add feature $REMOTE_REPO + git fetch feature $FEATURE_BRANCH + + cargo build --release --bin rustfmt && cp target/release/rustfmt $1/rustfmt + if [ -z "$OPTIONAL_COMMIT_HASH" ]; then + git switch $FEATURE_BRANCH + else + git switch $OPTIONAL_COMMIT_HASH --detach + fi + cargo build --release --bin rustfmt && cp target/release/rustfmt $1/feature_rustfmt + RUSFMT_BIN=$1/rustfmt + FEATURE_BIN=$1/feature_rustfmt +} + +# Check the diff for running rustfmt and the feature branch on all the .rs files in the repo. +# +# Parameters +# $1: Clone URL for the repo +# $2: Name of the repo (mostly used for logging) +# $3: Path to any submodules that should be initialized +function check_repo() { + WORKDIR=$(pwd) + REPO_URL=$1 + REPO_NAME=$2 + SUBMODULES=$3 + + local tmp_dir; + tmp_dir=$(mktemp -d -t $REPO_NAME-XXXXXXXX) + clone_repo $REPO_URL $tmp_dir + + if [ ! -z "$SUBMODULES" ]; then + init_submodules $SUBMODULES + fi + + check_diff $REPO_NAME + # append the status of running `check_diff` to the STATUSES array + STATUSES+=($?) + + echo "removing tmp_dir $tmp_dir" + rm -rf $tmp_dir + cd $WORKDIR +} + +function main() { + tmp_dir=$(mktemp -d -t rustfmt-XXXXXXXX) + echo Created tmp_dir $tmp_dir + + compile_rustfmt $tmp_dir + + # run checks + check_repo "https://github.com/rust-lang/rust.git" rust-lang-rust + check_repo "https://github.com/rust-lang/cargo.git" cargo + check_repo "https://github.com/rust-lang/miri.git" miri + check_repo "https://github.com/rust-lang/rust-analyzer.git" rust-analyzer + check_repo "https://github.com/bitflags/bitflags.git" bitflags + check_repo "https://github.com/rust-lang/log.git" log + check_repo "https://github.com/rust-lang/mdBook.git" mdBook + check_repo "https://github.com/rust-lang/packed_simd.git" packed_simd + check_repo "https://github.com/rust-lang/rust-semverver.git" check_repo + check_repo "https://github.com/Stebalien/tempfile.git" tempfile + check_repo "https://github.com/rust-lang/futures-rs.git" futures-rs + check_repo "https://github.com/dtolnay/anyhow.git" anyhow + check_repo "https://github.com/dtolnay/thiserror.git" thiserror + check_repo "https://github.com/dtolnay/syn.git" syn + check_repo "https://github.com/serde-rs/serde.git" serde + check_repo "https://github.com/rust-lang/rustlings.git" rustlings + check_repo "https://github.com/rust-lang/rustup.git" rustup + check_repo "https://github.com/SergioBenitez/Rocket.git" Rocket + check_repo "https://github.com/rustls/rustls.git" rustls + check_repo "https://github.com/rust-lang/rust-bindgen.git" rust-bindgen + check_repo "https://github.com/hyperium/hyper.git" hyper + check_repo "https://github.com/actix/actix.git" actix + check_repo "https://github.com/denoland/deno.git" denoland_deno + + # cleanup temp dir + echo removing tmp_dir $tmp_dir + rm -rf $tmp_dir + + # figure out the exit code + for status in ${STATUSES[@]} + do + if [ $status -eq 1 ]; then + echo "formatting diff found 💔" + return 1 + fi + done + + echo "no diff found 😊" +} + +main diff --git a/ci/integration.sh b/ci/integration.sh new file mode 100755 index 000000000000..19d502bc5c7b --- /dev/null +++ b/ci/integration.sh @@ -0,0 +1,121 @@ +#!/usr/bin/env bash + +set -ex + +: ${INTEGRATION?"The INTEGRATION environment variable must be set."} + +# FIXME: this means we can get a stale cargo-fmt from a previous run. +# +# `which rustfmt` fails if rustfmt is not found. Since we don't install +# `rustfmt` via `rustup`, this is the case unless we manually install it. Once +# that happens, `cargo install --force` will be called, which installs +# `rustfmt`, `cargo-fmt`, etc to `~/.cargo/bin`. This directory is cached by +# travis (see `.travis.yml`'s "cache" key), such that build-bots that arrive +# here after the first installation will find `rustfmt` and won't need to build +# it again. +# +#which cargo-fmt || cargo install --force +CFG_RELEASE=nightly CFG_RELEASE_CHANNEL=nightly cargo install --path . --force --locked + +echo "Integration tests for: ${INTEGRATION}" +cargo fmt -- --version + +# Checks that: +# +# * `cargo fmt --all` succeeds without any warnings or errors +# * `cargo fmt --all -- --check` after formatting returns success +# * `cargo test --all` still passes (formatting did not break the build) +function check_fmt_with_all_tests { + check_fmt_base "--all" + return $? +} + +# Checks that: +# +# * `cargo fmt --all` succeeds without any warnings or errors +# * `cargo fmt --all -- --check` after formatting returns success +# * `cargo test --lib` still passes (formatting did not break the build) +function check_fmt_with_lib_tests { + check_fmt_base "--lib" + return $? +} + +function check_fmt_base { + local test_args="$1" + local build=$(cargo test $test_args 2>&1) + if [[ "$build" =~ "build failed" ]] || [[ "$build" =~ "test result: FAILED." ]]; then + return 0 + fi + touch rustfmt.toml + cargo fmt --all -v |& tee rustfmt_output + if [[ ${PIPESTATUS[0]} != 0 ]]; then + cat rustfmt_output + return 1 + fi + cat rustfmt_output + ! cat rustfmt_output | grep -q "internal error" + if [[ $? != 0 ]]; then + return 1 + fi + ! cat rustfmt_output | grep -q "warning" + if [[ $? != 0 ]]; then + return 1 + fi + ! cat rustfmt_output | grep -q "Warning" + if [[ $? != 0 ]]; then + return 1 + fi + cargo fmt --all -- --check |& tee rustfmt_check_output + if [[ ${PIPESTATUS[0]} != 0 ]]; then + cat rustfmt_check_output + return 1 + fi + cargo test $test_args + if [[ $? != 0 ]]; then + return $? + fi +} + +function show_head { + local head=$(git rev-parse HEAD) + echo "Head commit of ${INTEGRATION}: $head" +} + +case ${INTEGRATION} in + cargo) + git clone --depth=1 https://github.com/rust-lang/${INTEGRATION}.git + cd ${INTEGRATION} + show_head + export CFG_DISABLE_CROSS_TESTS=1 + check_fmt_with_all_tests + cd - + ;; + crater) + git clone --depth=1 https://github.com/rust-lang/${INTEGRATION}.git + cd ${INTEGRATION} + show_head + check_fmt_with_lib_tests + cd - + ;; + bitflags) + git clone --depth=1 https://github.com/bitflags/${INTEGRATION}.git + cd ${INTEGRATION} + show_head + check_fmt_with_all_tests + cd - + ;; + error-chain | tempdir) + git clone --depth=1 https://github.com/rust-lang-deprecated/${INTEGRATION}.git + cd ${INTEGRATION} + show_head + check_fmt_with_all_tests + cd - + ;; + *) + git clone --depth=1 https://github.com/rust-lang/${INTEGRATION}.git + cd ${INTEGRATION} + show_head + check_fmt_with_all_tests + cd - + ;; +esac diff --git a/config_proc_macro/.gitignore b/config_proc_macro/.gitignore new file mode 100644 index 000000000000..9f970225adb6 --- /dev/null +++ b/config_proc_macro/.gitignore @@ -0,0 +1 @@ +target/ \ No newline at end of file diff --git a/config_proc_macro/Cargo.lock b/config_proc_macro/Cargo.lock new file mode 100644 index 000000000000..6267958646bf --- /dev/null +++ b/config_proc_macro/Cargo.lock @@ -0,0 +1,68 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "proc-macro2" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustfmt-config_proc_macro" +version = "0.3.0" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "syn", +] + +[[package]] +name = "serde" +version = "1.0.160" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.160" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "2.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcf316d5356ed6847742d036f8a39c3b8435cac10bd528a4bd461928a6ab34d5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" diff --git a/config_proc_macro/Cargo.toml b/config_proc_macro/Cargo.toml new file mode 100644 index 000000000000..34e8c237f556 --- /dev/null +++ b/config_proc_macro/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "rustfmt-config_proc_macro" +version = "0.3.0" +edition = "2018" +description = "A collection of procedural macros for rustfmt" +license = "Apache-2.0/MIT" +categories = ["development-tools::procedural-macro-helpers"] +repository = "https://github.com/rust-lang/rustfmt" + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = "1.0" +quote = "1.0" +syn = { version = "2.0", features = ["full", "visit"] } + +[dev-dependencies] +serde = { version = "1.0.160", features = ["derive"] } + +[features] +default = [] +debug-with-rustfmt = [] diff --git a/config_proc_macro/src/attrs.rs b/config_proc_macro/src/attrs.rs new file mode 100644 index 000000000000..d8de9aae088d --- /dev/null +++ b/config_proc_macro/src/attrs.rs @@ -0,0 +1,76 @@ +//! This module provides utilities for handling attributes on variants +//! of `config_type` enum. Currently there are the following attributes +//! that could appear on the variants of `config_type` enum: +//! +//! - `doc_hint`: name-value pair whose value is string literal +//! - `value`: name-value pair whose value is string literal +//! - `unstable_variant`: name only + +/// Returns the value of the first `doc_hint` attribute in the given slice or +/// `None` if `doc_hint` attribute is not available. +pub fn find_doc_hint(attrs: &[syn::Attribute]) -> Option { + attrs.iter().filter_map(doc_hint).next() +} + +/// Returns `true` if the given attribute is a `doc_hint` attribute. +pub fn is_doc_hint(attr: &syn::Attribute) -> bool { + is_attr_name_value(attr, "doc_hint") +} + +/// Returns a string literal value if the given attribute is `doc_hint` +/// attribute or `None` otherwise. +pub fn doc_hint(attr: &syn::Attribute) -> Option { + get_name_value_str_lit(attr, "doc_hint") +} + +/// Returns the value of the first `value` attribute in the given slice or +/// `None` if `value` attribute is not available. +pub fn find_config_value(attrs: &[syn::Attribute]) -> Option { + attrs.iter().filter_map(config_value).next() +} + +/// Returns `true` if the there is at least one `unstable` attribute in the given slice. +pub fn any_unstable_variant(attrs: &[syn::Attribute]) -> bool { + attrs.iter().any(is_unstable_variant) +} + +/// Returns a string literal value if the given attribute is `value` +/// attribute or `None` otherwise. +pub fn config_value(attr: &syn::Attribute) -> Option { + get_name_value_str_lit(attr, "value") +} + +/// Returns `true` if the given attribute is a `value` attribute. +pub fn is_config_value(attr: &syn::Attribute) -> bool { + is_attr_name_value(attr, "value") +} + +/// Returns `true` if the given attribute is an `unstable` attribute. +pub fn is_unstable_variant(attr: &syn::Attribute) -> bool { + is_attr_path(attr, "unstable_variant") +} + +fn is_attr_name_value(attr: &syn::Attribute, name: &str) -> bool { + match &attr.meta { + syn::Meta::NameValue(syn::MetaNameValue { path, .. }) if path.is_ident(name) => true, + _ => false, + } +} + +fn is_attr_path(attr: &syn::Attribute, name: &str) -> bool { + match &attr.meta { + syn::Meta::Path(path) if path.is_ident(name) => true, + _ => false, + } +} + +fn get_name_value_str_lit(attr: &syn::Attribute, name: &str) -> Option { + match &attr.meta { + syn::Meta::NameValue(syn::MetaNameValue { + path, + value: syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(lit_str), .. }), + .. + }) if path.is_ident(name) => Some(lit_str.value()), + _ => None, + } +} diff --git a/config_proc_macro/src/config_type.rs b/config_proc_macro/src/config_type.rs new file mode 100644 index 000000000000..93a78b8463ec --- /dev/null +++ b/config_proc_macro/src/config_type.rs @@ -0,0 +1,15 @@ +use proc_macro2::TokenStream; + +use crate::item_enum::define_config_type_on_enum; +use crate::item_struct::define_config_type_on_struct; + +/// Defines `config_type` on enum or struct. +// FIXME: Implement this on struct. +pub fn define_config_type(input: &syn::Item) -> TokenStream { + match input { + syn::Item::Struct(st) => define_config_type_on_struct(st), + syn::Item::Enum(en) => define_config_type_on_enum(en), + _ => panic!("Expected enum or struct"), + } + .unwrap() +} diff --git a/config_proc_macro/src/item_enum.rs b/config_proc_macro/src/item_enum.rs new file mode 100644 index 000000000000..731a7ea06077 --- /dev/null +++ b/config_proc_macro/src/item_enum.rs @@ -0,0 +1,242 @@ +use proc_macro2::TokenStream; +use quote::{quote, quote_spanned}; +use syn::spanned::Spanned; + +use crate::attrs::*; +use crate::utils::*; + +type Variants = syn::punctuated::Punctuated; + +/// Defines and implements `config_type` enum. +pub fn define_config_type_on_enum(em: &syn::ItemEnum) -> syn::Result { + let syn::ItemEnum { + vis, + enum_token, + ident, + generics, + variants, + .. + } = em; + + let mod_name_str = format!("__define_config_type_on_enum_{}", ident); + let mod_name = syn::Ident::new(&mod_name_str, ident.span()); + let variants = fold_quote(variants.iter().map(process_variant), |meta| quote!(#meta,)); + + let impl_doc_hint = impl_doc_hint(&em.ident, &em.variants); + let impl_from_str = impl_from_str(&em.ident, &em.variants); + let impl_display = impl_display(&em.ident, &em.variants); + let impl_serde = impl_serde(&em.ident, &em.variants); + let impl_deserialize = impl_deserialize(&em.ident, &em.variants); + + Ok(quote! { + #[allow(non_snake_case)] + mod #mod_name { + #[derive(Debug, Copy, Clone, Eq, PartialEq)] + pub #enum_token #ident #generics { #variants } + #impl_display + #impl_doc_hint + #impl_from_str + #impl_serde + #impl_deserialize + } + #vis use #mod_name::#ident; + }) +} + +/// Remove attributes specific to `config_proc_macro` from enum variant fields. +fn process_variant(variant: &syn::Variant) -> TokenStream { + let metas = variant + .attrs + .iter() + .filter(|attr| !is_doc_hint(attr) && !is_config_value(attr) && !is_unstable_variant(attr)); + let attrs = fold_quote(metas, |meta| quote!(#meta)); + let syn::Variant { ident, fields, .. } = variant; + quote!(#attrs #ident #fields) +} + +/// Return the correct syntax to pattern match on the enum variant, discarding all +/// internal field data. +fn fields_in_variant(variant: &syn::Variant) -> TokenStream { + // With thanks to https://stackoverflow.com/a/65182902 + match &variant.fields { + syn::Fields::Unnamed(_) => quote_spanned! { variant.span() => (..) }, + syn::Fields::Unit => quote_spanned! { variant.span() => }, + syn::Fields::Named(_) => quote_spanned! { variant.span() => {..} }, + } +} + +fn impl_doc_hint(ident: &syn::Ident, variants: &Variants) -> TokenStream { + let doc_hint = variants + .iter() + .map(doc_hint_of_variant) + .collect::>() + .join("|"); + let doc_hint = format!("[{}]", doc_hint); + + let variant_stables = variants + .iter() + .map(|v| (&v.ident, fields_in_variant(&v), !unstable_of_variant(v))); + let match_patterns = fold_quote(variant_stables, |(v, fields, stable)| { + quote! { + #ident::#v #fields => #stable, + } + }); + quote! { + use crate::config::ConfigType; + impl ConfigType for #ident { + fn doc_hint() -> String { + #doc_hint.to_owned() + } + fn stable_variant(&self) -> bool { + match self { + #match_patterns + } + } + } + } +} + +fn impl_display(ident: &syn::Ident, variants: &Variants) -> TokenStream { + let vs = variants + .iter() + .filter(|v| is_unit(v)) + .map(|v| (config_value_of_variant(v), &v.ident)); + let match_patterns = fold_quote(vs, |(s, v)| { + quote! { + #ident::#v => write!(f, "{}", #s), + } + }); + quote! { + use std::fmt; + impl fmt::Display for #ident { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + #match_patterns + _ => unimplemented!(), + } + } + } + } +} + +fn impl_from_str(ident: &syn::Ident, variants: &Variants) -> TokenStream { + let vs = variants + .iter() + .filter(|v| is_unit(v)) + .map(|v| (config_value_of_variant(v), &v.ident)); + let if_patterns = fold_quote(vs, |(s, v)| { + quote! { + if #s.eq_ignore_ascii_case(s) { + return Ok(#ident::#v); + } + } + }); + let mut err_msg = String::from("Bad variant, expected one of:"); + for v in variants.iter().filter(|v| is_unit(v)) { + err_msg.push_str(&format!(" `{}`", v.ident)); + } + + quote! { + impl ::std::str::FromStr for #ident { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + #if_patterns + return Err(#err_msg); + } + } + } +} + +fn doc_hint_of_variant(variant: &syn::Variant) -> String { + let mut text = find_doc_hint(&variant.attrs).unwrap_or(variant.ident.to_string()); + if unstable_of_variant(&variant) { + text.push_str(" (unstable)") + }; + text +} + +fn config_value_of_variant(variant: &syn::Variant) -> String { + find_config_value(&variant.attrs).unwrap_or(variant.ident.to_string()) +} + +fn unstable_of_variant(variant: &syn::Variant) -> bool { + any_unstable_variant(&variant.attrs) +} + +fn impl_serde(ident: &syn::Ident, variants: &Variants) -> TokenStream { + let arms = fold_quote(variants.iter(), |v| { + let v_ident = &v.ident; + let pattern = match v.fields { + syn::Fields::Named(..) => quote!(#ident::v_ident{..}), + syn::Fields::Unnamed(..) => quote!(#ident::#v_ident(..)), + syn::Fields::Unit => quote!(#ident::#v_ident), + }; + let option_value = config_value_of_variant(v); + quote! { + #pattern => serializer.serialize_str(&#option_value), + } + }); + + quote! { + impl ::serde::ser::Serialize for #ident { + fn serialize(&self, serializer: S) -> Result + where + S: ::serde::ser::Serializer, + { + use serde::ser::Error; + match self { + #arms + _ => Err(S::Error::custom(format!("Cannot serialize {:?}", self))), + } + } + } + } +} + +// Currently only unit variants are supported. +fn impl_deserialize(ident: &syn::Ident, variants: &Variants) -> TokenStream { + let supported_vs = variants.iter().filter(|v| is_unit(v)); + let if_patterns = fold_quote(supported_vs, |v| { + let config_value = config_value_of_variant(v); + let variant_ident = &v.ident; + quote! { + if #config_value.eq_ignore_ascii_case(s) { + return Ok(#ident::#variant_ident); + } + } + }); + + let supported_vs = variants.iter().filter(|v| is_unit(v)); + let allowed = fold_quote(supported_vs.map(config_value_of_variant), |s| quote!(#s,)); + + quote! { + impl<'de> serde::de::Deserialize<'de> for #ident { + fn deserialize(d: D) -> Result + where + D: serde::Deserializer<'de>, + { + use serde::de::{Error, Visitor}; + use std::marker::PhantomData; + use std::fmt; + struct StringOnly(PhantomData); + impl<'de, T> Visitor<'de> for StringOnly + where T: serde::Deserializer<'de> { + type Value = String; + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("string") + } + fn visit_str(self, value: &str) -> Result { + Ok(String::from(value)) + } + } + let s = &d.deserialize_string(StringOnly::(PhantomData))?; + + #if_patterns + + static ALLOWED: &'static[&str] = &[#allowed]; + Err(D::Error::unknown_variant(&s, ALLOWED)) + } + } + } +} diff --git a/config_proc_macro/src/item_struct.rs b/config_proc_macro/src/item_struct.rs new file mode 100644 index 000000000000..f03ff7e30d82 --- /dev/null +++ b/config_proc_macro/src/item_struct.rs @@ -0,0 +1,5 @@ +use proc_macro2::TokenStream; + +pub fn define_config_type_on_struct(_st: &syn::ItemStruct) -> syn::Result { + unimplemented!() +} diff --git a/config_proc_macro/src/lib.rs b/config_proc_macro/src/lib.rs new file mode 100644 index 000000000000..0c54c132c97d --- /dev/null +++ b/config_proc_macro/src/lib.rs @@ -0,0 +1,84 @@ +//! This crate provides a derive macro for `ConfigType`. + +#![recursion_limit = "256"] + +mod attrs; +mod config_type; +mod item_enum; +mod item_struct; +mod utils; + +use std::str::FromStr; + +use proc_macro::TokenStream; +use syn::parse_macro_input; + +#[proc_macro_attribute] +pub fn config_type(_args: TokenStream, input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as syn::Item); + let output = config_type::define_config_type(&input); + + #[cfg(feature = "debug-with-rustfmt")] + { + utils::debug_with_rustfmt(&output); + } + + TokenStream::from(output) +} + +/// Used to conditionally output the TokenStream for tests that need to be run on nightly only. +/// +/// ```rust +/// # use rustfmt_config_proc_macro::nightly_only_test; +/// +/// #[nightly_only_test] +/// #[test] +/// fn test_needs_nightly_rustfmt() { +/// assert!(true); +/// } +/// ``` +#[proc_macro_attribute] +pub fn nightly_only_test(_args: TokenStream, input: TokenStream) -> TokenStream { + // if CFG_RELEASE_CHANNEL is not set we default to nightly, hence why the default is true + if option_env!("CFG_RELEASE_CHANNEL").map_or(true, |c| c == "nightly" || c == "dev") { + input + } else { + // output an empty token stream if CFG_RELEASE_CHANNEL is not set to "nightly" or "dev" + TokenStream::from_str("").unwrap() + } +} + +/// Used to conditionally output the TokenStream for tests that need to be run on stable only. +/// +/// ```rust +/// # use rustfmt_config_proc_macro::stable_only_test; +/// +/// #[stable_only_test] +/// #[test] +/// fn test_needs_stable_rustfmt() { +/// assert!(true); +/// } +/// ``` +#[proc_macro_attribute] +pub fn stable_only_test(_args: TokenStream, input: TokenStream) -> TokenStream { + // if CFG_RELEASE_CHANNEL is not set we default to nightly, hence why the default is false + if option_env!("CFG_RELEASE_CHANNEL").map_or(false, |c| c == "stable") { + input + } else { + // output an empty token stream if CFG_RELEASE_CHANNEL is not set or is not 'stable' + TokenStream::from_str("").unwrap() + } +} + +/// Used to conditionally output the TokenStream for tests that should be run as part of rustfmts +/// test suite, but should be ignored when running in the rust-lang/rust test suite. +#[proc_macro_attribute] +pub fn rustfmt_only_ci_test(_args: TokenStream, input: TokenStream) -> TokenStream { + if option_env!("RUSTFMT_CI").is_some() { + input + } else { + let mut token_stream = TokenStream::from_str("#[ignore]").unwrap(); + token_stream.extend(input); + token_stream + } +} diff --git a/config_proc_macro/src/utils.rs b/config_proc_macro/src/utils.rs new file mode 100644 index 000000000000..f5cba87b07b6 --- /dev/null +++ b/config_proc_macro/src/utils.rs @@ -0,0 +1,52 @@ +use proc_macro2::TokenStream; +use quote::{quote, ToTokens}; + +pub fn fold_quote(input: impl Iterator, f: F) -> TokenStream +where + F: Fn(I) -> T, + T: ToTokens, +{ + input.fold(quote! {}, |acc, x| { + let y = f(x); + quote! { #acc #y } + }) +} + +pub fn is_unit(v: &syn::Variant) -> bool { + match v.fields { + syn::Fields::Unit => true, + _ => false, + } +} + +#[cfg(feature = "debug-with-rustfmt")] +/// Pretty-print the output of proc macro using rustfmt. +pub fn debug_with_rustfmt(input: &TokenStream) { + 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 { + Some(rustfmt) => rustfmt, + None => OsStr::new("rustfmt"), + }; + let mut child = Command::new(rustfmt) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn() + .expect("Failed to spawn rustfmt in stdio mode"); + { + let stdin = child.stdin.as_mut().expect("Failed to get stdin"); + stdin + .write_all(format!("{}", input).as_bytes()) + .expect("Failed to write to stdin"); + } + let rustfmt_output = child.wait_with_output().expect("rustfmt has failed"); + + eprintln!( + "{}", + String::from_utf8(rustfmt_output.stdout).expect("rustfmt returned non-UTF8 string") + ); +} diff --git a/config_proc_macro/tests/smoke.rs b/config_proc_macro/tests/smoke.rs new file mode 100644 index 000000000000..c8a83e39c9ef --- /dev/null +++ b/config_proc_macro/tests/smoke.rs @@ -0,0 +1,21 @@ +pub mod config { + pub trait ConfigType: Sized { + fn doc_hint() -> String; + fn stable_variant(&self) -> bool; + } +} + +#[allow(dead_code)] +#[allow(unused_imports)] +mod tests { + use rustfmt_config_proc_macro::config_type; + + #[config_type] + enum Bar { + Foo, + Bar, + #[doc_hint = "foo_bar"] + FooBar, + FooFoo(i32), + } +} diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 000000000000..4fa932d4c762 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,311 @@ + + + + + Rustfmt + + + + + + + + + + +
+
+
+
+
+ +
+ + +
+
+
+
+ + +
+
+ + +
+
+
+
+
+
+
+ + + diff --git a/intellij.md b/intellij.md new file mode 100644 index 000000000000..6a711c0171ae --- /dev/null +++ b/intellij.md @@ -0,0 +1,35 @@ +# Running Rustfmt from IntelliJ or CLion + +## Installation + +- Install [CLion](https://www.jetbrains.com/clion/), [IntelliJ Ultimate or CE](https://www.jetbrains.com/idea/) through the direct download link or using the [JetBrains Toolbox](https://www.jetbrains.com/toolbox/). + CLion and IntelliJ Ultimate [provide a built-in debugger interface](https://github.com/intellij-rust/intellij-rust#compatible-ides) but they are not free like IntelliJ CE. + +- Install the [Rust Plugin](https://intellij-rust.github.io/) by navigating to File → Settings → Plugins and searching the plugin in the Marketplace + ![plugins](https://user-images.githubusercontent.com/6505554/83944518-6f1e5c00-a81d-11ea-9c35-e16948811ba8.png) + +- Press "Install" on the Rust plugin + ![install rust](https://user-images.githubusercontent.com/6505554/83944533-82c9c280-a81d-11ea-86b3-ee2e31bc7d12.png) + +- Restart CLion/IntelliJ + +## Configuration + +### Run Rustfmt on save + +- Open Rustfmt settings (File → Settings → Languages & Frameworks → Rust → Rustfmt) and enable "Run rustfmt on Save" + ![run_rustfmt_on_save](https://user-images.githubusercontent.com/6505554/83944610-3468f380-a81e-11ea-9c34-0cbd18dd4969.png) + +- IntellJ uses autosave, so now your files will always be formatted according to rustfmt. Alternatively you can use Ctrl+S to reformat file manually + +### Bind shortcut to "Reformat File with Rustfmt" action + +- Open the settings window (File → Settings) and search for "reformat" + ![keymap](https://user-images.githubusercontent.com/1133787/47240922-2ae10c80-d3ea-11e8-9d8f-c798d9749240.png) +- Right-click on "Reformat File with Rustfmt" and assign a keyboard shortcut + + ![shortcut_window](https://user-images.githubusercontent.com/1133787/47240981-5b28ab00-d3ea-11e8-882e-8b864164db74.png) +- Press "OK" + ![shortcut_after](https://user-images.githubusercontent.com/1133787/47241000-6976c700-d3ea-11e8-9342-50ebc2f9f97b.png) + +- Done. You can now use rustfmt in an opened *.rs file with your previously specified shortcut diff --git a/rust-toolchain b/rust-toolchain new file mode 100644 index 000000000000..33ff8b03da2a --- /dev/null +++ b/rust-toolchain @@ -0,0 +1,3 @@ +[toolchain] +channel = "nightly-2023-07-01" +components = ["llvm-tools", "rustc-dev"] diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 000000000000..eccd5f9bd19e --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,3 @@ +error_on_line_overflow = true +error_on_unformatted = true +version = "Two" diff --git a/src/attr.rs b/src/attr.rs new file mode 100644 index 000000000000..22e45082a9f7 --- /dev/null +++ b/src/attr.rs @@ -0,0 +1,531 @@ +//! Format attributes and meta items. + +use rustc_ast::ast; +use rustc_ast::HasAttrs; +use rustc_span::{symbol::sym, Span}; + +use self::doc_comment::DocCommentFormatter; +use crate::comment::{contains_comment, rewrite_doc_comment, CommentStyle}; +use crate::config::lists::*; +use crate::config::IndentStyle; +use crate::expr::rewrite_literal; +use crate::lists::{definitive_tactic, itemize_list, write_list, ListFormatting, Separator}; +use crate::overflow; +use crate::rewrite::{Rewrite, RewriteContext}; +use crate::shape::Shape; +use crate::source_map::SpanUtils; +use crate::types::{rewrite_path, PathContext}; +use crate::utils::{count_newlines, mk_sp}; + +mod doc_comment; + +/// Returns attributes on the given statement. +pub(crate) fn get_attrs_from_stmt(stmt: &ast::Stmt) -> &[ast::Attribute] { + stmt.attrs() +} + +pub(crate) fn get_span_without_attrs(stmt: &ast::Stmt) -> Span { + match stmt.kind { + ast::StmtKind::Local(ref local) => local.span, + ast::StmtKind::Item(ref item) => item.span, + ast::StmtKind::Expr(ref expr) | ast::StmtKind::Semi(ref expr) => expr.span, + ast::StmtKind::MacCall(ref mac_stmt) => mac_stmt.mac.span(), + ast::StmtKind::Empty => stmt.span, + } +} + +/// Returns attributes that are within `outer_span`. +pub(crate) fn filter_inline_attrs(attrs: &[ast::Attribute], outer_span: Span) -> ast::AttrVec { + attrs + .iter() + .filter(|a| outer_span.lo() <= a.span.lo() && a.span.hi() <= outer_span.hi()) + .cloned() + .collect() +} + +fn is_derive(attr: &ast::Attribute) -> bool { + attr.has_name(sym::derive) +} + +// The shape of the arguments to a function-like attribute. +fn argument_shape( + left: usize, + right: usize, + combine: bool, + shape: Shape, + context: &RewriteContext<'_>, +) -> Option { + match context.config.indent_style() { + IndentStyle::Block => { + if combine { + shape.offset_left(left) + } else { + Some( + shape + .block_indent(context.config.tab_spaces()) + .with_max_width(context.config), + ) + } + } + IndentStyle::Visual => shape + .visual_indent(0) + .shrink_left(left) + .and_then(|s| s.sub_width(right)), + } +} + +fn format_derive( + derives: &[ast::Attribute], + shape: Shape, + context: &RewriteContext<'_>, +) -> Option { + // Collect all items from all attributes + let all_items = derives + .iter() + .map(|attr| { + // Parse the derive items and extract the span for each item; if any + // attribute is not parseable, none of the attributes will be + // reformatted. + let item_spans = attr.meta_item_list().map(|meta_item_list| { + meta_item_list + .into_iter() + .map(|nested_meta_item| nested_meta_item.span()) + })?; + + let items = itemize_list( + context.snippet_provider, + item_spans, + ")", + ",", + |span| span.lo(), + |span| span.hi(), + |span| Some(context.snippet(*span).to_owned()), + // We update derive attribute spans to start after the opening '(' + // This helps us focus parsing to just what's inside #[derive(...)] + context.snippet_provider.span_after(attr.span, "("), + attr.span.hi(), + false, + ); + + Some(items) + }) + // Fail if any attribute failed. + .collect::>>()? + // Collect the results into a single, flat, Vec. + .into_iter() + .flatten() + .collect::>(); + + // Collect formatting parameters. + let prefix = attr_prefix(&derives[0]); + let argument_shape = argument_shape( + "[derive()]".len() + prefix.len(), + ")]".len(), + false, + shape, + context, + )?; + let one_line_shape = shape + .offset_left("[derive()]".len() + prefix.len())? + .sub_width("()]".len())?; + let one_line_budget = one_line_shape.width; + + let tactic = definitive_tactic( + &all_items, + ListTactic::HorizontalVertical, + Separator::Comma, + argument_shape.width, + ); + let trailing_separator = match context.config.indent_style() { + // We always add the trailing comma and remove it if it is not needed. + IndentStyle::Block => SeparatorTactic::Always, + IndentStyle::Visual => SeparatorTactic::Never, + }; + + // Format the collection of items. + let fmt = ListFormatting::new(argument_shape, context.config) + .tactic(tactic) + .trailing_separator(trailing_separator) + .ends_with_newline(false); + let item_str = write_list(&all_items, &fmt)?; + + debug!("item_str: '{}'", item_str); + + // Determine if the result will be nested, i.e. if we're using the block + // indent style and either the items are on multiple lines or we've exceeded + // our budget to fit on a single line. + let nested = context.config.indent_style() == IndentStyle::Block + && (item_str.contains('\n') || item_str.len() > one_line_budget); + + // Format the final result. + let mut result = String::with_capacity(128); + result.push_str(prefix); + result.push_str("[derive("); + if nested { + let nested_indent = argument_shape.indent.to_string_with_newline(context.config); + result.push_str(&nested_indent); + result.push_str(&item_str); + result.push_str(&shape.indent.to_string_with_newline(context.config)); + } else if let SeparatorTactic::Always = context.config.trailing_comma() { + // Retain the trailing comma. + result.push_str(&item_str); + } else if item_str.ends_with(',') { + // Remove the trailing comma. + result.push_str(&item_str[..item_str.len() - 1]); + } else { + result.push_str(&item_str); + } + result.push_str(")]"); + + Some(result) +} + +/// Returns the first group of attributes that fills the given predicate. +/// We consider two doc comments are in different group if they are separated by normal comments. +fn take_while_with_pred<'a, P>( + context: &RewriteContext<'_>, + attrs: &'a [ast::Attribute], + pred: P, +) -> &'a [ast::Attribute] +where + P: Fn(&ast::Attribute) -> bool, +{ + let mut len = 0; + let mut iter = attrs.iter().peekable(); + + while let Some(attr) = iter.next() { + if pred(attr) { + len += 1; + } else { + break; + } + if let Some(next_attr) = iter.peek() { + // Extract comments between two attributes. + let span_between_attr = mk_sp(attr.span.hi(), next_attr.span.lo()); + let snippet = context.snippet(span_between_attr); + if count_newlines(snippet) >= 2 || snippet.contains('/') { + break; + } + } + } + + &attrs[..len] +} + +/// Rewrite the any doc comments which come before any other attributes. +fn rewrite_initial_doc_comments( + context: &RewriteContext<'_>, + attrs: &[ast::Attribute], + shape: Shape, +) -> Option<(usize, Option)> { + if attrs.is_empty() { + return Some((0, None)); + } + // Rewrite doc comments + let sugared_docs = take_while_with_pred(context, attrs, |a| a.is_doc_comment()); + if !sugared_docs.is_empty() { + let snippet = sugared_docs + .iter() + .map(|a| context.snippet(a.span)) + .collect::>() + .join("\n"); + return Some(( + sugared_docs.len(), + Some(rewrite_doc_comment( + &snippet, + shape.comment(context.config), + context.config, + )?), + )); + } + + Some((0, None)) +} + +impl Rewrite for ast::NestedMetaItem { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + match self { + ast::NestedMetaItem::MetaItem(ref meta_item) => meta_item.rewrite(context, shape), + ast::NestedMetaItem::Lit(ref l) => { + rewrite_literal(context, l.as_token_lit(), l.span, shape) + } + } + } +} + +fn has_newlines_before_after_comment(comment: &str) -> (&str, &str) { + // Look at before and after comment and see if there are any empty lines. + let comment_begin = comment.find('/'); + let len = comment_begin.unwrap_or_else(|| comment.len()); + let mlb = count_newlines(&comment[..len]) > 1; + let mla = if comment_begin.is_none() { + mlb + } else { + comment + .chars() + .rev() + .take_while(|c| c.is_whitespace()) + .filter(|&c| c == '\n') + .count() + > 1 + }; + (if mlb { "\n" } else { "" }, if mla { "\n" } else { "" }) +} + +impl Rewrite for ast::MetaItem { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + Some(match self.kind { + ast::MetaItemKind::Word => { + rewrite_path(context, PathContext::Type, &None, &self.path, shape)? + } + ast::MetaItemKind::List(ref list) => { + let path = rewrite_path(context, PathContext::Type, &None, &self.path, shape)?; + let has_trailing_comma = crate::expr::span_ends_with_comma(context, self.span); + overflow::rewrite_with_parens( + context, + &path, + list.iter(), + // 1 = "]" + shape.sub_width(1)?, + self.span, + context.config.attr_fn_like_width(), + Some(if has_trailing_comma { + SeparatorTactic::Always + } else { + SeparatorTactic::Never + }), + )? + } + ast::MetaItemKind::NameValue(ref lit) => { + let path = rewrite_path(context, PathContext::Type, &None, &self.path, shape)?; + // 3 = ` = ` + let lit_shape = shape.shrink_left(path.len() + 3)?; + // `rewrite_literal` returns `None` when `lit` exceeds max + // width. Since a literal is basically unformattable unless it + // is a string literal (and only if `format_strings` is set), + // we might be better off ignoring the fact that the attribute + // is longer than the max width and continue on formatting. + // See #2479 for example. + let value = rewrite_literal(context, lit.as_token_lit(), lit.span, lit_shape) + .unwrap_or_else(|| context.snippet(lit.span).to_owned()); + format!("{} = {}", path, value) + } + }) + } +} + +impl Rewrite for ast::Attribute { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + let snippet = context.snippet(self.span); + if self.is_doc_comment() { + rewrite_doc_comment(snippet, shape.comment(context.config), context.config) + } else { + let should_skip = self + .ident() + .map(|s| context.skip_context.attributes.skip(s.name.as_str())) + .unwrap_or(false); + let prefix = attr_prefix(self); + + if should_skip || contains_comment(snippet) { + return Some(snippet.to_owned()); + } + + if let Some(ref meta) = self.meta() { + // This attribute is possibly a doc attribute needing normalization to a doc comment + if context.config.normalize_doc_attributes() && meta.has_name(sym::doc) { + if let Some(ref literal) = meta.value_str() { + let comment_style = match self.style { + ast::AttrStyle::Inner => CommentStyle::Doc, + ast::AttrStyle::Outer => CommentStyle::TripleSlash, + }; + + let literal_str = literal.as_str(); + let doc_comment_formatter = + DocCommentFormatter::new(literal_str, comment_style); + let doc_comment = format!("{}", doc_comment_formatter); + return rewrite_doc_comment( + &doc_comment, + shape.comment(context.config), + context.config, + ); + } + } + + // 1 = `[` + let shape = shape.offset_left(prefix.len() + 1)?; + Some( + meta.rewrite(context, shape) + .map_or_else(|| snippet.to_owned(), |rw| format!("{}[{}]", prefix, rw)), + ) + } else { + Some(snippet.to_owned()) + } + } + } +} + +impl Rewrite for [ast::Attribute] { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + if self.is_empty() { + return Some(String::new()); + } + + // The current remaining attributes. + 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.attributes.skip("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. + loop { + if attrs.is_empty() { + return Some(result); + } + + // Handle doc comments. + let (doc_comment_len, doc_comment_str) = + rewrite_initial_doc_comments(context, attrs, shape)?; + if doc_comment_len > 0 { + let doc_comment_str = doc_comment_str.expect("doc comments, but no result"); + result.push_str(&doc_comment_str); + + let missing_span = attrs + .get(doc_comment_len) + .map(|next| mk_sp(attrs[doc_comment_len - 1].span.hi(), next.span.lo())); + if let Some(missing_span) = missing_span { + let snippet = context.snippet(missing_span); + let (mla, mlb) = has_newlines_before_after_comment(snippet); + let comment = crate::comment::recover_missing_comment_in_span( + missing_span, + shape.with_max_width(context.config), + context, + 0, + )?; + let comment = if comment.is_empty() { + format!("\n{}", mlb) + } else { + format!("{}{}\n{}", mla, comment, mlb) + }; + result.push_str(&comment); + result.push_str(&shape.indent.to_string(context.config)); + } + + attrs = &attrs[doc_comment_len..]; + + continue; + } + + // Handle derives if we will merge them. + 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); + + let missing_span = attrs + .get(derives.len()) + .map(|next| mk_sp(attrs[derives.len() - 1].span.hi(), next.span.lo())); + if let Some(missing_span) = missing_span { + let comment = crate::comment::recover_missing_comment_in_span( + missing_span, + shape.with_max_width(context.config), + context, + 0, + )?; + result.push_str(&comment); + if let Some(next) = attrs.get(derives.len()) { + if next.is_doc_comment() { + let snippet = context.snippet(missing_span); + let (_, mlb) = has_newlines_before_after_comment(snippet); + result.push_str(mlb); + } + } + result.push('\n'); + result.push_str(&shape.indent.to_string(context.config)); + } + + attrs = &attrs[derives.len()..]; + + continue; + } + + // If we get here, then we have a regular attribute, just handle one + // at a time. + + let formatted_attr = attrs[0].rewrite(context, shape)?; + result.push_str(&formatted_attr); + + let missing_span = attrs + .get(1) + .map(|next| mk_sp(attrs[0].span.hi(), next.span.lo())); + if let Some(missing_span) = missing_span { + let comment = crate::comment::recover_missing_comment_in_span( + missing_span, + shape.with_max_width(context.config), + context, + 0, + )?; + result.push_str(&comment); + if let Some(next) = attrs.get(1) { + if next.is_doc_comment() { + let snippet = context.snippet(missing_span); + let (_, mlb) = has_newlines_before_after_comment(snippet); + result.push_str(mlb); + } + } + result.push('\n'); + result.push_str(&shape.indent.to_string(context.config)); + } + + attrs = &attrs[1..]; + } + } +} + +fn attr_prefix(attr: &ast::Attribute) -> &'static str { + match attr.style { + ast::AttrStyle::Inner => "#!", + ast::AttrStyle::Outer => "#", + } +} + +pub(crate) trait MetaVisitor<'ast> { + fn visit_meta_item(&mut self, meta_item: &'ast ast::MetaItem) { + match meta_item.kind { + ast::MetaItemKind::Word => self.visit_meta_word(meta_item), + ast::MetaItemKind::List(ref list) => self.visit_meta_list(meta_item, list), + ast::MetaItemKind::NameValue(ref lit) => self.visit_meta_name_value(meta_item, lit), + } + } + + fn visit_meta_list( + &mut self, + _meta_item: &'ast ast::MetaItem, + list: &'ast [ast::NestedMetaItem], + ) { + for nm in list { + self.visit_nested_meta_item(nm); + } + } + + fn visit_meta_word(&mut self, _meta_item: &'ast ast::MetaItem) {} + + fn visit_meta_name_value( + &mut self, + _meta_item: &'ast ast::MetaItem, + _lit: &'ast ast::MetaItemLit, + ) { + } + + fn visit_nested_meta_item(&mut self, nm: &'ast ast::NestedMetaItem) { + match nm { + ast::NestedMetaItem::MetaItem(ref meta_item) => self.visit_meta_item(meta_item), + ast::NestedMetaItem::Lit(ref lit) => self.visit_meta_item_lit(lit), + } + } + + fn visit_meta_item_lit(&mut self, _lit: &'ast ast::MetaItemLit) {} +} diff --git a/src/attr/doc_comment.rs b/src/attr/doc_comment.rs new file mode 100644 index 000000000000..25c8158df8c5 --- /dev/null +++ b/src/attr/doc_comment.rs @@ -0,0 +1,88 @@ +use crate::comment::CommentStyle; +use std::fmt::{self, Display}; + +/// Formats a string as a doc comment using the given [`CommentStyle`]. +pub(super) struct DocCommentFormatter<'a> { + literal: &'a str, + style: CommentStyle<'a>, +} + +impl<'a> DocCommentFormatter<'a> { + pub(super) const fn new(literal: &'a str, style: CommentStyle<'a>) -> Self { + Self { literal, style } + } +} + +impl Display for DocCommentFormatter<'_> { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + let opener = self.style.opener().trim_end(); + let mut lines = self.literal.lines().peekable(); + + // Handle `#[doc = ""]`. + if lines.peek().is_none() { + return write!(formatter, "{}", opener); + } + + while let Some(line) = lines.next() { + let is_last_line = lines.peek().is_none(); + if is_last_line { + write!(formatter, "{}{}", opener, line)?; + } else { + writeln!(formatter, "{}{}", opener, line)?; + } + } + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn literal_controls_leading_spaces() { + test_doc_comment_is_formatted_correctly( + " Lorem ipsum", + "/// Lorem ipsum", + CommentStyle::TripleSlash, + ); + } + + #[test] + fn single_line_doc_comment_is_formatted_correctly() { + test_doc_comment_is_formatted_correctly( + "Lorem ipsum", + "///Lorem ipsum", + CommentStyle::TripleSlash, + ); + } + + #[test] + fn multi_line_doc_comment_is_formatted_correctly() { + test_doc_comment_is_formatted_correctly( + "Lorem ipsum\nDolor sit amet", + "///Lorem ipsum\n///Dolor sit amet", + CommentStyle::TripleSlash, + ); + } + + #[test] + fn whitespace_within_lines_is_preserved() { + test_doc_comment_is_formatted_correctly( + " Lorem ipsum \n Dolor sit amet ", + "/// Lorem ipsum \n/// Dolor sit amet ", + CommentStyle::TripleSlash, + ); + } + + fn test_doc_comment_is_formatted_correctly( + literal: &str, + expected_comment: &str, + style: CommentStyle<'_>, + ) { + assert_eq!( + expected_comment, + format!("{}", DocCommentFormatter::new(literal, style)) + ); + } +} diff --git a/src/bin/main.rs b/src/bin/main.rs new file mode 100644 index 000000000000..03b75c1b0410 --- /dev/null +++ b/src/bin/main.rs @@ -0,0 +1,719 @@ +#![feature(rustc_private)] + +use anyhow::{format_err, Result}; + +use io::Error as IoError; +use thiserror::Error; + +use rustfmt_nightly as rustfmt; + +use std::collections::HashMap; +use std::env; +use std::fs::File; +use std::io::{self, stdout, Read, Write}; +use std::path::{Path, PathBuf}; +use std::str::FromStr; + +use getopts::{Matches, Options}; + +use crate::rustfmt::{ + load_config, CliOptions, Color, Config, Edition, EmitMode, FileLines, FileName, + FormatReportFormatterBuilder, Input, Session, Verbosity, +}; + +const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rustfmt/issues/new?labels=bug"; + +// N.B. these crates are loaded from the sysroot, so they need extern crate. +extern crate rustc_driver; + +fn main() { + rustc_driver::install_ice_hook(BUG_REPORT_URL, |_| ()); + + env_logger::Builder::from_env("RUSTFMT_LOG").init(); + let opts = make_opts(); + + let exit_code = match execute(&opts) { + Ok(code) => code, + Err(e) => { + eprintln!("{:#}", e); + 1 + } + }; + // Make sure standard output is flushed before we exit. + std::io::stdout().flush().unwrap(); + + // Exit with given exit code. + // + // NOTE: this immediately terminates the process without doing any cleanup, + // so make sure to finish all necessary cleanup before this is called. + std::process::exit(exit_code); +} + +/// Rustfmt operations. +enum Operation { + /// Format files and their child modules. + Format { + files: Vec, + minimal_config_path: Option, + }, + /// Print the help message. + Help(HelpOp), + /// Print version information + Version, + /// Output default config to a file, or stdout if None + ConfigOutputDefault { path: Option }, + /// Output current config (as if formatting to a file) to stdout + ConfigOutputCurrent { path: Option }, + /// No file specified, read from stdin + Stdin { input: String }, +} + +/// Rustfmt operations errors. +#[derive(Error, Debug)] +pub enum OperationError { + /// An unknown help topic was requested. + #[error("Unknown help topic: `{0}`.")] + UnknownHelpTopic(String), + /// An unknown print-config option was requested. + #[error("Unknown print-config option: `{0}`.")] + UnknownPrintConfigTopic(String), + /// Attempt to generate a minimal config from standard input. + #[error("The `--print-config=minimal` option doesn't work with standard input.")] + MinimalPathWithStdin, + /// An io error during reading or writing. + #[error("{0}")] + IoError(IoError), + /// Attempt to use --emit with a mode which is not currently + /// supported with standard input. + #[error("Emit mode {0} not supported with standard output.")] + StdinBadEmit(EmitMode), +} + +impl From for OperationError { + fn from(e: IoError) -> OperationError { + OperationError::IoError(e) + } +} + +/// Arguments to `--help` +enum HelpOp { + None, + Config, + FileLines, +} + +fn make_opts() -> Options { + let mut opts = Options::new(); + + opts.optflag( + "", + "check", + "Run in 'check' mode. Exits with 0 if input is formatted correctly. Exits \ + with 1 and prints a diff if formatting is required.", + ); + let is_nightly = is_nightly(); + let emit_opts = if is_nightly { + "[files|stdout|coverage|checkstyle|json]" + } else { + "[files|stdout]" + }; + opts.optopt("", "emit", "What data to emit and how", emit_opts); + opts.optflag("", "backup", "Backup any modified files."); + opts.optopt( + "", + "config-path", + "Recursively searches the given path for the rustfmt.toml config file. If not \ + found reverts to the input file path", + "[Path for the configuration file]", + ); + opts.optopt("", "edition", "Rust edition to use", "[2015|2018|2021]"); + opts.optopt( + "", + "color", + "Use colored output (if supported)", + "[always|never|auto]", + ); + opts.optopt( + "", + "print-config", + "Dumps a default or minimal config to PATH. A minimal config is the \ + subset of the current config file used for formatting the current program. \ + `current` writes to stdout current config as if formatting the file at PATH.", + "[default|minimal|current] PATH", + ); + opts.optflag( + "l", + "files-with-diff", + "Prints the names of mismatched files that were formatted. Prints the names of \ + files that would be formatted when used with `--check` mode. ", + ); + opts.optmulti( + "", + "config", + "Set options from command line. These settings take priority over .rustfmt.toml", + "[key1=val1,key2=val2...]", + ); + + if is_nightly { + opts.optflag( + "", + "unstable-features", + "Enables unstable features. Only available on nightly channel.", + ); + opts.optopt( + "", + "file-lines", + "Format specified line ranges. Run with `--help=file-lines` for \ + more detail (unstable).", + "JSON", + ); + opts.optflag( + "", + "error-on-unformatted", + "Error if unable to get comments or string literals within max_width, \ + or they are left with trailing whitespaces (unstable).", + ); + opts.optflag( + "", + "skip-children", + "Don't reformat child modules (unstable).", + ); + } + + opts.optflag("v", "verbose", "Print verbose output"); + opts.optflag("q", "quiet", "Print less output"); + opts.optflag("V", "version", "Show version information"); + let help_topics = if is_nightly { + "`config` or `file-lines`" + } else { + "`config`" + }; + let mut help_topic_msg = "Show this message or help about a specific topic: ".to_owned(); + help_topic_msg.push_str(help_topics); + + opts.optflagopt("h", "help", &help_topic_msg, "=TOPIC"); + + opts +} + +fn is_nightly() -> bool { + option_env!("CFG_RELEASE_CHANNEL").map_or(true, |c| c == "nightly" || c == "dev") +} + +// Returned i32 is an exit code +fn execute(opts: &Options) -> Result { + let matches = opts.parse(env::args().skip(1))?; + let options = GetOptsOptions::from_matches(&matches)?; + + match determine_operation(&matches)? { + Operation::Help(HelpOp::None) => { + print_usage_to_stdout(opts, ""); + Ok(0) + } + Operation::Help(HelpOp::Config) => { + Config::print_docs(&mut stdout(), options.unstable_features); + Ok(0) + } + Operation::Help(HelpOp::FileLines) => { + print_help_file_lines(); + Ok(0) + } + Operation::Version => { + print_version(); + Ok(0) + } + Operation::ConfigOutputDefault { path } => { + let toml = Config::default().all_options().to_toml()?; + if let Some(path) = path { + let mut file = File::create(path)?; + file.write_all(toml.as_bytes())?; + } else { + io::stdout().write_all(toml.as_bytes())?; + } + Ok(0) + } + Operation::ConfigOutputCurrent { path } => { + let path = match path { + Some(path) => path, + None => return Err(format_err!("PATH required for `--print-config current`")), + }; + + let file = PathBuf::from(path); + let file = file.canonicalize().unwrap_or(file); + + let (config, _) = load_config(Some(file.parent().unwrap()), Some(options))?; + let toml = config.all_options().to_toml()?; + io::stdout().write_all(toml.as_bytes())?; + + Ok(0) + } + Operation::Stdin { input } => format_string(input, options), + Operation::Format { + files, + minimal_config_path, + } => format(files, minimal_config_path, &options), + } +} + +fn format_string(input: String, options: GetOptsOptions) -> Result { + // try to read config from local directory + let (mut config, _) = load_config(Some(Path::new(".")), Some(options.clone()))?; + + if options.check { + 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)); + } + config.set().verbose(Verbosity::Quiet); + + // parse file_lines + config.set().file_lines(options.file_lines); + for f in config.file_lines().files() { + match *f { + FileName::Stdin => {} + _ => eprintln!("Warning: Extra file listed in file_lines option '{}'", f), + } + } + + let out = &mut stdout(); + let mut session = Session::new(config, Some(out)); + format_and_emit_report(&mut session, Input::Text(input)); + + let exit_code = if session.has_operational_errors() || session.has_parsing_errors() { + 1 + } else { + 0 + }; + Ok(exit_code) +} + +fn format( + files: Vec, + minimal_config_path: Option, + options: &GetOptsOptions, +) -> Result { + options.verify_file_lines(&files); + let (config, config_path) = load_config(None, Some(options.clone()))?; + + if config.verbose() == Verbosity::Verbose { + if let Some(path) = config_path.as_ref() { + println!("Using rustfmt config file {}", path.display()); + } + } + + let out = &mut stdout(); + let mut session = Session::new(config, Some(out)); + + for file in files { + if !file.exists() { + eprintln!("Error: file `{}` does not exist", file.to_str().unwrap()); + session.add_operational_error(); + } else if file.is_dir() { + eprintln!("Error: `{}` is a directory", file.to_str().unwrap()); + session.add_operational_error(); + } else { + // Check the file directory if the config-path could not be read or not provided + if config_path.is_none() { + let (local_config, config_path) = + load_config(Some(file.parent().unwrap()), Some(options.clone()))?; + if local_config.verbose() == Verbosity::Verbose { + if let Some(path) = config_path { + println!( + "Using rustfmt config file {} for {}", + path.display(), + file.display() + ); + } + } + + session.override_config(local_config, |sess| { + format_and_emit_report(sess, Input::File(file)) + }); + } else { + format_and_emit_report(&mut session, Input::File(file)); + } + } + } + + // If we were given a path via dump-minimal-config, output any options + // that were used during formatting as TOML. + if let Some(path) = minimal_config_path { + let mut file = File::create(path)?; + let toml = session.config.used_options().to_toml()?; + file.write_all(toml.as_bytes())?; + } + + let exit_code = if session.has_operational_errors() + || session.has_parsing_errors() + || ((session.has_diff() || session.has_check_errors()) && options.check) + { + 1 + } else { + 0 + }; + Ok(exit_code) +} + +fn format_and_emit_report(session: &mut Session<'_, T>, input: Input) { + match session.format(input) { + Ok(report) => { + if report.has_warnings() { + eprintln!( + "{}", + FormatReportFormatterBuilder::new(&report) + .enable_colors(should_print_with_colors(session)) + .build() + ); + } + } + Err(msg) => { + eprintln!("Error writing files: {}", msg); + session.add_operational_error(); + } + } +} + +fn should_print_with_colors(session: &mut Session<'_, T>) -> bool { + match term::stderr() { + Some(ref t) + if session.config.color().use_colored_tty() + && t.supports_color() + && t.supports_attr(term::Attr::Bold) => + { + true + } + _ => false, + } +} + +fn print_usage_to_stdout(opts: &Options, reason: &str) { + let sep = if reason.is_empty() { + String::new() + } else { + format!("{}\n\n", reason) + }; + let msg = format!( + "{}Format Rust code\n\nusage: rustfmt [options] ...", + sep + ); + println!("{}", opts.usage(&msg)); +} + +fn print_help_file_lines() { + println!( + "If you want to restrict reformatting to specific sets of lines, you can +use the `--file-lines` option. Its argument is a JSON array of objects +with `file` and `range` properties, where `file` is a file name, and +`range` is an array representing a range of lines like `[7,13]`. Ranges +are 1-based and inclusive of both end points. Specifying an empty array +will result in no files being formatted. For example, + +``` +rustfmt --file-lines '[ + {{\"file\":\"src/lib.rs\",\"range\":[7,13]}}, + {{\"file\":\"src/lib.rs\",\"range\":[21,29]}}, + {{\"file\":\"src/foo.rs\",\"range\":[10,11]}}, + {{\"file\":\"src/foo.rs\",\"range\":[15,15]}}]' +``` + +would format lines `7-13` and `21-29` of `src/lib.rs`, and lines `10-11`, +and `15` of `src/foo.rs`. No other files would be formatted, even if they +are included as out of line modules from `src/lib.rs`." + ); +} + +fn print_version() { + let version_info = format!( + "{}-{}", + option_env!("CARGO_PKG_VERSION").unwrap_or("unknown"), + include_str!(concat!(env!("OUT_DIR"), "/commit-info.txt")) + ); + + println!("rustfmt {}", version_info); +} + +fn determine_operation(matches: &Matches) -> Result { + if matches.opt_present("h") { + let topic = matches.opt_str("h"); + if topic == None { + return Ok(Operation::Help(HelpOp::None)); + } else if topic == Some("config".to_owned()) { + return Ok(Operation::Help(HelpOp::Config)); + } else if topic == Some("file-lines".to_owned()) && is_nightly() { + return Ok(Operation::Help(HelpOp::FileLines)); + } else { + return Err(OperationError::UnknownHelpTopic(topic.unwrap())); + } + } + let mut free_matches = matches.free.iter(); + + let mut minimal_config_path = None; + if let Some(kind) = matches.opt_str("print-config") { + let path = free_matches.next().cloned(); + match kind.as_str() { + "default" => return Ok(Operation::ConfigOutputDefault { path }), + "current" => return Ok(Operation::ConfigOutputCurrent { path }), + "minimal" => { + minimal_config_path = path; + if minimal_config_path.is_none() { + eprintln!("WARNING: PATH required for `--print-config minimal`."); + } + } + _ => { + return Err(OperationError::UnknownPrintConfigTopic(kind)); + } + } + } + + if matches.opt_present("version") { + return Ok(Operation::Version); + } + + let files: Vec<_> = free_matches + .map(|s| { + let p = PathBuf::from(s); + // we will do comparison later, so here tries to canonicalize first + // to get the expected behavior. + p.canonicalize().unwrap_or(p) + }) + .collect(); + + // if no file argument is supplied, read from stdin + if files.is_empty() { + if minimal_config_path.is_some() { + return Err(OperationError::MinimalPathWithStdin); + } + let mut buffer = String::new(); + io::stdin().read_to_string(&mut buffer)?; + + return Ok(Operation::Stdin { input: buffer }); + } + + Ok(Operation::Format { + files, + minimal_config_path, + }) +} + +const STABLE_EMIT_MODES: [EmitMode; 3] = [EmitMode::Files, EmitMode::Stdout, EmitMode::Diff]; + +/// Parsed command line options. +#[derive(Clone, Debug, Default)] +struct GetOptsOptions { + skip_children: Option, + quiet: bool, + verbose: bool, + config_path: Option, + inline_config: HashMap, + emit_mode: Option, + backup: bool, + check: bool, + edition: Option, + color: Option, + file_lines: FileLines, // Default is all lines in all files. + unstable_features: bool, + error_on_unformatted: Option, + print_misformatted_file_names: bool, +} + +impl GetOptsOptions { + pub fn from_matches(matches: &Matches) -> Result { + let mut options = GetOptsOptions::default(); + options.verbose = matches.opt_present("verbose"); + options.quiet = matches.opt_present("quiet"); + if options.verbose && options.quiet { + return Err(format_err!("Can't use both `--verbose` and `--quiet`")); + } + + let rust_nightly = is_nightly(); + + if rust_nightly { + options.unstable_features = matches.opt_present("unstable-features"); + + if options.unstable_features { + if matches.opt_present("skip-children") { + options.skip_children = Some(true); + } + if matches.opt_present("error-on-unformatted") { + options.error_on_unformatted = Some(true); + } + if let Some(ref file_lines) = matches.opt_str("file-lines") { + options.file_lines = file_lines.parse()?; + } + } else { + let mut unstable_options = vec![]; + if matches.opt_present("skip-children") { + unstable_options.push("`--skip-children`"); + } + if matches.opt_present("error-on-unformatted") { + unstable_options.push("`--error-on-unformatted`"); + } + if matches.opt_present("file-lines") { + unstable_options.push("`--file-lines`"); + } + if !unstable_options.is_empty() { + let s = if unstable_options.len() == 1 { "" } else { "s" }; + return Err(format_err!( + "Unstable option{} ({}) used without `--unstable-features`", + s, + unstable_options.join(", "), + )); + } + } + } + + options.config_path = matches.opt_str("config-path").map(PathBuf::from); + + options.inline_config = matches + .opt_strs("config") + .iter() + .flat_map(|config| config.split(',')) + .map( + |key_val| match key_val.char_indices().find(|(_, ch)| *ch == '=') { + Some((middle, _)) => { + let (key, val) = (&key_val[..middle], &key_val[middle + 1..]); + if !Config::is_valid_key_val(key, val) { + Err(format_err!("invalid key=val pair: `{}`", key_val)) + } else { + Ok((key.to_string(), val.to_string())) + } + } + + None => Err(format_err!( + "--config expects comma-separated list of key=val pairs, found `{}`", + key_val + )), + }, + ) + .collect::, _>>()?; + + options.check = matches.opt_present("check"); + if let Some(ref emit_str) = matches.opt_str("emit") { + if options.check { + return Err(format_err!("Invalid to use `--emit` and `--check`")); + } + + options.emit_mode = Some(emit_mode_from_emit_str(emit_str)?); + } + + if let Some(ref edition_str) = matches.opt_str("edition") { + options.edition = Some(edition_from_edition_str(edition_str)?); + } + + if matches.opt_present("backup") { + options.backup = true; + } + + if matches.opt_present("files-with-diff") { + options.print_misformatted_file_names = true; + } + + if !rust_nightly { + if let Some(ref emit_mode) = options.emit_mode { + if !STABLE_EMIT_MODES.contains(emit_mode) { + return Err(format_err!( + "Invalid value for `--emit` - using an unstable \ + value without `--unstable-features`", + )); + } + } + } + + if let Some(ref color) = matches.opt_str("color") { + match Color::from_str(color) { + Ok(color) => options.color = Some(color), + _ => return Err(format_err!("Invalid color: {}", color)), + } + } + + Ok(options) + } + + fn verify_file_lines(&self, files: &[PathBuf]) { + for f in self.file_lines.files() { + match *f { + FileName::Real(ref f) if files.contains(f) => {} + FileName::Real(_) => { + eprintln!("Warning: Extra file listed in file_lines option '{}'", f) + } + FileName::Stdin => eprintln!("Warning: Not a file '{}'", f), + } + } + } +} + +impl CliOptions for GetOptsOptions { + fn apply_to(self, config: &mut Config) { + if self.verbose { + config.set().verbose(Verbosity::Verbose); + } else if self.quiet { + config.set().verbose(Verbosity::Quiet); + } else { + config.set().verbose(Verbosity::Normal); + } + config.set().file_lines(self.file_lines); + config.set().unstable_features(self.unstable_features); + if let Some(skip_children) = self.skip_children { + config.set().skip_children(skip_children); + } + if let Some(error_on_unformatted) = self.error_on_unformatted { + config.set().error_on_unformatted(error_on_unformatted); + } + if let Some(edition) = self.edition { + config.set().edition(edition); + } + if self.check { + config.set().emit_mode(EmitMode::Diff); + } else if let Some(emit_mode) = self.emit_mode { + config.set().emit_mode(emit_mode); + } + if self.backup { + config.set().make_backup(true); + } + if let Some(color) = self.color { + config.set().color(color); + } + if self.print_misformatted_file_names { + config.set().print_misformatted_file_names(true); + } + + for (key, val) in self.inline_config { + config.override_value(&key, &val); + } + } + + fn config_path(&self) -> Option<&Path> { + self.config_path.as_deref() + } +} + +fn edition_from_edition_str(edition_str: &str) -> Result { + match edition_str { + "2015" => Ok(Edition::Edition2015), + "2018" => Ok(Edition::Edition2018), + "2021" => Ok(Edition::Edition2021), + "2024" => Ok(Edition::Edition2024), + _ => Err(format_err!("Invalid value for `--edition`")), + } +} + +fn emit_mode_from_emit_str(emit_str: &str) -> Result { + match emit_str { + "files" => Ok(EmitMode::Files), + "stdout" => Ok(EmitMode::Stdout), + "coverage" => Ok(EmitMode::Coverage), + "checkstyle" => Ok(EmitMode::Checkstyle), + "json" => Ok(EmitMode::Json), + _ => Err(format_err!("Invalid value for `--emit`")), + } +} diff --git a/src/cargo-fmt/main.rs b/src/cargo-fmt/main.rs new file mode 100644 index 000000000000..bc9745275f20 --- /dev/null +++ b/src/cargo-fmt/main.rs @@ -0,0 +1,549 @@ +// Inspired by Paul Woolcock's cargo-fmt (https://github.com/pwoolcoc/cargo-fmt/). + +#![deny(warnings)] +#![allow(clippy::match_like_matches_macro)] + +use std::cmp::Ordering; +use std::collections::{BTreeMap, BTreeSet}; +use std::env; +use std::ffi::OsStr; +use std::fs; +use std::hash::{Hash, Hasher}; +use std::io::{self, Write}; +use std::path::{Path, PathBuf}; +use std::process::Command; +use std::str; + +use cargo_metadata::Edition; +use clap::{CommandFactory, Parser}; + +#[path = "test/mod.rs"] +#[cfg(test)] +mod cargo_fmt_tests; + +#[derive(Parser)] +#[clap( + disable_version_flag = true, + bin_name = "cargo fmt", + about = "This utility formats all bin and lib files of \ + the current crate using rustfmt." +)] +pub struct Opts { + /// No output printed to stdout + #[clap(short = 'q', long = "quiet")] + quiet: bool, + + /// Use verbose output + #[clap(short = 'v', long = "verbose")] + verbose: bool, + + /// Print rustfmt version and exit + #[clap(long = "version")] + version: bool, + + /// Specify package to format + #[clap( + short = 'p', + long = "package", + value_name = "package", + num_args = 1.. + )] + packages: Vec, + + /// Specify path to Cargo.toml + #[clap(long = "manifest-path", value_name = "manifest-path")] + manifest_path: Option, + + /// Specify message-format: short|json|human + #[clap(long = "message-format", value_name = "message-format")] + message_format: Option, + + /// Options passed to rustfmt + // 'raw = true' to make `--` explicit. + #[clap(name = "rustfmt_options", raw(true))] + rustfmt_options: Vec, + + /// Format all packages, and also their local path-based dependencies + #[clap(long = "all")] + format_all: bool, + + /// Run rustfmt in check mode + #[clap(long = "check")] + check: bool, +} + +fn main() { + let exit_status = execute(); + std::io::stdout().flush().unwrap(); + std::process::exit(exit_status); +} + +const SUCCESS: i32 = 0; +const FAILURE: i32 = 1; + +fn execute() -> i32 { + // Drop extra `fmt` argument provided by `cargo`. + let mut found_fmt = false; + let args = env::args().filter(|x| { + if found_fmt { + true + } else { + found_fmt = x == "fmt"; + x != "fmt" + } + }); + + let opts = Opts::parse_from(args); + + let verbosity = match (opts.verbose, opts.quiet) { + (false, false) => Verbosity::Normal, + (false, true) => Verbosity::Quiet, + (true, false) => Verbosity::Verbose, + (true, true) => { + print_usage_to_stderr("quiet mode and verbose mode are not compatible"); + return FAILURE; + } + }; + + if opts.version { + return handle_command_status(get_rustfmt_info(&[String::from("--version")])); + } + if opts.rustfmt_options.iter().any(|s| { + ["--print-config", "-h", "--help", "-V", "--version"].contains(&s.as_str()) + || s.starts_with("--help=") + || s.starts_with("--print-config=") + }) { + return handle_command_status(get_rustfmt_info(&opts.rustfmt_options)); + } + + let strategy = CargoFmtStrategy::from_opts(&opts); + let mut rustfmt_args = opts.rustfmt_options; + if opts.check { + let check_flag = "--check"; + if !rustfmt_args.iter().any(|o| o == check_flag) { + rustfmt_args.push(check_flag.to_owned()); + } + } + if let Some(message_format) = opts.message_format { + if let Err(msg) = convert_message_format_to_rustfmt_args(&message_format, &mut rustfmt_args) + { + print_usage_to_stderr(&msg); + return FAILURE; + } + } + + if let Some(specified_manifest_path) = opts.manifest_path { + if !specified_manifest_path.ends_with("Cargo.toml") { + print_usage_to_stderr("the manifest-path must be a path to a Cargo.toml file"); + return FAILURE; + } + let manifest_path = PathBuf::from(specified_manifest_path); + handle_command_status(format_crate( + verbosity, + &strategy, + rustfmt_args, + Some(&manifest_path), + )) + } else { + handle_command_status(format_crate(verbosity, &strategy, rustfmt_args, None)) + } +} + +fn rustfmt_command() -> Command { + let rustfmt_var = env::var_os("RUSTFMT"); + let rustfmt = match &rustfmt_var { + Some(rustfmt) => rustfmt, + None => OsStr::new("rustfmt"), + }; + Command::new(rustfmt) +} + +fn convert_message_format_to_rustfmt_args( + message_format: &str, + rustfmt_args: &mut Vec, +) -> Result<(), String> { + let mut contains_emit_mode = false; + let mut contains_check = false; + let mut contains_list_files = false; + for arg in rustfmt_args.iter() { + if arg.starts_with("--emit") { + contains_emit_mode = true; + } + if arg == "--check" { + contains_check = true; + } + if arg == "-l" || arg == "--files-with-diff" { + contains_list_files = true; + } + } + match message_format { + "short" => { + if !contains_list_files { + rustfmt_args.push(String::from("-l")); + } + Ok(()) + } + "json" => { + if contains_emit_mode { + return Err(String::from( + "cannot include --emit arg when --message-format is set to json", + )); + } + if contains_check { + return Err(String::from( + "cannot include --check arg when --message-format is set to json", + )); + } + rustfmt_args.push(String::from("--emit")); + rustfmt_args.push(String::from("json")); + Ok(()) + } + "human" => Ok(()), + _ => Err(format!( + "invalid --message-format value: {}. Allowed values are: short|json|human", + message_format + )), + } +} + +fn print_usage_to_stderr(reason: &str) { + eprintln!("{}", reason); + let app = Opts::command(); + app.after_help("") + .write_help(&mut io::stderr()) + .expect("failed to write to stderr"); +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Verbosity { + Verbose, + Normal, + Quiet, +} + +fn handle_command_status(status: Result) -> i32 { + match status { + Err(e) => { + print_usage_to_stderr(&e.to_string()); + FAILURE + } + Ok(status) => status, + } +} + +fn get_rustfmt_info(args: &[String]) -> Result { + let mut command = rustfmt_command() + .stdout(std::process::Stdio::inherit()) + .args(args) + .spawn() + .map_err(|e| match e.kind() { + io::ErrorKind::NotFound => io::Error::new( + io::ErrorKind::Other, + "Could not run rustfmt, please make sure it is in your PATH.", + ), + _ => e, + })?; + let result = command.wait()?; + if result.success() { + Ok(SUCCESS) + } else { + Ok(result.code().unwrap_or(SUCCESS)) + } +} + +fn format_crate( + verbosity: Verbosity, + strategy: &CargoFmtStrategy, + rustfmt_args: Vec, + manifest_path: Option<&Path>, +) -> Result { + let targets = get_targets(strategy, manifest_path)?; + + // Currently only bin and lib files get formatted. + run_rustfmt(&targets, &rustfmt_args, verbosity) +} + +/// Target uses a `path` field for equality and hashing. +#[derive(Debug)] +pub struct Target { + /// A path to the main source file of the target. + path: PathBuf, + /// A kind of target (e.g., lib, bin, example, ...). + kind: String, + /// Rust edition for this target. + edition: Edition, +} + +impl Target { + pub fn from_target(target: &cargo_metadata::Target) -> Self { + let path = PathBuf::from(&target.src_path); + let canonicalized = fs::canonicalize(&path).unwrap_or(path); + + Target { + path: canonicalized, + kind: target.kind[0].clone(), + edition: target.edition, + } + } +} + +impl PartialEq for Target { + fn eq(&self, other: &Target) -> bool { + self.path == other.path + } +} + +impl PartialOrd for Target { + fn partial_cmp(&self, other: &Target) -> Option { + Some(self.path.cmp(&other.path)) + } +} + +impl Ord for Target { + fn cmp(&self, other: &Target) -> Ordering { + self.path.cmp(&other.path) + } +} + +impl Eq for Target {} + +impl Hash for Target { + fn hash(&self, state: &mut H) { + self.path.hash(state); + } +} + +#[derive(Debug, PartialEq, Eq)] +pub enum CargoFmtStrategy { + /// Format every packages and dependencies. + All, + /// Format packages that are specified by the command line argument. + Some(Vec), + /// Format the root packages only. + Root, +} + +impl CargoFmtStrategy { + pub fn from_opts(opts: &Opts) -> CargoFmtStrategy { + match (opts.format_all, opts.packages.is_empty()) { + (false, true) => CargoFmtStrategy::Root, + (true, _) => CargoFmtStrategy::All, + (false, false) => CargoFmtStrategy::Some(opts.packages.clone()), + } + } +} + +/// Based on the specified `CargoFmtStrategy`, returns a set of main source files. +fn get_targets( + strategy: &CargoFmtStrategy, + manifest_path: Option<&Path>, +) -> Result, io::Error> { + let mut targets = BTreeSet::new(); + + match *strategy { + CargoFmtStrategy::Root => get_targets_root_only(manifest_path, &mut targets)?, + CargoFmtStrategy::All => { + get_targets_recursive(manifest_path, &mut targets, &mut BTreeSet::new())? + } + CargoFmtStrategy::Some(ref hitlist) => { + get_targets_with_hitlist(manifest_path, hitlist, &mut targets)? + } + } + + if targets.is_empty() { + Err(io::Error::new( + io::ErrorKind::Other, + "Failed to find targets".to_owned(), + )) + } else { + Ok(targets) + } +} + +fn get_targets_root_only( + manifest_path: Option<&Path>, + targets: &mut BTreeSet, +) -> Result<(), io::Error> { + let metadata = get_cargo_metadata(manifest_path)?; + let workspace_root_path = PathBuf::from(&metadata.workspace_root).canonicalize()?; + let (in_workspace_root, current_dir_manifest) = if let Some(target_manifest) = manifest_path { + ( + workspace_root_path == target_manifest, + target_manifest.canonicalize()?, + ) + } else { + let current_dir = env::current_dir()?.canonicalize()?; + ( + workspace_root_path == current_dir, + current_dir.join("Cargo.toml"), + ) + }; + + let package_targets = match metadata.packages.len() { + 1 => metadata.packages.into_iter().next().unwrap().targets, + _ => metadata + .packages + .into_iter() + .filter(|p| { + in_workspace_root + || PathBuf::from(&p.manifest_path) + .canonicalize() + .unwrap_or_default() + == current_dir_manifest + }) + .flat_map(|p| p.targets) + .collect(), + }; + + for target in package_targets { + targets.insert(Target::from_target(&target)); + } + + Ok(()) +} + +fn get_targets_recursive( + manifest_path: Option<&Path>, + targets: &mut BTreeSet, + visited: &mut BTreeSet, +) -> Result<(), io::Error> { + let metadata = get_cargo_metadata(manifest_path)?; + for package in &metadata.packages { + add_targets(&package.targets, targets); + + // Look for local dependencies using information available since cargo v1.51 + // It's theoretically possible someone could use a newer version of rustfmt with + // a much older version of `cargo`, but we don't try to explicitly support that scenario. + // If someone reports an issue with path-based deps not being formatted, be sure to + // confirm their version of `cargo` (not `cargo-fmt`) is >= v1.51 + // https://github.com/rust-lang/cargo/pull/8994 + for dependency in &package.dependencies { + if dependency.path.is_none() || visited.contains(&dependency.name) { + continue; + } + + let manifest_path = PathBuf::from(dependency.path.as_ref().unwrap()).join("Cargo.toml"); + if manifest_path.exists() + && !metadata + .packages + .iter() + .any(|p| p.manifest_path.eq(&manifest_path)) + { + visited.insert(dependency.name.to_owned()); + get_targets_recursive(Some(&manifest_path), targets, visited)?; + } + } + } + + Ok(()) +} + +fn get_targets_with_hitlist( + manifest_path: Option<&Path>, + hitlist: &[String], + targets: &mut BTreeSet, +) -> Result<(), io::Error> { + let metadata = get_cargo_metadata(manifest_path)?; + let mut workspace_hitlist: BTreeSet<&String> = BTreeSet::from_iter(hitlist); + + for package in metadata.packages { + if workspace_hitlist.remove(&package.name) { + for target in package.targets { + targets.insert(Target::from_target(&target)); + } + } + } + + if workspace_hitlist.is_empty() { + Ok(()) + } else { + let package = workspace_hitlist.iter().next().unwrap(); + Err(io::Error::new( + io::ErrorKind::InvalidInput, + format!("package `{}` is not a member of the workspace", package), + )) + } +} + +fn add_targets(target_paths: &[cargo_metadata::Target], targets: &mut BTreeSet) { + for target in target_paths { + targets.insert(Target::from_target(target)); + } +} + +fn run_rustfmt( + targets: &BTreeSet, + fmt_args: &[String], + verbosity: Verbosity, +) -> Result { + let by_edition = targets + .iter() + .inspect(|t| { + if verbosity == Verbosity::Verbose { + println!("[{} ({})] {:?}", t.kind, t.edition, t.path) + } + }) + .fold(BTreeMap::new(), |mut h, t| { + h.entry(&t.edition).or_insert_with(Vec::new).push(&t.path); + h + }); + + let mut status = vec![]; + for (edition, files) in by_edition { + let stdout = if verbosity == Verbosity::Quiet { + std::process::Stdio::null() + } else { + std::process::Stdio::inherit() + }; + + if verbosity == Verbosity::Verbose { + print!("rustfmt"); + print!(" --edition {}", edition); + fmt_args.iter().for_each(|f| print!(" {}", f)); + files.iter().for_each(|f| print!(" {}", f.display())); + println!(); + } + + let mut command = rustfmt_command() + .stdout(stdout) + .args(files) + .args(&["--edition", edition.as_str()]) + .args(fmt_args) + .spawn() + .map_err(|e| match e.kind() { + io::ErrorKind::NotFound => io::Error::new( + io::ErrorKind::Other, + "Could not run rustfmt, please make sure it is in your PATH.", + ), + _ => e, + })?; + + status.push(command.wait()?); + } + + Ok(status + .iter() + .filter_map(|s| if s.success() { None } else { s.code() }) + .next() + .unwrap_or(SUCCESS)) +} + +fn get_cargo_metadata(manifest_path: Option<&Path>) -> Result { + let mut cmd = cargo_metadata::MetadataCommand::new(); + cmd.no_deps(); + if let Some(manifest_path) = manifest_path { + cmd.manifest_path(manifest_path); + } + cmd.other_options(vec![String::from("--offline")]); + + match cmd.exec() { + Ok(metadata) => Ok(metadata), + Err(_) => { + cmd.other_options(vec![]); + match cmd.exec() { + Ok(metadata) => Ok(metadata), + Err(error) => Err(io::Error::new(io::ErrorKind::Other, error.to_string())), + } + } + } +} diff --git a/src/cargo-fmt/test/message_format.rs b/src/cargo-fmt/test/message_format.rs new file mode 100644 index 000000000000..bf44924f13c3 --- /dev/null +++ b/src/cargo-fmt/test/message_format.rs @@ -0,0 +1,80 @@ +use super::*; + +#[test] +fn invalid_message_format() { + assert_eq!( + convert_message_format_to_rustfmt_args("awesome", &mut vec![]), + Err(String::from( + "invalid --message-format value: awesome. Allowed values are: short|json|human" + )), + ); +} + +#[test] +fn json_message_format_and_check_arg() { + let mut args = vec![String::from("--check")]; + assert_eq!( + convert_message_format_to_rustfmt_args("json", &mut args), + Err(String::from( + "cannot include --check arg when --message-format is set to json" + )), + ); +} + +#[test] +fn json_message_format_and_emit_arg() { + let mut args = vec![String::from("--emit"), String::from("checkstyle")]; + assert_eq!( + convert_message_format_to_rustfmt_args("json", &mut args), + Err(String::from( + "cannot include --emit arg when --message-format is set to json" + )), + ); +} + +#[test] +fn json_message_format() { + let mut args = vec![String::from("--edition"), String::from("2018")]; + assert!(convert_message_format_to_rustfmt_args("json", &mut args).is_ok()); + assert_eq!( + args, + vec![ + String::from("--edition"), + String::from("2018"), + String::from("--emit"), + String::from("json") + ] + ); +} + +#[test] +fn human_message_format() { + let exp_args = vec![String::from("--emit"), String::from("json")]; + let mut act_args = exp_args.clone(); + assert!(convert_message_format_to_rustfmt_args("human", &mut act_args).is_ok()); + assert_eq!(act_args, exp_args); +} + +#[test] +fn short_message_format() { + let mut args = vec![String::from("--check")]; + assert!(convert_message_format_to_rustfmt_args("short", &mut args).is_ok()); + assert_eq!(args, vec![String::from("--check"), String::from("-l")]); +} + +#[test] +fn short_message_format_included_short_list_files_flag() { + let mut args = vec![String::from("--check"), String::from("-l")]; + assert!(convert_message_format_to_rustfmt_args("short", &mut args).is_ok()); + assert_eq!(args, vec![String::from("--check"), String::from("-l")]); +} + +#[test] +fn short_message_format_included_long_list_files_flag() { + let mut args = vec![String::from("--check"), String::from("--files-with-diff")]; + assert!(convert_message_format_to_rustfmt_args("short", &mut args).is_ok()); + assert_eq!( + args, + vec![String::from("--check"), String::from("--files-with-diff")] + ); +} diff --git a/src/cargo-fmt/test/mod.rs b/src/cargo-fmt/test/mod.rs new file mode 100644 index 000000000000..696326e4f940 --- /dev/null +++ b/src/cargo-fmt/test/mod.rs @@ -0,0 +1,141 @@ +use super::*; + +mod message_format; +mod targets; + +#[test] +fn default_options() { + let empty: Vec = vec![]; + let o = Opts::parse_from(&empty); + assert_eq!(false, o.quiet); + assert_eq!(false, o.verbose); + assert_eq!(false, o.version); + assert_eq!(false, o.check); + assert_eq!(empty, o.packages); + assert_eq!(empty, o.rustfmt_options); + assert_eq!(false, o.format_all); + assert_eq!(None, o.manifest_path); + assert_eq!(None, o.message_format); +} + +#[test] +fn good_options() { + let o = Opts::parse_from(&[ + "test", + "-q", + "-p", + "p1", + "-p", + "p2", + "--message-format", + "short", + "--check", + "--", + "--edition", + "2018", + ]); + assert_eq!(true, o.quiet); + assert_eq!(false, o.verbose); + assert_eq!(false, o.version); + assert_eq!(true, o.check); + assert_eq!(vec!["p1", "p2"], o.packages); + assert_eq!(vec!["--edition", "2018"], o.rustfmt_options); + assert_eq!(false, o.format_all); + assert_eq!(Some(String::from("short")), o.message_format); +} + +#[test] +fn unexpected_option() { + assert!( + Opts::command() + .try_get_matches_from(&["test", "unexpected"]) + .is_err() + ); +} + +#[test] +fn unexpected_flag() { + assert!( + Opts::command() + .try_get_matches_from(&["test", "--flag"]) + .is_err() + ); +} + +#[test] +fn mandatory_separator() { + assert!( + Opts::command() + .try_get_matches_from(&["test", "--emit"]) + .is_err() + ); + assert!( + Opts::command() + .try_get_matches_from(&["test", "--", "--emit"]) + .is_ok() + ); +} + +#[test] +fn multiple_packages_one_by_one() { + let o = Opts::parse_from(&[ + "test", + "-p", + "package1", + "--package", + "package2", + "-p", + "package3", + ]); + assert_eq!(3, o.packages.len()); +} + +#[test] +fn multiple_packages_grouped() { + let o = Opts::parse_from(&[ + "test", + "--package", + "package1", + "package2", + "-p", + "package3", + "package4", + ]); + assert_eq!(4, o.packages.len()); +} + +#[test] +fn empty_packages_1() { + assert!( + Opts::command() + .try_get_matches_from(&["test", "-p"]) + .is_err() + ); +} + +#[test] +fn empty_packages_2() { + assert!( + Opts::command() + .try_get_matches_from(&["test", "-p", "--", "--check"]) + .is_err() + ); +} + +#[test] +fn empty_packages_3() { + assert!( + Opts::command() + .try_get_matches_from(&["test", "-p", "--verbose"]) + .is_err() + ); +} + +#[test] +fn empty_packages_4() { + assert!( + Opts::command() + .try_get_matches_from(&["test", "-p", "--check"]) + .is_err() + ); +} diff --git a/src/cargo-fmt/test/targets.rs b/src/cargo-fmt/test/targets.rs new file mode 100644 index 000000000000..34accb2136a4 --- /dev/null +++ b/src/cargo-fmt/test/targets.rs @@ -0,0 +1,134 @@ +use super::*; + +struct ExpTarget { + path: &'static str, + edition: Edition, + kind: &'static str, +} + +mod all_targets { + use super::*; + + fn assert_correct_targets_loaded( + manifest_suffix: &str, + source_root: &str, + exp_targets: &[ExpTarget], + exp_num_targets: usize, + ) { + let root_path = Path::new("tests/cargo-fmt/source").join(source_root); + let get_path = |exp: &str| PathBuf::from(&root_path).join(exp).canonicalize().unwrap(); + let manifest_path = Path::new(&root_path).join(manifest_suffix); + let targets = get_targets(&CargoFmtStrategy::All, Some(manifest_path.as_path())) + .expect("Targets should have been loaded"); + + assert_eq!(targets.len(), exp_num_targets); + + for target in exp_targets { + assert!(targets.contains(&Target { + path: get_path(target.path), + edition: target.edition, + kind: target.kind.to_owned(), + })); + } + } + + mod different_crate_and_dir_names { + use super::*; + + fn assert_correct_targets_loaded(manifest_suffix: &str) { + let exp_targets = vec![ + ExpTarget { + path: "dependency-dir-name/subdep-dir-name/src/lib.rs", + edition: Edition::E2018, + kind: "lib", + }, + ExpTarget { + path: "dependency-dir-name/src/lib.rs", + edition: Edition::E2018, + kind: "lib", + }, + ExpTarget { + path: "src/main.rs", + edition: Edition::E2018, + kind: "main", + }, + ]; + super::assert_correct_targets_loaded( + manifest_suffix, + "divergent-crate-dir-names", + &exp_targets, + 3, + ); + } + + #[test] + fn correct_targets_from_root() { + assert_correct_targets_loaded("Cargo.toml"); + } + + #[test] + fn correct_targets_from_sub_local_dep() { + assert_correct_targets_loaded("dependency-dir-name/Cargo.toml"); + } + } + + mod workspaces { + use super::*; + + fn assert_correct_targets_loaded(manifest_suffix: &str) { + let exp_targets = vec![ + ExpTarget { + path: "ws/a/src/main.rs", + edition: Edition::E2018, + kind: "bin", + }, + ExpTarget { + path: "ws/b/src/main.rs", + edition: Edition::E2018, + kind: "bin", + }, + ExpTarget { + path: "ws/c/src/lib.rs", + edition: Edition::E2018, + kind: "lib", + }, + ExpTarget { + path: "ws/a/d/src/lib.rs", + edition: Edition::E2018, + kind: "lib", + }, + ExpTarget { + path: "e/src/main.rs", + edition: Edition::E2018, + kind: "main", + }, + ExpTarget { + path: "ws/a/d/f/src/lib.rs", + edition: Edition::E2018, + kind: "lib", + }, + ]; + super::assert_correct_targets_loaded( + manifest_suffix, + "workspaces/path-dep-above", + &exp_targets, + 6, + ); + } + + #[test] + fn includes_outside_workspace_deps() { + assert_correct_targets_loaded("ws/Cargo.toml"); + } + + #[test] + fn includes_workspace_from_dep_above() { + assert_correct_targets_loaded("e/Cargo.toml"); + } + + #[test] + fn includes_all_packages_from_workspace_subdir() { + assert_correct_targets_loaded("ws/a/d/f/Cargo.toml"); + } + } +} diff --git a/src/chains.rs b/src/chains.rs new file mode 100644 index 000000000000..0afce7cf6596 --- /dev/null +++ b/src/chains.rs @@ -0,0 +1,942 @@ +//! Formatting of chained expressions, i.e., expressions that are chained by +//! dots: struct and enum field access, method calls, and try shorthand (`?`). +//! +//! Instead of walking these subexpressions one-by-one, as is our usual strategy +//! for expression formatting, we collect maximal sequences of these expressions +//! and handle them simultaneously. +//! +//! Whenever possible, the entire chain is put on a single line. If that fails, +//! we put each subexpression on a separate, much like the (default) function +//! argument function argument strategy. +//! +//! Depends on config options: `chain_indent` is the indent to use for +//! blocks in the parent/root/base of the chain (and the rest of the chain's +//! alignment). +//! E.g., `let foo = { aaaa; bbb; ccc }.bar.baz();`, we would layout for the +//! following values of `chain_indent`: +//! Block: +//! +//! ```text +//! let foo = { +//! aaaa; +//! bbb; +//! ccc +//! }.bar +//! .baz(); +//! ``` +//! +//! Visual: +//! +//! ```text +//! let foo = { +//! aaaa; +//! bbb; +//! ccc +//! } +//! .bar +//! .baz(); +//! ``` +//! +//! If the first item in the chain is a block expression, we align the dots with +//! the braces. +//! Block: +//! +//! ```text +//! let a = foo.bar +//! .baz() +//! .qux +//! ``` +//! +//! Visual: +//! +//! ```text +//! let a = foo.bar +//! .baz() +//! .qux +//! ``` + +use std::borrow::Cow; +use std::cmp::min; + +use rustc_ast::{ast, ptr}; +use rustc_span::{symbol, BytePos, Span}; + +use crate::comment::{rewrite_comment, CharClasses, FullCodeCharKind, RichChar}; +use crate::config::{IndentStyle, Version}; +use crate::expr::rewrite_call; +use crate::lists::extract_pre_comment; +use crate::macros::convert_try_mac; +use crate::rewrite::{Rewrite, RewriteContext}; +use crate::shape::Shape; +use crate::source_map::SpanUtils; +use crate::utils::{ + self, filtered_str_fits, first_line_width, last_line_extendable, last_line_width, mk_sp, + rewrite_ident, trimmed_last_line_width, wrap_str, +}; + +use thin_vec::ThinVec; + +/// Provides the original input contents from the span +/// of a chain element with trailing spaces trimmed. +fn format_overflow_style(span: Span, context: &RewriteContext<'_>) -> Option { + context.snippet_provider.span_to_snippet(span).map(|s| { + s.lines() + .map(|l| l.trim_end()) + .collect::>() + .join("\n") + }) +} + +fn format_chain_item( + item: &ChainItem, + context: &RewriteContext<'_>, + rewrite_shape: Shape, + allow_overflow: bool, +) -> Option { + if allow_overflow { + item.rewrite(context, rewrite_shape) + .or_else(|| format_overflow_style(item.span, context)) + } else { + item.rewrite(context, rewrite_shape) + } +} + +fn get_block_child_shape( + prev_ends_with_block: bool, + context: &RewriteContext<'_>, + shape: Shape, +) -> Shape { + if prev_ends_with_block { + shape.block_indent(0) + } else { + shape.block_indent(context.config.tab_spaces()) + } + .with_max_width(context.config) +} + +fn get_visual_style_child_shape( + context: &RewriteContext<'_>, + shape: Shape, + offset: usize, + parent_overflowing: bool, +) -> Option { + if !parent_overflowing { + shape + .with_max_width(context.config) + .offset_left(offset) + .map(|s| s.visual_indent(0)) + } else { + Some(shape.visual_indent(offset)) + } +} + +pub(crate) fn rewrite_chain( + expr: &ast::Expr, + context: &RewriteContext<'_>, + shape: Shape, +) -> Option { + let chain = Chain::from_ast(expr, context); + debug!("rewrite_chain {:?} {:?}", chain, shape); + + // If this is just an expression with some `?`s, then format it trivially and + // return early. + if chain.children.is_empty() { + return chain.parent.rewrite(context, shape); + } + + chain.rewrite(context, shape) +} + +#[derive(Debug)] +enum CommentPosition { + Back, + Top, +} + +// An expression plus trailing `?`s to be formatted together. +#[derive(Debug)] +struct ChainItem { + kind: ChainItemKind, + tries: usize, + span: Span, +} + +// FIXME: we can't use a reference here because to convert `try!` to `?` we +// synthesise the AST node. However, I think we could use `Cow` and that +// would remove a lot of cloning. +#[derive(Debug)] +enum ChainItemKind { + Parent(ast::Expr), + MethodCall( + ast::PathSegment, + Vec, + ThinVec>, + ), + StructField(symbol::Ident), + TupleField(symbol::Ident, bool), + Await, + Comment(String, CommentPosition), +} + +impl ChainItemKind { + fn is_block_like(&self, context: &RewriteContext<'_>, reps: &str) -> bool { + match self { + ChainItemKind::Parent(ref expr) => utils::is_block_expr(context, expr, reps), + ChainItemKind::MethodCall(..) + | ChainItemKind::StructField(..) + | ChainItemKind::TupleField(..) + | ChainItemKind::Await + | ChainItemKind::Comment(..) => false, + } + } + + fn is_tup_field_access(expr: &ast::Expr) -> bool { + match expr.kind { + ast::ExprKind::Field(_, ref field) => { + field.name.to_string().chars().all(|c| c.is_digit(10)) + } + _ => false, + } + } + + fn from_ast(context: &RewriteContext<'_>, expr: &ast::Expr) -> (ChainItemKind, Span) { + let (kind, span) = match expr.kind { + ast::ExprKind::MethodCall(ref call) => { + let types = if let Some(ref generic_args) = call.seg.args { + if let ast::GenericArgs::AngleBracketed(ref data) = **generic_args { + data.args + .iter() + .filter_map(|x| match x { + ast::AngleBracketedArg::Arg(ref generic_arg) => { + Some(generic_arg.clone()) + } + _ => None, + }) + .collect::>() + } else { + vec![] + } + } else { + vec![] + }; + let span = mk_sp(call.receiver.span.hi(), expr.span.hi()); + let kind = ChainItemKind::MethodCall(call.seg.clone(), types, call.args.clone()); + (kind, span) + } + ast::ExprKind::Field(ref nested, field) => { + let kind = if Self::is_tup_field_access(expr) { + ChainItemKind::TupleField(field, Self::is_tup_field_access(nested)) + } else { + ChainItemKind::StructField(field) + }; + let span = mk_sp(nested.span.hi(), field.span.hi()); + (kind, span) + } + ast::ExprKind::Await(ref nested, _) => { + let span = mk_sp(nested.span.hi(), expr.span.hi()); + (ChainItemKind::Await, span) + } + _ => return (ChainItemKind::Parent(expr.clone()), expr.span), + }; + + // Remove comments from the span. + let lo = context.snippet_provider.span_before(span, "."); + (kind, mk_sp(lo, span.hi())) + } +} + +impl Rewrite for ChainItem { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + let shape = shape.sub_width(self.tries)?; + let rewrite = match self.kind { + ChainItemKind::Parent(ref expr) => expr.rewrite(context, shape)?, + ChainItemKind::MethodCall(ref segment, ref types, ref exprs) => { + Self::rewrite_method_call(segment.ident, types, exprs, self.span, context, shape)? + } + ChainItemKind::StructField(ident) => format!(".{}", rewrite_ident(context, ident)), + ChainItemKind::TupleField(ident, nested) => format!( + "{}.{}", + if nested && context.config.version() == Version::One { + " " + } else { + "" + }, + rewrite_ident(context, ident) + ), + ChainItemKind::Await => ".await".to_owned(), + ChainItemKind::Comment(ref comment, _) => { + rewrite_comment(comment, false, shape, context.config)? + } + }; + Some(format!("{}{}", rewrite, "?".repeat(self.tries))) + } +} + +impl ChainItem { + fn new(context: &RewriteContext<'_>, expr: &ast::Expr, tries: usize) -> ChainItem { + let (kind, span) = ChainItemKind::from_ast(context, expr); + ChainItem { kind, tries, span } + } + + fn comment(span: Span, comment: String, pos: CommentPosition) -> ChainItem { + ChainItem { + kind: ChainItemKind::Comment(comment, pos), + tries: 0, + span, + } + } + + fn is_comment(&self) -> bool { + matches!(self.kind, ChainItemKind::Comment(..)) + } + + fn rewrite_method_call( + method_name: symbol::Ident, + types: &[ast::GenericArg], + args: &[ptr::P], + span: Span, + context: &RewriteContext<'_>, + shape: Shape, + ) -> Option { + let type_str = if types.is_empty() { + String::new() + } else { + let type_list = types + .iter() + .map(|ty| ty.rewrite(context, shape)) + .collect::>>()?; + + format!("::<{}>", type_list.join(", ")) + }; + let callee_str = format!(".{}{}", rewrite_ident(context, method_name), type_str); + rewrite_call(context, &callee_str, &args, span, shape) + } +} + +#[derive(Debug)] +struct Chain { + parent: ChainItem, + children: Vec, +} + +impl Chain { + fn from_ast(expr: &ast::Expr, context: &RewriteContext<'_>) -> Chain { + let subexpr_list = Self::make_subexpr_list(expr, context); + + // Un-parse the expression tree into ChainItems + let mut rev_children = vec![]; + let mut sub_tries = 0; + for subexpr in &subexpr_list { + match subexpr.kind { + ast::ExprKind::Try(_) => sub_tries += 1, + _ => { + rev_children.push(ChainItem::new(context, subexpr, sub_tries)); + sub_tries = 0; + } + } + } + + fn is_tries(s: &str) -> bool { + s.chars().all(|c| c == '?') + } + + fn is_post_comment(s: &str) -> bool { + let comment_start_index = s.chars().position(|c| c == '/'); + if comment_start_index.is_none() { + return false; + } + + let newline_index = s.chars().position(|c| c == '\n'); + if newline_index.is_none() { + return true; + } + + comment_start_index.unwrap() < newline_index.unwrap() + } + + fn handle_post_comment( + post_comment_span: Span, + post_comment_snippet: &str, + prev_span_end: &mut BytePos, + children: &mut Vec, + ) { + let white_spaces: &[_] = &[' ', '\t']; + if post_comment_snippet + .trim_matches(white_spaces) + .starts_with('\n') + { + // No post comment. + return; + } + let trimmed_snippet = trim_tries(post_comment_snippet); + if is_post_comment(&trimmed_snippet) { + children.push(ChainItem::comment( + post_comment_span, + trimmed_snippet.trim().to_owned(), + CommentPosition::Back, + )); + *prev_span_end = post_comment_span.hi(); + } + } + + let parent = rev_children.pop().unwrap(); + let mut children = vec![]; + let mut prev_span_end = parent.span.hi(); + let mut iter = rev_children.into_iter().rev().peekable(); + if let Some(first_chain_item) = iter.peek() { + let comment_span = mk_sp(prev_span_end, first_chain_item.span.lo()); + let comment_snippet = context.snippet(comment_span); + if !is_tries(comment_snippet.trim()) { + handle_post_comment( + comment_span, + comment_snippet, + &mut prev_span_end, + &mut children, + ); + } + } + while let Some(chain_item) = iter.next() { + let comment_snippet = context.snippet(chain_item.span); + // FIXME: Figure out the way to get a correct span when converting `try!` to `?`. + let handle_comment = + !(context.config.use_try_shorthand() || is_tries(comment_snippet.trim())); + + // Pre-comment + if handle_comment { + let pre_comment_span = mk_sp(prev_span_end, chain_item.span.lo()); + let pre_comment_snippet = trim_tries(context.snippet(pre_comment_span)); + let (pre_comment, _) = extract_pre_comment(&pre_comment_snippet); + match pre_comment { + Some(ref comment) if !comment.is_empty() => { + children.push(ChainItem::comment( + pre_comment_span, + comment.to_owned(), + CommentPosition::Top, + )); + } + _ => (), + } + } + + prev_span_end = chain_item.span.hi(); + children.push(chain_item); + + // Post-comment + if !handle_comment || iter.peek().is_none() { + continue; + } + + let next_lo = iter.peek().unwrap().span.lo(); + let post_comment_span = mk_sp(prev_span_end, next_lo); + let post_comment_snippet = context.snippet(post_comment_span); + handle_post_comment( + post_comment_span, + post_comment_snippet, + &mut prev_span_end, + &mut children, + ); + } + + Chain { parent, children } + } + + // Returns a Vec of the prefixes of the chain. + // E.g., for input `a.b.c` we return [`a.b.c`, `a.b`, 'a'] + fn make_subexpr_list(expr: &ast::Expr, context: &RewriteContext<'_>) -> Vec { + let mut subexpr_list = vec![expr.clone()]; + + while let Some(subexpr) = Self::pop_expr_chain(subexpr_list.last().unwrap(), context) { + subexpr_list.push(subexpr.clone()); + } + + subexpr_list + } + + // Returns the expression's subexpression, if it exists. When the subexpr + // is a try! macro, we'll convert it to shorthand when the option is set. + fn pop_expr_chain(expr: &ast::Expr, context: &RewriteContext<'_>) -> Option { + match expr.kind { + ast::ExprKind::MethodCall(ref call) => Some(Self::convert_try(&call.receiver, context)), + ast::ExprKind::Field(ref subexpr, _) + | ast::ExprKind::Try(ref subexpr) + | ast::ExprKind::Await(ref subexpr, _) => Some(Self::convert_try(subexpr, context)), + _ => None, + } + } + + fn convert_try(expr: &ast::Expr, context: &RewriteContext<'_>) -> ast::Expr { + match expr.kind { + ast::ExprKind::MacCall(ref mac) if context.config.use_try_shorthand() => { + if let Some(subexpr) = convert_try_mac(mac, context) { + subexpr + } else { + expr.clone() + } + } + _ => expr.clone(), + } + } +} + +impl Rewrite for Chain { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + debug!("rewrite chain {:?} {:?}", self, shape); + + let mut formatter = match context.config.indent_style() { + IndentStyle::Block => { + Box::new(ChainFormatterBlock::new(self)) as Box + } + IndentStyle::Visual => { + Box::new(ChainFormatterVisual::new(self)) as Box + } + }; + + formatter.format_root(&self.parent, context, shape)?; + if let Some(result) = formatter.pure_root() { + return wrap_str(result, context.config.max_width(), shape); + } + + // Decide how to layout the rest of the chain. + let child_shape = formatter.child_shape(context, shape)?; + + formatter.format_children(context, child_shape)?; + formatter.format_last_child(context, shape, child_shape)?; + + let result = formatter.join_rewrites(context, child_shape)?; + wrap_str(result, context.config.max_width(), shape) + } +} + +// There are a few types for formatting chains. This is because there is a lot +// in common between formatting with block vs visual indent, but they are +// different enough that branching on the indent all over the place gets ugly. +// Anything that can format a chain is a ChainFormatter. +trait ChainFormatter { + // Parent is the first item in the chain, e.g., `foo` in `foo.bar.baz()`. + // Root is the parent plus any other chain items placed on the first line to + // avoid an orphan. E.g., + // ```text + // foo.bar + // .baz() + // ``` + // If `bar` were not part of the root, then foo would be orphaned and 'float'. + fn format_root( + &mut self, + parent: &ChainItem, + context: &RewriteContext<'_>, + shape: Shape, + ) -> Option<()>; + fn child_shape(&self, context: &RewriteContext<'_>, shape: Shape) -> Option; + fn format_children(&mut self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<()>; + fn format_last_child( + &mut self, + context: &RewriteContext<'_>, + shape: Shape, + child_shape: Shape, + ) -> Option<()>; + fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> Option; + // Returns `Some` if the chain is only a root, None otherwise. + fn pure_root(&mut self) -> Option; +} + +// Data and behaviour that is shared by both chain formatters. The concrete +// formatters can delegate much behaviour to `ChainFormatterShared`. +struct ChainFormatterShared<'a> { + // The current working set of child items. + children: &'a [ChainItem], + // The current rewrites of items (includes trailing `?`s, but not any way to + // connect the rewrites together). + rewrites: Vec, + // Whether the chain can fit on one line. + fits_single_line: bool, + // The number of children in the chain. This is not equal to `self.children.len()` + // because `self.children` will change size as we process the chain. + child_count: usize, + // Whether elements are allowed to overflow past the max_width limit + allow_overflow: bool, +} + +impl<'a> ChainFormatterShared<'a> { + fn new(chain: &'a Chain) -> ChainFormatterShared<'a> { + ChainFormatterShared { + children: &chain.children, + rewrites: Vec::with_capacity(chain.children.len() + 1), + fits_single_line: false, + child_count: chain.children.len(), + // TODO(calebcartwright) + allow_overflow: false, + } + } + + fn pure_root(&mut self) -> Option { + if self.children.is_empty() { + assert_eq!(self.rewrites.len(), 1); + Some(self.rewrites.pop().unwrap()) + } else { + None + } + } + + fn format_children(&mut self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<()> { + for item in &self.children[..self.children.len() - 1] { + let rewrite = format_chain_item(item, context, child_shape, self.allow_overflow)?; + self.rewrites.push(rewrite); + } + Some(()) + } + + // Rewrite the last child. The last child of a chain requires special treatment. We need to + // know whether 'overflowing' the last child make a better formatting: + // + // A chain with overflowing the last child: + // ```text + // parent.child1.child2.last_child( + // a, + // b, + // c, + // ) + // ``` + // + // A chain without overflowing the last child (in vertical layout): + // ```text + // parent + // .child1 + // .child2 + // .last_child(a, b, c) + // ``` + // + // In particular, overflowing is effective when the last child is a method with a multi-lined + // block-like argument (e.g., closure): + // ```text + // parent.child1.child2.last_child(|a, b, c| { + // let x = foo(a, b, c); + // let y = bar(a, b, c); + // + // // ... + // + // result + // }) + // ``` + fn format_last_child( + &mut self, + may_extend: bool, + context: &RewriteContext<'_>, + shape: Shape, + child_shape: Shape, + ) -> Option<()> { + let last = self.children.last()?; + let extendable = may_extend && last_line_extendable(&self.rewrites[0]); + let prev_last_line_width = last_line_width(&self.rewrites[0]); + + // Total of all items excluding the last. + let almost_total = if extendable { + prev_last_line_width + } else { + self.rewrites + .iter() + .map(|rw| utils::unicode_str_width(rw)) + .sum() + } + last.tries; + let one_line_budget = if self.child_count == 1 { + shape.width + } else { + min(shape.width, context.config.chain_width()) + } + .saturating_sub(almost_total); + + let all_in_one_line = !self.children.iter().any(ChainItem::is_comment) + && self.rewrites.iter().all(|s| !s.contains('\n')) + && one_line_budget > 0; + let last_shape = if all_in_one_line { + shape.sub_width(last.tries)? + } else if extendable { + child_shape.sub_width(last.tries)? + } else { + child_shape.sub_width(shape.rhs_overhead(context.config) + last.tries)? + }; + + let mut last_subexpr_str = None; + if all_in_one_line || extendable { + // First we try to 'overflow' the last child and see if it looks better than using + // vertical layout. + let one_line_shape = if context.use_block_indent() { + last_shape.offset_left(almost_total) + } else { + last_shape + .visual_indent(almost_total) + .sub_width(almost_total) + }; + + if let Some(one_line_shape) = one_line_shape { + if let Some(rw) = last.rewrite(context, one_line_shape) { + // We allow overflowing here only if both of the following conditions match: + // 1. The entire chain fits in a single line except the last child. + // 2. `last_child_str.lines().count() >= 5`. + let line_count = rw.lines().count(); + let could_fit_single_line = first_line_width(&rw) <= one_line_budget; + if could_fit_single_line && line_count >= 5 { + last_subexpr_str = Some(rw); + self.fits_single_line = all_in_one_line; + } else { + // We could not know whether overflowing is better than using vertical + // layout, just by looking at the overflowed rewrite. Now we rewrite the + // last child on its own line, and compare two rewrites to choose which is + // better. + let last_shape = child_shape + .sub_width(shape.rhs_overhead(context.config) + last.tries)?; + match last.rewrite(context, last_shape) { + Some(ref new_rw) if !could_fit_single_line => { + last_subexpr_str = Some(new_rw.clone()); + } + Some(ref new_rw) if new_rw.lines().count() >= line_count => { + last_subexpr_str = Some(rw); + self.fits_single_line = could_fit_single_line && all_in_one_line; + } + new_rw @ Some(..) => { + last_subexpr_str = new_rw; + } + _ => { + last_subexpr_str = Some(rw); + self.fits_single_line = could_fit_single_line && all_in_one_line; + } + } + } + } + } + } + + let last_shape = if context.use_block_indent() { + last_shape + } else { + child_shape.sub_width(shape.rhs_overhead(context.config) + last.tries)? + }; + + last_subexpr_str = last_subexpr_str.or_else(|| last.rewrite(context, last_shape)); + self.rewrites.push(last_subexpr_str?); + Some(()) + } + + fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> Option { + let connector = if self.fits_single_line { + // Yay, we can put everything on one line. + Cow::from("") + } else { + // Use new lines. + if context.force_one_line_chain.get() { + return None; + } + child_shape.to_string_with_newline(context.config) + }; + + let mut rewrite_iter = self.rewrites.iter(); + let mut result = rewrite_iter.next().unwrap().clone(); + let children_iter = self.children.iter(); + let iter = rewrite_iter.zip(children_iter); + + for (rewrite, chain_item) in iter { + match chain_item.kind { + ChainItemKind::Comment(_, CommentPosition::Back) => result.push(' '), + ChainItemKind::Comment(_, CommentPosition::Top) => result.push_str(&connector), + _ => result.push_str(&connector), + } + result.push_str(rewrite); + } + + Some(result) + } +} + +// Formats a chain using block indent. +struct ChainFormatterBlock<'a> { + shared: ChainFormatterShared<'a>, + root_ends_with_block: bool, +} + +impl<'a> ChainFormatterBlock<'a> { + fn new(chain: &'a Chain) -> ChainFormatterBlock<'a> { + ChainFormatterBlock { + shared: ChainFormatterShared::new(chain), + root_ends_with_block: false, + } + } +} + +impl<'a> ChainFormatter for ChainFormatterBlock<'a> { + fn format_root( + &mut self, + parent: &ChainItem, + context: &RewriteContext<'_>, + shape: Shape, + ) -> Option<()> { + let mut root_rewrite: String = parent.rewrite(context, shape)?; + + let mut root_ends_with_block = parent.kind.is_block_like(context, &root_rewrite); + let tab_width = context.config.tab_spaces().saturating_sub(shape.offset); + + while root_rewrite.len() <= tab_width && !root_rewrite.contains('\n') { + let item = &self.shared.children[0]; + if let ChainItemKind::Comment(..) = item.kind { + break; + } + let shape = shape.offset_left(root_rewrite.len())?; + match &item.rewrite(context, shape) { + Some(rewrite) => root_rewrite.push_str(rewrite), + None => break, + } + + root_ends_with_block = last_line_extendable(&root_rewrite); + + self.shared.children = &self.shared.children[1..]; + if self.shared.children.is_empty() { + break; + } + } + self.shared.rewrites.push(root_rewrite); + self.root_ends_with_block = root_ends_with_block; + Some(()) + } + + fn child_shape(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + let block_end = self.root_ends_with_block; + Some(get_block_child_shape(block_end, context, shape)) + } + + fn format_children(&mut self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<()> { + self.shared.format_children(context, child_shape) + } + + fn format_last_child( + &mut self, + context: &RewriteContext<'_>, + shape: Shape, + child_shape: Shape, + ) -> Option<()> { + self.shared + .format_last_child(true, context, shape, child_shape) + } + + fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> Option { + self.shared.join_rewrites(context, child_shape) + } + + fn pure_root(&mut self) -> Option { + self.shared.pure_root() + } +} + +// Format a chain using visual indent. +struct ChainFormatterVisual<'a> { + shared: ChainFormatterShared<'a>, + // The extra offset from the chain's shape to the position of the `.` + offset: usize, +} + +impl<'a> ChainFormatterVisual<'a> { + fn new(chain: &'a Chain) -> ChainFormatterVisual<'a> { + ChainFormatterVisual { + shared: ChainFormatterShared::new(chain), + offset: 0, + } + } +} + +impl<'a> ChainFormatter for ChainFormatterVisual<'a> { + fn format_root( + &mut self, + parent: &ChainItem, + context: &RewriteContext<'_>, + shape: Shape, + ) -> Option<()> { + let parent_shape = shape.visual_indent(0); + let mut root_rewrite = parent.rewrite(context, parent_shape)?; + let multiline = root_rewrite.contains('\n'); + self.offset = if multiline { + last_line_width(&root_rewrite).saturating_sub(shape.used_width()) + } else { + trimmed_last_line_width(&root_rewrite) + }; + + if !multiline || parent.kind.is_block_like(context, &root_rewrite) { + let item = &self.shared.children[0]; + if let ChainItemKind::Comment(..) = item.kind { + self.shared.rewrites.push(root_rewrite); + return Some(()); + } + let child_shape = parent_shape + .visual_indent(self.offset) + .sub_width(self.offset)?; + let rewrite = item.rewrite(context, child_shape)?; + if filtered_str_fits(&rewrite, context.config.max_width(), shape) { + root_rewrite.push_str(&rewrite); + } else { + // We couldn't fit in at the visual indent, try the last + // indent. + let rewrite = item.rewrite(context, parent_shape)?; + root_rewrite.push_str(&rewrite); + self.offset = 0; + } + + self.shared.children = &self.shared.children[1..]; + } + + self.shared.rewrites.push(root_rewrite); + Some(()) + } + + fn child_shape(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + get_visual_style_child_shape( + context, + shape, + self.offset, + // TODO(calebcartwright): self.shared.permissibly_overflowing_parent, + false, + ) + } + + fn format_children(&mut self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<()> { + self.shared.format_children(context, child_shape) + } + + fn format_last_child( + &mut self, + context: &RewriteContext<'_>, + shape: Shape, + child_shape: Shape, + ) -> Option<()> { + self.shared + .format_last_child(false, context, shape, child_shape) + } + + fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> Option { + self.shared.join_rewrites(context, child_shape) + } + + fn pure_root(&mut self) -> Option { + self.shared.pure_root() + } +} + +/// Removes try operators (`?`s) that appear in the given string. If removing +/// them leaves an empty line, remove that line as well unless it is the first +/// line (we need the first newline for detecting pre/post comment). +fn trim_tries(s: &str) -> String { + let mut result = String::with_capacity(s.len()); + let mut line_buffer = String::with_capacity(s.len()); + for (kind, rich_char) in CharClasses::new(s.chars()) { + match rich_char.get_char() { + '\n' => { + if result.is_empty() || !line_buffer.trim().is_empty() { + result.push_str(&line_buffer); + result.push('\n') + } + line_buffer.clear(); + } + '?' if kind == FullCodeCharKind::Normal => continue, + c => line_buffer.push(c), + } + } + if !line_buffer.trim().is_empty() { + result.push_str(&line_buffer); + } + result +} diff --git a/src/closures.rs b/src/closures.rs new file mode 100644 index 000000000000..c95e9a97b43d --- /dev/null +++ b/src/closures.rs @@ -0,0 +1,469 @@ +use rustc_ast::{ast, ptr}; +use rustc_span::Span; +use thin_vec::thin_vec; + +use crate::attr::get_attrs_from_stmt; +use crate::config::lists::*; +use crate::config::Version; +use crate::expr::{block_contains_comment, is_simple_block, is_unsafe_block, rewrite_cond}; +use crate::items::{span_hi_for_param, span_lo_for_param}; +use crate::lists::{definitive_tactic, itemize_list, write_list, ListFormatting, Separator}; +use crate::overflow::OverflowableItem; +use crate::rewrite::{Rewrite, RewriteContext}; +use crate::shape::Shape; +use crate::source_map::SpanUtils; +use crate::types::rewrite_lifetime_param; +use crate::utils::{last_line_width, left_most_sub_expr, stmt_expr, NodeIdExt}; + +// This module is pretty messy because of the rules around closures and blocks: +// FIXME - the below is probably no longer true in full. +// * if there is a return type, then there must be braces, +// * given a closure with braces, whether that is parsed to give an inner block +// or not depends on if there is a return type and if there are statements +// in that block, +// * if the first expression in the body ends with a block (i.e., is a +// statement without needing a semi-colon), then adding or removing braces +// can change whether it is treated as an expression or statement. + +pub(crate) fn rewrite_closure( + binder: &ast::ClosureBinder, + constness: ast::Const, + capture: ast::CaptureBy, + is_async: &ast::Async, + movability: ast::Movability, + fn_decl: &ast::FnDecl, + body: &ast::Expr, + span: Span, + context: &RewriteContext<'_>, + shape: Shape, +) -> Option { + debug!("rewrite_closure {:?}", body); + + let (prefix, extra_offset) = rewrite_closure_fn_decl( + binder, constness, capture, is_async, movability, fn_decl, body, span, context, shape, + )?; + // 1 = space between `|...|` and body. + let body_shape = shape.offset_left(extra_offset)?; + + if let ast::ExprKind::Block(ref block, _) = body.kind { + // The body of the closure is an empty block. + if block.stmts.is_empty() && !block_contains_comment(context, block) { + return body + .rewrite(context, shape) + .map(|s| format!("{} {}", prefix, s)); + } + + let result = match fn_decl.output { + ast::FnRetTy::Default(_) if !context.inside_macro() => { + try_rewrite_without_block(body, &prefix, context, shape, body_shape) + } + _ => None, + }; + + result.or_else(|| { + // Either we require a block, or tried without and failed. + rewrite_closure_block(block, &prefix, context, body_shape) + }) + } else { + rewrite_closure_expr(body, &prefix, context, body_shape).or_else(|| { + // The closure originally had a non-block expression, but we can't fit on + // one line, so we'll insert a block. + rewrite_closure_with_block(body, &prefix, context, body_shape) + }) + } +} + +fn try_rewrite_without_block( + expr: &ast::Expr, + prefix: &str, + context: &RewriteContext<'_>, + shape: Shape, + body_shape: Shape, +) -> Option { + let expr = get_inner_expr(expr, prefix, context); + + if is_block_closure_forced(context, expr) { + rewrite_closure_with_block(expr, prefix, context, shape) + } else { + rewrite_closure_expr(expr, prefix, context, body_shape) + } +} + +fn get_inner_expr<'a>( + expr: &'a ast::Expr, + prefix: &str, + context: &RewriteContext<'_>, +) -> &'a ast::Expr { + if let ast::ExprKind::Block(ref block, _) = expr.kind { + if !needs_block(block, prefix, context) { + // block.stmts.len() == 1 except with `|| {{}}`; + // https://github.com/rust-lang/rustfmt/issues/3844 + if let Some(expr) = block.stmts.first().and_then(stmt_expr) { + return get_inner_expr(expr, prefix, context); + } + } + } + + expr +} + +// Figure out if a block is necessary. +fn needs_block(block: &ast::Block, prefix: &str, context: &RewriteContext<'_>) -> bool { + let has_attributes = block.stmts.first().map_or(false, |first_stmt| { + !get_attrs_from_stmt(first_stmt).is_empty() + }); + + is_unsafe_block(block) + || block.stmts.len() > 1 + || has_attributes + || block_contains_comment(context, block) + || prefix.contains('\n') +} + +fn veto_block(e: &ast::Expr) -> bool { + match e.kind { + ast::ExprKind::Call(..) + | ast::ExprKind::Binary(..) + | ast::ExprKind::Cast(..) + | ast::ExprKind::Type(..) + | ast::ExprKind::Assign(..) + | ast::ExprKind::AssignOp(..) + | ast::ExprKind::Field(..) + | ast::ExprKind::Index(..) + | ast::ExprKind::Range(..) + | ast::ExprKind::Try(..) => true, + _ => false, + } +} + +// Rewrite closure with a single expression wrapping its body with block. +// || { #[attr] foo() } -> Block { #[attr] foo() } +fn rewrite_closure_with_block( + body: &ast::Expr, + prefix: &str, + context: &RewriteContext<'_>, + shape: Shape, +) -> Option { + let left_most = left_most_sub_expr(body); + let veto_block = veto_block(body) && !expr_requires_semi_to_be_stmt(left_most); + if veto_block { + return None; + } + + let block = ast::Block { + stmts: thin_vec![ast::Stmt { + id: ast::NodeId::root(), + kind: ast::StmtKind::Expr(ptr::P(body.clone())), + span: body.span, + }], + id: ast::NodeId::root(), + rules: ast::BlockCheckMode::Default, + tokens: None, + span: body + .attrs + .first() + .map(|attr| attr.span.to(body.span)) + .unwrap_or(body.span), + could_be_bare_literal: false, + }; + let block = crate::expr::rewrite_block_with_visitor( + context, + "", + &block, + Some(&body.attrs), + None, + shape, + false, + )?; + Some(format!("{} {}", prefix, block)) +} + +// Rewrite closure with a single expression without wrapping its body with block. +fn rewrite_closure_expr( + expr: &ast::Expr, + prefix: &str, + context: &RewriteContext<'_>, + shape: Shape, +) -> Option { + fn allow_multi_line(expr: &ast::Expr) -> bool { + match expr.kind { + ast::ExprKind::Match(..) + | ast::ExprKind::Async(..) + | ast::ExprKind::Block(..) + | ast::ExprKind::TryBlock(..) + | ast::ExprKind::Loop(..) + | ast::ExprKind::Struct(..) => true, + + ast::ExprKind::AddrOf(_, _, ref expr) + | ast::ExprKind::Try(ref expr) + | ast::ExprKind::Unary(_, ref expr) + | ast::ExprKind::Cast(ref expr, _) => allow_multi_line(expr), + + _ => false, + } + } + + // When rewriting closure's body without block, we require it to fit in a single line + // unless it is a block-like expression or we are inside macro call. + let veto_multiline = (!allow_multi_line(expr) && !context.inside_macro()) + || context.config.force_multiline_blocks(); + expr.rewrite(context, shape) + .and_then(|rw| { + if veto_multiline && rw.contains('\n') { + None + } else { + Some(rw) + } + }) + .map(|rw| format!("{} {}", prefix, rw)) +} + +// Rewrite closure whose body is block. +fn rewrite_closure_block( + block: &ast::Block, + prefix: &str, + context: &RewriteContext<'_>, + shape: Shape, +) -> Option { + Some(format!("{} {}", prefix, block.rewrite(context, shape)?)) +} + +// Return type is (prefix, extra_offset) +fn rewrite_closure_fn_decl( + binder: &ast::ClosureBinder, + constness: ast::Const, + capture: ast::CaptureBy, + asyncness: &ast::Async, + movability: ast::Movability, + fn_decl: &ast::FnDecl, + body: &ast::Expr, + span: Span, + context: &RewriteContext<'_>, + shape: Shape, +) -> Option<(String, usize)> { + let binder = match binder { + ast::ClosureBinder::For { generic_params, .. } if generic_params.is_empty() => { + "for<> ".to_owned() + } + ast::ClosureBinder::For { generic_params, .. } => { + let lifetime_str = rewrite_lifetime_param(context, shape, generic_params)?; + format!("for<{lifetime_str}> ") + } + ast::ClosureBinder::NotPresent => "".to_owned(), + }; + + let const_ = if matches!(constness, ast::Const::Yes(_)) { + "const " + } else { + "" + }; + + let immovable = if movability == ast::Movability::Static { + "static " + } else { + "" + }; + let is_async = if asyncness.is_async() { "async " } else { "" }; + let mover = if capture == ast::CaptureBy::Value { + "move " + } else { + "" + }; + // 4 = "|| {".len(), which is overconservative when the closure consists of + // a single expression. + let nested_shape = shape + .shrink_left(binder.len() + const_.len() + immovable.len() + is_async.len() + mover.len())? + .sub_width(4)?; + + // 1 = | + let param_offset = nested_shape.indent + 1; + let param_shape = nested_shape.offset_left(1)?.visual_indent(0); + let ret_str = fn_decl.output.rewrite(context, param_shape)?; + + let param_items = itemize_list( + context.snippet_provider, + fn_decl.inputs.iter(), + "|", + ",", + |param| span_lo_for_param(param), + |param| span_hi_for_param(context, param), + |param| param.rewrite(context, param_shape), + context.snippet_provider.span_after(span, "|"), + body.span.lo(), + false, + ); + let item_vec = param_items.collect::>(); + // 1 = space between parameters and return type. + let horizontal_budget = nested_shape.width.saturating_sub(ret_str.len() + 1); + let tactic = definitive_tactic( + &item_vec, + ListTactic::HorizontalVertical, + Separator::Comma, + horizontal_budget, + ); + let param_shape = match tactic { + DefinitiveListTactic::Horizontal => param_shape.sub_width(ret_str.len() + 1)?, + _ => param_shape, + }; + + let fmt = ListFormatting::new(param_shape, context.config) + .tactic(tactic) + .preserve_newline(true); + let list_str = write_list(&item_vec, &fmt)?; + let mut prefix = format!( + "{}{}{}{}{}|{}|", + binder, const_, immovable, is_async, mover, list_str + ); + + if !ret_str.is_empty() { + if prefix.contains('\n') { + prefix.push('\n'); + prefix.push_str(¶m_offset.to_string(context.config)); + } else { + prefix.push(' '); + } + prefix.push_str(&ret_str); + } + // 1 = space between `|...|` and body. + let extra_offset = last_line_width(&prefix) + 1; + + Some((prefix, extra_offset)) +} + +// Rewriting closure which is placed at the end of the function call's arg. +// Returns `None` if the reformatted closure 'looks bad'. +pub(crate) fn rewrite_last_closure( + context: &RewriteContext<'_>, + expr: &ast::Expr, + shape: Shape, +) -> Option { + if let ast::ExprKind::Closure(ref closure) = expr.kind { + let ast::Closure { + ref binder, + constness, + capture_clause, + ref asyncness, + movability, + ref fn_decl, + ref body, + fn_decl_span: _, + fn_arg_span: _, + } = **closure; + let body = match body.kind { + ast::ExprKind::Block(ref block, _) + if !is_unsafe_block(block) + && !context.inside_macro() + && is_simple_block(context, block, Some(&body.attrs)) => + { + stmt_expr(&block.stmts[0]).unwrap_or(body) + } + _ => body, + }; + let (prefix, extra_offset) = rewrite_closure_fn_decl( + binder, + constness, + capture_clause, + asyncness, + movability, + fn_decl, + body, + expr.span, + context, + shape, + )?; + // If the closure goes multi line before its body, do not overflow the closure. + if prefix.contains('\n') { + return None; + } + + let body_shape = shape.offset_left(extra_offset)?; + + // We force to use block for the body of the closure for certain kinds of expressions. + if is_block_closure_forced(context, body) { + return rewrite_closure_with_block(body, &prefix, context, body_shape).map( + |body_str| { + match fn_decl.output { + ast::FnRetTy::Default(..) if body_str.lines().count() <= 7 => { + // If the expression can fit in a single line, we need not force block + // closure. However, if the closure has a return type, then we must + // keep the blocks. + match rewrite_closure_expr(body, &prefix, context, shape) { + Some(single_line_body_str) + if !single_line_body_str.contains('\n') => + { + single_line_body_str + } + _ => body_str, + } + } + _ => body_str, + } + }, + ); + } + + // When overflowing the closure which consists of a single control flow expression, + // force to use block if its condition uses multi line. + let is_multi_lined_cond = rewrite_cond(context, body, body_shape).map_or(false, |cond| { + cond.contains('\n') || cond.len() > body_shape.width + }); + if is_multi_lined_cond { + return rewrite_closure_with_block(body, &prefix, context, body_shape); + } + + // Seems fine, just format the closure in usual manner. + return expr.rewrite(context, shape); + } + None +} + +/// Returns `true` if the given vector of arguments has more than one `ast::ExprKind::Closure`. +pub(crate) fn args_have_many_closure(args: &[OverflowableItem<'_>]) -> bool { + args.iter() + .filter_map(OverflowableItem::to_expr) + .filter(|expr| matches!(expr.kind, ast::ExprKind::Closure(..))) + .count() + > 1 +} + +fn is_block_closure_forced(context: &RewriteContext<'_>, expr: &ast::Expr) -> bool { + // If we are inside macro, we do not want to add or remove block from closure body. + if context.inside_macro() { + false + } else { + is_block_closure_forced_inner(expr, context.config.version()) + } +} + +fn is_block_closure_forced_inner(expr: &ast::Expr, version: Version) -> bool { + match expr.kind { + ast::ExprKind::If(..) | ast::ExprKind::While(..) | ast::ExprKind::ForLoop(..) => true, + ast::ExprKind::Loop(..) if version == Version::Two => true, + ast::ExprKind::AddrOf(_, _, ref expr) + | ast::ExprKind::Try(ref expr) + | ast::ExprKind::Unary(_, ref expr) + | ast::ExprKind::Cast(ref expr, _) => is_block_closure_forced_inner(expr, version), + _ => false, + } +} + +/// Does this expression require a semicolon to be treated +/// as a statement? The negation of this: 'can this expression +/// be used as a statement without a semicolon' -- is used +/// as an early-bail-out in the parser so that, for instance, +/// if true {...} else {...} +/// |x| 5 +/// isn't parsed as (if true {...} else {...} | x) | 5 +// From https://github.com/rust-lang/rust/blob/master/src/libsyntax/parse/classify.rs. +fn expr_requires_semi_to_be_stmt(e: &ast::Expr) -> bool { + match e.kind { + ast::ExprKind::If(..) + | ast::ExprKind::Match(..) + | ast::ExprKind::Block(..) + | ast::ExprKind::While(..) + | ast::ExprKind::Loop(..) + | ast::ExprKind::ForLoop(..) + | ast::ExprKind::TryBlock(..) => false, + _ => true, + } +} diff --git a/src/comment.rs b/src/comment.rs new file mode 100644 index 000000000000..85918ecc1164 --- /dev/null +++ b/src/comment.rs @@ -0,0 +1,2155 @@ +// Formatting and tools for comments. + +use std::{self, borrow::Cow, iter}; + +use itertools::{multipeek, MultiPeek}; +use lazy_static::lazy_static; +use regex::Regex; +use rustc_span::Span; + +use crate::config::Config; +use crate::rewrite::RewriteContext; +use crate::shape::{Indent, Shape}; +use crate::string::{rewrite_string, StringFormat}; +use crate::utils::{ + count_newlines, first_line_width, last_line_width, trim_left_preserve_layout, + trimmed_last_line_width, unicode_str_width, +}; +use crate::{ErrorKind, FormattingError}; + +lazy_static! { + /// A regex matching reference doc links. + /// + /// ```markdown + /// /// An [example]. + /// /// + /// /// [example]: this::is::a::link + /// ``` + static ref REFERENCE_LINK_URL: Regex = Regex::new(r"^\[.+\]\s?:").unwrap(); +} + +fn is_custom_comment(comment: &str) -> bool { + if !comment.starts_with("//") { + false + } else if let Some(c) = comment.chars().nth(2) { + !c.is_alphanumeric() && !c.is_whitespace() + } else { + false + } +} + +#[derive(Copy, Clone, PartialEq, Eq)] +pub(crate) enum CommentStyle<'a> { + DoubleSlash, + TripleSlash, + Doc, + SingleBullet, + DoubleBullet, + Exclamation, + Custom(&'a str), +} + +fn custom_opener(s: &str) -> &str { + s.lines().next().map_or("", |first_line| { + first_line + .find(' ') + .map_or(first_line, |space_index| &first_line[0..=space_index]) + }) +} + +impl<'a> CommentStyle<'a> { + /// Returns `true` if the commenting style covers a line only. + pub(crate) fn is_line_comment(&self) -> bool { + match *self { + CommentStyle::DoubleSlash + | CommentStyle::TripleSlash + | CommentStyle::Doc + | CommentStyle::Custom(_) => true, + _ => false, + } + } + + /// Returns `true` if the commenting style can span over multiple lines. + pub(crate) fn is_block_comment(&self) -> bool { + match *self { + CommentStyle::SingleBullet | CommentStyle::DoubleBullet | CommentStyle::Exclamation => { + true + } + _ => false, + } + } + + /// Returns `true` if the commenting style is for documentation. + pub(crate) fn is_doc_comment(&self) -> bool { + matches!(*self, CommentStyle::TripleSlash | CommentStyle::Doc) + } + + pub(crate) fn opener(&self) -> &'a str { + match *self { + CommentStyle::DoubleSlash => "// ", + CommentStyle::TripleSlash => "/// ", + CommentStyle::Doc => "//! ", + CommentStyle::SingleBullet => "/* ", + CommentStyle::DoubleBullet => "/** ", + CommentStyle::Exclamation => "/*! ", + CommentStyle::Custom(opener) => opener, + } + } + + pub(crate) fn closer(&self) -> &'a str { + match *self { + CommentStyle::DoubleSlash + | CommentStyle::TripleSlash + | CommentStyle::Custom(..) + | CommentStyle::Doc => "", + CommentStyle::SingleBullet | CommentStyle::DoubleBullet | CommentStyle::Exclamation => { + " */" + } + } + } + + pub(crate) fn line_start(&self) -> &'a str { + match *self { + CommentStyle::DoubleSlash => "// ", + CommentStyle::TripleSlash => "/// ", + CommentStyle::Doc => "//! ", + CommentStyle::SingleBullet | CommentStyle::DoubleBullet | CommentStyle::Exclamation => { + " * " + } + CommentStyle::Custom(opener) => opener, + } + } + + pub(crate) fn to_str_tuplet(&self) -> (&'a str, &'a str, &'a str) { + (self.opener(), self.closer(), self.line_start()) + } +} + +pub(crate) fn comment_style(orig: &str, normalize_comments: bool) -> CommentStyle<'_> { + if !normalize_comments { + if orig.starts_with("/**") && !orig.starts_with("/**/") { + CommentStyle::DoubleBullet + } else if orig.starts_with("/*!") { + CommentStyle::Exclamation + } else if orig.starts_with("/*") { + CommentStyle::SingleBullet + } else if orig.starts_with("///") && orig.chars().nth(3).map_or(true, |c| c != '/') { + CommentStyle::TripleSlash + } else if orig.starts_with("//!") { + CommentStyle::Doc + } else if is_custom_comment(orig) { + CommentStyle::Custom(custom_opener(orig)) + } else { + CommentStyle::DoubleSlash + } + } else if (orig.starts_with("///") && orig.chars().nth(3).map_or(true, |c| c != '/')) + || (orig.starts_with("/**") && !orig.starts_with("/**/")) + { + CommentStyle::TripleSlash + } else if orig.starts_with("//!") || orig.starts_with("/*!") { + CommentStyle::Doc + } else if is_custom_comment(orig) { + CommentStyle::Custom(custom_opener(orig)) + } else { + CommentStyle::DoubleSlash + } +} + +/// Returns true if the last line of the passed string finishes with a block-comment. +pub(crate) fn is_last_comment_block(s: &str) -> bool { + s.trim_end().ends_with("*/") +} + +/// Combine `prev_str` and `next_str` into a single `String`. `span` may contain +/// comments between two strings. If there are such comments, then that will be +/// recovered. If `allow_extend` is true and there is no comment between the two +/// strings, then they will be put on a single line as long as doing so does not +/// exceed max width. +pub(crate) fn combine_strs_with_missing_comments( + context: &RewriteContext<'_>, + prev_str: &str, + next_str: &str, + span: Span, + shape: Shape, + allow_extend: bool, +) -> Option { + trace!( + "combine_strs_with_missing_comments `{}` `{}` {:?} {:?}", + prev_str, + next_str, + span, + shape + ); + + let mut result = + String::with_capacity(prev_str.len() + next_str.len() + shape.indent.width() + 128); + result.push_str(prev_str); + let mut allow_one_line = !prev_str.contains('\n') && !next_str.contains('\n'); + let first_sep = + if prev_str.is_empty() || next_str.is_empty() || trimmed_last_line_width(prev_str) == 0 { + "" + } else { + " " + }; + let mut one_line_width = + last_line_width(prev_str) + first_line_width(next_str) + first_sep.len(); + + let config = context.config; + let indent = shape.indent; + let missing_comment = rewrite_missing_comment(span, shape, context)?; + + if missing_comment.is_empty() { + if allow_extend && one_line_width <= shape.width { + result.push_str(first_sep); + } else if !prev_str.is_empty() { + result.push_str(&indent.to_string_with_newline(config)) + } + result.push_str(next_str); + return Some(result); + } + + // We have a missing comment between the first expression and the second expression. + + // Peek the the original source code and find out whether there is a newline between the first + // expression and the second expression or the missing comment. We will preserve the original + // layout whenever possible. + let original_snippet = context.snippet(span); + let prefer_same_line = if let Some(pos) = original_snippet.find('/') { + !original_snippet[..pos].contains('\n') + } else { + !original_snippet.contains('\n') + }; + + one_line_width -= first_sep.len(); + let first_sep = if prev_str.is_empty() || missing_comment.is_empty() { + Cow::from("") + } else { + let one_line_width = last_line_width(prev_str) + first_line_width(&missing_comment) + 1; + if prefer_same_line && one_line_width <= shape.width { + Cow::from(" ") + } else { + indent.to_string_with_newline(config) + } + }; + result.push_str(&first_sep); + result.push_str(&missing_comment); + + let second_sep = if missing_comment.is_empty() || next_str.is_empty() { + Cow::from("") + } else if missing_comment.starts_with("//") { + indent.to_string_with_newline(config) + } else { + one_line_width += missing_comment.len() + first_sep.len() + 1; + allow_one_line &= !missing_comment.starts_with("//") && !missing_comment.contains('\n'); + if prefer_same_line && allow_one_line && one_line_width <= shape.width { + Cow::from(" ") + } else { + indent.to_string_with_newline(config) + } + }; + result.push_str(&second_sep); + result.push_str(next_str); + + Some(result) +} + +pub(crate) fn rewrite_doc_comment(orig: &str, shape: Shape, config: &Config) -> Option { + identify_comment(orig, false, shape, config, true) +} + +pub(crate) fn rewrite_comment( + orig: &str, + block_style: bool, + shape: Shape, + config: &Config, +) -> Option { + identify_comment(orig, block_style, shape, config, false) +} + +fn identify_comment( + orig: &str, + block_style: bool, + shape: Shape, + config: &Config, + is_doc_comment: bool, +) -> Option { + let style = comment_style(orig, false); + + // Computes the byte length of line taking into account a newline if the line is part of a + // paragraph. + fn compute_len(orig: &str, line: &str) -> usize { + if orig.len() > line.len() { + if orig.as_bytes()[line.len()] == b'\r' { + line.len() + 2 + } else { + line.len() + 1 + } + } else { + line.len() + } + } + + // Get the first group of line comments having the same commenting style. + // + // Returns a tuple with: + // - a boolean indicating if there is a blank line + // - a number indicating the size of the first group of comments + fn consume_same_line_comments( + style: CommentStyle<'_>, + orig: &str, + line_start: &str, + ) -> (bool, usize) { + let mut first_group_ending = 0; + let mut hbl = false; + + for line in orig.lines() { + let trimmed_line = line.trim_start(); + if trimmed_line.is_empty() { + hbl = true; + break; + } else if trimmed_line.starts_with(line_start) + || comment_style(trimmed_line, false) == style + { + first_group_ending += compute_len(&orig[first_group_ending..], line); + } else { + break; + } + } + (hbl, first_group_ending) + } + + let (has_bare_lines, first_group_ending) = match style { + CommentStyle::DoubleSlash | CommentStyle::TripleSlash | CommentStyle::Doc => { + let line_start = style.line_start().trim_start(); + consume_same_line_comments(style, orig, line_start) + } + CommentStyle::Custom(opener) => { + let trimmed_opener = opener.trim_end(); + consume_same_line_comments(style, orig, trimmed_opener) + } + // for a block comment, search for the closing symbol + CommentStyle::DoubleBullet | CommentStyle::SingleBullet | CommentStyle::Exclamation => { + let closer = style.closer().trim_start(); + let mut count = orig.matches(closer).count(); + let mut closing_symbol_offset = 0; + let mut hbl = false; + let mut first = true; + for line in orig.lines() { + closing_symbol_offset += compute_len(&orig[closing_symbol_offset..], line); + let mut trimmed_line = line.trim_start(); + if !trimmed_line.starts_with('*') + && !trimmed_line.starts_with("//") + && !trimmed_line.starts_with("/*") + { + hbl = true; + } + + // Remove opener from consideration when searching for closer + if first { + let opener = style.opener().trim_end(); + trimmed_line = &trimmed_line[opener.len()..]; + first = false; + } + if trimmed_line.ends_with(closer) { + count -= 1; + if count == 0 { + break; + } + } + } + (hbl, closing_symbol_offset) + } + }; + + let (first_group, rest) = orig.split_at(first_group_ending); + let rewritten_first_group = + if !config.normalize_comments() && has_bare_lines && style.is_block_comment() { + trim_left_preserve_layout(first_group, shape.indent, config)? + } else if !config.normalize_comments() + && !config.wrap_comments() + && !config.format_code_in_doc_comments() + { + light_rewrite_comment(first_group, shape.indent, config, is_doc_comment) + } else { + rewrite_comment_inner( + first_group, + block_style, + style, + shape, + config, + is_doc_comment || style.is_doc_comment(), + )? + }; + if rest.is_empty() { + Some(rewritten_first_group) + } else { + identify_comment( + rest.trim_start(), + block_style, + shape, + config, + is_doc_comment, + ) + .map(|rest_str| { + format!( + "{}\n{}{}{}", + rewritten_first_group, + // insert back the blank line + if has_bare_lines && style.is_line_comment() { + "\n" + } else { + "" + }, + shape.indent.to_string(config), + rest_str + ) + }) + } +} + +/// Enum indicating if the code block contains rust based on attributes +enum CodeBlockAttribute { + Rust, + NotRust, +} + +impl CodeBlockAttribute { + /// Parse comma separated attributes list. Return rust only if all + /// attributes are valid rust attributes + /// See + fn new(attributes: &str) -> CodeBlockAttribute { + for attribute in attributes.split(',') { + match attribute.trim() { + "" | "rust" | "should_panic" | "no_run" | "edition2015" | "edition2018" + | "edition2021" => (), + "ignore" | "compile_fail" | "text" => return CodeBlockAttribute::NotRust, + _ => return CodeBlockAttribute::NotRust, + } + } + CodeBlockAttribute::Rust + } +} + +/// Block that is formatted as an item. +/// +/// An item starts with either a star `*`, a dash `-`, a greater-than `>`, a plus '+', or a number +/// `12.` or `34)` (with at most 2 digits). An item represents CommonMark's ["list +/// items"](https://spec.commonmark.org/0.30/#list-items) and/or ["block +/// quotes"](https://spec.commonmark.org/0.30/#block-quotes), but note that only a subset of +/// CommonMark is recognized - see the doc comment of [`ItemizedBlock::get_marker_length`] for more +/// details. +/// +/// 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 characters (typically whitespaces) up to the item marker + indent: usize, + /// the string that marks the start of an item + opener: String, + /// sequence of characters (typically whitespaces) to prefix new lines that are part of the item + line_start: String, +} + +impl ItemizedBlock { + /// Checks whether the `trimmed` line includes an item marker. Returns `None` if there is no + /// marker. Returns the length of the marker (in bytes) if one is present. Note that the length + /// includes the whitespace that follows the marker, for example the marker in `"* list item"` + /// has the length of 2. + /// + /// This function recognizes item markers that correspond to CommonMark's + /// ["bullet list marker"](https://spec.commonmark.org/0.30/#bullet-list-marker), + /// ["block quote marker"](https://spec.commonmark.org/0.30/#block-quote-marker), and/or + /// ["ordered list marker"](https://spec.commonmark.org/0.30/#ordered-list-marker). + /// + /// Compared to CommonMark specification, the number of digits that are allowed in an ["ordered + /// list marker"](https://spec.commonmark.org/0.30/#ordered-list-marker) is more limited (to at + /// most 2 digits). Limiting the length of the marker helps reduce the risk of recognizing + /// arbitrary numbers as markers. See also + /// which gives the + /// following example where a number (i.e. "1868") doesn't signify an ordered list: + /// ```md + /// The Captain died in + /// 1868. He wes buried in... + /// ``` + fn get_marker_length(trimmed: &str) -> Option { + // https://spec.commonmark.org/0.30/#bullet-list-marker or + // https://spec.commonmark.org/0.30/#block-quote-marker + let itemized_start = ["* ", "- ", "> ", "+ "]; + if itemized_start.iter().any(|s| trimmed.starts_with(s)) { + return Some(2); // All items in `itemized_start` have length 2. + } + + // https://spec.commonmark.org/0.30/#ordered-list-marker, where at most 2 digits are + // allowed. + for suffix in [". ", ") "] { + if let Some((prefix, _)) = trimmed.split_once(suffix) { + if prefix.len() <= 2 && prefix.chars().all(|c| char::is_ascii_digit(&c)) { + return Some(prefix.len() + suffix.len()); + } + } + } + + None // No markers found. + } + + /// Creates a new `ItemizedBlock` described with the given `line`. + /// Returns `None` if `line` doesn't start an item. + fn new(line: &str) -> Option { + let marker_length = ItemizedBlock::get_marker_length(line.trim_start())?; + let space_to_marker = line.chars().take_while(|c| c.is_whitespace()).count(); + let mut indent = space_to_marker + marker_length; + 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 the 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(); + } + Some(ItemizedBlock { + lines: vec![line[indent..].to_string()], + indent, + opener: line[..indent].to_string(), + line_start, + }) + } + + /// Returns a `StringFormat` used for formatting the content of an item. + fn create_string_format<'a>(&'a self, fmt: &'a StringFormat<'_>) -> StringFormat<'a> { + StringFormat { + opener: "", + closer: "", + line_start: "", + line_end: "", + shape: Shape::legacy(fmt.shape.width.saturating_sub(self.indent), Indent::empty()), + trim_end: true, + config: fmt.config, + } + } + + /// Returns `true` if the line is part of the current itemized block. + /// If it is, then it is added to the internal lines list. + fn add_line(&mut self, line: &str) -> bool { + if ItemizedBlock::get_marker_length(line.trim_start()).is_none() + && self.indent <= line.chars().take_while(|c| c.is_whitespace()).count() + { + self.lines.push(line.to_string()); + return true; + } + false + } + + /// Returns the block as a string, with each line trimmed at the start. + fn trimmed_block_as_string(&self) -> String { + self.lines + .iter() + .map(|line| format!("{} ", line.trim_start())) + .collect::() + } + + /// Returns the block as a string under its original form. + fn original_block_as_string(&self) -> String { + self.lines.join("\n") + } +} + +/// 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, + is_prev_line_multi_line: bool, + code_block_attr: Option, + item_block: Option, + comment_line_separator: String, + indent_str: String, + max_width: usize, + fmt_indent: Indent, + fmt: StringFormat<'a>, + + opener: String, + closer: String, + line_start: String, + style: CommentStyle<'a>, +} + +impl<'a> CommentRewrite<'a> { + fn new( + orig: &'a str, + block_style: bool, + shape: Shape, + config: &'a Config, + ) -> CommentRewrite<'a> { + let ((opener, closer, line_start), style) = if block_style { + ( + CommentStyle::SingleBullet.to_str_tuplet(), + CommentStyle::SingleBullet, + ) + } else { + let style = comment_style(orig, config.normalize_comments()); + (style.to_str_tuplet(), style) + }; + + let max_width = shape + .width + .checked_sub(closer.len() + opener.len()) + .unwrap_or(1); + let indent_str = shape.indent.to_string_with_newline(config).to_string(); + + let mut cr = CommentRewrite { + result: String::with_capacity(orig.len() * 2), + code_block_buffer: String::with_capacity(128), + is_prev_line_multi_line: false, + code_block_attr: None, + item_block: None, + comment_line_separator: format!("{}{}", indent_str, line_start), + max_width, + indent_str, + fmt_indent: shape.indent, + + fmt: StringFormat { + opener: "", + closer: "", + line_start, + line_end: "", + shape: Shape::legacy(max_width, shape.indent), + trim_end: true, + config, + }, + + opener: opener.to_owned(), + closer: closer.to_owned(), + line_start: line_start.to_owned(), + style, + }; + cr.result.push_str(opener); + cr + } + + fn join_block(s: &str, sep: &str) -> String { + let mut result = String::with_capacity(s.len() + 128); + let mut iter = s.lines().peekable(); + while let Some(line) = iter.next() { + result.push_str(line); + result.push_str(match iter.peek() { + Some(next_line) if next_line.is_empty() => sep.trim_end(), + Some(..) => sep, + None => "", + }); + } + result + } + + /// Check if any characters were written to the result buffer after the start of the comment. + /// when calling [`CommentRewrite::new()`] the result buffer is initiazlied with the opening + /// characters for the comment. + fn buffer_contains_comment(&self) -> bool { + // if self.result.len() < self.opener.len() then an empty comment is in the buffer + // if self.result.len() > self.opener.len() then a non empty comment is in the buffer + self.result.len() != self.opener.len() + } + + fn finish(mut self) -> String { + if !self.code_block_buffer.is_empty() { + // There is a code block that is not properly enclosed by backticks. + // We will leave them untouched. + self.result.push_str(&self.comment_line_separator); + self.result.push_str(&Self::join_block( + &trim_custom_comment_prefix(&self.code_block_buffer), + &self.comment_line_separator, + )); + } + + if let Some(ref ib) = self.item_block { + // the last few lines are part of an itemized block + self.fmt.shape = Shape::legacy(self.max_width, self.fmt_indent); + let item_fmt = ib.create_string_format(&self.fmt); + + // only push a comment_line_separator for ItemizedBlocks if the comment is not empty + if self.buffer_contains_comment() { + self.result.push_str(&self.comment_line_separator); + } + + self.result.push_str(&ib.opener); + match rewrite_string( + &ib.trimmed_block_as_string(), + &item_fmt, + self.max_width.saturating_sub(ib.indent), + ) { + Some(s) => self.result.push_str(&Self::join_block( + &s, + &format!("{}{}", self.comment_line_separator, ib.line_start), + )), + None => self.result.push_str(&Self::join_block( + &ib.original_block_as_string(), + &self.comment_line_separator, + )), + }; + } + + self.result.push_str(&self.closer); + if self.result.ends_with(&self.opener) && self.opener.ends_with(' ') { + // Trailing space. + self.result.pop(); + } + + self.result + } + + fn handle_line( + &mut self, + orig: &'a str, + 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; + let needs_new_comment_line = if self.style.is_block_comment() { + num_newlines > 0 || self.buffer_contains_comment() + } else { + self.buffer_contains_comment() + }; + + if let Some(ref mut ib) = self.item_block { + if ib.add_line(line) { + return false; + } + self.is_prev_line_multi_line = false; + self.fmt.shape = Shape::legacy(self.max_width, self.fmt_indent); + let item_fmt = ib.create_string_format(&self.fmt); + + // only push a comment_line_separator if we need to start a new comment line + if needs_new_comment_line { + self.result.push_str(&self.comment_line_separator); + } + + self.result.push_str(&ib.opener); + match rewrite_string( + &ib.trimmed_block_as_string(), + &item_fmt, + self.max_width.saturating_sub(ib.indent), + ) { + Some(s) => self.result.push_str(&Self::join_block( + &s, + &format!("{}{}", self.comment_line_separator, ib.line_start), + )), + None => self.result.push_str(&Self::join_block( + &ib.original_block_as_string(), + &self.comment_line_separator, + )), + }; + } else if self.code_block_attr.is_some() { + if line.starts_with("```") { + let code_block = match self.code_block_attr.as_ref().unwrap() { + CodeBlockAttribute::Rust + if self.fmt.config.format_code_in_doc_comments() + && !self.code_block_buffer.trim().is_empty() => + { + let mut config = self.fmt.config.clone(); + config.set().wrap_comments(false); + let comment_max_width = config + .doc_comment_code_block_width() + .min(config.max_width()); + config.set().max_width(comment_max_width); + if let Some(s) = + crate::format_code_block(&self.code_block_buffer, &config, false) + { + trim_custom_comment_prefix(&s.snippet) + } else { + trim_custom_comment_prefix(&self.code_block_buffer) + } + } + _ => trim_custom_comment_prefix(&self.code_block_buffer), + }; + if !code_block.is_empty() { + self.result.push_str(&self.comment_line_separator); + self.result + .push_str(&Self::join_block(&code_block, &self.comment_line_separator)); + } + self.code_block_buffer.clear(); + self.result.push_str(&self.comment_line_separator); + self.result.push_str(line); + self.code_block_attr = None; + } else { + self.code_block_buffer + .push_str(&hide_sharp_behind_comment(line)); + self.code_block_buffer.push('\n'); + } + return false; + } + + self.code_block_attr = None; + self.item_block = None; + if let Some(stripped) = line.strip_prefix("```") { + self.code_block_attr = Some(CodeBlockAttribute::new(stripped)) + } else if self.fmt.config.wrap_comments() { + if let Some(ib) = ItemizedBlock::new(line) { + self.item_block = Some(ib); + return false; + } + } + + if self.result == self.opener { + let force_leading_whitespace = &self.opener == "/* " && count_newlines(orig) == 0; + if !has_leading_whitespace && !force_leading_whitespace && self.result.ends_with(' ') { + self.result.pop(); + } + if line.is_empty() { + return false; + } + } else if self.is_prev_line_multi_line && !line.is_empty() { + self.result.push(' ') + } else if is_last && line.is_empty() { + // trailing blank lines are unwanted + if !self.closer.is_empty() { + self.result.push_str(&self.indent_str); + } + return true; + } else { + self.result.push_str(&self.comment_line_separator); + if !has_leading_whitespace && self.result.ends_with(' ') { + self.result.pop(); + } + } + + 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 comment + // If this changes, the documentation in ../Configurations.md#wrap_comments + // should be changed accordingly. + 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) + && !is_table_item(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'); + self.result.push_str(s); + } + None if self.is_prev_line_multi_line => { + // We failed to put the current `line` next to the previous `line`. + // Remove the trailing space, then start rewrite on the next line. + self.result.pop(); + self.result.push_str(&self.comment_line_separator); + self.fmt.shape = Shape::legacy(self.max_width, self.fmt_indent); + match rewrite_string(line, &self.fmt, self.max_width) { + Some(ref s) => { + self.is_prev_line_multi_line = s.contains('\n'); + self.result.push_str(s); + } + None => { + self.is_prev_line_multi_line = false; + self.result.push_str(line); + } + } + } + None => { + self.is_prev_line_multi_line = false; + self.result.push_str(line); + } + } + + self.fmt.shape = if self.is_prev_line_multi_line { + // 1 = " " + let offset = 1 + last_line_width(&self.result) - self.line_start.len(); + Shape { + width: self.max_width.saturating_sub(offset), + indent: self.fmt_indent, + offset: self.fmt.shape.offset + offset, + } + } else { + Shape::legacy(self.max_width, self.fmt_indent) + }; + } else { + if line.is_empty() && self.result.ends_with(' ') && !is_last { + // Remove space if this is an empty comment or a doc comment. + self.result.pop(); + } + self.result.push_str(line); + self.fmt.shape = Shape::legacy(self.max_width, self.fmt_indent); + self.is_prev_line_multi_line = false; + } + + false + } +} + +fn rewrite_comment_inner( + orig: &str, + block_style: bool, + style: CommentStyle<'_>, + shape: Shape, + config: &Config, + is_doc_comment: bool, +) -> Option { + let mut rewriter = CommentRewrite::new(orig, block_style, shape, config); + + let line_breaks = count_newlines(orig.trim_end()); + let lines = orig + .lines() + .enumerate() + .map(|(i, mut line)| { + line = trim_end_unless_two_whitespaces(line.trim_start(), is_doc_comment); + // Drop old closer. + if i == line_breaks && line.ends_with("*/") && !line.starts_with("//") { + line = line[..(line.len() - 2)].trim_end(); + } + + line + }) + .map(|s| left_trim_comment_line(s, &style)) + .map(|(line, has_leading_whitespace)| { + if orig.starts_with("/*") && line_breaks == 0 { + ( + line.trim_start(), + has_leading_whitespace || config.normalize_comments(), + ) + } else { + (line, has_leading_whitespace || config.normalize_comments()) + } + }); + + for (i, (line, has_leading_whitespace)) in lines.enumerate() { + if rewriter.handle_line(orig, i, line, has_leading_whitespace, is_doc_comment) { + break; + } + } + + Some(rewriter.finish()) +} + +const RUSTFMT_CUSTOM_COMMENT_PREFIX: &str = "//#### "; + +fn hide_sharp_behind_comment(s: &str) -> Cow<'_, str> { + let s_trimmed = s.trim(); + if s_trimmed.starts_with("# ") || s_trimmed == "#" { + Cow::from(format!("{}{}", RUSTFMT_CUSTOM_COMMENT_PREFIX, s)) + } else { + Cow::from(s) + } +} + +fn trim_custom_comment_prefix(s: &str) -> String { + s.lines() + .map(|line| { + let left_trimmed = line.trim_start(); + if left_trimmed.starts_with(RUSTFMT_CUSTOM_COMMENT_PREFIX) { + left_trimmed.trim_start_matches(RUSTFMT_CUSTOM_COMMENT_PREFIX) + } else { + line + } + }) + .collect::>() + .join("\n") +} + +/// Returns `true` if the given string MAY include URLs or alike. +fn has_url(s: &str) -> bool { + // This function may return false positive, but should get its job done in most cases. + s.contains("https://") + || s.contains("http://") + || s.contains("ftp://") + || s.contains("file://") + || REFERENCE_LINK_URL.is_match(s) +} + +/// Returns true if the given string may be part of a Markdown table. +fn is_table_item(mut s: &str) -> bool { + // This function may return false positive, but should get its job done in most cases (i.e. + // markdown tables with two column delimiters). + s = s.trim_start(); + return s.starts_with('|') + && match s.rfind('|') { + Some(0) | None => false, + _ => true, + }; +} + +/// Given the span, rewrite the missing comment inside it if available. +/// Note that the given span must only include comments (or leading/trailing whitespaces). +pub(crate) fn rewrite_missing_comment( + span: Span, + shape: Shape, + context: &RewriteContext<'_>, +) -> Option { + let missing_snippet = context.snippet(span); + let trimmed_snippet = missing_snippet.trim(); + // check the span starts with a comment + let pos = trimmed_snippet.find('/'); + if !trimmed_snippet.is_empty() && pos.is_some() { + rewrite_comment(trimmed_snippet, false, shape, context.config) + } else { + Some(String::new()) + } +} + +/// Recover the missing comments in the specified span, if available. +/// The layout of the comments will be preserved as long as it does not break the code +/// and its total width does not exceed the max width. +pub(crate) fn recover_missing_comment_in_span( + span: Span, + shape: Shape, + context: &RewriteContext<'_>, + used_width: usize, +) -> Option { + let missing_comment = rewrite_missing_comment(span, shape, context)?; + if missing_comment.is_empty() { + Some(String::new()) + } else { + let missing_snippet = context.snippet(span); + let pos = missing_snippet.find('/')?; + // 1 = ` ` + let total_width = missing_comment.len() + used_width + 1; + let force_new_line_before_comment = + missing_snippet[..pos].contains('\n') || total_width > context.config.max_width(); + let sep = if force_new_line_before_comment { + shape.indent.to_string_with_newline(context.config) + } else { + Cow::from(" ") + }; + Some(format!("{}{}", sep, missing_comment)) + } +} + +/// Trim trailing whitespaces unless they consist of two or more whitespaces. +fn trim_end_unless_two_whitespaces(s: &str, is_doc_comment: bool) -> &str { + if is_doc_comment && s.ends_with(" ") { + s + } else { + s.trim_end() + } +} + +/// Trims whitespace and aligns to indent, but otherwise does not change comments. +fn light_rewrite_comment( + orig: &str, + offset: Indent, + config: &Config, + is_doc_comment: bool, +) -> String { + let lines: Vec<&str> = orig + .lines() + .map(|l| { + // This is basically just l.trim(), but in the case that a line starts + // with `*` we want to leave one space before it, so it aligns with the + // `*` in `/*`. + let first_non_whitespace = l.find(|c| !char::is_whitespace(c)); + let left_trimmed = if let Some(fnw) = first_non_whitespace { + if l.as_bytes()[fnw] == b'*' && fnw > 0 { + &l[fnw - 1..] + } else { + &l[fnw..] + } + } else { + "" + }; + // Preserve markdown's double-space line break syntax in doc comment. + trim_end_unless_two_whitespaces(left_trimmed, is_doc_comment) + }) + .collect(); + lines.join(&format!("\n{}", offset.to_string(config))) +} + +/// Trims comment characters and possibly a single space from the left of a string. +/// Does not trim all whitespace. If a single space is trimmed from the left of the string, +/// this function returns true. +fn left_trim_comment_line<'a>(line: &'a str, style: &CommentStyle<'_>) -> (&'a str, bool) { + if line.starts_with("//! ") + || line.starts_with("/// ") + || line.starts_with("/*! ") + || line.starts_with("/** ") + { + (&line[4..], true) + } else if let CommentStyle::Custom(opener) = *style { + if let Some(stripped) = line.strip_prefix(opener) { + (stripped, true) + } else { + (&line[opener.trim_end().len()..], false) + } + } else if line.starts_with("/* ") + || line.starts_with("// ") + || line.starts_with("//!") + || line.starts_with("///") + || line.starts_with("** ") + || line.starts_with("/*!") + || (line.starts_with("/**") && !line.starts_with("/**/")) + { + (&line[3..], line.chars().nth(2).unwrap() == ' ') + } else if line.starts_with("/*") + || line.starts_with("* ") + || line.starts_with("//") + || line.starts_with("**") + { + (&line[2..], line.chars().nth(1).unwrap() == ' ') + } else if let Some(stripped) = line.strip_prefix('*') { + (stripped, false) + } else { + (line, line.starts_with(' ')) + } +} + +pub(crate) trait FindUncommented { + fn find_uncommented(&self, pat: &str) -> Option; + fn find_last_uncommented(&self, pat: &str) -> Option; +} + +impl FindUncommented for str { + fn find_uncommented(&self, pat: &str) -> Option { + let mut needle_iter = pat.chars(); + for (kind, (i, b)) in CharClasses::new(self.char_indices()) { + match needle_iter.next() { + None => { + return Some(i - pat.len()); + } + Some(c) => match kind { + FullCodeCharKind::Normal | FullCodeCharKind::InString if b == c => {} + _ => { + needle_iter = pat.chars(); + } + }, + } + } + + // Handle case where the pattern is a suffix of the search string + match needle_iter.next() { + Some(_) => None, + None => Some(self.len() - pat.len()), + } + } + + fn find_last_uncommented(&self, pat: &str) -> Option { + if let Some(left) = self.find_uncommented(pat) { + let mut result = left; + // add 1 to use find_last_uncommented for &str after pat + while let Some(next) = self[(result + 1)..].find_last_uncommented(pat) { + result += next + 1; + } + Some(result) + } else { + None + } + } +} + +// Returns the first byte position after the first comment. The given string +// is expected to be prefixed by a comment, including delimiters. +// Good: `/* /* inner */ outer */ code();` +// Bad: `code(); // hello\n world!` +pub(crate) fn find_comment_end(s: &str) -> Option { + let mut iter = CharClasses::new(s.char_indices()); + for (kind, (i, _c)) in &mut iter { + if kind == FullCodeCharKind::Normal || kind == FullCodeCharKind::InString { + return Some(i); + } + } + + // Handle case where the comment ends at the end of `s`. + if iter.status == CharClassesStatus::Normal { + Some(s.len()) + } else { + None + } +} + +/// Returns `true` if text contains any comment. +pub(crate) fn contains_comment(text: &str) -> bool { + CharClasses::new(text.chars()).any(|(kind, _)| kind.is_comment()) +} + +pub(crate) struct CharClasses +where + T: Iterator, + T::Item: RichChar, +{ + base: MultiPeek, + status: CharClassesStatus, +} + +pub(crate) trait RichChar { + fn get_char(&self) -> char; +} + +impl RichChar for char { + fn get_char(&self) -> char { + *self + } +} + +impl RichChar for (usize, char) { + fn get_char(&self) -> char { + self.1 + } +} + +#[derive(PartialEq, Eq, Debug, Clone, Copy)] +enum CharClassesStatus { + Normal, + /// Character is within a string + LitString, + LitStringEscape, + /// Character is within a raw string + LitRawString(u32), + RawStringPrefix(u32), + RawStringSuffix(u32), + LitChar, + LitCharEscape, + /// Character inside a block comment, with the integer indicating the nesting deepness of the + /// comment + BlockComment(u32), + /// Character inside a block-commented string, with the integer indicating the nesting deepness + /// of the comment + StringInBlockComment(u32), + /// Status when the '/' has been consumed, but not yet the '*', deepness is + /// the new deepness (after the comment opening). + BlockCommentOpening(u32), + /// Status when the '*' has been consumed, but not yet the '/', deepness is + /// the new deepness (after the comment closing). + BlockCommentClosing(u32), + /// Character is within a line comment + LineComment, +} + +/// Distinguish between functional part of code and comments +#[derive(PartialEq, Eq, Debug, Clone, Copy)] +pub(crate) enum CodeCharKind { + Normal, + Comment, +} + +/// Distinguish between functional part of code and comments, +/// describing opening and closing of comments for ease when chunking +/// code from tagged characters +#[derive(PartialEq, Eq, Debug, Clone, Copy)] +pub(crate) enum FullCodeCharKind { + Normal, + /// The first character of a comment, there is only one for a comment (always '/') + StartComment, + /// Any character inside a comment including the second character of comment + /// marks ("//", "/*") + InComment, + /// Last character of a comment, '\n' for a line comment, '/' for a block comment. + EndComment, + /// Start of a mutlitine string inside a comment + StartStringCommented, + /// End of a mutlitine string inside a comment + EndStringCommented, + /// Inside a commented string + InStringCommented, + /// Start of a mutlitine string + StartString, + /// End of a mutlitine string + EndString, + /// Inside a string. + InString, +} + +impl FullCodeCharKind { + pub(crate) fn is_comment(self) -> bool { + match self { + FullCodeCharKind::StartComment + | FullCodeCharKind::InComment + | FullCodeCharKind::EndComment + | FullCodeCharKind::StartStringCommented + | FullCodeCharKind::InStringCommented + | FullCodeCharKind::EndStringCommented => true, + _ => false, + } + } + + /// Returns true if the character is inside a comment + pub(crate) fn inside_comment(self) -> bool { + match self { + FullCodeCharKind::InComment + | FullCodeCharKind::StartStringCommented + | FullCodeCharKind::InStringCommented + | FullCodeCharKind::EndStringCommented => true, + _ => false, + } + } + + pub(crate) fn is_string(self) -> bool { + self == FullCodeCharKind::InString || self == FullCodeCharKind::StartString + } + + /// Returns true if the character is within a commented string + pub(crate) fn is_commented_string(self) -> bool { + self == FullCodeCharKind::InStringCommented + || self == FullCodeCharKind::StartStringCommented + } + + fn to_codecharkind(self) -> CodeCharKind { + if self.is_comment() { + CodeCharKind::Comment + } else { + CodeCharKind::Normal + } + } +} + +impl CharClasses +where + T: Iterator, + T::Item: RichChar, +{ + pub(crate) fn new(base: T) -> CharClasses { + CharClasses { + base: multipeek(base), + status: CharClassesStatus::Normal, + } + } +} + +fn is_raw_string_suffix(iter: &mut MultiPeek, count: u32) -> bool +where + T: Iterator, + T::Item: RichChar, +{ + for _ in 0..count { + match iter.peek() { + Some(c) if c.get_char() == '#' => continue, + _ => return false, + } + } + true +} + +impl Iterator for CharClasses +where + T: Iterator, + T::Item: RichChar, +{ + type Item = (FullCodeCharKind, T::Item); + + fn next(&mut self) -> Option<(FullCodeCharKind, T::Item)> { + let item = self.base.next()?; + let chr = item.get_char(); + let mut char_kind = FullCodeCharKind::Normal; + self.status = match self.status { + CharClassesStatus::LitRawString(sharps) => { + char_kind = FullCodeCharKind::InString; + match chr { + '"' => { + if sharps == 0 { + char_kind = FullCodeCharKind::Normal; + CharClassesStatus::Normal + } else if is_raw_string_suffix(&mut self.base, sharps) { + CharClassesStatus::RawStringSuffix(sharps) + } else { + CharClassesStatus::LitRawString(sharps) + } + } + _ => CharClassesStatus::LitRawString(sharps), + } + } + CharClassesStatus::RawStringPrefix(sharps) => { + char_kind = FullCodeCharKind::InString; + match chr { + '#' => CharClassesStatus::RawStringPrefix(sharps + 1), + '"' => CharClassesStatus::LitRawString(sharps), + _ => CharClassesStatus::Normal, // Unreachable. + } + } + CharClassesStatus::RawStringSuffix(sharps) => { + match chr { + '#' => { + if sharps == 1 { + CharClassesStatus::Normal + } else { + char_kind = FullCodeCharKind::InString; + CharClassesStatus::RawStringSuffix(sharps - 1) + } + } + _ => CharClassesStatus::Normal, // Unreachable + } + } + CharClassesStatus::LitString => { + char_kind = FullCodeCharKind::InString; + match chr { + '"' => CharClassesStatus::Normal, + '\\' => CharClassesStatus::LitStringEscape, + _ => CharClassesStatus::LitString, + } + } + CharClassesStatus::LitStringEscape => { + char_kind = FullCodeCharKind::InString; + CharClassesStatus::LitString + } + CharClassesStatus::LitChar => match chr { + '\\' => CharClassesStatus::LitCharEscape, + '\'' => CharClassesStatus::Normal, + _ => CharClassesStatus::LitChar, + }, + CharClassesStatus::LitCharEscape => CharClassesStatus::LitChar, + CharClassesStatus::Normal => match chr { + 'r' => match self.base.peek().map(RichChar::get_char) { + Some('#') | Some('"') => { + char_kind = FullCodeCharKind::InString; + CharClassesStatus::RawStringPrefix(0) + } + _ => CharClassesStatus::Normal, + }, + '"' => { + char_kind = FullCodeCharKind::InString; + CharClassesStatus::LitString + } + '\'' => { + // HACK: Work around mut borrow. + match self.base.peek() { + Some(next) if next.get_char() == '\\' => { + self.status = CharClassesStatus::LitChar; + return Some((char_kind, item)); + } + _ => (), + } + + match self.base.peek() { + Some(next) if next.get_char() == '\'' => CharClassesStatus::LitChar, + _ => CharClassesStatus::Normal, + } + } + '/' => match self.base.peek() { + Some(next) if next.get_char() == '*' => { + self.status = CharClassesStatus::BlockCommentOpening(1); + return Some((FullCodeCharKind::StartComment, item)); + } + Some(next) if next.get_char() == '/' => { + self.status = CharClassesStatus::LineComment; + return Some((FullCodeCharKind::StartComment, item)); + } + _ => CharClassesStatus::Normal, + }, + _ => CharClassesStatus::Normal, + }, + CharClassesStatus::StringInBlockComment(deepness) => { + char_kind = FullCodeCharKind::InStringCommented; + if chr == '"' { + CharClassesStatus::BlockComment(deepness) + } else if chr == '*' && self.base.peek().map(RichChar::get_char) == Some('/') { + char_kind = FullCodeCharKind::InComment; + CharClassesStatus::BlockCommentClosing(deepness - 1) + } else { + CharClassesStatus::StringInBlockComment(deepness) + } + } + CharClassesStatus::BlockComment(deepness) => { + assert_ne!(deepness, 0); + char_kind = FullCodeCharKind::InComment; + match self.base.peek() { + Some(next) if next.get_char() == '/' && chr == '*' => { + CharClassesStatus::BlockCommentClosing(deepness - 1) + } + Some(next) if next.get_char() == '*' && chr == '/' => { + CharClassesStatus::BlockCommentOpening(deepness + 1) + } + _ if chr == '"' => CharClassesStatus::StringInBlockComment(deepness), + _ => self.status, + } + } + CharClassesStatus::BlockCommentOpening(deepness) => { + assert_eq!(chr, '*'); + self.status = CharClassesStatus::BlockComment(deepness); + return Some((FullCodeCharKind::InComment, item)); + } + CharClassesStatus::BlockCommentClosing(deepness) => { + assert_eq!(chr, '/'); + if deepness == 0 { + self.status = CharClassesStatus::Normal; + return Some((FullCodeCharKind::EndComment, item)); + } else { + self.status = CharClassesStatus::BlockComment(deepness); + return Some((FullCodeCharKind::InComment, item)); + } + } + CharClassesStatus::LineComment => match chr { + '\n' => { + self.status = CharClassesStatus::Normal; + return Some((FullCodeCharKind::EndComment, item)); + } + _ => { + self.status = CharClassesStatus::LineComment; + return Some((FullCodeCharKind::InComment, item)); + } + }, + }; + Some((char_kind, item)) + } +} + +/// An iterator over the lines of a string, paired with the char kind at the +/// end of the line. +pub(crate) struct LineClasses<'a> { + base: iter::Peekable>>, + kind: FullCodeCharKind, +} + +impl<'a> LineClasses<'a> { + pub(crate) fn new(s: &'a str) -> Self { + LineClasses { + base: CharClasses::new(s.chars()).peekable(), + kind: FullCodeCharKind::Normal, + } + } +} + +impl<'a> Iterator for LineClasses<'a> { + type Item = (FullCodeCharKind, String); + + fn next(&mut self) -> Option { + self.base.peek()?; + + let mut line = String::new(); + + let start_kind = match self.base.peek() { + Some((kind, _)) => *kind, + None => unreachable!(), + }; + + for (kind, c) in self.base.by_ref() { + // needed to set the kind of the ending character on the last line + self.kind = kind; + if c == '\n' { + self.kind = match (start_kind, kind) { + (FullCodeCharKind::Normal, FullCodeCharKind::InString) => { + FullCodeCharKind::StartString + } + (FullCodeCharKind::InString, FullCodeCharKind::Normal) => { + FullCodeCharKind::EndString + } + (FullCodeCharKind::InComment, FullCodeCharKind::InStringCommented) => { + FullCodeCharKind::StartStringCommented + } + (FullCodeCharKind::InStringCommented, FullCodeCharKind::InComment) => { + FullCodeCharKind::EndStringCommented + } + _ => kind, + }; + break; + } + line.push(c); + } + + // Workaround for CRLF newline. + if line.ends_with('\r') { + line.pop(); + } + + Some((self.kind, line)) + } +} + +/// Iterator over functional and commented parts of a string. Any part of a string is either +/// functional code, either *one* block comment, either *one* line comment. Whitespace between +/// comments is functional code. Line comments contain their ending newlines. +struct UngroupedCommentCodeSlices<'a> { + slice: &'a str, + iter: iter::Peekable>>, +} + +impl<'a> UngroupedCommentCodeSlices<'a> { + fn new(code: &'a str) -> UngroupedCommentCodeSlices<'a> { + UngroupedCommentCodeSlices { + slice: code, + iter: CharClasses::new(code.char_indices()).peekable(), + } + } +} + +impl<'a> Iterator for UngroupedCommentCodeSlices<'a> { + type Item = (CodeCharKind, usize, &'a str); + + fn next(&mut self) -> Option { + let (kind, (start_idx, _)) = self.iter.next()?; + match kind { + FullCodeCharKind::Normal | FullCodeCharKind::InString => { + // Consume all the Normal code + while let Some(&(char_kind, _)) = self.iter.peek() { + if char_kind.is_comment() { + break; + } + let _ = self.iter.next(); + } + } + FullCodeCharKind::StartComment => { + // Consume the whole comment + loop { + match self.iter.next() { + Some((kind, ..)) if kind.inside_comment() => continue, + _ => break, + } + } + } + _ => panic!(), + } + let slice = match self.iter.peek() { + Some(&(_, (end_idx, _))) => &self.slice[start_idx..end_idx], + None => &self.slice[start_idx..], + }; + Some(( + if kind.is_comment() { + CodeCharKind::Comment + } else { + CodeCharKind::Normal + }, + start_idx, + slice, + )) + } +} + +/// Iterator over an alternating sequence of functional and commented parts of +/// a string. The first item is always a, possibly zero length, subslice of +/// functional text. Line style comments contain their ending newlines. +pub(crate) struct CommentCodeSlices<'a> { + slice: &'a str, + last_slice_kind: CodeCharKind, + last_slice_end: usize, +} + +impl<'a> CommentCodeSlices<'a> { + pub(crate) fn new(slice: &'a str) -> CommentCodeSlices<'a> { + CommentCodeSlices { + slice, + last_slice_kind: CodeCharKind::Comment, + last_slice_end: 0, + } + } +} + +impl<'a> Iterator for CommentCodeSlices<'a> { + type Item = (CodeCharKind, usize, &'a str); + + fn next(&mut self) -> Option { + if self.last_slice_end == self.slice.len() { + return None; + } + + let mut sub_slice_end = self.last_slice_end; + let mut first_whitespace = None; + let subslice = &self.slice[self.last_slice_end..]; + let mut iter = CharClasses::new(subslice.char_indices()); + + for (kind, (i, c)) in &mut iter { + let is_comment_connector = self.last_slice_kind == CodeCharKind::Normal + && &subslice[..2] == "//" + && [' ', '\t'].contains(&c); + + if is_comment_connector && first_whitespace.is_none() { + first_whitespace = Some(i); + } + + if kind.to_codecharkind() == self.last_slice_kind && !is_comment_connector { + let last_index = match first_whitespace { + Some(j) => j, + None => i, + }; + sub_slice_end = self.last_slice_end + last_index; + break; + } + + if !is_comment_connector { + first_whitespace = None; + } + } + + if let (None, true) = (iter.next(), sub_slice_end == self.last_slice_end) { + // This was the last subslice. + sub_slice_end = match first_whitespace { + Some(i) => self.last_slice_end + i, + None => self.slice.len(), + }; + } + + let kind = match self.last_slice_kind { + CodeCharKind::Comment => CodeCharKind::Normal, + CodeCharKind::Normal => CodeCharKind::Comment, + }; + let res = ( + kind, + self.last_slice_end, + &self.slice[self.last_slice_end..sub_slice_end], + ); + self.last_slice_end = sub_slice_end; + self.last_slice_kind = kind; + + Some(res) + } +} + +/// Checks is `new` didn't miss any comment from `span`, if it removed any, return previous text +/// (if it fits in the width/offset, else return `None`), else return `new` +pub(crate) fn recover_comment_removed( + new: String, + span: Span, + context: &RewriteContext<'_>, +) -> Option { + let snippet = context.snippet(span); + if snippet != new && changed_comment_content(snippet, &new) { + // We missed some comments. Warn and keep the original text. + if context.config.error_on_unformatted() { + context.report.append( + context.parse_sess.span_to_filename(span), + vec![FormattingError::from_span( + span, + context.parse_sess, + ErrorKind::LostComment, + )], + ); + } + Some(snippet.to_owned()) + } else { + Some(new) + } +} + +pub(crate) fn filter_normal_code(code: &str) -> String { + let mut buffer = String::with_capacity(code.len()); + LineClasses::new(code).for_each(|(kind, line)| match kind { + FullCodeCharKind::Normal + | FullCodeCharKind::StartString + | FullCodeCharKind::InString + | FullCodeCharKind::EndString => { + buffer.push_str(&line); + buffer.push('\n'); + } + _ => (), + }); + if !code.ends_with('\n') && buffer.ends_with('\n') { + buffer.pop(); + } + buffer +} + +/// Returns `true` if the two strings of code have the same payload of comments. +/// The payload of comments is everything in the string except: +/// - actual code (not comments), +/// - comment start/end marks, +/// - whitespace, +/// - '*' at the beginning of lines in block comments. +fn changed_comment_content(orig: &str, new: &str) -> bool { + // Cannot write this as a fn since we cannot return types containing closures. + let code_comment_content = |code| { + let slices = UngroupedCommentCodeSlices::new(code); + slices + .filter(|&(ref kind, _, _)| *kind == CodeCharKind::Comment) + .flat_map(|(_, _, s)| CommentReducer::new(s)) + }; + let res = code_comment_content(orig).ne(code_comment_content(new)); + debug!( + "comment::changed_comment_content: {}\norig: '{}'\nnew: '{}'\nraw_old: {}\nraw_new: {}", + res, + orig, + new, + code_comment_content(orig).collect::(), + code_comment_content(new).collect::() + ); + res +} + +/// Iterator over the 'payload' characters of a comment. +/// It skips whitespace, comment start/end marks, and '*' at the beginning of lines. +/// The comment must be one comment, ie not more than one start mark (no multiple line comments, +/// for example). +struct CommentReducer<'a> { + is_block: bool, + at_start_line: bool, + iter: std::str::Chars<'a>, +} + +impl<'a> CommentReducer<'a> { + fn new(comment: &'a str) -> CommentReducer<'a> { + let is_block = comment.starts_with("/*"); + let comment = remove_comment_header(comment); + CommentReducer { + is_block, + // There are no supplementary '*' on the first line. + at_start_line: false, + iter: comment.chars(), + } + } +} + +impl<'a> Iterator for CommentReducer<'a> { + type Item = char; + + fn next(&mut self) -> Option { + loop { + let mut c = self.iter.next()?; + if self.is_block && self.at_start_line { + while c.is_whitespace() { + c = self.iter.next()?; + } + // Ignore leading '*'. + if c == '*' { + c = self.iter.next()?; + } + } else if c == '\n' { + self.at_start_line = true; + } + if !c.is_whitespace() { + return Some(c); + } + } + } +} + +fn remove_comment_header(comment: &str) -> &str { + if comment.starts_with("///") || comment.starts_with("//!") { + &comment[3..] + } else if let Some(stripped) = comment.strip_prefix("//") { + stripped + } else if (comment.starts_with("/**") && !comment.starts_with("/**/")) + || comment.starts_with("/*!") + { + &comment[3..comment.len() - 2] + } else { + assert!( + comment.starts_with("/*"), + "string '{}' is not a comment", + comment + ); + &comment[2..comment.len() - 2] + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::shape::{Indent, Shape}; + + #[test] + fn char_classes() { + let mut iter = CharClasses::new("//\n\n".chars()); + + assert_eq!((FullCodeCharKind::StartComment, '/'), iter.next().unwrap()); + assert_eq!((FullCodeCharKind::InComment, '/'), iter.next().unwrap()); + assert_eq!((FullCodeCharKind::EndComment, '\n'), iter.next().unwrap()); + assert_eq!((FullCodeCharKind::Normal, '\n'), iter.next().unwrap()); + assert_eq!(None, iter.next()); + } + + #[test] + fn comment_code_slices() { + let input = "code(); /* test */ 1 + 1"; + let mut iter = CommentCodeSlices::new(input); + + assert_eq!((CodeCharKind::Normal, 0, "code(); "), iter.next().unwrap()); + assert_eq!( + (CodeCharKind::Comment, 8, "/* test */"), + iter.next().unwrap() + ); + assert_eq!((CodeCharKind::Normal, 18, " 1 + 1"), iter.next().unwrap()); + assert_eq!(None, iter.next()); + } + + #[test] + fn comment_code_slices_two() { + let input = "// comment\n test();"; + let mut iter = CommentCodeSlices::new(input); + + assert_eq!((CodeCharKind::Normal, 0, ""), iter.next().unwrap()); + assert_eq!( + (CodeCharKind::Comment, 0, "// comment\n"), + iter.next().unwrap() + ); + assert_eq!( + (CodeCharKind::Normal, 11, " test();"), + iter.next().unwrap() + ); + assert_eq!(None, iter.next()); + } + + #[test] + fn comment_code_slices_three() { + let input = "1 // comment\n // comment2\n\n"; + let mut iter = CommentCodeSlices::new(input); + + assert_eq!((CodeCharKind::Normal, 0, "1 "), iter.next().unwrap()); + assert_eq!( + (CodeCharKind::Comment, 2, "// comment\n // comment2\n"), + iter.next().unwrap() + ); + assert_eq!((CodeCharKind::Normal, 29, "\n"), iter.next().unwrap()); + assert_eq!(None, iter.next()); + } + + #[test] + #[rustfmt::skip] + fn format_doc_comments() { + let mut wrap_normalize_config: crate::config::Config = Default::default(); + wrap_normalize_config.set().wrap_comments(true); + wrap_normalize_config.set().normalize_comments(true); + + let mut wrap_config: crate::config::Config = Default::default(); + wrap_config.set().wrap_comments(true); + + let comment = rewrite_comment(" //test", + true, + Shape::legacy(100, Indent::new(0, 100)), + &wrap_normalize_config).unwrap(); + assert_eq!("/* test */", comment); + + let comment = rewrite_comment("// comment on a", + false, + Shape::legacy(10, Indent::empty()), + &wrap_normalize_config).unwrap(); + assert_eq!("// comment\n// on a", comment); + + let comment = rewrite_comment("// A multi line comment\n // between args.", + false, + Shape::legacy(60, Indent::new(0, 12)), + &wrap_normalize_config).unwrap(); + assert_eq!("// A multi line comment\n // between args.", comment); + + let input = "// comment"; + let expected = + "/* comment */"; + let comment = rewrite_comment(input, + true, + Shape::legacy(9, Indent::new(0, 69)), + &wrap_normalize_config).unwrap(); + assert_eq!(expected, comment); + + let comment = rewrite_comment("/* trimmed */", + true, + Shape::legacy(100, Indent::new(0, 100)), + &wrap_normalize_config).unwrap(); + assert_eq!("/* trimmed */", comment); + + // Check that different comment style are properly recognised. + let comment = rewrite_comment(r#"/// test1 + /// test2 + /* + * test3 + */"#, + false, + Shape::legacy(100, Indent::new(0, 0)), + &wrap_normalize_config).unwrap(); + assert_eq!("/// test1\n/// test2\n// test3", comment); + + // Check that the blank line marks the end of a commented paragraph. + let comment = rewrite_comment(r#"// test1 + + // test2"#, + false, + Shape::legacy(100, Indent::new(0, 0)), + &wrap_normalize_config).unwrap(); + assert_eq!("// test1\n\n// test2", comment); + + // Check that the blank line marks the end of a custom-commented paragraph. + let comment = rewrite_comment(r#"//@ test1 + + //@ test2"#, + false, + Shape::legacy(100, Indent::new(0, 0)), + &wrap_normalize_config).unwrap(); + assert_eq!("//@ test1\n\n//@ test2", comment); + + // Check that bare lines are just indented but otherwise left unchanged. + let comment = rewrite_comment(r#"// test1 + /* + a bare line! + + another bare line! + */"#, + false, + Shape::legacy(100, Indent::new(0, 0)), + &wrap_config).unwrap(); + assert_eq!("// test1\n/*\n a bare line!\n\n another bare line!\n*/", comment); + } + + // This is probably intended to be a non-test fn, but it is not used. + // We should keep this around unless it helps us test stuff to remove it. + fn uncommented(text: &str) -> String { + CharClasses::new(text.chars()) + .filter_map(|(s, c)| match s { + FullCodeCharKind::Normal | FullCodeCharKind::InString => Some(c), + _ => None, + }) + .collect() + } + + #[test] + fn test_uncommented() { + assert_eq!(&uncommented("abc/*...*/"), "abc"); + assert_eq!( + &uncommented("// .... /* \n../* /* *** / */ */a/* // */c\n"), + "..ac\n" + ); + assert_eq!(&uncommented("abc \" /* */\" qsdf"), "abc \" /* */\" qsdf"); + } + + #[test] + fn test_contains_comment() { + assert_eq!(contains_comment("abc"), false); + assert_eq!(contains_comment("abc // qsdf"), true); + assert_eq!(contains_comment("abc /* kqsdf"), true); + assert_eq!(contains_comment("abc \" /* */\" qsdf"), false); + } + + #[test] + fn test_find_uncommented() { + fn check(haystack: &str, needle: &str, expected: Option) { + assert_eq!(expected, haystack.find_uncommented(needle)); + } + + check("/*/ */test", "test", Some(6)); + check("//test\ntest", "test", Some(7)); + check("/* comment only */", "whatever", None); + check( + "/* comment */ some text /* more commentary */ result", + "result", + Some(46), + ); + check("sup // sup", "p", Some(2)); + check("sup", "x", None); + check(r#"π? /**/ π is nice!"#, r#"π is nice"#, Some(9)); + check("/*sup yo? \n sup*/ sup", "p", Some(20)); + check("hel/*lohello*/lo", "hello", None); + check("acb", "ab", None); + check(",/*A*/ ", ",", Some(0)); + check("abc", "abc", Some(0)); + check("/* abc */", "abc", None); + check("/**/abc/* */", "abc", Some(4)); + check("\"/* abc */\"", "abc", Some(4)); + check("\"/* abc", "abc", Some(4)); + } + + #[test] + fn test_filter_normal_code() { + let s = r#" +fn main() { + println!("hello, world"); +} +"#; + assert_eq!(s, filter_normal_code(s)); + let s_with_comment = r#" +fn main() { + // hello, world + println!("hello, world"); +} +"#; + assert_eq!(s, filter_normal_code(s_with_comment)); + } + + #[test] + fn test_itemized_block_first_line_handling() { + fn run_test( + test_input: &str, + expected_line: &str, + expected_indent: usize, + expected_opener: &str, + expected_line_start: &str, + ) { + let block = ItemizedBlock::new(test_input).unwrap(); + assert_eq!(1, block.lines.len(), "test_input: {:?}", test_input); + assert_eq!( + expected_line, &block.lines[0], + "test_input: {:?}", + test_input + ); + assert_eq!( + expected_indent, block.indent, + "test_input: {:?}", + test_input + ); + assert_eq!( + expected_opener, &block.opener, + "test_input: {:?}", + test_input + ); + assert_eq!( + expected_line_start, &block.line_start, + "test_input: {:?}", + test_input + ); + } + + run_test("- foo", "foo", 2, "- ", " "); + run_test("* foo", "foo", 2, "* ", " "); + run_test("> foo", "foo", 2, "> ", "> "); + + run_test("1. foo", "foo", 3, "1. ", " "); + run_test("12. foo", "foo", 4, "12. ", " "); + run_test("1) foo", "foo", 3, "1) ", " "); + run_test("12) foo", "foo", 4, "12) ", " "); + + run_test(" - foo", "foo", 6, " - ", " "); + + // https://spec.commonmark.org/0.30 says: "A start number may begin with 0s": + run_test("0. foo", "foo", 3, "0. ", " "); + run_test("01. foo", "foo", 4, "01. ", " "); + } + + #[test] + fn test_itemized_block_nonobvious_markers_are_rejected() { + let test_inputs = vec![ + // Non-numeric item markers (e.g. `a.` or `iv.`) are not allowed by + // https://spec.commonmark.org/0.30/#ordered-list-marker. We also note that allowing + // them would risk misidentifying regular words as item markers. See also the + // discussion in https://talk.commonmark.org/t/blank-lines-before-lists-revisited/1990 + "word. rest of the paragraph.", + "a. maybe this is a list item? maybe not?", + "iv. maybe this is a list item? maybe not?", + // Numbers with 3 or more digits are not recognized as item markers, to avoid + // formatting the following example as a list: + // + // ``` + // The Captain died in + // 1868. He was buried in... + // ``` + "123. only 2-digit numbers are recognized as item markers.", + // Parens: + "123) giving some coverage to parens as well.", + "a) giving some coverage to parens as well.", + // https://spec.commonmark.org/0.30 says that "at least one space or tab is needed + // between the list marker and any following content": + "1.Not a list item.", + "1.2.3. Not a list item.", + "1)Not a list item.", + "-Not a list item.", + "+Not a list item.", + "+1 not a list item.", + // https://spec.commonmark.org/0.30 says: "A start number may not be negative": + "-1. Not a list item.", + "-1 Not a list item.", + ]; + for line in test_inputs.iter() { + let maybe_block = ItemizedBlock::new(line); + assert!( + maybe_block.is_none(), + "The following line shouldn't be classified as a list item: {}", + line + ); + } + } +} diff --git a/src/config/config_type.rs b/src/config/config_type.rs new file mode 100644 index 000000000000..c836b4bbb789 --- /dev/null +++ b/src/config/config_type.rs @@ -0,0 +1,522 @@ +use crate::config::file_lines::FileLines; +use crate::config::macro_names::MacroSelectors; +use crate::config::options::{IgnoreList, WidthHeuristics}; + +/// Trait for types that can be used in `Config`. +pub(crate) trait ConfigType: Sized { + /// Returns hint text for use in `Config::print_docs()`. For enum types, this is a + /// pipe-separated list of variants; for other types it returns ``. + fn doc_hint() -> String; + + /// Return `true` if the variant (i.e. value of this type) is stable. + /// + /// By default, return true for all values. Enums annotated with `#[config_type]` + /// are automatically implemented, based on the `#[unstable_variant]` annotation. + fn stable_variant(&self) -> bool { + true + } +} + +impl ConfigType for bool { + fn doc_hint() -> String { + String::from("") + } +} + +impl ConfigType for usize { + fn doc_hint() -> String { + String::from("") + } +} + +impl ConfigType for isize { + fn doc_hint() -> String { + String::from("") + } +} + +impl ConfigType for String { + fn doc_hint() -> String { + String::from("") + } +} + +impl ConfigType for FileLines { + fn doc_hint() -> String { + String::from("") + } +} + +impl ConfigType for MacroSelectors { + fn doc_hint() -> String { + String::from("[, ...]") + } +} + +impl ConfigType for WidthHeuristics { + fn doc_hint() -> String { + String::new() + } +} + +impl ConfigType for IgnoreList { + fn doc_hint() -> String { + String::from("[,..]") + } +} + +macro_rules! create_config { + // Options passed in to the macro. + // + // - $i: the ident name of the option + // - $ty: the type of the option value + // - $def: the default value of the option + // - $stb: true if the option is stable + // - $dstring: description of the option + ($($i:ident: $ty:ty, $def:expr, $stb:expr, $( $dstring:expr ),+ );+ $(;)*) => ( + #[cfg(test)] + use std::collections::HashSet; + use std::io::Write; + + use serde::{Deserialize, Serialize}; + + #[derive(Clone)] + #[allow(unreachable_pub)] + pub struct Config { + // For each config item, we store: + // + // - 0: true if the value has been access + // - 1: true if the option was manually initialized + // - 2: the option value + // - 3: true if the option is unstable + $($i: (Cell, bool, $ty, bool)),+ + } + + // Just like the Config struct but with each property wrapped + // as Option. This is used to parse a rustfmt.toml that doesn't + // specify all properties of `Config`. + // We first parse into `PartialConfig`, then create a default `Config` + // and overwrite the properties with corresponding values from `PartialConfig`. + #[derive(Deserialize, Serialize, Clone)] + #[allow(unreachable_pub)] + pub struct PartialConfig { + $(pub $i: Option<$ty>),+ + } + + // Macro hygiene won't allow us to make `set_$i()` methods on Config + // for each item, so this struct is used to give the API to set values: + // `config.set().option(false)`. It's pretty ugly. Consider replacing + // with `config.set_option(false)` if we ever get a stable/usable + // `concat_idents!()`. + #[allow(unreachable_pub)] + pub struct ConfigSetter<'a>(&'a mut Config); + + impl<'a> ConfigSetter<'a> { + $( + #[allow(unreachable_pub)] + pub fn $i(&mut self, value: $ty) { + (self.0).$i.2 = value; + match stringify!($i) { + "max_width" + | "use_small_heuristics" + | "fn_call_width" + | "single_line_if_else_max_width" + | "single_line_let_else_max_width" + | "attr_fn_like_width" + | "struct_lit_width" + | "struct_variant_width" + | "array_width" + | "chain_width" => self.0.set_heuristics(), + "merge_imports" => self.0.set_merge_imports(), + "fn_args_layout" => self.0.set_fn_args_layout(), + &_ => (), + } + } + )+ + } + + // Query each option, returns true if the user set the option, false if + // a default was used. + #[allow(unreachable_pub)] + pub struct ConfigWasSet<'a>(&'a Config); + + impl<'a> ConfigWasSet<'a> { + $( + #[allow(unreachable_pub)] + pub fn $i(&self) -> bool { + (self.0).$i.1 + } + )+ + } + + impl Config { + $( + #[allow(unreachable_pub)] + pub fn $i(&self) -> $ty { + self.$i.0.set(true); + self.$i.2.clone() + } + )+ + + #[allow(unreachable_pub)] + pub fn set(&mut self) -> ConfigSetter<'_> { + ConfigSetter(self) + } + + #[allow(unreachable_pub)] + pub fn was_set(&self) -> ConfigWasSet<'_> { + ConfigWasSet(self) + } + + fn fill_from_parsed_config(mut self, parsed: PartialConfig, dir: &Path) -> Config { + $( + if let Some(option_value) = parsed.$i { + let option_stable = self.$i.3; + if $crate::config::config_type::is_stable_option_and_value( + stringify!($i), option_stable, &option_value + ) { + self.$i.1 = true; + self.$i.2 = option_value; + } + } + )+ + self.set_heuristics(); + self.set_ignore(dir); + self.set_merge_imports(); + self.set_fn_args_layout(); + self + } + + /// Returns a hash set initialized with every user-facing config option name. + #[cfg(test)] + pub(crate) fn hash_set() -> HashSet { + let mut hash_set = HashSet::new(); + $( + hash_set.insert(stringify!($i).to_owned()); + )+ + hash_set + } + + pub(crate) fn is_valid_name(name: &str) -> bool { + match name { + $( + stringify!($i) => true, + )+ + _ => false, + } + } + + #[allow(unreachable_pub)] + pub fn is_valid_key_val(key: &str, val: &str) -> bool { + match key { + $( + stringify!($i) => val.parse::<$ty>().is_ok(), + )+ + _ => false, + } + } + + #[allow(unreachable_pub)] + pub fn used_options(&self) -> PartialConfig { + PartialConfig { + $( + $i: if self.$i.0.get() { + Some(self.$i.2.clone()) + } else { + None + }, + )+ + } + } + + #[allow(unreachable_pub)] + pub fn all_options(&self) -> PartialConfig { + PartialConfig { + $( + $i: Some(self.$i.2.clone()), + )+ + } + } + + #[allow(unreachable_pub)] + pub fn override_value(&mut self, key: &str, val: &str) + { + match key { + $( + stringify!($i) => { + let option_value = val.parse::<$ty>() + .expect(&format!("Failed to parse override for {} (\"{}\") as a {}", + stringify!($i), + val, + stringify!($ty))); + + // Users are currently allowed to set unstable + // options/variants via the `--config` options override. + // + // There is ongoing discussion about how to move forward here: + // https://github.com/rust-lang/rustfmt/pull/5379 + // + // For now, do not validate whether the option or value is stable, + // just always set it. + self.$i.1 = true; + self.$i.2 = option_value; + } + )+ + _ => panic!("Unknown config key in override: {}", key) + } + + match key { + "max_width" + | "use_small_heuristics" + | "fn_call_width" + | "single_line_if_else_max_width" + | "single_line_let_else_max_width" + | "attr_fn_like_width" + | "struct_lit_width" + | "struct_variant_width" + | "array_width" + | "chain_width" => self.set_heuristics(), + "merge_imports" => self.set_merge_imports(), + "fn_args_layout" => self.set_fn_args_layout(), + &_ => (), + } + } + + #[allow(unreachable_pub)] + pub fn is_hidden_option(name: &str) -> bool { + const HIDE_OPTIONS: [&str; 6] = [ + "verbose", + "verbose_diff", + "file_lines", + "width_heuristics", + "merge_imports", + "fn_args_layout" + ]; + HIDE_OPTIONS.contains(&name) + } + + #[allow(unreachable_pub)] + pub fn print_docs(out: &mut dyn Write, include_unstable: bool) { + use std::cmp; + let max = 0; + $( let max = cmp::max(max, stringify!($i).len()+1); )+ + let space_str = " ".repeat(max); + writeln!(out, "Configuration Options:").unwrap(); + $( + if $stb || include_unstable { + let name_raw = stringify!($i); + + if !Config::is_hidden_option(name_raw) { + let mut name_out = String::with_capacity(max); + for _ in name_raw.len()..max-1 { + name_out.push(' ') + } + name_out.push_str(name_raw); + name_out.push(' '); + let mut default_str = format!("{}", $def); + if default_str.is_empty() { + default_str = String::from("\"\""); + } + writeln!(out, + "{}{} Default: {}{}", + name_out, + <$ty>::doc_hint(), + default_str, + if !$stb { " (unstable)" } else { "" }).unwrap(); + $( + writeln!(out, "{}{}", space_str, $dstring).unwrap(); + )+ + writeln!(out).unwrap(); + } + } + )+ + } + + fn set_width_heuristics(&mut self, heuristics: WidthHeuristics) { + let max_width = self.max_width.2; + let get_width_value = | + was_set: bool, + override_value: usize, + heuristic_value: usize, + config_key: &str, + | -> usize { + if !was_set { + return heuristic_value; + } + if override_value > max_width { + eprintln!( + "`{0}` cannot have a value that exceeds `max_width`. \ + `{0}` will be set to the same value as `max_width`", + config_key, + ); + return max_width; + } + override_value + }; + + let fn_call_width = get_width_value( + self.was_set().fn_call_width(), + self.fn_call_width.2, + heuristics.fn_call_width, + "fn_call_width", + ); + self.fn_call_width.2 = fn_call_width; + + let attr_fn_like_width = get_width_value( + self.was_set().attr_fn_like_width(), + self.attr_fn_like_width.2, + heuristics.attr_fn_like_width, + "attr_fn_like_width", + ); + self.attr_fn_like_width.2 = attr_fn_like_width; + + let struct_lit_width = get_width_value( + self.was_set().struct_lit_width(), + self.struct_lit_width.2, + heuristics.struct_lit_width, + "struct_lit_width", + ); + self.struct_lit_width.2 = struct_lit_width; + + let struct_variant_width = get_width_value( + self.was_set().struct_variant_width(), + self.struct_variant_width.2, + heuristics.struct_variant_width, + "struct_variant_width", + ); + self.struct_variant_width.2 = struct_variant_width; + + let array_width = get_width_value( + self.was_set().array_width(), + self.array_width.2, + heuristics.array_width, + "array_width", + ); + self.array_width.2 = array_width; + + let chain_width = get_width_value( + self.was_set().chain_width(), + self.chain_width.2, + heuristics.chain_width, + "chain_width", + ); + self.chain_width.2 = chain_width; + + let single_line_if_else_max_width = get_width_value( + self.was_set().single_line_if_else_max_width(), + self.single_line_if_else_max_width.2, + heuristics.single_line_if_else_max_width, + "single_line_if_else_max_width", + ); + self.single_line_if_else_max_width.2 = single_line_if_else_max_width; + + let single_line_let_else_max_width = get_width_value( + self.was_set().single_line_let_else_max_width(), + self.single_line_let_else_max_width.2, + heuristics.single_line_let_else_max_width, + "single_line_let_else_max_width", + ); + self.single_line_let_else_max_width.2 = single_line_let_else_max_width; + } + + fn set_heuristics(&mut self) { + let max_width = self.max_width.2; + match self.use_small_heuristics.2 { + Heuristics::Default => + self.set_width_heuristics(WidthHeuristics::scaled(max_width)), + Heuristics::Max => self.set_width_heuristics(WidthHeuristics::set(max_width)), + Heuristics::Off => self.set_width_heuristics(WidthHeuristics::null()), + }; + } + + fn set_ignore(&mut self, dir: &Path) { + self.ignore.2.add_prefix(dir); + } + + fn set_merge_imports(&mut self) { + if self.was_set().merge_imports() { + eprintln!( + "Warning: the `merge_imports` option is deprecated. \ + Use `imports_granularity=\"Crate\"` instead" + ); + if !self.was_set().imports_granularity() { + self.imports_granularity.2 = if self.merge_imports() { + ImportGranularity::Crate + } else { + ImportGranularity::Preserve + }; + } + } + } + + fn set_fn_args_layout(&mut self) { + if self.was_set().fn_args_layout() { + eprintln!( + "Warning: the `fn_args_layout` option is deprecated. \ + Use `fn_params_layout`. instead" + ); + if !self.was_set().fn_params_layout() { + self.fn_params_layout.2 = self.fn_args_layout(); + } + } + } + + #[allow(unreachable_pub)] + /// Returns `true` if the config key was explicitly set and is the default value. + pub fn is_default(&self, key: &str) -> bool { + $( + if let stringify!($i) = key { + return self.$i.1 && self.$i.2 == $def; + } + )+ + false + } + } + + // Template for the default configuration + impl Default for Config { + fn default() -> Config { + Config { + $( + $i: (Cell::new(false), false, $def, $stb), + )+ + } + } + } + ) +} + +pub(crate) fn is_stable_option_and_value( + option_name: &str, + option_stable: bool, + option_value: &T, +) -> bool +where + T: PartialEq + std::fmt::Debug + ConfigType, +{ + let nightly = crate::is_nightly_channel!(); + let variant_stable = option_value.stable_variant(); + match (nightly, option_stable, variant_stable) { + // Stable with an unstable option + (false, false, _) => { + eprintln!( + "Warning: can't set `{} = {:?}`, unstable features are only \ + available in nightly channel.", + option_name, option_value + ); + false + } + // Stable with a stable option, but an unstable variant + (false, true, false) => { + eprintln!( + "Warning: can't set `{} = {:?}`, unstable variants are only \ + available in nightly channel.", + option_name, option_value + ); + false + } + // Nightly: everything allowed + // Stable with stable option and variant: allowed + (true, _, _) | (false, true, true) => true, + } +} diff --git a/src/config/file_lines.rs b/src/config/file_lines.rs new file mode 100644 index 000000000000..e4e51a3f3b40 --- /dev/null +++ b/src/config/file_lines.rs @@ -0,0 +1,440 @@ +//! This module contains types and functions to support formatting specific line ranges. + +use itertools::Itertools; +use std::collections::HashMap; +use std::path::PathBuf; +use std::{cmp, fmt, iter, str}; + +use rustc_data_structures::sync::Lrc; +use rustc_span::{self, SourceFile}; +use serde::{ser, Deserialize, Deserializer, Serialize, Serializer}; +use serde_json as json; +use thiserror::Error; + +/// A range of lines in a file, inclusive of both ends. +pub struct LineRange { + pub(crate) file: Lrc, + pub(crate) lo: usize, + pub(crate) hi: usize, +} + +/// Defines the name of an input - either a file or stdin. +#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] +pub enum FileName { + Real(PathBuf), + Stdin, +} + +impl From for FileName { + fn from(name: rustc_span::FileName) -> FileName { + match name { + rustc_span::FileName::Real(rustc_span::RealFileName::LocalPath(p)) => FileName::Real(p), + rustc_span::FileName::Custom(ref f) if f == "stdin" => FileName::Stdin, + _ => unreachable!(), + } + } +} + +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, ""), + } + } +} + +impl<'de> Deserialize<'de> for FileName { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + if s == "stdin" { + Ok(FileName::Stdin) + } else { + Ok(FileName::Real(s.into())) + } + } +} + +impl Serialize for FileName { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let s = match self { + FileName::Stdin => Ok("stdin"), + FileName::Real(path) => path + .to_str() + .ok_or_else(|| ser::Error::custom("path can't be serialized as UTF-8 string")), + }; + + s.and_then(|s| serializer.serialize_str(s)) + } +} + +impl LineRange { + pub(crate) fn file_name(&self) -> FileName { + self.file.name.clone().into() + } +} + +/// A range that is inclusive of both ends. +#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Deserialize)] +pub struct Range { + lo: usize, + hi: usize, +} + +impl<'a> From<&'a LineRange> for Range { + fn from(range: &'a LineRange) -> Range { + Range::new(range.lo, range.hi) + } +} + +impl fmt::Display for Range { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}..{}", self.lo, self.hi) + } +} + +impl Range { + pub fn new(lo: usize, hi: usize) -> Range { + Range { lo, hi } + } + + fn is_empty(self) -> bool { + self.lo > self.hi + } + + #[allow(dead_code)] + fn contains(self, other: Range) -> bool { + if other.is_empty() { + true + } else { + !self.is_empty() && self.lo <= other.lo && self.hi >= other.hi + } + } + + fn intersects(self, other: Range) -> bool { + if self.is_empty() || other.is_empty() { + false + } else { + (self.lo <= other.hi && other.hi <= self.hi) + || (other.lo <= self.hi && self.hi <= other.hi) + } + } + + fn adjacent_to(self, other: Range) -> bool { + if self.is_empty() || other.is_empty() { + false + } else { + self.hi + 1 == other.lo || other.hi + 1 == self.lo + } + } + + /// Returns a new `Range` with lines from `self` and `other` if they were adjacent or + /// intersect; returns `None` otherwise. + fn merge(self, other: Range) -> Option { + if self.adjacent_to(other) || self.intersects(other) { + Some(Range::new( + cmp::min(self.lo, other.lo), + cmp::max(self.hi, other.hi), + )) + } else { + None + } + } +} + +/// A set of lines in files. +/// +/// It is represented as a multimap keyed on file names, with values a collection of +/// non-overlapping ranges sorted by their start point. An inner `None` is interpreted to mean all +/// lines in all files. +#[derive(Clone, Debug, Default, PartialEq)] +pub struct FileLines(Option>>); + +impl fmt::Display for FileLines { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match &self.0 { + None => write!(f, "None")?, + Some(map) => { + for (file_name, ranges) in map.iter() { + write!(f, "{}: ", file_name)?; + write!(f, "{}\n", ranges.iter().format(", "))?; + } + } + }; + Ok(()) + } +} + +/// Normalizes the ranges so that the invariants for `FileLines` hold: ranges are non-overlapping, +/// and ordered by their start point. +fn normalize_ranges(ranges: &mut HashMap>) { + for ranges in ranges.values_mut() { + ranges.sort(); + let mut result = vec![]; + let mut iter = ranges.iter_mut().peekable(); + while let Some(next) = iter.next() { + let mut next = *next; + while let Some(&&mut peek) = iter.peek() { + if let Some(merged) = next.merge(peek) { + iter.next().unwrap(); + next = merged; + } else { + break; + } + } + result.push(next) + } + *ranges = result; + } +} + +impl FileLines { + /// Creates a `FileLines` that contains all lines in all files. + pub(crate) fn all() -> FileLines { + FileLines(None) + } + + /// Returns `true` if this `FileLines` contains all lines in all files. + pub(crate) fn is_all(&self) -> bool { + self.0.is_none() + } + + pub fn from_ranges(mut ranges: HashMap>) -> FileLines { + normalize_ranges(&mut ranges); + FileLines(Some(ranges)) + } + + /// Returns an iterator over the files contained in `self`. + pub fn files(&self) -> Files<'_> { + Files(self.0.as_ref().map(HashMap::keys)) + } + + /// Returns JSON representation as accepted by the `--file-lines JSON` arg. + pub fn to_json_spans(&self) -> Vec { + match &self.0 { + None => vec![], + Some(file_ranges) => file_ranges + .iter() + .flat_map(|(file, ranges)| ranges.iter().map(move |r| (file, r))) + .map(|(file, range)| JsonSpan { + file: file.to_owned(), + range: (range.lo, range.hi), + }) + .collect(), + } + } + + /// Returns `true` if `self` includes all lines in all files. Otherwise runs `f` on all ranges + /// in the designated file (if any) and returns true if `f` ever does. + fn file_range_matches(&self, file_name: &FileName, f: F) -> bool + where + F: FnMut(&Range) -> bool, + { + let map = match self.0 { + // `None` means "all lines in all files". + None => return true, + Some(ref map) => map, + }; + + match canonicalize_path_string(file_name).and_then(|file| map.get(&file)) { + Some(ranges) => ranges.iter().any(f), + None => false, + } + } + + /// Returns `true` if `range` is fully contained in `self`. + #[allow(dead_code)] + pub(crate) fn contains(&self, range: &LineRange) -> bool { + self.file_range_matches(&range.file_name(), |r| r.contains(Range::from(range))) + } + + /// Returns `true` if any lines in `range` are in `self`. + pub(crate) fn intersects(&self, range: &LineRange) -> bool { + self.file_range_matches(&range.file_name(), |r| r.intersects(Range::from(range))) + } + + /// Returns `true` if `line` from `file_name` is in `self`. + pub(crate) fn contains_line(&self, file_name: &FileName, line: usize) -> bool { + self.file_range_matches(file_name, |r| r.lo <= line && r.hi >= line) + } + + /// Returns `true` if all the lines between `lo` and `hi` from `file_name` are in `self`. + pub(crate) fn contains_range(&self, file_name: &FileName, lo: usize, hi: usize) -> bool { + self.file_range_matches(file_name, |r| r.contains(Range::new(lo, hi))) + } +} + +/// `FileLines` files iterator. +pub struct Files<'a>(Option<::std::collections::hash_map::Keys<'a, FileName, Vec>>); + +impl<'a> iter::Iterator for Files<'a> { + type Item = &'a FileName; + + fn next(&mut self) -> Option<&'a FileName> { + self.0.as_mut().and_then(Iterator::next) + } +} + +fn canonicalize_path_string(file: &FileName) -> Option { + match *file { + FileName::Real(ref path) => path.canonicalize().ok().map(FileName::Real), + _ => Some(file.clone()), + } +} + +#[derive(Error, Debug)] +pub enum FileLinesError { + #[error("{0}")] + Json(json::Error), + #[error("Can't canonicalize {0}")] + CannotCanonicalize(FileName), +} + +// This impl is needed for `Config::override_value` to work for use in tests. +impl str::FromStr for FileLines { + type Err = FileLinesError; + + fn from_str(s: &str) -> Result { + let v: Vec = json::from_str(s).map_err(FileLinesError::Json)?; + let mut m = HashMap::new(); + for js in v { + let (s, r) = JsonSpan::into_tuple(js)?; + m.entry(s).or_insert_with(Vec::new).push(r); + } + Ok(FileLines::from_ranges(m)) + } +} + +// For JSON decoding. +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Deserialize, Serialize)] +pub struct JsonSpan { + file: FileName, + range: (usize, usize), +} + +impl JsonSpan { + fn into_tuple(self) -> Result<(FileName, Range), FileLinesError> { + let (lo, hi) = self.range; + let canonical = canonicalize_path_string(&self.file) + .ok_or(FileLinesError::CannotCanonicalize(self.file))?; + Ok((canonical, Range::new(lo, hi))) + } +} + +// This impl is needed for inclusion in the `Config` struct. We don't have a toml representation +// for `FileLines`, so it will just panic instead. +impl<'de> ::serde::de::Deserialize<'de> for FileLines { + fn deserialize(_: D) -> Result + where + D: ::serde::de::Deserializer<'de>, + { + panic!( + "FileLines cannot be deserialized from a project rustfmt.toml file: please \ + specify it via the `--file-lines` option instead" + ); + } +} + +// We also want to avoid attempting to serialize a FileLines to toml. The +// `Config` struct should ensure this impl is never reached. +impl ::serde::ser::Serialize for FileLines { + fn serialize(&self, _: S) -> Result + where + S: ::serde::ser::Serializer, + { + unreachable!("FileLines cannot be serialized. This is a rustfmt bug."); + } +} + +#[cfg(test)] +mod test { + use super::Range; + + #[test] + fn test_range_intersects() { + assert!(Range::new(1, 2).intersects(Range::new(1, 1))); + assert!(Range::new(1, 2).intersects(Range::new(2, 2))); + assert!(!Range::new(1, 2).intersects(Range::new(0, 0))); + assert!(!Range::new(1, 2).intersects(Range::new(3, 10))); + assert!(!Range::new(1, 3).intersects(Range::new(5, 5))); + } + + #[test] + fn test_range_adjacent_to() { + assert!(!Range::new(1, 2).adjacent_to(Range::new(1, 1))); + assert!(!Range::new(1, 2).adjacent_to(Range::new(2, 2))); + assert!(Range::new(1, 2).adjacent_to(Range::new(0, 0))); + assert!(Range::new(1, 2).adjacent_to(Range::new(3, 10))); + assert!(!Range::new(1, 3).adjacent_to(Range::new(5, 5))); + } + + #[test] + fn test_range_contains() { + assert!(Range::new(1, 2).contains(Range::new(1, 1))); + assert!(Range::new(1, 2).contains(Range::new(2, 2))); + assert!(!Range::new(1, 2).contains(Range::new(0, 0))); + assert!(!Range::new(1, 2).contains(Range::new(3, 10))); + } + + #[test] + fn test_range_merge() { + assert_eq!(None, Range::new(1, 3).merge(Range::new(5, 5))); + assert_eq!(None, Range::new(4, 7).merge(Range::new(0, 1))); + assert_eq!( + Some(Range::new(3, 7)), + Range::new(3, 5).merge(Range::new(4, 7)) + ); + assert_eq!( + Some(Range::new(3, 7)), + Range::new(3, 5).merge(Range::new(5, 7)) + ); + assert_eq!( + Some(Range::new(3, 7)), + Range::new(3, 5).merge(Range::new(6, 7)) + ); + assert_eq!( + Some(Range::new(3, 7)), + Range::new(3, 7).merge(Range::new(4, 5)) + ); + } + + use super::json::{self, json}; + use super::{FileLines, FileName}; + use std::{collections::HashMap, path::PathBuf}; + + #[test] + fn file_lines_to_json() { + let ranges: HashMap> = [ + ( + FileName::Real(PathBuf::from("src/main.rs")), + vec![Range::new(1, 3), Range::new(5, 7)], + ), + ( + FileName::Real(PathBuf::from("src/lib.rs")), + vec![Range::new(1, 7)], + ), + ] + .iter() + .cloned() + .collect(); + + let file_lines = FileLines::from_ranges(ranges); + let mut spans = file_lines.to_json_spans(); + spans.sort(); + let json = json::to_value(&spans).unwrap(); + assert_eq!( + json, + json! {[ + {"file": "src/lib.rs", "range": [1, 7]}, + {"file": "src/main.rs", "range": [1, 3]}, + {"file": "src/main.rs", "range": [5, 7]}, + ]} + ); + } +} diff --git a/src/config/lists.rs b/src/config/lists.rs new file mode 100644 index 000000000000..11cb17068fb4 --- /dev/null +++ b/src/config/lists.rs @@ -0,0 +1,92 @@ +//! Configuration options related to rewriting a list. + +use rustfmt_config_proc_macro::config_type; + +use crate::config::IndentStyle; + +/// The definitive formatting tactic for lists. +#[derive(Eq, PartialEq, Debug, Copy, Clone)] +pub enum DefinitiveListTactic { + Vertical, + Horizontal, + Mixed, + /// Special case tactic for `format!()`, `write!()` style macros. + SpecialMacro(usize), +} + +impl DefinitiveListTactic { + pub fn ends_with_newline(&self, indent_style: IndentStyle) -> bool { + match indent_style { + IndentStyle::Block => *self != DefinitiveListTactic::Horizontal, + IndentStyle::Visual => false, + } + } +} + +/// Formatting tactic for lists. This will be cast down to a +/// `DefinitiveListTactic` depending on the number and length of the items and +/// their comments. +#[config_type] +pub enum ListTactic { + /// One item per row. + Vertical, + /// All items on one row. + Horizontal, + /// Try Horizontal layout, if that fails then vertical. + HorizontalVertical, + /// HorizontalVertical with a soft limit of n characters. + LimitedHorizontalVertical(usize), + /// Pack as many items as possible per row over (possibly) many rows. + Mixed, +} + +#[config_type] +pub enum SeparatorTactic { + Always, + Never, + Vertical, +} + +impl SeparatorTactic { + pub fn from_bool(b: bool) -> SeparatorTactic { + if b { + SeparatorTactic::Always + } else { + SeparatorTactic::Never + } + } +} + +/// Where to put separator. +#[config_type] +pub enum SeparatorPlace { + Front, + Back, +} + +impl SeparatorPlace { + pub fn is_front(self) -> bool { + self == SeparatorPlace::Front + } + + pub fn is_back(self) -> bool { + self == SeparatorPlace::Back + } + + pub fn from_tactic( + default: SeparatorPlace, + tactic: DefinitiveListTactic, + sep: &str, + ) -> SeparatorPlace { + match tactic { + DefinitiveListTactic::Vertical => default, + _ => { + if sep == "," { + SeparatorPlace::Back + } else { + default + } + } + } + } +} diff --git a/src/config/macro_names.rs b/src/config/macro_names.rs new file mode 100644 index 000000000000..26ad78d6dcae --- /dev/null +++ b/src/config/macro_names.rs @@ -0,0 +1,118 @@ +//! This module contains types and functions to support formatting specific macros. + +use itertools::Itertools; +use std::{fmt, str}; + +use serde::{Deserialize, Serialize}; +use serde_json as json; +use thiserror::Error; + +/// Defines the name of a macro. +#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize)] +pub struct MacroName(String); + +impl MacroName { + pub fn new(other: String) -> Self { + Self(other) + } +} + +impl fmt::Display for MacroName { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl From for String { + fn from(other: MacroName) -> Self { + other.0 + } +} + +/// Defines a selector to match against a macro. +#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize)] +pub enum MacroSelector { + Name(MacroName), + All, +} + +impl fmt::Display for MacroSelector { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Name(name) => name.fmt(f), + Self::All => write!(f, "*"), + } + } +} + +impl str::FromStr for MacroSelector { + type Err = std::convert::Infallible; + + fn from_str(s: &str) -> Result { + Ok(match s { + "*" => MacroSelector::All, + name => MacroSelector::Name(MacroName(name.to_owned())), + }) + } +} + +/// A set of macro selectors. +#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)] +pub struct MacroSelectors(pub Vec); + +impl fmt::Display for MacroSelectors { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0.iter().format(", ")) + } +} + +#[derive(Error, Debug)] +pub enum MacroSelectorsError { + #[error("{0}")] + Json(json::Error), +} + +// This impl is needed for `Config::override_value` to work for use in tests. +impl str::FromStr for MacroSelectors { + type Err = MacroSelectorsError; + + fn from_str(s: &str) -> Result { + let raw: Vec<&str> = json::from_str(s).map_err(MacroSelectorsError::Json)?; + Ok(Self( + raw.into_iter() + .map(|raw| { + MacroSelector::from_str(raw).expect("MacroSelector from_str is infallible") + }) + .collect(), + )) + } +} + +#[cfg(test)] +mod test { + use super::*; + use std::str::FromStr; + + #[test] + fn macro_names_from_str() { + let macro_names = MacroSelectors::from_str(r#"["foo", "*", "bar"]"#).unwrap(); + assert_eq!( + macro_names, + MacroSelectors( + [ + MacroSelector::Name(MacroName("foo".to_owned())), + MacroSelector::All, + MacroSelector::Name(MacroName("bar".to_owned())) + ] + .into_iter() + .collect() + ) + ); + } + + #[test] + fn macro_names_display() { + let macro_names = MacroSelectors::from_str(r#"["foo", "*", "bar"]"#).unwrap(); + assert_eq!(format!("{}", macro_names), "foo, *, bar"); + } +} diff --git a/src/config/mod.rs b/src/config/mod.rs new file mode 100644 index 000000000000..6f41b299e87d --- /dev/null +++ b/src/config/mod.rs @@ -0,0 +1,1060 @@ +use std::cell::Cell; +use std::default::Default; +use std::fs::File; +use std::io::{Error, ErrorKind, Read}; +use std::path::{Path, PathBuf}; +use std::{env, fs}; + +use thiserror::Error; + +use crate::config::config_type::ConfigType; +#[allow(unreachable_pub)] +pub use crate::config::file_lines::{FileLines, FileName, Range}; +#[allow(unreachable_pub)] +pub use crate::config::lists::*; +#[allow(unreachable_pub)] +pub use crate::config::macro_names::{MacroSelector, MacroSelectors}; +#[allow(unreachable_pub)] +pub use crate::config::options::*; + +#[macro_use] +pub(crate) mod config_type; +#[macro_use] +#[allow(unreachable_pub)] +pub(crate) mod options; + +pub(crate) mod file_lines; +#[allow(unreachable_pub)] +pub(crate) mod lists; +pub(crate) mod macro_names; + +// This macro defines configuration options used in rustfmt. Each option +// is defined as follows: +// +// `name: value type, default value, is stable, description;` +create_config! { + // Fundamental stuff + max_width: usize, 100, true, "Maximum width of each line"; + hard_tabs: bool, false, true, "Use tab characters for indentation, spaces for alignment"; + tab_spaces: usize, 4, true, "Number of spaces per tab"; + newline_style: NewlineStyle, NewlineStyle::Auto, true, "Unix or Windows line endings"; + indent_style: IndentStyle, IndentStyle::Block, false, "How do we indent expressions or items"; + + // Width Heuristics + use_small_heuristics: Heuristics, Heuristics::Default, true, "Whether to use different \ + formatting for items and expressions if they satisfy a heuristic notion of 'small'"; + width_heuristics: WidthHeuristics, WidthHeuristics::scaled(100), false, + "'small' heuristic values"; + fn_call_width: usize, 60, true, "Maximum width of the args of a function call before \ + falling back to vertical formatting."; + attr_fn_like_width: usize, 70, true, "Maximum width of the args of a function-like \ + attributes before falling back to vertical formatting."; + struct_lit_width: usize, 18, true, "Maximum width in the body of a struct lit before \ + falling back to vertical formatting."; + struct_variant_width: usize, 35, true, "Maximum width in the body of a struct variant before \ + falling back to vertical formatting."; + array_width: usize, 60, true, "Maximum width of an array literal before falling \ + back to vertical formatting."; + chain_width: usize, 60, true, "Maximum length of a chain to fit on a single line."; + single_line_if_else_max_width: usize, 50, true, "Maximum line length for single line if-else \ + expressions. A value of zero means always break if-else expressions."; + single_line_let_else_max_width: usize, 50, true, "Maximum line length for single line \ + let-else statements. A value of zero means always format the divergent `else` block \ + over multiple lines."; + + // Comments. macros, and strings + wrap_comments: bool, false, false, "Break comments to fit on the line"; + format_code_in_doc_comments: bool, false, false, "Format the code snippet in doc comments."; + doc_comment_code_block_width: usize, 100, false, "Maximum width for code snippets in doc \ + comments. No effect unless format_code_in_doc_comments = true"; + comment_width: usize, 80, false, + "Maximum length of comments. No effect unless wrap_comments = true"; + normalize_comments: bool, false, false, "Convert /* */ comments to // comments where possible"; + normalize_doc_attributes: bool, false, false, "Normalize doc attributes as doc comments"; + format_strings: bool, false, false, "Format string literals where necessary"; + format_macro_matchers: bool, false, false, + "Format the metavariable matching patterns in macros"; + format_macro_bodies: bool, true, false, "Format the bodies of macros"; + skip_macro_invocations: MacroSelectors, MacroSelectors::default(), false, + "Skip formatting the bodies of macros invoked with the following names."; + hex_literal_case: HexLiteralCase, HexLiteralCase::Preserve, false, + "Format hexadecimal integer literals"; + + // Single line expressions and items + empty_item_single_line: bool, true, false, + "Put empty-body functions and impls on a single line"; + struct_lit_single_line: bool, true, false, + "Put small struct literals on a single line"; + fn_single_line: bool, false, false, "Put single-expression functions on a single line"; + where_single_line: bool, false, false, "Force where-clauses to be on a single line"; + + // Imports + imports_indent: IndentStyle, IndentStyle::Block, false, "Indent of imports"; + imports_layout: ListTactic, ListTactic::Mixed, false, "Item layout inside a import block"; + imports_granularity: ImportGranularity, ImportGranularity::Preserve, false, + "Merge or split imports to the provided granularity"; + group_imports: GroupImportsTactic, GroupImportsTactic::Preserve, false, + "Controls the strategy for how imports are grouped together"; + merge_imports: bool, false, false, "(deprecated: use imports_granularity instead)"; + + // Ordering + reorder_imports: bool, true, true, "Reorder import and extern crate statements alphabetically"; + reorder_modules: bool, true, true, "Reorder module statements alphabetically in group"; + reorder_impl_items: bool, false, false, "Reorder impl items"; + + // Spaces around punctuation + type_punctuation_density: TypeDensity, TypeDensity::Wide, false, + "Determines if '+' or '=' are wrapped in spaces in the punctuation of types"; + space_before_colon: bool, false, false, "Leave a space before the colon"; + space_after_colon: bool, true, false, "Leave a space after the colon"; + spaces_around_ranges: bool, false, false, "Put spaces around the .. and ..= range operators"; + binop_separator: SeparatorPlace, SeparatorPlace::Front, false, + "Where to put a binary operator when a binary expression goes multiline"; + + // 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, + "Align struct fields if their diffs fits within threshold"; + enum_discrim_align_threshold: usize, 0, false, + "Align enum variants discrims, if their diffs fit within threshold"; + match_arm_blocks: bool, true, false, "Wrap the body of arms in blocks when it does not fit on \ + the same line with the pattern of arms"; + match_arm_leading_pipes: MatchArmLeadingPipe, MatchArmLeadingPipe::Never, true, + "Determines whether leading pipes are emitted on match arms"; + force_multiline_blocks: bool, false, false, + "Force multiline closure bodies and match arms to be wrapped in a block"; + fn_args_layout: Density, Density::Tall, true, + "(deprecated: use fn_params_layout instead)"; + fn_params_layout: Density, Density::Tall, true, + "Control the layout of parameters in function signatures."; + brace_style: BraceStyle, BraceStyle::SameLineWhere, false, "Brace style for items"; + control_brace_style: ControlBraceStyle, ControlBraceStyle::AlwaysSameLine, false, + "Brace style for control flow constructs"; + trailing_semicolon: bool, true, false, + "Add trailing semicolon after break, continue and return"; + trailing_comma: SeparatorTactic, SeparatorTactic::Vertical, false, + "How to handle trailing commas for lists"; + match_block_trailing_comma: bool, false, true, + "Put a trailing comma after a block based match arm (non-block arms are not affected)"; + blank_lines_upper_bound: usize, 1, false, + "Maximum number of blank lines which can be put between items"; + blank_lines_lower_bound: usize, 0, false, + "Minimum number of blank lines which must be put between items"; + edition: Edition, Edition::Edition2015, true, "The edition of the parser (RFC 2052)"; + version: Version, Version::One, false, "Version of formatting rules"; + inline_attribute_width: usize, 0, false, + "Write an item and its attribute on the same line \ + if their combined width is below a threshold"; + format_generated_files: bool, true, false, "Format generated files"; + + // Options that can change the source code beyond whitespace/blocks (somewhat linty things) + merge_derives: bool, true, true, "Merge multiple `#[derive(...)]` into a single one"; + use_try_shorthand: bool, false, true, "Replace uses of the try! macro by the ? shorthand"; + use_field_init_shorthand: bool, false, true, "Use field initialization shorthand if possible"; + force_explicit_abi: bool, true, true, "Always print the abi for extern items"; + condense_wildcard_suffixes: bool, false, false, "Replace strings of _ wildcards by a single .. \ + in tuple patterns"; + + // Control options (changes the operation of rustfmt, rather than the formatting) + color: Color, Color::Auto, false, + "What Color option to use when none is supplied: Always, Never, Auto"; + required_version: String, env!("CARGO_PKG_VERSION").to_owned(), false, + "Require a specific version of rustfmt"; + unstable_features: bool, false, false, + "Enables unstable features. Only available on nightly channel"; + disable_all_formatting: bool, false, true, "Don't reformat anything"; + skip_children: bool, false, false, "Don't reformat out of line modules"; + hide_parse_errors: bool, false, false, "Hide errors from the parser"; + error_on_line_overflow: bool, false, false, "Error if unable to get all lines within max_width"; + error_on_unformatted: bool, false, false, + "Error if unable to get comments or string literals within max_width, \ + or they are left with trailing whitespaces"; + ignore: IgnoreList, IgnoreList::default(), false, + "Skip formatting the specified files and directories"; + + // Not user-facing + verbose: Verbosity, Verbosity::Normal, false, "How much to information to emit to the user"; + file_lines: FileLines, FileLines::all(), false, + "Lines to format; this is not supported in rustfmt.toml, and can only be specified \ + via the --file-lines option"; + emit_mode: EmitMode, EmitMode::Files, false, + "What emit Mode to use when none is supplied"; + make_backup: bool, false, false, "Backup changed files"; + print_misformatted_file_names: bool, false, true, + "Prints the names of mismatched files that were formatted. Prints the names of \ + files that would be formatted when used with `--check` mode. "; +} + +#[derive(Error, Debug)] +#[error("Could not output config: {0}")] +pub struct ToTomlError(toml::ser::Error); + +impl PartialConfig { + pub fn to_toml(&self) -> Result { + // Non-user-facing options can't be specified in TOML + let mut cloned = self.clone(); + cloned.file_lines = None; + cloned.verbose = None; + cloned.width_heuristics = None; + cloned.print_misformatted_file_names = None; + cloned.merge_imports = None; + cloned.fn_args_layout = None; + + ::toml::to_string(&cloned).map_err(ToTomlError) + } +} + +impl Config { + pub(crate) fn version_meets_requirement(&self) -> bool { + if self.was_set().required_version() { + let version = env!("CARGO_PKG_VERSION"); + let required_version = self.required_version(); + if version != required_version { + println!( + "Error: rustfmt version ({}) doesn't match the required version ({})", + version, required_version, + ); + return false; + } + } + + true + } + + /// Constructs a `Config` from the toml file specified at `file_path`. + /// + /// This method only looks at the provided path, for a method that + /// searches parents for a `rustfmt.toml` see `from_resolved_toml_path`. + /// + /// Returns a `Config` if the config could be read and parsed from + /// the file, otherwise errors. + pub(super) fn from_toml_path(file_path: &Path) -> Result { + let mut file = File::open(&file_path)?; + let mut toml = String::new(); + file.read_to_string(&mut toml)?; + Config::from_toml(&toml, file_path.parent().unwrap()) + .map_err(|err| Error::new(ErrorKind::InvalidData, err)) + } + + /// Resolves the config for input in `dir`. + /// + /// Searches for `rustfmt.toml` beginning with `dir`, and + /// recursively checking parents of `dir` if no config file is found. + /// If no config file exists in `dir` or in any parent, a + /// default `Config` will be returned (and the returned path will be empty). + /// + /// Returns the `Config` to use, and the path of the project file if there was + /// one. + pub(super) fn from_resolved_toml_path(dir: &Path) -> Result<(Config, Option), Error> { + /// Try to find a project file in the given directory and its parents. + /// Returns the path of a the nearest project file if one exists, + /// or `None` if no project file was found. + fn resolve_project_file(dir: &Path) -> Result, Error> { + let mut current = if dir.is_relative() { + env::current_dir()?.join(dir) + } else { + dir.to_path_buf() + }; + + current = fs::canonicalize(current)?; + + loop { + match get_toml_path(¤t) { + Ok(Some(path)) => return Ok(Some(path)), + Err(e) => return Err(e), + _ => (), + } + + // If the current directory has no parent, we're done searching. + if !current.pop() { + break; + } + } + + // If nothing was found, check in the home directory. + if let Some(home_dir) = dirs::home_dir() { + if let Some(path) = get_toml_path(&home_dir)? { + return Ok(Some(path)); + } + } + + // If none was found ther either, check in the user's configuration directory. + if let Some(mut config_dir) = dirs::config_dir() { + config_dir.push("rustfmt"); + if let Some(path) = get_toml_path(&config_dir)? { + return Ok(Some(path)); + } + } + + Ok(None) + } + + match resolve_project_file(dir)? { + None => Ok((Config::default(), None)), + Some(path) => Config::from_toml_path(&path).map(|config| (config, Some(path))), + } + } + + pub(crate) fn from_toml(toml: &str, dir: &Path) -> Result { + let parsed: ::toml::Value = toml + .parse() + .map_err(|e| format!("Could not parse TOML: {}", e))?; + let mut err = String::new(); + let table = parsed + .as_table() + .ok_or_else(|| String::from("Parsed config was not table"))?; + for key in table.keys() { + if !Config::is_valid_name(key) { + let msg = &format!("Warning: Unknown configuration option `{}`\n", key); + err.push_str(msg) + } + } + match parsed.try_into() { + Ok(parsed_config) => { + if !err.is_empty() { + eprint!("{}", err); + } + Ok(Config::default().fill_from_parsed_config(parsed_config, dir)) + } + Err(e) => { + err.push_str("Error: Decoding config file failed:\n"); + err.push_str(format!("{}\n", e).as_str()); + err.push_str("Please check your config file."); + Err(err) + } + } + } +} + +/// Loads a config by checking the client-supplied options and if appropriate, the +/// file system (including searching the file system for overrides). +pub fn load_config( + file_path: Option<&Path>, + options: Option, +) -> Result<(Config, Option), Error> { + let over_ride = match options { + Some(ref opts) => config_path(opts)?, + None => None, + }; + + let result = if let Some(over_ride) = over_ride { + Config::from_toml_path(over_ride.as_ref()).map(|p| (p, Some(over_ride.to_owned()))) + } else if let Some(file_path) = file_path { + Config::from_resolved_toml_path(file_path) + } else { + Ok((Config::default(), None)) + }; + + result.map(|(mut c, p)| { + if let Some(options) = options { + options.apply_to(&mut c); + } + (c, p) + }) +} + +// Check for the presence of known config file names (`rustfmt.toml, `.rustfmt.toml`) in `dir` +// +// Return the path if a config file exists, empty if no file exists, and Error for IO errors +fn get_toml_path(dir: &Path) -> Result, Error> { + const CONFIG_FILE_NAMES: [&str; 2] = [".rustfmt.toml", "rustfmt.toml"]; + for config_file_name in &CONFIG_FILE_NAMES { + let config_file = dir.join(config_file_name); + match fs::metadata(&config_file) { + // Only return if it's a file to handle the unlikely situation of a directory named + // `rustfmt.toml`. + Ok(ref md) if md.is_file() => return Ok(Some(config_file)), + // Return the error if it's something other than `NotFound`; otherwise we didn't + // find the project file yet, and continue searching. + Err(e) => { + if e.kind() != ErrorKind::NotFound { + 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)); + } + } + _ => {} + } + } + Ok(None) +} + +fn config_path(options: &dyn CliOptions) -> Result, Error> { + let config_path_not_found = |path: &str| -> Result, Error> { + Err(Error::new( + ErrorKind::NotFound, + format!( + "Error: unable to find a config file for the given path: `{}`", + path + ), + )) + }; + + // Read the config_path and convert to parent dir if a file is provided. + // If a config file cannot be found from the given path, return error. + match options.config_path() { + Some(path) if !path.exists() => config_path_not_found(path.to_str().unwrap()), + Some(path) if path.is_dir() => { + let config_file_path = get_toml_path(path)?; + if config_file_path.is_some() { + Ok(config_file_path) + } else { + config_path_not_found(path.to_str().unwrap()) + } + } + path => Ok(path.map(ToOwned::to_owned)), + } +} + +#[cfg(test)] +mod test { + use super::*; + use std::str; + + use crate::config::macro_names::MacroName; + use rustfmt_config_proc_macro::{nightly_only_test, stable_only_test}; + + #[allow(dead_code)] + mod mock { + use super::super::*; + use rustfmt_config_proc_macro::config_type; + + #[config_type] + pub(crate) enum PartiallyUnstableOption { + V1, + V2, + #[unstable_variant] + V3, + } + + create_config! { + // Options that are used by the generated functions + max_width: usize, 100, true, "Maximum width of each line"; + required_version: String, env!("CARGO_PKG_VERSION").to_owned(), false, + "Require a specific version of rustfmt."; + ignore: IgnoreList, IgnoreList::default(), false, + "Skip formatting the specified files and directories."; + verbose: Verbosity, Verbosity::Normal, false, + "How much to information to emit to the user"; + file_lines: FileLines, FileLines::all(), false, + "Lines to format; this is not supported in rustfmt.toml, and can only be specified \ + via the --file-lines option"; + + // merge_imports deprecation + imports_granularity: ImportGranularity, ImportGranularity::Preserve, false, + "Merge imports"; + merge_imports: bool, false, false, "(deprecated: use imports_granularity instead)"; + + // fn_args_layout renamed to fn_params_layout + fn_args_layout: Density, Density::Tall, true, + "(deprecated: use fn_params_layout instead)"; + fn_params_layout: Density, Density::Tall, true, + "Control the layout of parameters in a function signatures."; + + // Width Heuristics + use_small_heuristics: Heuristics, Heuristics::Default, true, + "Whether to use different formatting for items and \ + expressions if they satisfy a heuristic notion of 'small'."; + width_heuristics: WidthHeuristics, WidthHeuristics::scaled(100), false, + "'small' heuristic values"; + + fn_call_width: usize, 60, true, "Maximum width of the args of a function call before \ + falling back to vertical formatting."; + attr_fn_like_width: usize, 70, true, "Maximum width of the args of a function-like \ + attributes before falling back to vertical formatting."; + struct_lit_width: usize, 18, true, "Maximum width in the body of a struct lit before \ + falling back to vertical formatting."; + struct_variant_width: usize, 35, true, "Maximum width in the body of a struct \ + variant before falling back to vertical formatting."; + array_width: usize, 60, true, "Maximum width of an array literal before falling \ + back to vertical formatting."; + chain_width: usize, 60, true, "Maximum length of a chain to fit on a single line."; + single_line_if_else_max_width: usize, 50, true, "Maximum line length for single \ + line if-else expressions. A value of zero means always break if-else expressions."; + single_line_let_else_max_width: usize, 50, false, "Maximum line length for single \ + line let-else statements. A value of zero means always format the divergent \ + `else` block over multiple lines."; + + // Options that are used by the tests + stable_option: bool, false, true, "A stable option"; + unstable_option: bool, false, false, "An unstable option"; + partially_unstable_option: PartiallyUnstableOption, PartiallyUnstableOption::V1, true, + "A partially unstable option"; + } + + #[cfg(test)] + mod partially_unstable_option { + use super::{Config, PartialConfig, PartiallyUnstableOption}; + use rustfmt_config_proc_macro::{nightly_only_test, stable_only_test}; + use std::path::Path; + + /// From the config file, we can fill with a stable variant + #[test] + fn test_from_toml_stable_value() { + let toml = r#" + partially_unstable_option = "V2" + "#; + let partial_config: PartialConfig = toml::from_str(toml).unwrap(); + let config = Config::default(); + let config = config.fill_from_parsed_config(partial_config, Path::new("")); + assert_eq!( + config.partially_unstable_option(), + PartiallyUnstableOption::V2 + ); + } + + /// From the config file, we cannot fill with an unstable variant (stable only) + #[stable_only_test] + #[test] + fn test_from_toml_unstable_value_on_stable() { + let toml = r#" + partially_unstable_option = "V3" + "#; + let partial_config: PartialConfig = toml::from_str(toml).unwrap(); + let config = Config::default(); + let config = config.fill_from_parsed_config(partial_config, Path::new("")); + assert_eq!( + config.partially_unstable_option(), + // default value from config, i.e. fill failed + PartiallyUnstableOption::V1 + ); + } + + /// From the config file, we can fill with an unstable variant (nightly only) + #[nightly_only_test] + #[test] + fn test_from_toml_unstable_value_on_nightly() { + let toml = r#" + partially_unstable_option = "V3" + "#; + let partial_config: PartialConfig = toml::from_str(toml).unwrap(); + let config = Config::default(); + let config = config.fill_from_parsed_config(partial_config, Path::new("")); + assert_eq!( + config.partially_unstable_option(), + PartiallyUnstableOption::V3 + ); + } + } + } + + #[test] + fn test_config_set() { + let mut config = Config::default(); + config.set().verbose(Verbosity::Quiet); + assert_eq!(config.verbose(), Verbosity::Quiet); + config.set().verbose(Verbosity::Normal); + assert_eq!(config.verbose(), Verbosity::Normal); + } + + #[test] + fn test_config_used_to_toml() { + let config = Config::default(); + + let merge_derives = config.merge_derives(); + let skip_children = config.skip_children(); + + let used_options = config.used_options(); + let toml = used_options.to_toml().unwrap(); + assert_eq!( + toml, + format!( + "merge_derives = {}\nskip_children = {}\n", + merge_derives, skip_children, + ) + ); + } + + #[test] + fn test_was_set() { + let config = Config::from_toml("hard_tabs = true", Path::new("")).unwrap(); + + assert_eq!(config.was_set().hard_tabs(), true); + assert_eq!(config.was_set().verbose(), false); + } + + const PRINT_DOCS_STABLE_OPTION: &str = "stable_option Default: false"; + const PRINT_DOCS_UNSTABLE_OPTION: &str = "unstable_option Default: false (unstable)"; + const PRINT_DOCS_PARTIALLY_UNSTABLE_OPTION: &str = + "partially_unstable_option [V1|V2|V3 (unstable)] Default: V1"; + + #[test] + fn test_print_docs_exclude_unstable() { + use self::mock::Config; + + let mut output = Vec::new(); + Config::print_docs(&mut output, false); + + let s = str::from_utf8(&output).unwrap(); + assert_eq!(s.contains(PRINT_DOCS_STABLE_OPTION), true); + assert_eq!(s.contains(PRINT_DOCS_UNSTABLE_OPTION), false); + assert_eq!(s.contains(PRINT_DOCS_PARTIALLY_UNSTABLE_OPTION), true); + } + + #[test] + fn test_print_docs_include_unstable() { + use self::mock::Config; + + let mut output = Vec::new(); + Config::print_docs(&mut output, true); + + let s = str::from_utf8(&output).unwrap(); + assert_eq!(s.contains(PRINT_DOCS_STABLE_OPTION), true); + assert_eq!(s.contains(PRINT_DOCS_UNSTABLE_OPTION), true); + assert_eq!(s.contains(PRINT_DOCS_PARTIALLY_UNSTABLE_OPTION), true); + } + + #[test] + fn test_dump_default_config() { + let default_config = format!( + r#"max_width = 100 +hard_tabs = false +tab_spaces = 4 +newline_style = "Auto" +indent_style = "Block" +use_small_heuristics = "Default" +fn_call_width = 60 +attr_fn_like_width = 70 +struct_lit_width = 18 +struct_variant_width = 35 +array_width = 60 +chain_width = 60 +single_line_if_else_max_width = 50 +single_line_let_else_max_width = 50 +wrap_comments = false +format_code_in_doc_comments = false +doc_comment_code_block_width = 100 +comment_width = 80 +normalize_comments = false +normalize_doc_attributes = false +format_strings = false +format_macro_matchers = false +format_macro_bodies = true +skip_macro_invocations = [] +hex_literal_case = "Preserve" +empty_item_single_line = true +struct_lit_single_line = true +fn_single_line = false +where_single_line = false +imports_indent = "Block" +imports_layout = "Mixed" +imports_granularity = "Preserve" +group_imports = "Preserve" +reorder_imports = true +reorder_modules = true +reorder_impl_items = false +type_punctuation_density = "Wide" +space_before_colon = false +space_after_colon = true +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 +match_arm_blocks = true +match_arm_leading_pipes = "Never" +force_multiline_blocks = false +fn_params_layout = "Tall" +brace_style = "SameLineWhere" +control_brace_style = "AlwaysSameLine" +trailing_semicolon = true +trailing_comma = "Vertical" +match_block_trailing_comma = false +blank_lines_upper_bound = 1 +blank_lines_lower_bound = 0 +edition = "2015" +version = "One" +inline_attribute_width = 0 +format_generated_files = true +merge_derives = true +use_try_shorthand = false +use_field_init_shorthand = false +force_explicit_abi = true +condense_wildcard_suffixes = false +color = "Auto" +required_version = "{}" +unstable_features = false +disable_all_formatting = false +skip_children = false +hide_parse_errors = false +error_on_line_overflow = false +error_on_unformatted = false +ignore = [] +emit_mode = "Files" +make_backup = false +"#, + env!("CARGO_PKG_VERSION") + ); + let toml = Config::default().all_options().to_toml().unwrap(); + assert_eq!(&toml, &default_config); + } + + #[stable_only_test] + #[test] + fn test_as_not_nightly_channel() { + let mut config = Config::default(); + assert_eq!(config.was_set().unstable_features(), false); + config.set().unstable_features(true); + assert_eq!(config.was_set().unstable_features(), false); + } + + #[nightly_only_test] + #[test] + fn test_as_nightly_channel() { + let mut config = Config::default(); + config.set().unstable_features(true); + // When we don't set the config from toml or command line options it + // doesn't get marked as set by the user. + assert_eq!(config.was_set().unstable_features(), false); + config.set().unstable_features(true); + assert_eq!(config.unstable_features(), true); + } + + #[nightly_only_test] + #[test] + fn test_unstable_from_toml() { + let config = Config::from_toml("unstable_features = true", Path::new("")).unwrap(); + assert_eq!(config.was_set().unstable_features(), true); + assert_eq!(config.unstable_features(), true); + } + + #[cfg(test)] + mod deprecated_option_merge_imports { + use super::*; + + #[nightly_only_test] + #[test] + fn test_old_option_set() { + let toml = r#" + unstable_features = true + merge_imports = true + "#; + let config = Config::from_toml(toml, Path::new("")).unwrap(); + assert_eq!(config.imports_granularity(), ImportGranularity::Crate); + } + + #[nightly_only_test] + #[test] + fn test_both_set() { + let toml = r#" + unstable_features = true + merge_imports = true + imports_granularity = "Preserve" + "#; + let config = Config::from_toml(toml, Path::new("")).unwrap(); + assert_eq!(config.imports_granularity(), ImportGranularity::Preserve); + } + + #[nightly_only_test] + #[test] + fn test_new_overridden() { + let toml = r#" + unstable_features = true + merge_imports = true + "#; + let mut config = Config::from_toml(toml, Path::new("")).unwrap(); + config.override_value("imports_granularity", "Preserve"); + assert_eq!(config.imports_granularity(), ImportGranularity::Preserve); + } + + #[nightly_only_test] + #[test] + fn test_old_overridden() { + let toml = r#" + unstable_features = true + imports_granularity = "Module" + "#; + let mut config = Config::from_toml(toml, Path::new("")).unwrap(); + config.override_value("merge_imports", "true"); + // no effect: the new option always takes precedence + assert_eq!(config.imports_granularity(), ImportGranularity::Module); + } + } + + #[cfg(test)] + mod use_small_heuristics { + use super::*; + + #[test] + fn test_default_sets_correct_widths() { + let toml = r#" + use_small_heuristics = "Default" + max_width = 200 + "#; + let config = Config::from_toml(toml, Path::new("")).unwrap(); + assert_eq!(config.array_width(), 120); + assert_eq!(config.attr_fn_like_width(), 140); + assert_eq!(config.chain_width(), 120); + assert_eq!(config.fn_call_width(), 120); + assert_eq!(config.single_line_if_else_max_width(), 100); + assert_eq!(config.struct_lit_width(), 36); + assert_eq!(config.struct_variant_width(), 70); + } + + #[test] + fn test_max_sets_correct_widths() { + let toml = r#" + use_small_heuristics = "Max" + max_width = 120 + "#; + let config = Config::from_toml(toml, Path::new("")).unwrap(); + assert_eq!(config.array_width(), 120); + assert_eq!(config.attr_fn_like_width(), 120); + assert_eq!(config.chain_width(), 120); + assert_eq!(config.fn_call_width(), 120); + assert_eq!(config.single_line_if_else_max_width(), 120); + assert_eq!(config.struct_lit_width(), 120); + assert_eq!(config.struct_variant_width(), 120); + } + + #[test] + fn test_off_sets_correct_widths() { + let toml = r#" + use_small_heuristics = "Off" + max_width = 100 + "#; + let config = Config::from_toml(toml, Path::new("")).unwrap(); + assert_eq!(config.array_width(), usize::max_value()); + assert_eq!(config.attr_fn_like_width(), usize::max_value()); + assert_eq!(config.chain_width(), usize::max_value()); + assert_eq!(config.fn_call_width(), usize::max_value()); + assert_eq!(config.single_line_if_else_max_width(), 0); + assert_eq!(config.struct_lit_width(), 0); + assert_eq!(config.struct_variant_width(), 0); + } + + #[test] + fn test_override_works_with_default() { + let toml = r#" + use_small_heuristics = "Default" + array_width = 20 + attr_fn_like_width = 40 + chain_width = 20 + fn_call_width = 90 + single_line_if_else_max_width = 40 + struct_lit_width = 30 + struct_variant_width = 34 + "#; + let config = Config::from_toml(toml, Path::new("")).unwrap(); + assert_eq!(config.array_width(), 20); + assert_eq!(config.attr_fn_like_width(), 40); + assert_eq!(config.chain_width(), 20); + assert_eq!(config.fn_call_width(), 90); + assert_eq!(config.single_line_if_else_max_width(), 40); + assert_eq!(config.struct_lit_width(), 30); + assert_eq!(config.struct_variant_width(), 34); + } + + #[test] + fn test_override_with_max() { + let toml = r#" + use_small_heuristics = "Max" + array_width = 20 + attr_fn_like_width = 40 + chain_width = 20 + fn_call_width = 90 + single_line_if_else_max_width = 40 + struct_lit_width = 30 + struct_variant_width = 34 + "#; + let config = Config::from_toml(toml, Path::new("")).unwrap(); + assert_eq!(config.array_width(), 20); + assert_eq!(config.attr_fn_like_width(), 40); + assert_eq!(config.chain_width(), 20); + assert_eq!(config.fn_call_width(), 90); + assert_eq!(config.single_line_if_else_max_width(), 40); + assert_eq!(config.struct_lit_width(), 30); + assert_eq!(config.struct_variant_width(), 34); + } + + #[test] + fn test_override_with_off() { + let toml = r#" + use_small_heuristics = "Off" + array_width = 20 + attr_fn_like_width = 40 + chain_width = 20 + fn_call_width = 90 + single_line_if_else_max_width = 40 + struct_lit_width = 30 + struct_variant_width = 34 + "#; + let config = Config::from_toml(toml, Path::new("")).unwrap(); + assert_eq!(config.array_width(), 20); + assert_eq!(config.attr_fn_like_width(), 40); + assert_eq!(config.chain_width(), 20); + assert_eq!(config.fn_call_width(), 90); + assert_eq!(config.single_line_if_else_max_width(), 40); + assert_eq!(config.struct_lit_width(), 30); + assert_eq!(config.struct_variant_width(), 34); + } + + #[test] + fn test_fn_call_width_config_exceeds_max_width() { + let toml = r#" + max_width = 90 + fn_call_width = 95 + "#; + let config = Config::from_toml(toml, Path::new("")).unwrap(); + assert_eq!(config.fn_call_width(), 90); + } + + #[test] + fn test_attr_fn_like_width_config_exceeds_max_width() { + let toml = r#" + max_width = 80 + attr_fn_like_width = 90 + "#; + let config = Config::from_toml(toml, Path::new("")).unwrap(); + assert_eq!(config.attr_fn_like_width(), 80); + } + + #[test] + fn test_struct_lit_config_exceeds_max_width() { + let toml = r#" + max_width = 78 + struct_lit_width = 90 + "#; + let config = Config::from_toml(toml, Path::new("")).unwrap(); + assert_eq!(config.struct_lit_width(), 78); + } + + #[test] + fn test_struct_variant_width_config_exceeds_max_width() { + let toml = r#" + max_width = 80 + struct_variant_width = 90 + "#; + let config = Config::from_toml(toml, Path::new("")).unwrap(); + assert_eq!(config.struct_variant_width(), 80); + } + + #[test] + fn test_array_width_config_exceeds_max_width() { + let toml = r#" + max_width = 60 + array_width = 80 + "#; + let config = Config::from_toml(toml, Path::new("")).unwrap(); + assert_eq!(config.array_width(), 60); + } + + #[test] + fn test_chain_width_config_exceeds_max_width() { + let toml = r#" + max_width = 80 + chain_width = 90 + "#; + let config = Config::from_toml(toml, Path::new("")).unwrap(); + assert_eq!(config.chain_width(), 80); + } + + #[test] + fn test_single_line_if_else_max_width_config_exceeds_max_width() { + let toml = r#" + max_width = 70 + single_line_if_else_max_width = 90 + "#; + let config = Config::from_toml(toml, Path::new("")).unwrap(); + assert_eq!(config.single_line_if_else_max_width(), 70); + } + + #[test] + fn test_override_fn_call_width_exceeds_max_width() { + let mut config = Config::default(); + config.override_value("fn_call_width", "101"); + assert_eq!(config.fn_call_width(), 100); + } + + #[test] + fn test_override_attr_fn_like_width_exceeds_max_width() { + let mut config = Config::default(); + config.override_value("attr_fn_like_width", "101"); + assert_eq!(config.attr_fn_like_width(), 100); + } + + #[test] + fn test_override_struct_lit_exceeds_max_width() { + let mut config = Config::default(); + config.override_value("struct_lit_width", "101"); + assert_eq!(config.struct_lit_width(), 100); + } + + #[test] + fn test_override_struct_variant_width_exceeds_max_width() { + let mut config = Config::default(); + config.override_value("struct_variant_width", "101"); + assert_eq!(config.struct_variant_width(), 100); + } + + #[test] + fn test_override_array_width_exceeds_max_width() { + let mut config = Config::default(); + config.override_value("array_width", "101"); + assert_eq!(config.array_width(), 100); + } + + #[test] + fn test_override_chain_width_exceeds_max_width() { + let mut config = Config::default(); + config.override_value("chain_width", "101"); + assert_eq!(config.chain_width(), 100); + } + + #[test] + fn test_override_single_line_if_else_max_width_exceeds_max_width() { + let mut config = Config::default(); + config.override_value("single_line_if_else_max_width", "101"); + assert_eq!(config.single_line_if_else_max_width(), 100); + } + } + + #[cfg(test)] + mod partially_unstable_option { + use super::mock::{Config, PartiallyUnstableOption}; + use super::*; + + /// From the command line, we can override with a stable variant. + #[test] + fn test_override_stable_value() { + let mut config = Config::default(); + config.override_value("partially_unstable_option", "V2"); + assert_eq!( + config.partially_unstable_option(), + PartiallyUnstableOption::V2 + ); + } + + /// From the command line, we can override with an unstable variant. + #[test] + fn test_override_unstable_value() { + let mut config = Config::default(); + config.override_value("partially_unstable_option", "V3"); + assert_eq!( + config.partially_unstable_option(), + PartiallyUnstableOption::V3 + ); + } + } + + #[test] + fn test_override_skip_macro_invocations() { + let mut config = Config::default(); + config.override_value("skip_macro_invocations", r#"["*", "println"]"#); + assert_eq!( + config.skip_macro_invocations(), + MacroSelectors(vec![ + MacroSelector::All, + MacroSelector::Name(MacroName::new("println".to_owned())) + ]) + ); + } +} diff --git a/src/config/options.rs b/src/config/options.rs new file mode 100644 index 000000000000..3aa1a4de99d6 --- /dev/null +++ b/src/config/options.rs @@ -0,0 +1,470 @@ +use std::collections::{hash_set, HashSet}; +use std::fmt; +use std::path::{Path, PathBuf}; +use std::str::FromStr; + +use itertools::Itertools; +use rustfmt_config_proc_macro::config_type; +use serde::de::{SeqAccess, Visitor}; +use serde::ser::SerializeSeq; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +use crate::config::lists::*; +use crate::config::Config; + +#[config_type] +pub enum NewlineStyle { + /// Auto-detect based on the raw source input. + Auto, + /// Force CRLF (`\r\n`). + Windows, + /// Force CR (`\n`). + Unix, + /// `\r\n` in Windows, `\n` on other platforms. + Native, +} + +#[config_type] +/// Where to put the opening brace of items (`fn`, `impl`, etc.). +pub enum BraceStyle { + /// Put the opening brace on the next line. + AlwaysNextLine, + /// Put the opening brace on the same line, if possible. + PreferSameLine, + /// Prefer the same line except where there is a where-clause, in which + /// case force the brace to be put on the next line. + SameLineWhere, +} + +#[config_type] +/// Where to put the opening brace of conditional expressions (`if`, `match`, etc.). +pub enum ControlBraceStyle { + /// K&R style, Rust community default + AlwaysSameLine, + /// Stroustrup style + ClosingNextLine, + /// Allman style + AlwaysNextLine, +} + +#[config_type] +/// How to indent. +pub enum IndentStyle { + /// First line on the same line as the opening brace, all lines aligned with + /// the first line. + Visual, + /// First line is on a new line and all lines align with **block** indent. + Block, +} + +#[config_type] +/// How to place a list-like items. +/// FIXME: Issue-3581: this should be renamed to ItemsLayout when publishing 2.0 +pub enum Density { + /// Fit as much on one line as possible. + Compressed, + /// Items are placed horizontally if sufficient space, vertically otherwise. + Tall, + /// Place every item on a separate line. + Vertical, +} + +#[config_type] +/// Spacing around type combinators. +pub enum TypeDensity { + /// No spaces around "=" and "+" + Compressed, + /// Spaces around " = " and " + " + Wide, +} + +#[config_type] +/// Heuristic settings that can be used to simply +/// the configuration of the granular width configurations +/// like `struct_lit_width`, `array_width`, etc. +pub enum Heuristics { + /// Turn off any heuristics + Off, + /// Turn on max heuristics + Max, + /// Use scaled values based on the value of `max_width` + Default, +} + +impl Density { + pub fn to_list_tactic(self, len: usize) -> ListTactic { + match self { + Density::Compressed => ListTactic::Mixed, + Density::Tall => ListTactic::HorizontalVertical, + Density::Vertical if len == 1 => ListTactic::Horizontal, + Density::Vertical => ListTactic::Vertical, + } + } +} + +#[config_type] +/// Configuration for import groups, i.e. sets of imports separated by newlines. +pub enum GroupImportsTactic { + /// Keep groups as they are. + Preserve, + /// Discard existing groups, and create new groups for + /// 1. `std` / `core` / `alloc` imports + /// 2. other imports + /// 3. `self` / `crate` / `super` imports + StdExternalCrate, + /// Discard existing groups, and create a single group for everything + One, +} + +#[config_type] +/// How to merge imports. +pub enum ImportGranularity { + /// Do not merge imports. + Preserve, + /// Use one `use` statement per crate. + Crate, + /// Use one `use` statement per module. + Module, + /// Use one `use` statement per imported item. + Item, + /// Use one `use` statement including all items. + One, +} + +/// Controls how rustfmt should handle case in hexadecimal literals. +#[config_type] +pub enum HexLiteralCase { + /// Leave the literal as-is + Preserve, + /// Ensure all literals use uppercase lettering + Upper, + /// Ensure all literals use lowercase lettering + Lower, +} + +#[config_type] +pub enum ReportTactic { + Always, + Unnumbered, + Never, +} + +/// What Rustfmt should emit. Mostly corresponds to the `--emit` command line +/// option. +#[config_type] +pub enum EmitMode { + /// Emits to files. + Files, + /// Writes the output to stdout. + Stdout, + /// Displays how much of the input file was processed + Coverage, + /// Unfancy stdout + Checkstyle, + /// Writes the resulting diffs in a JSON format. Returns an empty array + /// `[]` if there were no diffs. + Json, + /// Output the changed lines (for internal value only) + ModifiedLines, + /// Checks if a diff can be generated. If so, rustfmt outputs a diff and + /// quits with exit code 1. + /// This option is designed to be run in CI where a non-zero exit signifies + /// non-standard code formatting. Used for `--check`. + Diff, +} + +/// Client-preference for coloured output. +#[config_type] +pub enum Color { + /// Always use color, whether it is a piped or terminal output + Always, + /// Never use color + Never, + /// Automatically use color, if supported by terminal + Auto, +} + +#[config_type] +/// rustfmt format style version. +pub enum Version { + /// 1.x.y. When specified, rustfmt will format in the same style as 1.0.0. + One, + /// 2.x.y. When specified, rustfmt will format in the the latest style. + Two, +} + +impl Color { + /// Whether we should use a coloured terminal. + pub fn use_colored_tty(self) -> bool { + match self { + Color::Always | Color::Auto => true, + Color::Never => false, + } + } +} + +/// How chatty should Rustfmt be? +#[config_type] +pub enum Verbosity { + /// Emit more. + Verbose, + /// Default. + Normal, + /// Emit as little as possible. + Quiet, +} + +#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)] +pub struct WidthHeuristics { + // Maximum width of the args of a function call before falling back + // to vertical formatting. + pub(crate) fn_call_width: usize, + // Maximum width of the args of a function-like attributes before falling + // back to vertical formatting. + pub(crate) attr_fn_like_width: usize, + // Maximum width in the body of a struct lit before falling back to + // vertical formatting. + pub(crate) struct_lit_width: usize, + // Maximum width in the body of a struct variant before falling back + // to vertical formatting. + pub(crate) struct_variant_width: usize, + // Maximum width of an array literal before falling back to vertical + // formatting. + pub(crate) array_width: usize, + // Maximum length of a chain to fit on a single line. + pub(crate) chain_width: usize, + // Maximum line length for single line if-else expressions. A value + // of zero means always break if-else expressions. + pub(crate) single_line_if_else_max_width: usize, + // Maximum line length for single line let-else statements. A value of zero means + // always format the divergent `else` block over multiple lines. + pub(crate) single_line_let_else_max_width: usize, +} + +impl fmt::Display for WidthHeuristics { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self) + } +} + +impl WidthHeuristics { + // Using this WidthHeuristics means we ignore heuristics. + pub fn null() -> WidthHeuristics { + WidthHeuristics { + fn_call_width: usize::max_value(), + attr_fn_like_width: usize::max_value(), + struct_lit_width: 0, + struct_variant_width: 0, + array_width: usize::max_value(), + chain_width: usize::max_value(), + single_line_if_else_max_width: 0, + single_line_let_else_max_width: 0, + } + } + + pub fn set(max_width: usize) -> WidthHeuristics { + WidthHeuristics { + fn_call_width: max_width, + attr_fn_like_width: max_width, + struct_lit_width: max_width, + struct_variant_width: max_width, + array_width: max_width, + chain_width: max_width, + single_line_if_else_max_width: max_width, + single_line_let_else_max_width: max_width, + } + } + + // scale the default WidthHeuristics according to max_width + pub fn scaled(max_width: usize) -> WidthHeuristics { + const DEFAULT_MAX_WIDTH: usize = 100; + let max_width_ratio = if max_width > DEFAULT_MAX_WIDTH { + let ratio = max_width as f32 / DEFAULT_MAX_WIDTH as f32; + // round to the closest 0.1 + (ratio * 10.0).round() / 10.0 + } else { + 1.0 + }; + WidthHeuristics { + fn_call_width: (60.0 * max_width_ratio).round() as usize, + attr_fn_like_width: (70.0 * max_width_ratio).round() as usize, + struct_lit_width: (18.0 * max_width_ratio).round() as usize, + struct_variant_width: (35.0 * max_width_ratio).round() as usize, + array_width: (60.0 * max_width_ratio).round() as usize, + chain_width: (60.0 * max_width_ratio).round() as usize, + single_line_if_else_max_width: (50.0 * max_width_ratio).round() as usize, + single_line_let_else_max_width: (50.0 * max_width_ratio).round() as usize, + } + } +} + +impl ::std::str::FromStr for WidthHeuristics { + type Err = &'static str; + + fn from_str(_: &str) -> Result { + Err("WidthHeuristics is not parsable") + } +} + +impl Default for EmitMode { + fn default() -> EmitMode { + EmitMode::Files + } +} + +/// A set of directories, files and modules that rustfmt should ignore. +#[derive(Default, Clone, Debug, PartialEq)] +pub struct IgnoreList { + /// A set of path specified in rustfmt.toml. + path_set: HashSet, + /// A path to rustfmt.toml. + rustfmt_toml_path: PathBuf, +} + +impl fmt::Display for IgnoreList { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "[{}]", + self.path_set + .iter() + .format_with(", ", |path, f| f(&format_args!( + "{}", + path.to_string_lossy() + ))) + ) + } +} + +impl Serialize for IgnoreList { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_seq(Some(self.path_set.len()))?; + for e in &self.path_set { + seq.serialize_element(e)?; + } + seq.end() + } +} + +impl<'de> Deserialize<'de> for IgnoreList { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct HashSetVisitor; + impl<'v> Visitor<'v> for HashSetVisitor { + type Value = HashSet; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("a sequence of path") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'v>, + { + let mut path_set = HashSet::new(); + while let Some(elem) = seq.next_element()? { + path_set.insert(elem); + } + Ok(path_set) + } + } + Ok(IgnoreList { + path_set: deserializer.deserialize_seq(HashSetVisitor)?, + rustfmt_toml_path: PathBuf::new(), + }) + } +} + +impl<'a> IntoIterator for &'a IgnoreList { + type Item = &'a PathBuf; + type IntoIter = hash_set::Iter<'a, PathBuf>; + + fn into_iter(self) -> Self::IntoIter { + self.path_set.iter() + } +} + +impl IgnoreList { + pub fn add_prefix(&mut self, dir: &Path) { + self.rustfmt_toml_path = dir.to_path_buf(); + } + + pub fn rustfmt_toml_path(&self) -> &Path { + &self.rustfmt_toml_path + } +} + +impl FromStr for IgnoreList { + type Err = &'static str; + + fn from_str(_: &str) -> Result { + Err("IgnoreList is not parsable") + } +} + +/// Maps client-supplied options to Rustfmt's internals, mostly overriding +/// values in a config with values from the command line. +pub trait CliOptions { + fn apply_to(self, config: &mut Config); + fn config_path(&self) -> Option<&Path>; +} + +/// The edition of the syntax and semntics of code (RFC 2052). +#[config_type] +pub enum Edition { + #[value = "2015"] + #[doc_hint = "2015"] + /// Edition 2015. + Edition2015, + #[value = "2018"] + #[doc_hint = "2018"] + /// Edition 2018. + Edition2018, + #[value = "2021"] + #[doc_hint = "2021"] + /// Edition 2021. + Edition2021, + #[value = "2024"] + #[doc_hint = "2024"] + /// Edition 2024. + Edition2024, +} + +impl Default for Edition { + fn default() -> Edition { + Edition::Edition2015 + } +} + +impl From for rustc_span::edition::Edition { + fn from(edition: Edition) -> Self { + match edition { + Edition::Edition2015 => Self::Edition2015, + Edition::Edition2018 => Self::Edition2018, + Edition::Edition2021 => Self::Edition2021, + Edition::Edition2024 => Self::Edition2024, + } + } +} + +impl PartialOrd for Edition { + fn partial_cmp(&self, other: &Edition) -> Option { + rustc_span::edition::Edition::partial_cmp(&(*self).into(), &(*other).into()) + } +} + +/// Controls how rustfmt should handle leading pipes on match arms. +#[config_type] +pub enum MatchArmLeadingPipe { + /// Place leading pipes on all match arms + Always, + /// Never emit leading pipes on match arms + Never, + /// Preserve any existing leading pipes + Preserve, +} diff --git a/src/coverage.rs b/src/coverage.rs new file mode 100644 index 000000000000..f5a049742514 --- /dev/null +++ b/src/coverage.rs @@ -0,0 +1,15 @@ +use crate::{Config, EmitMode}; +use std::borrow::Cow; + +pub(crate) fn transform_missing_snippet<'a>(config: &Config, string: &'a str) -> Cow<'a, str> { + match config.emit_mode() { + EmitMode::Coverage => Cow::from(replace_chars(string)), + _ => Cow::from(string), + } +} + +fn replace_chars(s: &str) -> String { + s.chars() + .map(|ch| if ch.is_whitespace() { ch } else { 'X' }) + .collect() +} diff --git a/src/emitter.rs b/src/emitter.rs new file mode 100644 index 000000000000..dc2c99a301e3 --- /dev/null +++ b/src/emitter.rs @@ -0,0 +1,52 @@ +pub(crate) use self::checkstyle::*; +pub(crate) use self::diff::*; +pub(crate) use self::files::*; +pub(crate) use self::files_with_backup::*; +pub(crate) use self::json::*; +pub(crate) use self::modified_lines::*; +pub(crate) use self::stdout::*; +use crate::FileName; +use std::io::{self, Write}; +use std::path::Path; + +mod checkstyle; +mod diff; +mod files; +mod files_with_backup; +mod json; +mod modified_lines; +mod stdout; + +pub(crate) struct FormattedFile<'a> { + pub(crate) filename: &'a FileName, + pub(crate) original_text: &'a str, + pub(crate) formatted_text: &'a str, +} + +#[derive(Debug, Default, Clone)] +pub(crate) struct EmitterResult { + pub(crate) has_diff: bool, +} + +pub(crate) trait Emitter { + fn emit_formatted_file( + &mut self, + output: &mut dyn Write, + formatted_file: FormattedFile<'_>, + ) -> Result; + + fn emit_header(&self, _output: &mut dyn Write) -> Result<(), io::Error> { + Ok(()) + } + + fn emit_footer(&self, _output: &mut dyn Write) -> Result<(), io::Error> { + Ok(()) + } +} + +fn ensure_real_path(filename: &FileName) -> &Path { + match *filename { + FileName::Real(ref path) => path, + _ => panic!("cannot format `{}` and emit to files", filename), + } +} diff --git a/src/emitter/checkstyle.rs b/src/emitter/checkstyle.rs new file mode 100644 index 000000000000..545b259979d9 --- /dev/null +++ b/src/emitter/checkstyle.rs @@ -0,0 +1,150 @@ +use self::xml::XmlEscaped; +use super::*; +use crate::rustfmt_diff::{make_diff, DiffLine, Mismatch}; +use std::io::{self, Write}; + +mod xml; + +#[derive(Debug, Default)] +pub(crate) struct CheckstyleEmitter; + +impl Emitter for CheckstyleEmitter { + fn emit_header(&self, output: &mut dyn Write) -> Result<(), io::Error> { + writeln!(output, r#""#)?; + write!(output, r#""#)?; + Ok(()) + } + + fn emit_footer(&self, output: &mut dyn Write) -> Result<(), io::Error> { + writeln!(output, "") + } + + fn emit_formatted_file( + &mut self, + output: &mut dyn Write, + FormattedFile { + filename, + original_text, + formatted_text, + }: FormattedFile<'_>, + ) -> Result { + const CONTEXT_SIZE: usize = 0; + let diff = make_diff(original_text, formatted_text, CONTEXT_SIZE); + output_checkstyle_file(output, filename, diff)?; + Ok(EmitterResult::default()) + } +} + +pub(crate) fn output_checkstyle_file( + mut writer: T, + filename: &FileName, + diff: Vec, +) -> Result<(), io::Error> +where + T: Write, +{ + write!(writer, r#""#, filename)?; + for mismatch in diff { + let begin_line = mismatch.line_number; + let mut current_line; + let mut line_counter = 0; + for line in mismatch.lines { + // Do nothing with `DiffLine::Context` and `DiffLine::Resulting`. + if let DiffLine::Expected(message) = line { + current_line = begin_line + line_counter; + line_counter += 1; + write!( + writer, + r#""#, + current_line, + XmlEscaped(&message) + )?; + } + } + } + write!(writer, "")?; + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use std::path::PathBuf; + + #[test] + 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, + &FileName::Real(PathBuf::from(file_name)), + vec![], + ); + assert_eq!( + &writer[..], + format!(r#""#, file_name).as_bytes() + ); + } + + // https://github.com/rust-lang/rustfmt/issues/1636 + #[test] + fn emits_single_xml_tree_containing_all_files() { + let bin_file = "src/bin.rs"; + let bin_original = vec!["fn main() {", "println!(\"Hello, world!\");", "}"]; + let bin_formatted = vec!["fn main() {", " println!(\"Hello, world!\");", "}"]; + let lib_file = "src/lib.rs"; + let lib_original = vec!["fn greet() {", "println!(\"Greetings!\");", "}"]; + let lib_formatted = vec!["fn greet() {", " println!(\"Greetings!\");", "}"]; + let mut writer = Vec::new(); + let mut emitter = CheckstyleEmitter::default(); + let _ = emitter.emit_header(&mut writer); + let _ = emitter + .emit_formatted_file( + &mut writer, + FormattedFile { + filename: &FileName::Real(PathBuf::from(bin_file)), + original_text: &bin_original.join("\n"), + formatted_text: &bin_formatted.join("\n"), + }, + ) + .unwrap(); + let _ = emitter + .emit_formatted_file( + &mut writer, + FormattedFile { + filename: &FileName::Real(PathBuf::from(lib_file)), + original_text: &lib_original.join("\n"), + formatted_text: &lib_formatted.join("\n"), + }, + ) + .unwrap(); + let _ = emitter.emit_footer(&mut writer); + let exp_bin_xml = vec![ + format!(r#""#, bin_file), + format!( + r#""#, + XmlEscaped(r#" println!("Hello, world!");"#), + ), + String::from(""), + ]; + let exp_lib_xml = vec![ + format!(r#""#, lib_file), + format!( + r#""#, + XmlEscaped(r#" println!("Greetings!");"#), + ), + String::from(""), + ]; + assert_eq!( + String::from_utf8(writer).unwrap(), + vec![ + r#""#, + "\n", + r#""#, + &format!("{}{}", exp_bin_xml.join(""), exp_lib_xml.join("")), + "\n", + ] + .join(""), + ); + } +} diff --git a/src/emitter/checkstyle/xml.rs b/src/emitter/checkstyle/xml.rs new file mode 100644 index 000000000000..f251aabe8785 --- /dev/null +++ b/src/emitter/checkstyle/xml.rs @@ -0,0 +1,52 @@ +use std::fmt::{self, Display}; + +/// Convert special characters into XML entities. +/// This is needed for checkstyle output. +pub(super) struct XmlEscaped<'a>(pub(super) &'a str); + +impl<'a> Display for XmlEscaped<'a> { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + for char in self.0.chars() { + match char { + '<' => write!(formatter, "<"), + '>' => write!(formatter, ">"), + '"' => write!(formatter, """), + '\'' => write!(formatter, "'"), + '&' => write!(formatter, "&"), + _ => write!(formatter, "{}", char), + }?; + } + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn special_characters_are_escaped() { + assert_eq!( + "<>"'&", + format!("{}", XmlEscaped(r#"<>"'&"#)), + ); + } + + #[test] + fn special_characters_are_escaped_in_string_with_other_characters() { + assert_eq!( + "The quick brown "🦊" jumps <over> the lazy 🐶", + format!( + "{}", + XmlEscaped(r#"The quick brown "🦊" jumps the lazy 🐶"#) + ), + ); + } + + #[test] + fn other_characters_are_not_escaped() { + let string = "The quick brown 🦊 jumps over the lazy 🐶"; + assert_eq!(string, format!("{}", XmlEscaped(string))); + } +} diff --git a/src/emitter/diff.rs b/src/emitter/diff.rs new file mode 100644 index 000000000000..5e1f13446560 --- /dev/null +++ b/src/emitter/diff.rs @@ -0,0 +1,137 @@ +use super::*; +use crate::config::Config; +use crate::rustfmt_diff::{make_diff, print_diff}; + +pub(crate) struct DiffEmitter { + config: Config, +} + +impl DiffEmitter { + pub(crate) fn new(config: Config) -> Self { + Self { config } + } +} + +impl Emitter for DiffEmitter { + fn emit_formatted_file( + &mut self, + output: &mut dyn Write, + FormattedFile { + filename, + original_text, + formatted_text, + }: FormattedFile<'_>, + ) -> Result { + const CONTEXT_SIZE: usize = 3; + let mismatch = make_diff(original_text, formatted_text, CONTEXT_SIZE); + let has_diff = !mismatch.is_empty(); + + if has_diff { + if self.config.print_misformatted_file_names() { + writeln!(output, "{}", filename)?; + } else { + print_diff( + mismatch, + |line_num| format!("Diff in {} at line {}:", filename, line_num), + &self.config, + ); + } + } else if original_text != formatted_text { + // This occurs when the only difference between the original and formatted values + // is the newline style. This happens because The make_diff function compares the + // original and formatted values line by line, independent of line endings. + writeln!(output, "Incorrect newline style in {}", filename)?; + return Ok(EmitterResult { has_diff: true }); + } + + Ok(EmitterResult { has_diff }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::config::Config; + use crate::FileName; + use std::path::PathBuf; + + #[test] + fn does_not_print_when_no_files_reformatted() { + let mut writer = Vec::new(); + let config = Config::default(); + let mut emitter = DiffEmitter::new(config); + let result = emitter + .emit_formatted_file( + &mut writer, + FormattedFile { + filename: &FileName::Real(PathBuf::from("src/lib.rs")), + original_text: "fn empty() {}\n", + formatted_text: "fn empty() {}\n", + }, + ) + .unwrap(); + assert_eq!(result.has_diff, false); + assert_eq!(writer.len(), 0); + } + + #[test] + fn prints_file_names_when_config_is_enabled() { + let bin_file = "src/bin.rs"; + let bin_original = "fn main() {\nprintln!(\"Hello, world!\");\n}"; + let bin_formatted = "fn main() {\n println!(\"Hello, world!\");\n}"; + let lib_file = "src/lib.rs"; + let lib_original = "fn greet() {\nprintln!(\"Greetings!\");\n}"; + let lib_formatted = "fn greet() {\n println!(\"Greetings!\");\n}"; + + let mut writer = Vec::new(); + let mut config = Config::default(); + config.set().print_misformatted_file_names(true); + let mut emitter = DiffEmitter::new(config); + let _ = emitter + .emit_formatted_file( + &mut writer, + FormattedFile { + filename: &FileName::Real(PathBuf::from(bin_file)), + original_text: bin_original, + formatted_text: bin_formatted, + }, + ) + .unwrap(); + let _ = emitter + .emit_formatted_file( + &mut writer, + FormattedFile { + filename: &FileName::Real(PathBuf::from(lib_file)), + original_text: lib_original, + formatted_text: lib_formatted, + }, + ) + .unwrap(); + + assert_eq!( + String::from_utf8(writer).unwrap(), + format!("{}\n{}\n", bin_file, lib_file), + ) + } + + #[test] + fn prints_newline_message_with_only_newline_style_diff() { + let mut writer = Vec::new(); + let config = Config::default(); + let mut emitter = DiffEmitter::new(config); + let _ = emitter + .emit_formatted_file( + &mut writer, + FormattedFile { + filename: &FileName::Real(PathBuf::from("src/lib.rs")), + original_text: "fn empty() {}\n", + formatted_text: "fn empty() {}\r\n", + }, + ) + .unwrap(); + assert_eq!( + String::from_utf8(writer).unwrap(), + String::from("Incorrect newline style in src/lib.rs\n") + ); + } +} diff --git a/src/emitter/files.rs b/src/emitter/files.rs new file mode 100644 index 000000000000..6360b73ee615 --- /dev/null +++ b/src/emitter/files.rs @@ -0,0 +1,37 @@ +use super::*; +use std::fs; + +#[derive(Debug, Default)] +pub(crate) struct FilesEmitter { + print_misformatted_file_names: bool, +} + +impl FilesEmitter { + pub(crate) fn new(print_misformatted_file_names: bool) -> Self { + Self { + print_misformatted_file_names, + } + } +} + +impl Emitter for FilesEmitter { + fn emit_formatted_file( + &mut self, + output: &mut dyn Write, + FormattedFile { + filename, + original_text, + formatted_text, + }: FormattedFile<'_>, + ) -> Result { + // Write text directly over original file if there is a diff. + let filename = ensure_real_path(filename); + if original_text != formatted_text { + fs::write(filename, formatted_text)?; + if self.print_misformatted_file_names { + writeln!(output, "{}", filename.display())?; + } + } + Ok(EmitterResult::default()) + } +} diff --git a/src/emitter/files_with_backup.rs b/src/emitter/files_with_backup.rs new file mode 100644 index 000000000000..4c15f6fa5ec7 --- /dev/null +++ b/src/emitter/files_with_backup.rs @@ -0,0 +1,31 @@ +use super::*; +use std::fs; + +#[derive(Debug, Default)] +pub(crate) struct FilesWithBackupEmitter; + +impl Emitter for FilesWithBackupEmitter { + fn emit_formatted_file( + &mut self, + _output: &mut dyn Write, + FormattedFile { + filename, + original_text, + formatted_text, + }: FormattedFile<'_>, + ) -> Result { + let filename = ensure_real_path(filename); + if original_text != formatted_text { + // Do a little dance to make writing safer - write to a temp file + // rename the original to a .bk, then rename the temp file to the + // original. + let tmp_name = filename.with_extension("tmp"); + let bk_name = filename.with_extension("bk"); + + fs::write(&tmp_name, formatted_text)?; + fs::rename(filename, bk_name)?; + fs::rename(tmp_name, filename)?; + } + Ok(EmitterResult::default()) + } +} diff --git a/src/emitter/json.rs b/src/emitter/json.rs new file mode 100644 index 000000000000..c7f68d4675a6 --- /dev/null +++ b/src/emitter/json.rs @@ -0,0 +1,346 @@ +use super::*; +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}; + +#[derive(Debug, Default)] +pub(crate) struct JsonEmitter { + mismatched_files: Vec, +} + +#[derive(Debug, Default, PartialEq, Serialize)] +struct MismatchedBlock { + original_begin_line: u32, + original_end_line: u32, + expected_begin_line: u32, + expected_end_line: u32, + original: String, + expected: String, +} + +#[derive(Debug, Default, PartialEq, Serialize)] +struct MismatchedFile { + name: String, + mismatches: Vec, +} + +impl Emitter for JsonEmitter { + fn emit_footer(&self, output: &mut dyn Write) -> Result<(), io::Error> { + writeln!(output, "{}", &to_json_string(&self.mismatched_files)?) + } + + fn emit_formatted_file( + &mut self, + _output: &mut dyn Write, + FormattedFile { + filename, + original_text, + formatted_text, + }: FormattedFile<'_>, + ) -> Result { + const CONTEXT_SIZE: usize = 0; + let diff = make_diff(original_text, formatted_text, CONTEXT_SIZE); + let has_diff = !diff.is_empty(); + + if has_diff { + self.add_misformatted_file(filename, diff)?; + } + + Ok(EmitterResult { has_diff }) + } +} + +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.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, + } + } + + 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(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::FileName; + use std::path::PathBuf; + + #[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), + mismatches: vec![MismatchedBlock { + original_begin_line: 79, + original_end_line: 79, + expected_begin_line: 79, + expected_end_line: 82, + original: String::from("fn Foo() where T: Bar {\n"), + expected: String::from("fn Foo()\nwhere\n T: Bar,\n{\n"), + }], + }; + let mismatch = Mismatch { + line_number: 79, + line_number_orig: 79, + lines: vec![ + DiffLine::Resulting(String::from("fn Foo() where T: Bar {")), + DiffLine::Expected(String::from("fn Foo()")), + DiffLine::Expected(String::from("where")), + DiffLine::Expected(String::from(" T: Bar,")), + DiffLine::Expected(String::from("{")), + ], + }; + + 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), + mismatches: vec![MismatchedBlock { + original_begin_line: 5, + original_end_line: 5, + expected_begin_line: 5, + expected_end_line: 5, + original: String::from( + "fn foo(_x: &u64) -> Option<&(dyn::std::error::Error + 'static)> {\n", + ), + expected: String::from( + "fn foo(_x: &u64) -> Option<&(dyn ::std::error::Error + 'static)> {\n", + ), + }], + }; + let mismatch = Mismatch { + line_number: 5, + line_number_orig: 5, + lines: vec![ + DiffLine::Context(String::new()), + DiffLine::Resulting(String::from( + "fn foo(_x: &u64) -> Option<&(dyn::std::error::Error + 'static)> {", + )), + DiffLine::Context(String::new()), + DiffLine::Expected(String::from( + "fn foo(_x: &u64) -> Option<&(dyn ::std::error::Error + 'static)> {", + )), + DiffLine::Context(String::new()), + ], + }; + + 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 emits_empty_array_on_no_diffs() { + let mut writer = Vec::new(); + let mut emitter = JsonEmitter::default(); + let _ = emitter.emit_header(&mut writer); + let result = emitter + .emit_formatted_file( + &mut writer, + FormattedFile { + filename: &FileName::Real(PathBuf::from("src/lib.rs")), + original_text: "fn empty() {}\n", + formatted_text: "fn empty() {}\n", + }, + ) + .unwrap(); + let _ = emitter.emit_footer(&mut writer); + assert_eq!(result.has_diff, false); + assert_eq!(&writer[..], "[]\n".as_bytes()); + } + + #[test] + fn emits_array_with_files_with_diffs() { + let file_name = "src/bin.rs"; + let original = vec![ + "fn main() {", + "println!(\"Hello, world!\");", + "}", + "", + "#[cfg(test)]", + "mod tests {", + "#[test]", + "fn it_works() {", + " assert_eq!(2 + 2, 4);", + "}", + "}", + ]; + let formatted = vec![ + "fn main() {", + " println!(\"Hello, world!\");", + "}", + "", + "#[cfg(test)]", + "mod tests {", + " #[test]", + " fn it_works() {", + " assert_eq!(2 + 2, 4);", + " }", + "}", + ]; + let mut writer = Vec::new(); + let mut emitter = JsonEmitter::default(); + let _ = emitter.emit_header(&mut writer); + let result = emitter + .emit_formatted_file( + &mut writer, + FormattedFile { + filename: &FileName::Real(PathBuf::from(file_name)), + original_text: &original.join("\n"), + formatted_text: &formatted.join("\n"), + }, + ) + .unwrap(); + let _ = emitter.emit_footer(&mut writer); + let exp_json = to_json_string(&vec![MismatchedFile { + name: String::from(file_name), + 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!\");\n"), + expected: String::from(" println!(\"Hello, world!\");\n"), + }, + MismatchedBlock { + original_begin_line: 7, + original_end_line: 10, + expected_begin_line: 7, + expected_end_line: 10, + original: String::from( + "#[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 }\n", + ), + }, + ], + }]) + .unwrap(); + assert_eq!(result.has_diff, true); + assert_eq!(&writer[..], format!("{}\n", exp_json).as_bytes()); + } + + #[test] + fn emits_valid_json_with_multiple_files() { + let bin_file = "src/bin.rs"; + let bin_original = vec!["fn main() {", "println!(\"Hello, world!\");", "}"]; + let bin_formatted = vec!["fn main() {", " println!(\"Hello, world!\");", "}"]; + let lib_file = "src/lib.rs"; + let lib_original = vec!["fn greet() {", "println!(\"Greetings!\");", "}"]; + let lib_formatted = vec!["fn greet() {", " println!(\"Greetings!\");", "}"]; + let mut writer = Vec::new(); + let mut emitter = JsonEmitter::default(); + let _ = emitter.emit_header(&mut writer); + let _ = emitter + .emit_formatted_file( + &mut writer, + FormattedFile { + filename: &FileName::Real(PathBuf::from(bin_file)), + original_text: &bin_original.join("\n"), + formatted_text: &bin_formatted.join("\n"), + }, + ) + .unwrap(); + let _ = emitter + .emit_formatted_file( + &mut writer, + FormattedFile { + filename: &FileName::Real(PathBuf::from(lib_file)), + original_text: &lib_original.join("\n"), + formatted_text: &lib_formatted.join("\n"), + }, + ) + .unwrap(); + let _ = emitter.emit_footer(&mut writer); + 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!\");\n"), + expected: String::from(" println!(\"Hello, world!\");\n"), + }], + }; + + 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!\");\n"), + expected: String::from(" println!(\"Greetings!\");\n"), + }], + }; + + 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/emitter/modified_lines.rs b/src/emitter/modified_lines.rs new file mode 100644 index 000000000000..94ff570a8a9c --- /dev/null +++ b/src/emitter/modified_lines.rs @@ -0,0 +1,24 @@ +use super::*; +use crate::rustfmt_diff::{make_diff, ModifiedLines}; +use std::io::Write; + +#[derive(Debug, Default)] +pub(crate) struct ModifiedLinesEmitter; + +impl Emitter for ModifiedLinesEmitter { + fn emit_formatted_file( + &mut self, + output: &mut dyn Write, + FormattedFile { + original_text, + formatted_text, + .. + }: FormattedFile<'_>, + ) -> Result { + const CONTEXT_SIZE: usize = 0; + let mismatch = make_diff(original_text, formatted_text, CONTEXT_SIZE); + let has_diff = !mismatch.is_empty(); + write!(output, "{}", ModifiedLines::from(mismatch))?; + Ok(EmitterResult { has_diff }) + } +} diff --git a/src/emitter/stdout.rs b/src/emitter/stdout.rs new file mode 100644 index 000000000000..9fddd515e492 --- /dev/null +++ b/src/emitter/stdout.rs @@ -0,0 +1,32 @@ +use super::*; +use crate::config::Verbosity; +use std::io::Write; + +#[derive(Debug)] +pub(crate) struct StdoutEmitter { + verbosity: Verbosity, +} + +impl StdoutEmitter { + pub(crate) fn new(verbosity: Verbosity) -> Self { + Self { verbosity } + } +} + +impl Emitter for StdoutEmitter { + fn emit_formatted_file( + &mut self, + output: &mut dyn Write, + FormattedFile { + filename, + formatted_text, + .. + }: FormattedFile<'_>, + ) -> Result { + if self.verbosity != Verbosity::Quiet { + writeln!(output, "{}:\n", filename)?; + } + write!(output, "{}", formatted_text)?; + Ok(EmitterResult::default()) + } +} diff --git a/src/expr.rs b/src/expr.rs new file mode 100644 index 000000000000..5b1b4fbd491c --- /dev/null +++ b/src/expr.rs @@ -0,0 +1,2205 @@ +use std::borrow::Cow; +use std::cmp::min; + +use itertools::Itertools; +use rustc_ast::token::{Delimiter, LitKind}; +use rustc_ast::{ast, ptr, token}; +use rustc_span::{BytePos, Span}; + +use crate::chains::rewrite_chain; +use crate::closures; +use crate::comment::{ + combine_strs_with_missing_comments, contains_comment, recover_comment_removed, rewrite_comment, + rewrite_missing_comment, CharClasses, FindUncommented, +}; +use crate::config::lists::*; +use crate::config::{Config, ControlBraceStyle, HexLiteralCase, IndentStyle, Version}; +use crate::lists::{ + definitive_tactic, itemize_list, shape_for_tactic, struct_lit_formatting, struct_lit_shape, + struct_lit_tactic, write_list, ListFormatting, Separator, +}; +use crate::macros::{rewrite_macro, MacroPosition}; +use crate::matches::rewrite_match; +use crate::overflow::{self, IntoOverflowableItem, OverflowableItem}; +use crate::pairs::{rewrite_all_pairs, rewrite_pair, PairParts}; +use crate::rewrite::{Rewrite, RewriteContext}; +use crate::shape::{Indent, Shape}; +use crate::source_map::{LineRangeUtils, SpanUtils}; +use crate::spanned::Spanned; +use crate::string::{rewrite_string, StringFormat}; +use crate::types::{rewrite_path, PathContext}; +use crate::utils::{ + colon_spaces, contains_skip, count_newlines, filtered_str_fits, first_line_ends_with, + inner_attributes, last_line_extendable, last_line_width, mk_sp, outer_attributes, + semicolon_for_expr, unicode_str_width, wrap_str, +}; +use crate::vertical::rewrite_with_alignment; +use crate::visitor::FmtVisitor; + +impl Rewrite for ast::Expr { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + format_expr(self, ExprType::SubExpression, context, shape) + } +} + +#[derive(Copy, Clone, PartialEq)] +pub(crate) enum ExprType { + Statement, + SubExpression, +} + +pub(crate) fn format_expr( + expr: &ast::Expr, + expr_type: ExprType, + context: &RewriteContext<'_>, + shape: Shape, +) -> Option { + skip_out_of_file_lines_range!(context, expr.span); + + if contains_skip(&*expr.attrs) { + return Some(context.snippet(expr.span()).to_owned()); + } + let shape = if expr_type == ExprType::Statement && semicolon_for_expr(context, expr) { + shape.sub_width(1)? + } else { + shape + }; + + let expr_rw = match expr.kind { + ast::ExprKind::Array(ref expr_vec) => rewrite_array( + "", + expr_vec.iter(), + expr.span, + context, + shape, + choose_separator_tactic(context, expr.span), + None, + ), + ast::ExprKind::Lit(token_lit) => { + if let Some(expr_rw) = rewrite_literal(context, token_lit, expr.span, shape) { + Some(expr_rw) + } else { + if let LitKind::StrRaw(_) = token_lit.kind { + Some(context.snippet(expr.span).trim().into()) + } else { + None + } + } + } + ast::ExprKind::Call(ref callee, ref args) => { + let inner_span = mk_sp(callee.span.hi(), expr.span.hi()); + let callee_str = callee.rewrite(context, shape)?; + rewrite_call(context, &callee_str, args, inner_span, shape) + } + ast::ExprKind::Paren(ref subexpr) => rewrite_paren(context, subexpr, shape, expr.span), + ast::ExprKind::Binary(op, ref lhs, ref rhs) => { + // FIXME: format comments between operands and operator + rewrite_all_pairs(expr, shape, context).or_else(|| { + rewrite_pair( + &**lhs, + &**rhs, + PairParts::infix(&format!(" {} ", context.snippet(op.span))), + context, + shape, + context.config.binop_separator(), + ) + }) + } + ast::ExprKind::Unary(op, ref subexpr) => rewrite_unary_op(context, op, subexpr, shape), + ast::ExprKind::Struct(ref struct_expr) => { + let ast::StructExpr { + qself, + fields, + path, + rest, + } = &**struct_expr; + rewrite_struct_lit( + context, + path, + qself, + fields, + rest, + &expr.attrs, + expr.span, + shape, + ) + } + ast::ExprKind::Tup(ref items) => { + rewrite_tuple(context, items.iter(), expr.span, shape, items.len() == 1) + } + ast::ExprKind::Let(..) => None, + ast::ExprKind::If(..) + | ast::ExprKind::ForLoop(..) + | ast::ExprKind::Loop(..) + | ast::ExprKind::While(..) => to_control_flow(expr, expr_type) + .and_then(|control_flow| control_flow.rewrite(context, shape)), + ast::ExprKind::ConstBlock(ref anon_const) => { + Some(format!("const {}", anon_const.rewrite(context, shape)?)) + } + ast::ExprKind::Block(ref block, opt_label) => { + match expr_type { + ExprType::Statement => { + if is_unsafe_block(block) { + rewrite_block(block, Some(&expr.attrs), opt_label, context, shape) + } else if let rw @ Some(_) = + rewrite_empty_block(context, block, Some(&expr.attrs), opt_label, "", shape) + { + // Rewrite block without trying to put it in a single line. + rw + } else { + let prefix = block_prefix(context, block, shape)?; + + rewrite_block_with_visitor( + context, + &prefix, + block, + Some(&expr.attrs), + opt_label, + shape, + true, + ) + } + } + ExprType::SubExpression => { + rewrite_block(block, Some(&expr.attrs), opt_label, context, shape) + } + } + } + ast::ExprKind::Match(ref cond, ref arms) => { + rewrite_match(context, cond, arms, shape, expr.span, &expr.attrs) + } + ast::ExprKind::Path(ref qself, ref path) => { + rewrite_path(context, PathContext::Expr, qself, path, shape) + } + ast::ExprKind::Assign(ref lhs, ref rhs, _) => { + rewrite_assignment(context, lhs, rhs, None, shape) + } + ast::ExprKind::AssignOp(ref op, ref lhs, ref rhs) => { + rewrite_assignment(context, lhs, rhs, Some(op), shape) + } + ast::ExprKind::Continue(ref opt_label) => { + let id_str = match *opt_label { + Some(label) => format!(" {}", label.ident), + None => String::new(), + }; + Some(format!("continue{}", id_str)) + } + ast::ExprKind::Break(ref opt_label, ref opt_expr) => { + let id_str = match *opt_label { + Some(label) => format!(" {}", label.ident), + None => String::new(), + }; + + if let Some(ref expr) = *opt_expr { + rewrite_unary_prefix(context, &format!("break{} ", id_str), &**expr, shape) + } else { + Some(format!("break{}", id_str)) + } + } + ast::ExprKind::Yield(ref opt_expr) => { + if let Some(ref expr) = *opt_expr { + rewrite_unary_prefix(context, "yield ", &**expr, shape) + } else { + Some("yield".to_string()) + } + } + ast::ExprKind::Closure(ref cl) => closures::rewrite_closure( + &cl.binder, + cl.constness, + cl.capture_clause, + &cl.asyncness, + cl.movability, + &cl.fn_decl, + &cl.body, + expr.span, + context, + shape, + ), + ast::ExprKind::Try(..) + | ast::ExprKind::Field(..) + | ast::ExprKind::MethodCall(..) + | ast::ExprKind::Await(_, _) => rewrite_chain(expr, context, shape), + ast::ExprKind::MacCall(ref mac) => { + rewrite_macro(mac, None, context, shape, MacroPosition::Expression).or_else(|| { + wrap_str( + context.snippet(expr.span).to_owned(), + context.config.max_width(), + shape, + ) + }) + } + ast::ExprKind::Ret(None) => Some("return".to_owned()), + ast::ExprKind::Ret(Some(ref expr)) => { + rewrite_unary_prefix(context, "return ", &**expr, shape) + } + ast::ExprKind::Become(ref expr) => rewrite_unary_prefix(context, "become ", &**expr, shape), + ast::ExprKind::Yeet(None) => Some("do yeet".to_owned()), + ast::ExprKind::Yeet(Some(ref expr)) => { + rewrite_unary_prefix(context, "do yeet ", &**expr, shape) + } + ast::ExprKind::AddrOf(borrow_kind, mutability, ref expr) => { + rewrite_expr_addrof(context, borrow_kind, mutability, expr, shape) + } + ast::ExprKind::Cast(ref expr, ref ty) => rewrite_pair( + &**expr, + &**ty, + PairParts::infix(" as "), + context, + shape, + SeparatorPlace::Front, + ), + ast::ExprKind::Type(ref expr, ref ty) => rewrite_pair( + &**expr, + &**ty, + PairParts::infix(": "), + context, + shape, + SeparatorPlace::Back, + ), + ast::ExprKind::Index(ref expr, ref index) => { + rewrite_index(&**expr, &**index, context, shape) + } + ast::ExprKind::Repeat(ref expr, ref repeats) => rewrite_pair( + &**expr, + &*repeats.value, + PairParts::new("[", "; ", "]"), + context, + shape, + SeparatorPlace::Back, + ), + ast::ExprKind::Range(ref lhs, ref rhs, limits) => { + let delim = match limits { + ast::RangeLimits::HalfOpen => "..", + ast::RangeLimits::Closed => "..=", + }; + + fn needs_space_before_range(context: &RewriteContext<'_>, lhs: &ast::Expr) -> bool { + match lhs.kind { + ast::ExprKind::Lit(token_lit) => match token_lit.kind { + token::LitKind::Float if token_lit.suffix.is_none() => { + context.snippet(lhs.span).ends_with('.') + } + _ => false, + }, + ast::ExprKind::Unary(_, ref expr) => needs_space_before_range(context, expr), + _ => false, + } + } + + fn needs_space_after_range(rhs: &ast::Expr) -> bool { + // Don't format `.. ..` into `....`, which is invalid. + // + // This check is unnecessary for `lhs`, because a range + // starting from another range needs parentheses as `(x ..) ..` + // (`x .. ..` is a range from `x` to `..`). + matches!(rhs.kind, ast::ExprKind::Range(None, _, _)) + } + + let default_sp_delim = |lhs: Option<&ast::Expr>, rhs: Option<&ast::Expr>| { + let space_if = |b: bool| if b { " " } else { "" }; + + format!( + "{}{}{}", + lhs.map_or("", |lhs| space_if(needs_space_before_range(context, lhs))), + delim, + rhs.map_or("", |rhs| space_if(needs_space_after_range(rhs))), + ) + }; + + match (lhs.as_ref().map(|x| &**x), rhs.as_ref().map(|x| &**x)) { + (Some(lhs), Some(rhs)) => { + let sp_delim = if context.config.spaces_around_ranges() { + format!(" {} ", delim) + } else { + default_sp_delim(Some(lhs), Some(rhs)) + }; + rewrite_pair( + &*lhs, + &*rhs, + PairParts::infix(&sp_delim), + context, + shape, + context.config.binop_separator(), + ) + } + (None, Some(rhs)) => { + let sp_delim = if context.config.spaces_around_ranges() { + format!("{} ", delim) + } else { + default_sp_delim(None, Some(rhs)) + }; + rewrite_unary_prefix(context, &sp_delim, &*rhs, shape) + } + (Some(lhs), None) => { + let sp_delim = if context.config.spaces_around_ranges() { + format!(" {}", delim) + } else { + default_sp_delim(Some(lhs), None) + }; + rewrite_unary_suffix(context, &sp_delim, &*lhs, shape) + } + (None, None) => Some(delim.to_owned()), + } + } + // We do not format these expressions yet, but they should still + // satisfy our width restrictions. + // Style Guide RFC for InlineAsm variant pending + // https://github.com/rust-dev-tools/fmt-rfcs/issues/152 + ast::ExprKind::InlineAsm(..) => Some(context.snippet(expr.span).to_owned()), + ast::ExprKind::TryBlock(ref block) => { + if let rw @ Some(_) = + rewrite_single_line_block(context, "try ", block, Some(&expr.attrs), None, shape) + { + rw + } else { + // 9 = `try ` + let budget = shape.width.saturating_sub(9); + Some(format!( + "{}{}", + "try ", + rewrite_block( + block, + Some(&expr.attrs), + None, + context, + Shape::legacy(budget, shape.indent) + )? + )) + } + } + ast::ExprKind::Async(capture_by, ref block) => { + let mover = if capture_by == ast::CaptureBy::Value { + "move " + } else { + "" + }; + if let rw @ Some(_) = rewrite_single_line_block( + context, + format!("{}{}", "async ", mover).as_str(), + block, + Some(&expr.attrs), + None, + shape, + ) { + rw + } else { + // 6 = `async ` + let budget = shape.width.saturating_sub(6); + Some(format!( + "{}{}{}", + "async ", + mover, + rewrite_block( + block, + Some(&expr.attrs), + None, + context, + Shape::legacy(budget, shape.indent) + )? + )) + } + } + ast::ExprKind::Underscore => Some("_".to_owned()), + ast::ExprKind::FormatArgs(..) + | ast::ExprKind::IncludedBytes(..) + | ast::ExprKind::OffsetOf(..) => { + // These do not occur in the AST because macros aren't expanded. + unreachable!() + } + ast::ExprKind::Err => None, + }; + + expr_rw + .and_then(|expr_str| recover_comment_removed(expr_str, expr.span, context)) + .and_then(|expr_str| { + let attrs = outer_attributes(&expr.attrs); + let attrs_str = attrs.rewrite(context, shape)?; + let span = mk_sp( + attrs.last().map_or(expr.span.lo(), |attr| attr.span.hi()), + expr.span.lo(), + ); + combine_strs_with_missing_comments(context, &attrs_str, &expr_str, span, shape, false) + }) +} + +pub(crate) fn rewrite_array<'a, T: 'a + IntoOverflowableItem<'a>>( + name: &'a str, + exprs: impl Iterator, + span: Span, + context: &'a RewriteContext<'_>, + shape: Shape, + force_separator_tactic: Option, + delim_token: Option, +) -> Option { + overflow::rewrite_with_square_brackets( + context, + name, + exprs, + shape, + span, + force_separator_tactic, + delim_token, + ) +} + +fn rewrite_empty_block( + context: &RewriteContext<'_>, + block: &ast::Block, + attrs: Option<&[ast::Attribute]>, + label: Option, + prefix: &str, + shape: Shape, +) -> Option { + if block_has_statements(block) { + return None; + } + + let label_str = rewrite_label(label); + if attrs.map_or(false, |a| !inner_attributes(a).is_empty()) { + return None; + } + + if !block_contains_comment(context, block) && shape.width >= 2 { + return Some(format!("{}{}{{}}", prefix, label_str)); + } + + // If a block contains only a single-line comment, then leave it on one line. + let user_str = context.snippet(block.span); + let user_str = user_str.trim(); + if user_str.starts_with('{') && user_str.ends_with('}') { + let comment_str = user_str[1..user_str.len() - 1].trim(); + if block.stmts.is_empty() + && !comment_str.contains('\n') + && !comment_str.starts_with("//") + && comment_str.len() + 4 <= shape.width + { + return Some(format!("{}{}{{ {} }}", prefix, label_str, comment_str)); + } + } + + None +} + +fn block_prefix(context: &RewriteContext<'_>, block: &ast::Block, shape: Shape) -> Option { + Some(match block.rules { + ast::BlockCheckMode::Unsafe(..) => { + let snippet = context.snippet(block.span); + let open_pos = snippet.find_uncommented("{")?; + // Extract comment between unsafe and block start. + let trimmed = &snippet[6..open_pos].trim(); + + if !trimmed.is_empty() { + // 9 = "unsafe {".len(), 7 = "unsafe ".len() + let budget = shape.width.checked_sub(9)?; + format!( + "unsafe {} ", + rewrite_comment( + trimmed, + true, + Shape::legacy(budget, shape.indent + 7), + context.config, + )? + ) + } else { + "unsafe ".to_owned() + } + } + ast::BlockCheckMode::Default => String::new(), + }) +} + +fn rewrite_single_line_block( + context: &RewriteContext<'_>, + prefix: &str, + block: &ast::Block, + attrs: Option<&[ast::Attribute]>, + label: Option, + shape: Shape, +) -> Option { + if is_simple_block(context, block, attrs) { + let expr_shape = shape.offset_left(last_line_width(prefix))?; + let expr_str = block.stmts[0].rewrite(context, expr_shape)?; + let label_str = rewrite_label(label); + let result = format!("{}{}{{ {} }}", prefix, label_str, expr_str); + if result.len() <= shape.width && !result.contains('\n') { + return Some(result); + } + } + None +} + +pub(crate) fn rewrite_block_with_visitor( + context: &RewriteContext<'_>, + prefix: &str, + block: &ast::Block, + attrs: Option<&[ast::Attribute]>, + label: Option, + shape: Shape, + has_braces: bool, +) -> Option { + if let rw @ Some(_) = rewrite_empty_block(context, block, attrs, label, prefix, shape) { + return rw; + } + + let mut visitor = FmtVisitor::from_context(context); + visitor.block_indent = shape.indent; + visitor.is_if_else_block = context.is_if_else_block(); + match (block.rules, label) { + (ast::BlockCheckMode::Unsafe(..), _) | (ast::BlockCheckMode::Default, Some(_)) => { + let snippet = context.snippet(block.span); + let open_pos = snippet.find_uncommented("{")?; + visitor.last_pos = block.span.lo() + BytePos(open_pos as u32) + } + (ast::BlockCheckMode::Default, None) => visitor.last_pos = block.span.lo(), + } + + let inner_attrs = attrs.map(inner_attributes); + let label_str = rewrite_label(label); + visitor.visit_block(block, inner_attrs.as_deref(), has_braces); + let visitor_context = visitor.get_context(); + context + .skipped_range + .borrow_mut() + .append(&mut visitor_context.skipped_range.borrow_mut()); + Some(format!("{}{}{}", prefix, label_str, visitor.buffer)) +} + +impl Rewrite for ast::Block { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + rewrite_block(self, None, None, context, shape) + } +} + +fn rewrite_block( + block: &ast::Block, + attrs: Option<&[ast::Attribute]>, + label: Option, + context: &RewriteContext<'_>, + shape: Shape, +) -> Option { + rewrite_block_inner(block, attrs, label, true, context, shape) +} + +fn rewrite_block_inner( + block: &ast::Block, + attrs: Option<&[ast::Attribute]>, + label: Option, + allow_single_line: bool, + context: &RewriteContext<'_>, + shape: Shape, +) -> Option { + let prefix = block_prefix(context, block, shape)?; + + // shape.width is used only for the single line case: either the empty block `{}`, + // or an unsafe expression `unsafe { e }`. + if let rw @ Some(_) = rewrite_empty_block(context, block, attrs, label, &prefix, shape) { + return rw; + } + + let result = rewrite_block_with_visitor(context, &prefix, block, attrs, label, shape, true); + if let Some(ref result_str) = result { + if allow_single_line && result_str.lines().count() <= 3 { + if let rw @ Some(_) = + rewrite_single_line_block(context, &prefix, block, attrs, label, shape) + { + return rw; + } + } + } + + result +} + +/// Rewrite the divergent block of a `let-else` statement. +pub(crate) fn rewrite_let_else_block( + block: &ast::Block, + allow_single_line: bool, + context: &RewriteContext<'_>, + shape: Shape, +) -> Option { + rewrite_block_inner(block, None, None, allow_single_line, context, shape) +} + +// Rewrite condition if the given expression has one. +pub(crate) fn rewrite_cond( + context: &RewriteContext<'_>, + expr: &ast::Expr, + shape: Shape, +) -> Option { + match expr.kind { + ast::ExprKind::Match(ref cond, _) => { + // `match `cond` {` + let cond_shape = match context.config.indent_style() { + IndentStyle::Visual => shape.shrink_left(6).and_then(|s| s.sub_width(2))?, + IndentStyle::Block => shape.offset_left(8)?, + }; + cond.rewrite(context, cond_shape) + } + _ => to_control_flow(expr, ExprType::SubExpression).and_then(|control_flow| { + let alt_block_sep = + String::from("\n") + &shape.indent.block_only().to_string(context.config); + control_flow + .rewrite_cond(context, shape, &alt_block_sep) + .map(|rw| rw.0) + }), + } +} + +// Abstraction over control flow expressions +#[derive(Debug)] +struct ControlFlow<'a> { + cond: Option<&'a ast::Expr>, + block: &'a ast::Block, + else_block: Option<&'a ast::Expr>, + label: Option, + pat: Option<&'a ast::Pat>, + keyword: &'a str, + matcher: &'a str, + connector: &'a str, + allow_single_line: bool, + // HACK: `true` if this is an `if` expression in an `else if`. + nested_if: bool, + span: Span, +} + +fn extract_pats_and_cond(expr: &ast::Expr) -> (Option<&ast::Pat>, &ast::Expr) { + match expr.kind { + ast::ExprKind::Let(ref pat, ref cond, _) => (Some(pat), cond), + _ => (None, expr), + } +} + +// FIXME: Refactor this. +fn to_control_flow(expr: &ast::Expr, expr_type: ExprType) -> Option> { + match expr.kind { + ast::ExprKind::If(ref cond, ref if_block, ref else_block) => { + let (pat, cond) = extract_pats_and_cond(cond); + Some(ControlFlow::new_if( + cond, + pat, + if_block, + else_block.as_ref().map(|e| &**e), + expr_type == ExprType::SubExpression, + false, + expr.span, + )) + } + ast::ExprKind::ForLoop(ref pat, ref cond, ref block, label) => { + Some(ControlFlow::new_for(pat, cond, block, label, expr.span)) + } + ast::ExprKind::Loop(ref block, label, _) => { + Some(ControlFlow::new_loop(block, label, expr.span)) + } + ast::ExprKind::While(ref cond, ref block, label) => { + let (pat, cond) = extract_pats_and_cond(cond); + Some(ControlFlow::new_while(pat, cond, block, label, expr.span)) + } + _ => None, + } +} + +fn choose_matcher(pat: Option<&ast::Pat>) -> &'static str { + pat.map_or("", |_| "let") +} + +impl<'a> ControlFlow<'a> { + fn new_if( + cond: &'a ast::Expr, + pat: Option<&'a ast::Pat>, + block: &'a ast::Block, + else_block: Option<&'a ast::Expr>, + allow_single_line: bool, + nested_if: bool, + span: Span, + ) -> ControlFlow<'a> { + let matcher = choose_matcher(pat); + ControlFlow { + cond: Some(cond), + block, + else_block, + label: None, + pat, + keyword: "if", + matcher, + connector: " =", + allow_single_line, + nested_if, + span, + } + } + + fn new_loop(block: &'a ast::Block, label: Option, span: Span) -> ControlFlow<'a> { + ControlFlow { + cond: None, + block, + else_block: None, + label, + pat: None, + keyword: "loop", + matcher: "", + connector: "", + allow_single_line: false, + nested_if: false, + span, + } + } + + fn new_while( + pat: Option<&'a ast::Pat>, + cond: &'a ast::Expr, + block: &'a ast::Block, + label: Option, + span: Span, + ) -> ControlFlow<'a> { + let matcher = choose_matcher(pat); + ControlFlow { + cond: Some(cond), + block, + else_block: None, + label, + pat, + keyword: "while", + matcher, + connector: " =", + allow_single_line: false, + nested_if: false, + span, + } + } + + fn new_for( + pat: &'a ast::Pat, + cond: &'a ast::Expr, + block: &'a ast::Block, + label: Option, + span: Span, + ) -> ControlFlow<'a> { + ControlFlow { + cond: Some(cond), + block, + else_block: None, + label, + pat: Some(pat), + keyword: "for", + matcher: "", + connector: " in", + allow_single_line: false, + nested_if: false, + span, + } + } + + fn rewrite_single_line( + &self, + pat_expr_str: &str, + context: &RewriteContext<'_>, + width: usize, + ) -> Option { + assert!(self.allow_single_line); + let else_block = self.else_block?; + let fixed_cost = self.keyword.len() + " { } else { }".len(); + + if let ast::ExprKind::Block(ref else_node, _) = else_block.kind { + if !is_simple_block(context, self.block, None) + || !is_simple_block(context, else_node, None) + || pat_expr_str.contains('\n') + { + return None; + } + + let new_width = width.checked_sub(pat_expr_str.len() + fixed_cost)?; + let expr = &self.block.stmts[0]; + let if_str = expr.rewrite(context, Shape::legacy(new_width, Indent::empty()))?; + + let new_width = new_width.checked_sub(if_str.len())?; + let else_expr = &else_node.stmts[0]; + let else_str = else_expr.rewrite(context, Shape::legacy(new_width, Indent::empty()))?; + + if if_str.contains('\n') || else_str.contains('\n') { + return None; + } + + let result = format!( + "{} {} {{ {} }} else {{ {} }}", + self.keyword, pat_expr_str, if_str, else_str + ); + + if result.len() <= width { + return Some(result); + } + } + + None + } +} + +/// Returns `true` if the last line of pat_str has leading whitespace and it is wider than the +/// shape's indent. +fn last_line_offsetted(start_column: usize, pat_str: &str) -> bool { + let mut leading_whitespaces = 0; + for c in pat_str.chars().rev() { + match c { + '\n' => break, + _ if c.is_whitespace() => leading_whitespaces += 1, + _ => leading_whitespaces = 0, + } + } + leading_whitespaces > start_column +} + +impl<'a> ControlFlow<'a> { + fn rewrite_pat_expr( + &self, + context: &RewriteContext<'_>, + expr: &ast::Expr, + shape: Shape, + offset: usize, + ) -> Option { + debug!("rewrite_pat_expr {:?} {:?} {:?}", shape, self.pat, expr); + + let cond_shape = shape.offset_left(offset)?; + if let Some(pat) = self.pat { + let matcher = if self.matcher.is_empty() { + self.matcher.to_owned() + } else { + format!("{} ", self.matcher) + }; + let pat_shape = cond_shape + .offset_left(matcher.len())? + .sub_width(self.connector.len())?; + let pat_string = pat.rewrite(context, pat_shape)?; + let comments_lo = context + .snippet_provider + .span_after(self.span.with_lo(pat.span.hi()), self.connector.trim()); + let comments_span = mk_sp(comments_lo, expr.span.lo()); + return rewrite_assign_rhs_with_comments( + context, + &format!("{}{}{}", matcher, pat_string, self.connector), + expr, + cond_shape, + &RhsAssignKind::Expr(&expr.kind, expr.span), + RhsTactics::Default, + comments_span, + true, + ); + } + + let expr_rw = expr.rewrite(context, cond_shape); + // The expression may (partially) fit on the current line. + // We do not allow splitting between `if` and condition. + if self.keyword == "if" || expr_rw.is_some() { + return expr_rw; + } + + // The expression won't fit on the current line, jump to next. + let nested_shape = shape + .block_indent(context.config.tab_spaces()) + .with_max_width(context.config); + let nested_indent_str = nested_shape.indent.to_string_with_newline(context.config); + expr.rewrite(context, nested_shape) + .map(|expr_rw| format!("{}{}", nested_indent_str, expr_rw)) + } + + fn rewrite_cond( + &self, + context: &RewriteContext<'_>, + shape: Shape, + alt_block_sep: &str, + ) -> Option<(String, usize)> { + // Do not take the rhs overhead from the upper expressions into account + // when rewriting pattern. + let new_width = context.budget(shape.used_width()); + let fresh_shape = Shape { + width: new_width, + ..shape + }; + let constr_shape = if self.nested_if { + // We are part of an if-elseif-else chain. Our constraints are tightened. + // 7 = "} else " .len() + fresh_shape.offset_left(7)? + } else { + fresh_shape + }; + + let label_string = rewrite_label(self.label); + // 1 = space after keyword. + let offset = self.keyword.len() + label_string.len() + 1; + + let pat_expr_string = match self.cond { + Some(cond) => self.rewrite_pat_expr(context, cond, constr_shape, offset)?, + None => String::new(), + }; + + let brace_overhead = + if context.config.control_brace_style() != ControlBraceStyle::AlwaysNextLine { + // 2 = ` {` + 2 + } else { + 0 + }; + let one_line_budget = context + .config + .max_width() + .saturating_sub(constr_shape.used_width() + offset + brace_overhead); + let force_newline_brace = (pat_expr_string.contains('\n') + || pat_expr_string.len() > one_line_budget) + && (!last_line_extendable(&pat_expr_string) + || last_line_offsetted(shape.used_width(), &pat_expr_string)); + + // Try to format if-else on single line. + if self.allow_single_line && context.config.single_line_if_else_max_width() > 0 { + let trial = self.rewrite_single_line(&pat_expr_string, context, shape.width); + + if let Some(cond_str) = trial { + if cond_str.len() <= context.config.single_line_if_else_max_width() { + return Some((cond_str, 0)); + } + } + } + + let cond_span = if let Some(cond) = self.cond { + cond.span + } else { + mk_sp(self.block.span.lo(), self.block.span.lo()) + }; + + // `for event in event` + // Do not include label in the span. + let lo = self + .label + .map_or(self.span.lo(), |label| label.ident.span.hi()); + let between_kwd_cond = mk_sp( + context + .snippet_provider + .span_after(mk_sp(lo, self.span.hi()), self.keyword.trim()), + if self.pat.is_none() { + cond_span.lo() + } else if self.matcher.is_empty() { + self.pat.unwrap().span.lo() + } else { + context + .snippet_provider + .span_before(self.span, self.matcher.trim()) + }, + ); + + let between_kwd_cond_comment = extract_comment(between_kwd_cond, context, shape); + + let after_cond_comment = + extract_comment(mk_sp(cond_span.hi(), self.block.span.lo()), context, shape); + + let block_sep = if self.cond.is_none() && between_kwd_cond_comment.is_some() { + "" + } else if context.config.control_brace_style() == ControlBraceStyle::AlwaysNextLine + || force_newline_brace + { + alt_block_sep + } else { + " " + }; + + let used_width = if pat_expr_string.contains('\n') { + last_line_width(&pat_expr_string) + } else { + // 2 = spaces after keyword and condition. + label_string.len() + self.keyword.len() + pat_expr_string.len() + 2 + }; + + Some(( + format!( + "{}{}{}{}{}", + label_string, + self.keyword, + between_kwd_cond_comment.as_ref().map_or( + if pat_expr_string.is_empty() || pat_expr_string.starts_with('\n') { + "" + } else { + " " + }, + |s| &**s, + ), + pat_expr_string, + after_cond_comment.as_ref().map_or(block_sep, |s| &**s) + ), + used_width, + )) + } +} + +/// Rewrite the `else` keyword with surrounding comments. +/// +/// force_newline_else: whether or not to rewrite the `else` keyword on a newline. +/// is_last: true if this is an `else` and `false` if this is an `else if` block. +/// context: rewrite context +/// span: Span between the end of the last expression and the start of the else block, +/// which contains the `else` keyword +/// shape: Shape +pub(crate) fn rewrite_else_kw_with_comments( + force_newline_else: bool, + is_last: bool, + context: &RewriteContext<'_>, + span: Span, + shape: Shape, +) -> String { + let else_kw_lo = context.snippet_provider.span_before(span, "else"); + let before_else_kw = mk_sp(span.lo(), else_kw_lo); + let before_else_kw_comment = extract_comment(before_else_kw, context, shape); + + let else_kw_hi = context.snippet_provider.span_after(span, "else"); + let after_else_kw = mk_sp(else_kw_hi, span.hi()); + let after_else_kw_comment = extract_comment(after_else_kw, context, shape); + + let newline_sep = &shape.indent.to_string_with_newline(context.config); + let before_sep = match context.config.control_brace_style() { + _ if force_newline_else => newline_sep.as_ref(), + ControlBraceStyle::AlwaysNextLine | ControlBraceStyle::ClosingNextLine => { + newline_sep.as_ref() + } + ControlBraceStyle::AlwaysSameLine => " ", + }; + let after_sep = match context.config.control_brace_style() { + ControlBraceStyle::AlwaysNextLine if is_last => newline_sep.as_ref(), + _ => " ", + }; + + format!( + "{}else{}", + before_else_kw_comment.as_ref().map_or(before_sep, |s| &**s), + after_else_kw_comment.as_ref().map_or(after_sep, |s| &**s), + ) +} + +impl<'a> Rewrite for ControlFlow<'a> { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + debug!("ControlFlow::rewrite {:?} {:?}", self, shape); + + let alt_block_sep = &shape.indent.to_string_with_newline(context.config); + let (cond_str, used_width) = self.rewrite_cond(context, shape, alt_block_sep)?; + // If `used_width` is 0, it indicates that whole control flow is written in a single line. + if used_width == 0 { + return Some(cond_str); + } + + let block_width = shape.width.saturating_sub(used_width); + // This is used only for the empty block case: `{}`. So, we use 1 if we know + // we should avoid the single line case. + let block_width = if self.else_block.is_some() || self.nested_if { + min(1, block_width) + } else { + block_width + }; + let block_shape = Shape { + width: block_width, + ..shape + }; + let block_str = { + let old_val = context.is_if_else_block.replace(self.else_block.is_some()); + let result = + rewrite_block_with_visitor(context, "", self.block, None, None, block_shape, true); + context.is_if_else_block.replace(old_val); + result? + }; + + let mut result = format!("{}{}", cond_str, block_str); + + if let Some(else_block) = self.else_block { + let shape = Shape::indented(shape.indent, context.config); + let mut last_in_chain = false; + let rewrite = match else_block.kind { + // If the else expression is another if-else expression, prevent it + // from being formatted on a single line. + // Note how we're passing the original shape, as the + // cost of "else" should not cascade. + ast::ExprKind::If(ref cond, ref if_block, ref next_else_block) => { + let (pats, cond) = extract_pats_and_cond(cond); + ControlFlow::new_if( + cond, + pats, + if_block, + next_else_block.as_ref().map(|e| &**e), + false, + true, + mk_sp(else_block.span.lo(), self.span.hi()), + ) + .rewrite(context, shape) + } + _ => { + last_in_chain = true; + // When rewriting a block, the width is only used for single line + // blocks, passing 1 lets us avoid that. + let else_shape = Shape { + width: min(1, shape.width), + ..shape + }; + format_expr(else_block, ExprType::Statement, context, else_shape) + } + }; + + let else_kw = rewrite_else_kw_with_comments( + false, + last_in_chain, + context, + self.block.span.between(else_block.span), + shape, + ); + result.push_str(&else_kw); + result.push_str(&rewrite?); + } + + Some(result) + } +} + +fn rewrite_label(opt_label: Option) -> Cow<'static, str> { + match opt_label { + Some(label) => Cow::from(format!("{}: ", label.ident)), + None => Cow::from(""), + } +} + +fn extract_comment(span: Span, context: &RewriteContext<'_>, shape: Shape) -> Option { + match rewrite_missing_comment(span, shape, context) { + Some(ref comment) if !comment.is_empty() => Some(format!( + "{indent}{}{indent}", + comment, + indent = shape.indent.to_string_with_newline(context.config) + )), + _ => None, + } +} + +pub(crate) fn block_contains_comment(context: &RewriteContext<'_>, block: &ast::Block) -> bool { + contains_comment(context.snippet(block.span)) +} + +// Checks that a block contains no statements, an expression and no comments or +// attributes. +// FIXME: incorrectly returns false when comment is contained completely within +// the expression. +pub(crate) fn is_simple_block( + context: &RewriteContext<'_>, + block: &ast::Block, + attrs: Option<&[ast::Attribute]>, +) -> bool { + block.stmts.len() == 1 + && stmt_is_expr(&block.stmts[0]) + && !block_contains_comment(context, block) + && attrs.map_or(true, |a| a.is_empty()) +} + +/// Checks whether a block contains at most one statement or expression, and no +/// comments or attributes. +pub(crate) fn is_simple_block_stmt( + context: &RewriteContext<'_>, + block: &ast::Block, + attrs: Option<&[ast::Attribute]>, +) -> bool { + block.stmts.len() <= 1 + && !block_contains_comment(context, block) + && attrs.map_or(true, |a| a.is_empty()) +} + +fn block_has_statements(block: &ast::Block) -> bool { + block + .stmts + .iter() + .any(|stmt| !matches!(stmt.kind, ast::StmtKind::Empty)) +} + +/// Checks whether a block contains no statements, expressions, comments, or +/// inner attributes. +pub(crate) fn is_empty_block( + context: &RewriteContext<'_>, + block: &ast::Block, + attrs: Option<&[ast::Attribute]>, +) -> bool { + !block_has_statements(block) + && !block_contains_comment(context, block) + && attrs.map_or(true, |a| inner_attributes(a).is_empty()) +} + +pub(crate) fn stmt_is_expr(stmt: &ast::Stmt) -> bool { + matches!(stmt.kind, ast::StmtKind::Expr(..)) +} + +pub(crate) fn is_unsafe_block(block: &ast::Block) -> bool { + matches!(block.rules, ast::BlockCheckMode::Unsafe(..)) +} + +pub(crate) fn rewrite_literal( + context: &RewriteContext<'_>, + token_lit: token::Lit, + span: Span, + shape: Shape, +) -> Option { + match token_lit.kind { + token::LitKind::Str => rewrite_string_lit(context, span, shape), + token::LitKind::Integer => rewrite_int_lit(context, token_lit, span, shape), + _ => wrap_str( + context.snippet(span).to_owned(), + context.config.max_width(), + shape, + ), + } +} + +fn rewrite_string_lit(context: &RewriteContext<'_>, span: Span, shape: Shape) -> Option { + let string_lit = context.snippet(span); + + if !context.config.format_strings() { + if string_lit + .lines() + .dropping_back(1) + .all(|line| line.ends_with('\\')) + && context.config.version() == Version::Two + { + return Some(string_lit.to_owned()); + } else { + return wrap_str(string_lit.to_owned(), context.config.max_width(), shape); + } + } + + // Remove the quote characters. + let str_lit = &string_lit[1..string_lit.len() - 1]; + + rewrite_string( + str_lit, + &StringFormat::new(shape.visual_indent(0), context.config), + shape.width.saturating_sub(2), + ) +} + +fn rewrite_int_lit( + context: &RewriteContext<'_>, + token_lit: token::Lit, + span: Span, + shape: Shape, +) -> Option { + let symbol = token_lit.symbol.as_str(); + + if let Some(symbol_stripped) = symbol.strip_prefix("0x") { + let hex_lit = match context.config.hex_literal_case() { + HexLiteralCase::Preserve => None, + HexLiteralCase::Upper => Some(symbol_stripped.to_ascii_uppercase()), + HexLiteralCase::Lower => Some(symbol_stripped.to_ascii_lowercase()), + }; + if let Some(hex_lit) = hex_lit { + return wrap_str( + format!( + "0x{}{}", + hex_lit, + token_lit.suffix.map_or(String::new(), |s| s.to_string()) + ), + context.config.max_width(), + shape, + ); + } + } + + wrap_str( + context.snippet(span).to_owned(), + context.config.max_width(), + shape, + ) +} + +fn choose_separator_tactic(context: &RewriteContext<'_>, span: Span) -> Option { + if context.inside_macro() { + if span_ends_with_comma(context, span) { + Some(SeparatorTactic::Always) + } else { + Some(SeparatorTactic::Never) + } + } else { + None + } +} + +pub(crate) fn rewrite_call( + context: &RewriteContext<'_>, + callee: &str, + args: &[ptr::P], + span: Span, + shape: Shape, +) -> Option { + overflow::rewrite_with_parens( + context, + callee, + args.iter(), + shape, + span, + context.config.fn_call_width(), + choose_separator_tactic(context, span), + ) +} + +pub(crate) fn is_simple_expr(expr: &ast::Expr) -> bool { + match expr.kind { + ast::ExprKind::Lit(..) => true, + ast::ExprKind::Path(ref qself, ref path) => qself.is_none() && path.segments.len() <= 1, + ast::ExprKind::AddrOf(_, _, ref expr) + | ast::ExprKind::Cast(ref expr, _) + | ast::ExprKind::Field(ref expr, _) + | ast::ExprKind::Try(ref expr) + | ast::ExprKind::Unary(_, ref expr) => is_simple_expr(expr), + ast::ExprKind::Index(ref lhs, ref rhs) => is_simple_expr(lhs) && is_simple_expr(rhs), + ast::ExprKind::Repeat(ref lhs, ref rhs) => { + is_simple_expr(lhs) && is_simple_expr(&*rhs.value) + } + _ => false, + } +} + +pub(crate) fn is_every_expr_simple(lists: &[OverflowableItem<'_>]) -> bool { + lists.iter().all(OverflowableItem::is_simple) +} + +pub(crate) fn can_be_overflowed_expr( + context: &RewriteContext<'_>, + expr: &ast::Expr, + args_len: usize, +) -> bool { + match expr.kind { + _ if !expr.attrs.is_empty() => false, + ast::ExprKind::Match(..) => { + (context.use_block_indent() && args_len == 1) + || (context.config.indent_style() == IndentStyle::Visual && args_len > 1) + || context.config.overflow_delimited_expr() + } + ast::ExprKind::If(..) + | ast::ExprKind::ForLoop(..) + | ast::ExprKind::Loop(..) + | ast::ExprKind::While(..) => { + context.config.combine_control_expr() && context.use_block_indent() && args_len == 1 + } + + // Handle always block-like expressions + ast::ExprKind::Async(..) | ast::ExprKind::Block(..) | ast::ExprKind::Closure(..) => true, + + // Handle `[]` and `{}`-like expressions + ast::ExprKind::Array(..) | ast::ExprKind::Struct(..) => { + context.config.overflow_delimited_expr() + || (context.use_block_indent() && args_len == 1) + } + ast::ExprKind::MacCall(ref mac) => { + match ( + rustc_ast::ast::MacDelimiter::from_token(mac.args.delim.to_token()), + context.config.overflow_delimited_expr(), + ) { + (Some(ast::MacDelimiter::Bracket), true) + | (Some(ast::MacDelimiter::Brace), true) => true, + _ => context.use_block_indent() && args_len == 1, + } + } + + // Handle parenthetical expressions + ast::ExprKind::Call(..) | ast::ExprKind::MethodCall(..) | ast::ExprKind::Tup(..) => { + context.use_block_indent() && args_len == 1 + } + + // Handle unary-like expressions + ast::ExprKind::AddrOf(_, _, ref expr) + | ast::ExprKind::Try(ref expr) + | ast::ExprKind::Unary(_, ref expr) + | ast::ExprKind::Cast(ref expr, _) => can_be_overflowed_expr(context, expr, args_len), + _ => false, + } +} + +pub(crate) fn is_nested_call(expr: &ast::Expr) -> bool { + match expr.kind { + ast::ExprKind::Call(..) | ast::ExprKind::MacCall(..) => true, + ast::ExprKind::AddrOf(_, _, ref expr) + | ast::ExprKind::Try(ref expr) + | ast::ExprKind::Unary(_, ref expr) + | ast::ExprKind::Cast(ref expr, _) => is_nested_call(expr), + _ => false, + } +} + +/// Returns `true` if a function call or a method call represented by the given span ends with a +/// trailing comma. This function is used when rewriting macro, as adding or removing a trailing +/// comma from macro can potentially break the code. +pub(crate) fn span_ends_with_comma(context: &RewriteContext<'_>, span: Span) -> bool { + let mut result: bool = Default::default(); + let mut prev_char: char = Default::default(); + let closing_delimiters = &[')', '}', ']']; + + for (kind, c) in CharClasses::new(context.snippet(span).chars()) { + match c { + _ if kind.is_comment() || c.is_whitespace() => continue, + c if closing_delimiters.contains(&c) => { + result &= !closing_delimiters.contains(&prev_char); + } + ',' => result = true, + _ => result = false, + } + prev_char = c; + } + + result +} + +fn rewrite_paren( + context: &RewriteContext<'_>, + mut subexpr: &ast::Expr, + shape: Shape, + mut span: Span, +) -> Option { + debug!("rewrite_paren, shape: {:?}", shape); + + // Extract comments within parens. + let mut pre_span; + let mut post_span; + let mut pre_comment; + let mut post_comment; + let remove_nested_parens = context.config.remove_nested_parens(); + loop { + // 1 = "(" or ")" + pre_span = mk_sp(span.lo() + BytePos(1), subexpr.span.lo()); + post_span = mk_sp(subexpr.span.hi(), span.hi() - BytePos(1)); + pre_comment = rewrite_missing_comment(pre_span, shape, context)?; + post_comment = rewrite_missing_comment(post_span, shape, context)?; + + // Remove nested parens if there are no comments. + if let ast::ExprKind::Paren(ref subsubexpr) = subexpr.kind { + if remove_nested_parens && pre_comment.is_empty() && post_comment.is_empty() { + span = subexpr.span; + subexpr = subsubexpr; + continue; + } + } + + break; + } + + // 1 = `(` and `)` + let sub_shape = shape.offset_left(1)?.sub_width(1)?; + let subexpr_str = subexpr.rewrite(context, sub_shape)?; + let fits_single_line = !pre_comment.contains("//") && !post_comment.contains("//"); + if fits_single_line { + Some(format!("({}{}{})", pre_comment, subexpr_str, post_comment)) + } else { + rewrite_paren_in_multi_line(context, subexpr, shape, pre_span, post_span) + } +} + +fn rewrite_paren_in_multi_line( + context: &RewriteContext<'_>, + subexpr: &ast::Expr, + shape: Shape, + pre_span: Span, + post_span: Span, +) -> Option { + let nested_indent = shape.indent.block_indent(context.config); + let nested_shape = Shape::indented(nested_indent, context.config); + let pre_comment = rewrite_missing_comment(pre_span, nested_shape, context)?; + let post_comment = rewrite_missing_comment(post_span, nested_shape, context)?; + let subexpr_str = subexpr.rewrite(context, nested_shape)?; + + let mut result = String::with_capacity(subexpr_str.len() * 2); + result.push('('); + if !pre_comment.is_empty() { + result.push_str(&nested_indent.to_string_with_newline(context.config)); + result.push_str(&pre_comment); + } + result.push_str(&nested_indent.to_string_with_newline(context.config)); + result.push_str(&subexpr_str); + if !post_comment.is_empty() { + result.push_str(&nested_indent.to_string_with_newline(context.config)); + result.push_str(&post_comment); + } + result.push_str(&shape.indent.to_string_with_newline(context.config)); + result.push(')'); + + Some(result) +} + +fn rewrite_index( + expr: &ast::Expr, + index: &ast::Expr, + context: &RewriteContext<'_>, + shape: Shape, +) -> Option { + let expr_str = expr.rewrite(context, shape)?; + + let offset = last_line_width(&expr_str) + 1; + let rhs_overhead = shape.rhs_overhead(context.config); + let index_shape = if expr_str.contains('\n') { + Shape::legacy(context.config.max_width(), shape.indent) + .offset_left(offset) + .and_then(|shape| shape.sub_width(1 + rhs_overhead)) + } else { + match context.config.indent_style() { + IndentStyle::Block => shape + .offset_left(offset) + .and_then(|shape| shape.sub_width(1)), + IndentStyle::Visual => shape.visual_indent(offset).sub_width(offset + 1), + } + }; + let orig_index_rw = index_shape.and_then(|s| index.rewrite(context, s)); + + // Return if index fits in a single line. + match orig_index_rw { + Some(ref index_str) if !index_str.contains('\n') => { + return Some(format!("{}[{}]", expr_str, index_str)); + } + _ => (), + } + + // Try putting index on the next line and see if it fits in a single line. + let indent = shape.indent.block_indent(context.config); + let index_shape = Shape::indented(indent, context.config).offset_left(1)?; + let index_shape = index_shape.sub_width(1 + rhs_overhead)?; + let new_index_rw = index.rewrite(context, index_shape); + match (orig_index_rw, new_index_rw) { + (_, Some(ref new_index_str)) if !new_index_str.contains('\n') => Some(format!( + "{}{}[{}]", + expr_str, + indent.to_string_with_newline(context.config), + new_index_str, + )), + (None, Some(ref new_index_str)) => Some(format!( + "{}{}[{}]", + expr_str, + indent.to_string_with_newline(context.config), + new_index_str, + )), + (Some(ref index_str), _) => Some(format!("{}[{}]", expr_str, index_str)), + _ => None, + } +} + +fn struct_lit_can_be_aligned(fields: &[ast::ExprField], has_base: bool) -> bool { + !has_base && fields.iter().all(|field| !field.is_shorthand) +} + +fn rewrite_struct_lit<'a>( + context: &RewriteContext<'_>, + path: &ast::Path, + qself: &Option>, + fields: &'a [ast::ExprField], + struct_rest: &ast::StructRest, + attrs: &[ast::Attribute], + span: Span, + shape: Shape, +) -> Option { + debug!("rewrite_struct_lit: shape {:?}", shape); + + enum StructLitField<'a> { + Regular(&'a ast::ExprField), + Base(&'a ast::Expr), + Rest(Span), + } + + // 2 = " {".len() + let path_shape = shape.sub_width(2)?; + let path_str = rewrite_path(context, PathContext::Expr, qself, path, path_shape)?; + + let has_base_or_rest = match struct_rest { + ast::StructRest::None if fields.is_empty() => return Some(format!("{} {{}}", path_str)), + ast::StructRest::Rest(_) if fields.is_empty() => { + return Some(format!("{} {{ .. }}", path_str)); + } + ast::StructRest::Rest(_) | ast::StructRest::Base(_) => true, + _ => false, + }; + + // Foo { a: Foo } - indent is +3, width is -5. + let (h_shape, v_shape) = struct_lit_shape(shape, context, path_str.len() + 3, 2)?; + + let one_line_width = h_shape.map_or(0, |shape| shape.width); + let body_lo = context.snippet_provider.span_after(span, "{"); + let fields_str = if struct_lit_can_be_aligned(fields, has_base_or_rest) + && context.config.struct_field_align_threshold() > 0 + { + rewrite_with_alignment( + fields, + context, + v_shape, + mk_sp(body_lo, span.hi()), + one_line_width, + )? + } else { + let field_iter = fields.iter().map(StructLitField::Regular).chain( + match struct_rest { + ast::StructRest::Base(expr) => Some(StructLitField::Base(&**expr)), + ast::StructRest::Rest(span) => Some(StructLitField::Rest(*span)), + ast::StructRest::None => None, + } + .into_iter(), + ); + + let span_lo = |item: &StructLitField<'_>| match *item { + StructLitField::Regular(field) => field.span().lo(), + StructLitField::Base(expr) => { + let last_field_hi = fields.last().map_or(span.lo(), |field| field.span.hi()); + let snippet = context.snippet(mk_sp(last_field_hi, expr.span.lo())); + let pos = snippet.find_uncommented("..").unwrap(); + last_field_hi + BytePos(pos as u32) + } + StructLitField::Rest(span) => span.lo(), + }; + let span_hi = |item: &StructLitField<'_>| match *item { + StructLitField::Regular(field) => field.span().hi(), + StructLitField::Base(expr) => expr.span.hi(), + StructLitField::Rest(span) => span.hi(), + }; + let rewrite = |item: &StructLitField<'_>| match *item { + StructLitField::Regular(field) => { + // The 1 taken from the v_budget is for the comma. + rewrite_field(context, field, v_shape.sub_width(1)?, 0) + } + StructLitField::Base(expr) => { + // 2 = .. + expr.rewrite(context, v_shape.offset_left(2)?) + .map(|s| format!("..{}", s)) + } + StructLitField::Rest(_) => Some("..".to_owned()), + }; + + let items = itemize_list( + context.snippet_provider, + field_iter, + "}", + ",", + span_lo, + span_hi, + rewrite, + body_lo, + span.hi(), + false, + ); + let item_vec = items.collect::>(); + + let tactic = struct_lit_tactic(h_shape, context, &item_vec); + let nested_shape = shape_for_tactic(tactic, h_shape, v_shape); + + let ends_with_comma = span_ends_with_comma(context, span); + let force_no_trailing_comma = context.inside_macro() && !ends_with_comma; + + let fmt = struct_lit_formatting( + nested_shape, + tactic, + context, + force_no_trailing_comma || has_base_or_rest || !context.use_block_indent(), + ); + + write_list(&item_vec, &fmt)? + }; + + let fields_str = + wrap_struct_field(context, attrs, &fields_str, shape, v_shape, one_line_width)?; + Some(format!("{} {{{}}}", path_str, fields_str)) + + // FIXME if context.config.indent_style() == Visual, but we run out + // of space, we should fall back to BlockIndent. +} + +pub(crate) fn wrap_struct_field( + context: &RewriteContext<'_>, + attrs: &[ast::Attribute], + fields_str: &str, + shape: Shape, + nested_shape: Shape, + one_line_width: usize, +) -> Option { + let should_vertical = context.config.indent_style() == IndentStyle::Block + && (fields_str.contains('\n') + || !context.config.struct_lit_single_line() + || fields_str.len() > one_line_width); + + let inner_attrs = &inner_attributes(attrs); + if inner_attrs.is_empty() { + if should_vertical { + Some(format!( + "{}{}{}", + nested_shape.indent.to_string_with_newline(context.config), + fields_str, + shape.indent.to_string_with_newline(context.config) + )) + } else { + // One liner or visual indent. + Some(format!(" {} ", fields_str)) + } + } else { + Some(format!( + "{}{}{}{}{}", + nested_shape.indent.to_string_with_newline(context.config), + inner_attrs.rewrite(context, shape)?, + nested_shape.indent.to_string_with_newline(context.config), + fields_str, + shape.indent.to_string_with_newline(context.config) + )) + } +} + +pub(crate) fn struct_lit_field_separator(config: &Config) -> &str { + colon_spaces(config) +} + +pub(crate) fn rewrite_field( + context: &RewriteContext<'_>, + field: &ast::ExprField, + shape: Shape, + prefix_max_width: usize, +) -> Option { + if contains_skip(&field.attrs) { + return Some(context.snippet(field.span()).to_owned()); + } + let mut attrs_str = field.attrs.rewrite(context, shape)?; + if !attrs_str.is_empty() { + attrs_str.push_str(&shape.indent.to_string_with_newline(context.config)); + }; + let name = context.snippet(field.ident.span); + if field.is_shorthand { + Some(attrs_str + name) + } else { + let mut separator = String::from(struct_lit_field_separator(context.config)); + for _ in 0..prefix_max_width.saturating_sub(name.len()) { + separator.push(' '); + } + let overhead = name.len() + separator.len(); + let expr_shape = shape.offset_left(overhead)?; + let expr = field.expr.rewrite(context, expr_shape); + let is_lit = matches!(field.expr.kind, ast::ExprKind::Lit(_)); + match expr { + Some(ref e) + if !is_lit && e.as_str() == name && context.config.use_field_init_shorthand() => + { + Some(attrs_str + name) + } + Some(e) => Some(format!("{}{}{}{}", attrs_str, name, separator, e)), + None => { + let expr_offset = shape.indent.block_indent(context.config); + let expr = field + .expr + .rewrite(context, Shape::indented(expr_offset, context.config)); + expr.map(|s| { + format!( + "{}{}:\n{}{}", + attrs_str, + name, + expr_offset.to_string(context.config), + s + ) + }) + } + } + } +} + +fn rewrite_tuple_in_visual_indent_style<'a, T: 'a + IntoOverflowableItem<'a>>( + context: &RewriteContext<'_>, + mut items: impl Iterator, + span: Span, + shape: Shape, + is_singleton_tuple: bool, +) -> Option { + // In case of length 1, need a trailing comma + debug!("rewrite_tuple_in_visual_indent_style {:?}", shape); + if is_singleton_tuple { + // 3 = "(" + ",)" + let nested_shape = shape.sub_width(3)?.visual_indent(1); + return items + .next() + .unwrap() + .rewrite(context, nested_shape) + .map(|s| format!("({},)", s)); + } + + let list_lo = context.snippet_provider.span_after(span, "("); + let nested_shape = shape.sub_width(2)?.visual_indent(1); + let items = itemize_list( + context.snippet_provider, + items, + ")", + ",", + |item| item.span().lo(), + |item| item.span().hi(), + |item| item.rewrite(context, nested_shape), + list_lo, + span.hi() - BytePos(1), + false, + ); + let item_vec: Vec<_> = items.collect(); + let tactic = definitive_tactic( + &item_vec, + ListTactic::HorizontalVertical, + Separator::Comma, + nested_shape.width, + ); + let fmt = ListFormatting::new(nested_shape, context.config) + .tactic(tactic) + .ends_with_newline(false); + let list_str = write_list(&item_vec, &fmt)?; + + Some(format!("({})", list_str)) +} + +pub(crate) fn rewrite_tuple<'a, T: 'a + IntoOverflowableItem<'a>>( + context: &'a RewriteContext<'_>, + items: impl Iterator, + span: Span, + shape: Shape, + is_singleton_tuple: bool, +) -> Option { + debug!("rewrite_tuple {:?}", shape); + if context.use_block_indent() { + // We use the same rule as function calls for rewriting tuples. + let force_tactic = if context.inside_macro() { + if span_ends_with_comma(context, span) { + Some(SeparatorTactic::Always) + } else { + Some(SeparatorTactic::Never) + } + } else if is_singleton_tuple { + Some(SeparatorTactic::Always) + } else { + None + }; + overflow::rewrite_with_parens( + context, + "", + items, + shape, + span, + context.config.fn_call_width(), + force_tactic, + ) + } else { + rewrite_tuple_in_visual_indent_style(context, items, span, shape, is_singleton_tuple) + } +} + +pub(crate) fn rewrite_unary_prefix( + context: &RewriteContext<'_>, + prefix: &str, + rewrite: &R, + shape: Shape, +) -> Option { + rewrite + .rewrite(context, shape.offset_left(prefix.len())?) + .map(|r| format!("{}{}", prefix, r)) +} + +// FIXME: this is probably not correct for multi-line Rewrites. we should +// subtract suffix.len() from the last line budget, not the first! +pub(crate) fn rewrite_unary_suffix( + context: &RewriteContext<'_>, + suffix: &str, + rewrite: &R, + shape: Shape, +) -> Option { + rewrite + .rewrite(context, shape.sub_width(suffix.len())?) + .map(|mut r| { + r.push_str(suffix); + r + }) +} + +fn rewrite_unary_op( + context: &RewriteContext<'_>, + op: ast::UnOp, + expr: &ast::Expr, + shape: Shape, +) -> Option { + // For some reason, an UnOp is not spanned like BinOp! + rewrite_unary_prefix(context, ast::UnOp::to_string(op), expr, shape) +} + +pub(crate) enum RhsAssignKind<'ast> { + Expr(&'ast ast::ExprKind, Span), + Bounds, + Ty, +} + +impl<'ast> RhsAssignKind<'ast> { + // TODO(calebcartwright) + // Preemptive addition for handling RHS with chains, not yet utilized. + // It may make more sense to construct the chain first and then check + // whether there are actually chain elements. + #[allow(dead_code)] + fn is_chain(&self) -> bool { + match self { + RhsAssignKind::Expr(kind, _) => { + matches!( + kind, + ast::ExprKind::Try(..) + | ast::ExprKind::Field(..) + | ast::ExprKind::MethodCall(..) + | ast::ExprKind::Await(_, _) + ) + } + _ => false, + } + } +} + +fn rewrite_assignment( + context: &RewriteContext<'_>, + lhs: &ast::Expr, + rhs: &ast::Expr, + op: Option<&ast::BinOp>, + shape: Shape, +) -> Option { + let operator_str = match op { + Some(op) => context.snippet(op.span), + None => "=", + }; + + // 1 = space between lhs and operator. + let lhs_shape = shape.sub_width(operator_str.len() + 1)?; + let lhs_str = format!("{} {}", lhs.rewrite(context, lhs_shape)?, operator_str); + + rewrite_assign_rhs( + context, + lhs_str, + rhs, + &RhsAssignKind::Expr(&rhs.kind, rhs.span), + shape, + ) +} + +/// Controls where to put the rhs. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub(crate) enum RhsTactics { + /// Use heuristics. + Default, + /// Put the rhs on the next line if it uses multiple line, without extra indentation. + ForceNextLineWithoutIndent, + /// Allow overflowing max width if neither `Default` nor `ForceNextLineWithoutIndent` + /// did not work. + AllowOverflow, +} + +// The left hand side must contain everything up to, and including, the +// assignment operator. +pub(crate) fn rewrite_assign_rhs, R: Rewrite>( + context: &RewriteContext<'_>, + lhs: S, + ex: &R, + rhs_kind: &RhsAssignKind<'_>, + shape: Shape, +) -> Option { + rewrite_assign_rhs_with(context, lhs, ex, shape, rhs_kind, RhsTactics::Default) +} + +pub(crate) fn rewrite_assign_rhs_expr( + context: &RewriteContext<'_>, + lhs: &str, + ex: &R, + shape: Shape, + rhs_kind: &RhsAssignKind<'_>, + rhs_tactics: RhsTactics, +) -> Option { + let last_line_width = last_line_width(lhs).saturating_sub(if lhs.contains('\n') { + shape.indent.width() + } else { + 0 + }); + // 1 = space between operator and rhs. + let orig_shape = shape.offset_left(last_line_width + 1).unwrap_or(Shape { + width: 0, + offset: shape.offset + last_line_width + 1, + ..shape + }); + let has_rhs_comment = if let Some(offset) = lhs.find_last_uncommented("=") { + lhs.trim_end().len() > offset + 1 + } else { + false + }; + + choose_rhs( + context, + ex, + orig_shape, + ex.rewrite(context, orig_shape), + rhs_kind, + rhs_tactics, + has_rhs_comment, + ) +} + +pub(crate) fn rewrite_assign_rhs_with, R: Rewrite>( + context: &RewriteContext<'_>, + lhs: S, + ex: &R, + shape: Shape, + rhs_kind: &RhsAssignKind<'_>, + rhs_tactics: RhsTactics, +) -> Option { + let lhs = lhs.into(); + let rhs = rewrite_assign_rhs_expr(context, &lhs, ex, shape, rhs_kind, rhs_tactics)?; + Some(lhs + &rhs) +} + +pub(crate) fn rewrite_assign_rhs_with_comments, R: Rewrite>( + context: &RewriteContext<'_>, + lhs: S, + ex: &R, + shape: Shape, + rhs_kind: &RhsAssignKind<'_>, + rhs_tactics: RhsTactics, + between_span: Span, + allow_extend: bool, +) -> Option { + let lhs = lhs.into(); + let contains_comment = contains_comment(context.snippet(between_span)); + let shape = if contains_comment { + shape.block_left(context.config.tab_spaces())? + } else { + shape + }; + let rhs = rewrite_assign_rhs_expr(context, &lhs, ex, shape, rhs_kind, rhs_tactics)?; + + if contains_comment { + let rhs = rhs.trim_start(); + combine_strs_with_missing_comments(context, &lhs, rhs, between_span, shape, allow_extend) + } else { + Some(lhs + &rhs) + } +} + +fn choose_rhs( + context: &RewriteContext<'_>, + expr: &R, + shape: Shape, + orig_rhs: Option, + _rhs_kind: &RhsAssignKind<'_>, + rhs_tactics: RhsTactics, + has_rhs_comment: bool, +) -> Option { + match orig_rhs { + Some(ref new_str) if new_str.is_empty() => Some(String::new()), + Some(ref new_str) + if !new_str.contains('\n') && unicode_str_width(new_str) <= shape.width => + { + Some(format!(" {}", new_str)) + } + _ => { + // Expression did not fit on the same line as the identifier. + // Try splitting the line and see if that works better. + let new_shape = shape_from_rhs_tactic(context, shape, rhs_tactics)?; + let new_rhs = expr.rewrite(context, new_shape); + let new_indent_str = &shape + .indent + .block_indent(context.config) + .to_string_with_newline(context.config); + let before_space_str = if has_rhs_comment { "" } else { " " }; + + match (orig_rhs, new_rhs) { + (Some(ref orig_rhs), Some(ref new_rhs)) + if !filtered_str_fits(&new_rhs, context.config.max_width(), new_shape) => + { + Some(format!("{}{}", before_space_str, orig_rhs)) + } + (Some(ref orig_rhs), Some(ref new_rhs)) + if prefer_next_line(orig_rhs, new_rhs, rhs_tactics) => + { + Some(format!("{}{}", new_indent_str, new_rhs)) + } + (None, Some(ref new_rhs)) => Some(format!("{}{}", new_indent_str, new_rhs)), + (None, None) if rhs_tactics == RhsTactics::AllowOverflow => { + let shape = shape.infinite_width(); + expr.rewrite(context, shape) + .map(|s| format!("{}{}", before_space_str, s)) + } + (None, None) => None, + (Some(orig_rhs), _) => Some(format!("{}{}", before_space_str, orig_rhs)), + } + } + } +} + +fn shape_from_rhs_tactic( + context: &RewriteContext<'_>, + shape: Shape, + rhs_tactic: RhsTactics, +) -> Option { + match rhs_tactic { + RhsTactics::ForceNextLineWithoutIndent => shape + .with_max_width(context.config) + .sub_width(shape.indent.width()), + RhsTactics::Default | RhsTactics::AllowOverflow => { + Shape::indented(shape.indent.block_indent(context.config), context.config) + .sub_width(shape.rhs_overhead(context.config)) + } + } +} + +/// Returns true if formatting next_line_rhs is better on a new line when compared to the +/// original's line formatting. +/// +/// It is considered better if: +/// 1. the tactic is ForceNextLineWithoutIndent +/// 2. next_line_rhs doesn't have newlines +/// 3. the original line has more newlines than next_line_rhs +/// 4. the original formatting of the first line ends with `(`, `{`, or `[` and next_line_rhs +/// doesn't +pub(crate) fn prefer_next_line( + orig_rhs: &str, + next_line_rhs: &str, + rhs_tactics: RhsTactics, +) -> bool { + rhs_tactics == RhsTactics::ForceNextLineWithoutIndent + || !next_line_rhs.contains('\n') + || count_newlines(orig_rhs) > count_newlines(next_line_rhs) + 1 + || first_line_ends_with(orig_rhs, '(') && !first_line_ends_with(next_line_rhs, '(') + || first_line_ends_with(orig_rhs, '{') && !first_line_ends_with(next_line_rhs, '{') + || first_line_ends_with(orig_rhs, '[') && !first_line_ends_with(next_line_rhs, '[') +} + +fn rewrite_expr_addrof( + context: &RewriteContext<'_>, + borrow_kind: ast::BorrowKind, + mutability: ast::Mutability, + expr: &ast::Expr, + shape: Shape, +) -> Option { + let operator_str = match (mutability, borrow_kind) { + (ast::Mutability::Not, ast::BorrowKind::Ref) => "&", + (ast::Mutability::Not, ast::BorrowKind::Raw) => "&raw const ", + (ast::Mutability::Mut, ast::BorrowKind::Ref) => "&mut ", + (ast::Mutability::Mut, ast::BorrowKind::Raw) => "&raw mut ", + }; + rewrite_unary_prefix(context, operator_str, expr, shape) +} + +pub(crate) fn is_method_call(expr: &ast::Expr) -> bool { + match expr.kind { + ast::ExprKind::MethodCall(..) => true, + ast::ExprKind::AddrOf(_, _, ref expr) + | ast::ExprKind::Cast(ref expr, _) + | ast::ExprKind::Try(ref expr) + | ast::ExprKind::Unary(_, ref expr) => is_method_call(expr), + _ => false, + } +} + +#[cfg(test)] +mod test { + use super::last_line_offsetted; + + #[test] + fn test_last_line_offsetted() { + let lines = "one\n two"; + assert_eq!(last_line_offsetted(2, lines), true); + assert_eq!(last_line_offsetted(4, lines), false); + assert_eq!(last_line_offsetted(6, lines), false); + + let lines = "one two"; + assert_eq!(last_line_offsetted(2, lines), false); + assert_eq!(last_line_offsetted(0, lines), false); + + let lines = "\ntwo"; + assert_eq!(last_line_offsetted(2, lines), false); + assert_eq!(last_line_offsetted(0, lines), false); + + let lines = "one\n two three"; + assert_eq!(last_line_offsetted(2, lines), true); + let lines = "one\n two three"; + assert_eq!(last_line_offsetted(2, lines), false); + } +} diff --git a/src/format-diff/main.rs b/src/format-diff/main.rs new file mode 100644 index 000000000000..f6b739e1c2a3 --- /dev/null +++ b/src/format-diff/main.rs @@ -0,0 +1,281 @@ +// Inspired by Clang's clang-format-diff: +// +// https://github.com/llvm-mirror/clang/blob/master/tools/clang-format/clang-format-diff.py + +#![deny(warnings)] + +#[macro_use] +extern crate log; + +use serde::{Deserialize, Serialize}; +use serde_json as json; +use thiserror::Error; + +use std::collections::HashSet; +use std::env; +use std::ffi::OsStr; +use std::io::{self, BufRead}; +use std::process; + +use regex::Regex; + +use clap::{CommandFactory, Parser}; + +/// The default pattern of files to format. +/// +/// We only want to format rust files by default. +const DEFAULT_PATTERN: &str = r".*\.rs"; + +#[derive(Error, Debug)] +enum FormatDiffError { + #[error("{0}")] + IncorrectOptions(#[from] getopts::Fail), + #[error("{0}")] + IncorrectFilter(#[from] regex::Error), + #[error("{0}")] + IoError(#[from] io::Error), +} + +#[derive(Parser, Debug)] +#[clap( + name = "rustfmt-format-diff", + disable_version_flag = true, + next_line_help = true +)] +pub struct Opts { + /// Skip the smallest prefix containing NUMBER slashes + #[clap( + short = 'p', + long = "skip-prefix", + value_name = "NUMBER", + default_value = "0" + )] + skip_prefix: u32, + + /// Custom pattern selecting file paths to reformat + #[clap( + short = 'f', + long = "filter", + value_name = "PATTERN", + default_value = DEFAULT_PATTERN + )] + filter: String, +} + +fn main() { + env_logger::Builder::from_env("RUSTFMT_LOG").init(); + let opts = Opts::parse(); + if let Err(e) = run(opts) { + println!("{}", e); + Opts::command() + .print_help() + .expect("cannot write to stdout"); + process::exit(1); + } +} + +#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)] +struct Range { + file: String, + range: [u32; 2], +} + +fn run(opts: Opts) -> Result<(), FormatDiffError> { + let (files, ranges) = scan_diff(io::stdin(), opts.skip_prefix, &opts.filter)?; + run_rustfmt(&files, &ranges) +} + +fn run_rustfmt(files: &HashSet, ranges: &[Range]) -> Result<(), FormatDiffError> { + if files.is_empty() || ranges.is_empty() { + debug!("No files to format found"); + return Ok(()); + } + + let ranges_as_json = json::to_string(ranges).unwrap(); + + debug!("Files: {:?}", files); + debug!("Ranges: {:?}", ranges); + + let rustfmt_var = env::var_os("RUSTFMT"); + let rustfmt = match &rustfmt_var { + Some(rustfmt) => rustfmt, + None => OsStr::new("rustfmt"), + }; + let exit_status = process::Command::new(rustfmt) + .args(files) + .arg("--file-lines") + .arg(ranges_as_json) + .status()?; + + if !exit_status.success() { + return Err(FormatDiffError::IoError(io::Error::new( + io::ErrorKind::Other, + format!("rustfmt failed with {}", exit_status), + ))); + } + Ok(()) +} + +/// Scans a diff from `from`, and returns the set of files found, and the ranges +/// in those files. +fn scan_diff( + from: R, + skip_prefix: u32, + file_filter: &str, +) -> Result<(HashSet, Vec), FormatDiffError> +where + R: io::Read, +{ + let diff_pattern = format!(r"^\+\+\+\s(?:.*?/){{{}}}(\S*)", skip_prefix); + let diff_pattern = Regex::new(&diff_pattern).unwrap(); + + let lines_pattern = Regex::new(r"^@@.*\+(\d+)(,(\d+))?").unwrap(); + + let file_filter = Regex::new(&format!("^{}$", file_filter))?; + + let mut current_file = None; + + let mut files = HashSet::new(); + let mut ranges = vec![]; + for line in io::BufReader::new(from).lines() { + let line = line.unwrap(); + + if let Some(captures) = diff_pattern.captures(&line) { + current_file = Some(captures.get(1).unwrap().as_str().to_owned()); + } + + let file = match current_file { + Some(ref f) => &**f, + None => continue, + }; + + // FIXME(emilio): We could avoid this most of the time if needed, but + // it's not clear it's worth it. + if !file_filter.is_match(file) { + continue; + } + + let lines_captures = match lines_pattern.captures(&line) { + Some(captures) => captures, + None => continue, + }; + + let start_line = lines_captures + .get(1) + .unwrap() + .as_str() + .parse::() + .unwrap(); + let line_count = match lines_captures.get(3) { + Some(line_count) => line_count.as_str().parse::().unwrap(), + None => 1, + }; + + if line_count == 0 { + continue; + } + + let end_line = start_line + line_count - 1; + files.insert(file.to_owned()); + ranges.push(Range { + file: file.to_owned(), + range: [start_line, end_line], + }); + } + + Ok((files, ranges)) +} + +#[test] +fn scan_simple_git_diff() { + const DIFF: &str = include_str!("test/bindgen.diff"); + let (files, ranges) = scan_diff(DIFF.as_bytes(), 1, r".*\.rs").expect("scan_diff failed?"); + + assert!( + files.contains("src/ir/traversal.rs"), + "Should've matched the filter" + ); + + assert!( + !files.contains("tests/headers/anon_enum.hpp"), + "Shouldn't have matched the filter" + ); + + assert_eq!( + &ranges, + &[ + Range { + file: "src/ir/item.rs".to_owned(), + range: [148, 158], + }, + Range { + file: "src/ir/item.rs".to_owned(), + range: [160, 170], + }, + Range { + file: "src/ir/traversal.rs".to_owned(), + range: [9, 16], + }, + Range { + file: "src/ir/traversal.rs".to_owned(), + range: [35, 43], + }, + ] + ); +} + +#[cfg(test)] +mod cmd_line_tests { + use super::*; + + #[test] + fn default_options() { + let empty: Vec = vec![]; + let o = Opts::parse_from(&empty); + assert_eq!(DEFAULT_PATTERN, o.filter); + assert_eq!(0, o.skip_prefix); + } + + #[test] + fn good_options() { + let o = Opts::parse_from(&["test", "-p", "10", "-f", r".*\.hs"]); + assert_eq!(r".*\.hs", o.filter); + assert_eq!(10, o.skip_prefix); + } + + #[test] + fn unexpected_option() { + assert!( + Opts::command() + .try_get_matches_from(&["test", "unexpected"]) + .is_err() + ); + } + + #[test] + fn unexpected_flag() { + assert!( + Opts::command() + .try_get_matches_from(&["test", "--flag"]) + .is_err() + ); + } + + #[test] + fn overridden_option() { + assert!( + Opts::command() + .try_get_matches_from(&["test", "-p", "10", "-p", "20"]) + .is_err() + ); + } + + #[test] + fn negative_filter() { + assert!( + Opts::command() + .try_get_matches_from(&["test", "-p", "-1"]) + .is_err() + ); + } +} diff --git a/src/format-diff/test/bindgen.diff b/src/format-diff/test/bindgen.diff new file mode 100644 index 000000000000..d2fd379f4716 --- /dev/null +++ b/src/format-diff/test/bindgen.diff @@ -0,0 +1,67 @@ +diff --git a/src/ir/item.rs b/src/ir/item.rs +index 7f3afefb..90d15e96 100644 +--- a/src/ir/item.rs ++++ b/src/ir/item.rs +@@ -148,7 +148,11 @@ impl<'a, 'b> Iterator for ItemAncestorsIter<'a, 'b> + impl AsTemplateParam for ItemId { + type Extra = (); + +- fn as_template_param(&self, ctx: &BindgenContext, _: &()) -> Option { ++ fn as_template_param( ++ &self, ++ ctx: &BindgenContext, ++ _: &(), ++ ) -> Option { + ctx.resolve_item(*self).as_template_param(ctx, &()) + } + } +@@ -156,7 +160,11 @@ impl AsTemplateParam for ItemId { + impl AsTemplateParam for Item { + type Extra = (); + +- fn as_template_param(&self, ctx: &BindgenContext, _: &()) -> Option { ++ fn as_template_param( ++ &self, ++ ctx: &BindgenContext, ++ _: &(), ++ ) -> Option { + self.kind.as_template_param(ctx, self) + } + } +diff --git a/src/ir/traversal.rs b/src/ir/traversal.rs +index 762a3e2d..b9c9dd4e 100644 +--- a/src/ir/traversal.rs ++++ b/src/ir/traversal.rs +@@ -9,6 +9,8 @@ use std::collections::{BTreeMap, VecDeque}; + /// + /// from --> to + /// ++/// Random content to generate a diff. ++/// + /// The `from` is left implicit: it is the concrete `Trace` implementer which + /// yielded this outgoing edge. + #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +@@ -33,7 +35,9 @@ impl Into for Edge { + } + } + +-/// The kind of edge reference. This is useful when we wish to only consider ++/// The kind of edge reference. ++/// ++/// This is useful when we wish to only consider + /// certain kinds of edges for a particular traversal or analysis. + #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub enum EdgeKind { +diff --git a/tests/headers/anon_enum.hpp b/tests/headers/anon_enum.hpp +index 1961fe6c..34759df3 100644 +--- a/tests/headers/anon_enum.hpp ++++ b/tests/headers/anon_enum.hpp +@@ -1,7 +1,7 @@ + struct Test { + int foo; + float bar; +- enum { T_NONE }; ++ enum { T_NONE, T_SOME }; + }; + + typedef enum { diff --git a/src/format_report_formatter.rs b/src/format_report_formatter.rs new file mode 100644 index 000000000000..fd536d4df41a --- /dev/null +++ b/src/format_report_formatter.rs @@ -0,0 +1,150 @@ +use crate::formatting::FormattingError; +use crate::{ErrorKind, FormatReport}; +use annotate_snippets::display_list::{DisplayList, FormatOptions}; +use annotate_snippets::snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation}; +use std::fmt::{self, Display}; + +/// A builder for [`FormatReportFormatter`]. +pub struct FormatReportFormatterBuilder<'a> { + report: &'a FormatReport, + enable_colors: bool, +} + +impl<'a> FormatReportFormatterBuilder<'a> { + /// Creates a new [`FormatReportFormatterBuilder`]. + pub fn new(report: &'a FormatReport) -> Self { + Self { + report, + enable_colors: false, + } + } + + /// Enables colors and formatting in the output. + #[must_use] + pub fn enable_colors(self, enable_colors: bool) -> Self { + Self { + enable_colors, + ..self + } + } + + /// Creates a new [`FormatReportFormatter`] from the settings in this builder. + pub fn build(self) -> FormatReportFormatter<'a> { + FormatReportFormatter { + report: self.report, + enable_colors: self.enable_colors, + } + } +} + +/// Formats the warnings/errors in a [`FormatReport`]. +/// +/// Can be created using a [`FormatReportFormatterBuilder`]. +pub struct FormatReportFormatter<'a> { + report: &'a FormatReport, + enable_colors: bool, +} + +impl<'a> Display for FormatReportFormatter<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let errors_by_file = &self.report.internal.borrow().0; + + let opt = FormatOptions { + color: self.enable_colors, + ..Default::default() + }; + + for (file, errors) in errors_by_file { + for error in errors { + let error_kind = error.kind.to_string(); + let title = Some(Annotation { + id: if error.is_internal() { + Some("internal") + } else { + None + }, + label: Some(&error_kind), + annotation_type: error_kind_to_snippet_annotation_type(&error.kind), + }); + + let message_suffix = error.msg_suffix(); + let footer = if !message_suffix.is_empty() { + Some(Annotation { + id: None, + label: Some(message_suffix), + annotation_type: AnnotationType::Note, + }) + } else { + None + }; + + let origin = format!("{}:{}", file, error.line); + let slice = Slice { + source: &error.line_buffer.clone(), + line_start: error.line, + origin: Some(origin.as_str()), + fold: false, + annotations: slice_annotation(error).into_iter().collect(), + }; + + let snippet = Snippet { + title, + footer: footer.into_iter().collect(), + slices: vec![slice], + opt, + }; + writeln!(f, "{}\n", DisplayList::from(snippet))?; + } + } + + if !errors_by_file.is_empty() { + let label = format!( + "rustfmt has failed to format. See previous {} errors.", + self.report.warning_count() + ); + let snippet = Snippet { + title: Some(Annotation { + id: None, + label: Some(&label), + annotation_type: AnnotationType::Warning, + }), + footer: Vec::new(), + slices: Vec::new(), + opt, + }; + writeln!(f, "{}", DisplayList::from(snippet))?; + } + + Ok(()) + } +} + +fn slice_annotation(error: &FormattingError) -> Option> { + let (range_start, range_length) = error.format_len(); + let range_end = range_start + range_length; + + if range_length > 0 { + Some(SourceAnnotation { + annotation_type: AnnotationType::Error, + range: (range_start, range_end), + label: "", + }) + } else { + None + } +} + +fn error_kind_to_snippet_annotation_type(error_kind: &ErrorKind) -> AnnotationType { + match error_kind { + ErrorKind::LineOverflow(..) + | ErrorKind::TrailingWhitespace + | ErrorKind::IoError(_) + | ErrorKind::ModuleResolutionError(_) + | ErrorKind::ParseError + | ErrorKind::LostComment + | ErrorKind::BadAttr + | ErrorKind::InvalidGlobPattern(_) + | ErrorKind::VersionMismatch => AnnotationType::Error, + ErrorKind::DeprecatedAttr => AnnotationType::Warning, + } +} diff --git a/src/formatting.rs b/src/formatting.rs new file mode 100644 index 000000000000..1f4ad6960e20 --- /dev/null +++ b/src/formatting.rs @@ -0,0 +1,647 @@ +// High level formatting functions. + +use std::collections::HashMap; +use std::io::{self, Write}; +use std::time::{Duration, Instant}; + +use rustc_ast::ast; +use rustc_span::Span; + +use self::newline_style::apply_newline_style; +use crate::comment::{CharClasses, FullCodeCharKind}; +use crate::config::{Config, FileName, Verbosity}; +use crate::formatting::generated::is_generated_file; +use crate::modules::Module; +use crate::parse::parser::{DirectoryOwnership, Parser, ParserError}; +use crate::parse::session::ParseSess; +use crate::utils::{contains_skip, count_newlines}; +use crate::visitor::FmtVisitor; +use crate::{modules, source_file, ErrorKind, FormatReport, Input, Session}; + +mod generated; +mod newline_style; + +// A map of the files of a crate, with their new content +pub(crate) type SourceFile = Vec; +pub(crate) type FileRecord = (FileName, String); + +impl<'b, T: Write + 'b> Session<'b, T> { + pub(crate) fn format_input_inner( + &mut self, + input: Input, + is_macro_def: bool, + ) -> Result { + if !self.config.version_meets_requirement() { + return Err(ErrorKind::VersionMismatch); + } + + rustc_span::create_session_if_not_set_then(self.config.edition().into(), |_| { + if self.config.disable_all_formatting() { + // When the input is from stdin, echo back the input. + return match input { + Input::Text(ref buf) => echo_back_stdin(buf), + _ => Ok(FormatReport::new()), + }; + } + + let config = &self.config.clone(); + let format_result = format_project(input, config, self, is_macro_def); + + format_result.map(|report| { + self.errors.add(&report.internal.borrow().1); + report + }) + }) + } +} + +/// Determine if a module should be skipped. True if the module should be skipped, false otherwise. +fn should_skip_module( + config: &Config, + context: &FormatContext<'_, T>, + input_is_stdin: bool, + main_file: &FileName, + path: &FileName, + module: &Module<'_>, +) -> bool { + if contains_skip(module.attrs()) { + return true; + } + + if config.skip_children() && path != main_file { + return true; + } + + if !input_is_stdin && context.ignore_file(path) { + return true; + } + + // FIXME(calebcartwright) - we need to determine how we'll handle the + // `format_generated_files` option with stdin based input. + if !input_is_stdin && !config.format_generated_files() { + let source_file = context.parse_session.span_to_file_contents(module.span); + let src = source_file.src.as_ref().expect("SourceFile without src"); + + if is_generated_file(src) { + return true; + } + } + + false +} + +fn echo_back_stdin(input: &str) -> Result { + if let Err(e) = io::stdout().write_all(input.as_bytes()) { + return Err(From::from(e)); + } + Ok(FormatReport::new()) +} + +// Format an entire crate (or subset of the module tree). +fn format_project( + input: Input, + config: &Config, + handler: &mut T, + is_macro_def: bool, +) -> Result { + let mut timer = Timer::start(); + + let main_file = input.file_name(); + let input_is_stdin = main_file == FileName::Stdin; + + let parse_session = ParseSess::new(config)?; + if config.skip_children() && parse_session.ignore_file(&main_file) { + return Ok(FormatReport::new()); + } + + // Parse the crate. + let mut report = FormatReport::new(); + let directory_ownership = input.to_directory_ownership(); + let krate = match Parser::parse_crate(input, &parse_session) { + Ok(krate) => krate, + // Surface parse error via Session (errors are merged there from report) + Err(e) => { + let forbid_verbose = input_is_stdin || e != ParserError::ParsePanicError; + should_emit_verbose(forbid_verbose, config, || { + eprintln!("The Rust parser panicked"); + }); + report.add_parsing_error(); + return Ok(report); + } + }; + + let mut context = FormatContext::new(&krate, report, parse_session, config, handler); + let files = modules::ModResolver::new( + &context.parse_session, + directory_ownership.unwrap_or(DirectoryOwnership::UnownedViaBlock), + !input_is_stdin && !config.skip_children(), + ) + .visit_crate(&krate)? + .into_iter() + .filter(|(path, module)| { + input_is_stdin + || !should_skip_module(config, &context, input_is_stdin, &main_file, path, module) + }) + .collect::>(); + + timer = timer.done_parsing(); + + // Suppress error output if we have to do any further parsing. + context.parse_session.set_silent_emitter(); + + for (path, module) in files { + if input_is_stdin && contains_skip(module.attrs()) { + return echo_back_stdin( + context + .parse_session + .snippet_provider(module.span) + .entire_snippet(), + ); + } + should_emit_verbose(input_is_stdin, config, || println!("Formatting {}", path)); + context.format_file(path, &module, is_macro_def)?; + } + timer = timer.done_formatting(); + + should_emit_verbose(input_is_stdin, config, || { + println!( + "Spent {0:.3} secs in the parsing phase, and {1:.3} secs in the formatting phase", + timer.get_parse_time(), + timer.get_format_time(), + ) + }); + + Ok(context.report) +} + +// Used for formatting files. +struct FormatContext<'a, T: FormatHandler> { + krate: &'a ast::Crate, + report: FormatReport, + parse_session: ParseSess, + config: &'a Config, + handler: &'a mut T, +} + +impl<'a, T: FormatHandler + 'a> FormatContext<'a, T> { + fn new( + krate: &'a ast::Crate, + report: FormatReport, + parse_session: ParseSess, + config: &'a Config, + handler: &'a mut T, + ) -> Self { + FormatContext { + krate, + report, + parse_session, + config, + handler, + } + } + + fn ignore_file(&self, path: &FileName) -> bool { + self.parse_session.ignore_file(path) + } + + // Formats a single file/module. + fn format_file( + &mut self, + path: FileName, + module: &Module<'_>, + is_macro_def: bool, + ) -> Result<(), ErrorKind> { + let snippet_provider = self.parse_session.snippet_provider(module.span); + let mut visitor = FmtVisitor::from_parse_sess( + &self.parse_session, + self.config, + &snippet_provider, + self.report.clone(), + ); + visitor.skip_context.update_with_attrs(&self.krate.attrs); + visitor.is_macro_def = is_macro_def; + visitor.last_pos = snippet_provider.start_pos(); + visitor.skip_empty_lines(snippet_provider.end_pos()); + visitor.format_separate_mod(module, snippet_provider.end_pos()); + + debug_assert_eq!( + visitor.line_number, + count_newlines(&visitor.buffer), + "failed in format_file visitor.buffer:\n {:?}", + &visitor.buffer + ); + + // For some reason, the source_map does not include terminating + // newlines so we must add one on for each file. This is sad. + source_file::append_newline(&mut visitor.buffer); + + format_lines( + &mut visitor.buffer, + &path, + &visitor.skipped_range.borrow(), + self.config, + &self.report, + ); + + apply_newline_style( + self.config.newline_style(), + &mut visitor.buffer, + snippet_provider.entire_snippet(), + ); + + if visitor.macro_rewrite_failure { + self.report.add_macro_format_failure(); + } + self.report + .add_non_formatted_ranges(visitor.skipped_range.borrow().clone()); + + self.handler.handle_formatted_file( + &self.parse_session, + path, + visitor.buffer.to_owned(), + &mut self.report, + ) + } +} + +// Handle the results of formatting. +trait FormatHandler { + fn handle_formatted_file( + &mut self, + parse_session: &ParseSess, + path: FileName, + result: String, + report: &mut FormatReport, + ) -> Result<(), ErrorKind>; +} + +impl<'b, T: Write + 'b> FormatHandler for Session<'b, T> { + // Called for each formatted file. + fn handle_formatted_file( + &mut self, + parse_session: &ParseSess, + path: FileName, + result: String, + report: &mut FormatReport, + ) -> Result<(), ErrorKind> { + if let Some(ref mut out) = self.out { + match source_file::write_file( + Some(parse_session), + &path, + &result, + out, + &mut *self.emitter, + self.config.newline_style(), + ) { + Ok(ref result) if result.has_diff => report.add_diff(), + Err(e) => { + // Create a new error with path_str to help users see which files failed + let err_msg = format!("{}: {}", path, e); + return Err(io::Error::new(e.kind(), err_msg).into()); + } + _ => {} + } + } + + self.source_file.push((path, result)); + Ok(()) + } +} + +pub(crate) struct FormattingError { + pub(crate) line: usize, + pub(crate) kind: ErrorKind, + is_comment: bool, + is_string: bool, + pub(crate) line_buffer: String, +} + +impl FormattingError { + pub(crate) fn from_span( + span: Span, + parse_sess: &ParseSess, + kind: ErrorKind, + ) -> FormattingError { + FormattingError { + line: parse_sess.line_of_byte_pos(span.lo()), + is_comment: kind.is_comment(), + kind, + is_string: false, + line_buffer: parse_sess.span_to_first_line_string(span), + } + } + + pub(crate) fn is_internal(&self) -> bool { + match self.kind { + ErrorKind::LineOverflow(..) + | ErrorKind::TrailingWhitespace + | ErrorKind::IoError(_) + | ErrorKind::ParseError + | ErrorKind::LostComment => true, + _ => false, + } + } + + pub(crate) fn msg_suffix(&self) -> &str { + if self.is_comment || self.is_string { + "set `error_on_unformatted = false` to suppress \ + the warning against comments or string literals\n" + } else { + "" + } + } + + // (space, target) + pub(crate) fn format_len(&self) -> (usize, usize) { + match self.kind { + ErrorKind::LineOverflow(found, max) => (max, found - max), + ErrorKind::TrailingWhitespace + | ErrorKind::DeprecatedAttr + | ErrorKind::BadAttr + | ErrorKind::LostComment => { + let trailing_ws_start = self + .line_buffer + .rfind(|c: char| !c.is_whitespace()) + .map(|pos| pos + 1) + .unwrap_or(0); + ( + trailing_ws_start, + self.line_buffer.len() - trailing_ws_start, + ) + } + _ => unreachable!(), + } + } +} + +pub(crate) type FormatErrorMap = HashMap>; + +#[derive(Default, Debug, PartialEq)] +pub(crate) struct ReportedErrors { + // Encountered e.g., an IO error. + pub(crate) has_operational_errors: bool, + + // Failed to reformat code because of parsing errors. + pub(crate) has_parsing_errors: bool, + + // Code is valid, but it is impossible to format it properly. + pub(crate) has_formatting_errors: bool, + + // Code contains macro call that was unable to format. + pub(crate) has_macro_format_failure: bool, + + // Failed an opt-in checking. + pub(crate) has_check_errors: bool, + + /// Formatted code differs from existing code (--check only). + pub(crate) has_diff: bool, + + /// Formatted code missed something, like lost comments or extra trailing space + pub(crate) has_unformatted_code_errors: bool, +} + +impl ReportedErrors { + /// Combine two summaries together. + pub(crate) fn add(&mut self, other: &ReportedErrors) { + self.has_operational_errors |= other.has_operational_errors; + self.has_parsing_errors |= other.has_parsing_errors; + self.has_formatting_errors |= other.has_formatting_errors; + self.has_macro_format_failure |= other.has_macro_format_failure; + self.has_check_errors |= other.has_check_errors; + self.has_diff |= other.has_diff; + self.has_unformatted_code_errors |= other.has_unformatted_code_errors; + } +} + +#[derive(Clone, Copy, Debug)] +enum Timer { + Disabled, + Initialized(Instant), + DoneParsing(Instant, Instant), + DoneFormatting(Instant, Instant, Instant), +} + +impl Timer { + fn start() -> Timer { + if cfg!(target_arch = "wasm32") { + Timer::Disabled + } else { + Timer::Initialized(Instant::now()) + } + } + fn done_parsing(self) -> Self { + match self { + Timer::Disabled => Timer::Disabled, + Timer::Initialized(init_time) => Timer::DoneParsing(init_time, Instant::now()), + _ => panic!("Timer can only transition to DoneParsing from Initialized state"), + } + } + + fn done_formatting(self) -> Self { + match self { + Timer::Disabled => Timer::Disabled, + Timer::DoneParsing(init_time, parse_time) => { + Timer::DoneFormatting(init_time, parse_time, Instant::now()) + } + _ => panic!("Timer can only transition to DoneFormatting from DoneParsing state"), + } + } + + /// Returns the time it took to parse the source files in seconds. + fn get_parse_time(&self) -> f32 { + match *self { + Timer::Disabled => panic!("this platform cannot time execution"), + Timer::DoneParsing(init, parse_time) | Timer::DoneFormatting(init, parse_time, _) => { + // This should never underflow since `Instant::now()` guarantees monotonicity. + Self::duration_to_f32(parse_time.duration_since(init)) + } + Timer::Initialized(..) => unreachable!(), + } + } + + /// Returns the time it took to go from the parsed AST to the formatted output. Parsing time is + /// not included. + fn get_format_time(&self) -> f32 { + match *self { + Timer::Disabled => panic!("this platform cannot time execution"), + Timer::DoneFormatting(_init, parse_time, format_time) => { + Self::duration_to_f32(format_time.duration_since(parse_time)) + } + Timer::DoneParsing(..) | Timer::Initialized(..) => unreachable!(), + } + } + + fn duration_to_f32(d: Duration) -> f32 { + d.as_secs() as f32 + d.subsec_nanos() as f32 / 1_000_000_000f32 + } +} + +// Formatting done on a char by char or line by line basis. +// FIXME(#20): other stuff for parity with make tidy. +fn format_lines( + text: &mut String, + name: &FileName, + skipped_range: &[(usize, usize)], + config: &Config, + report: &FormatReport, +) { + let mut formatter = FormatLines::new(name, skipped_range, config); + formatter.iterate(text); + + if formatter.newline_count > 1 { + debug!("track truncate: {} {}", text.len(), formatter.newline_count); + let line = text.len() - formatter.newline_count + 1; + text.truncate(line); + } + + report.append(name.clone(), formatter.errors); +} + +struct FormatLines<'a> { + name: &'a FileName, + skipped_range: &'a [(usize, usize)], + last_was_space: bool, + line_len: usize, + cur_line: usize, + newline_count: usize, + errors: Vec, + line_buffer: String, + current_line_contains_string_literal: bool, + format_line: bool, + config: &'a Config, +} + +impl<'a> FormatLines<'a> { + fn new( + name: &'a FileName, + skipped_range: &'a [(usize, usize)], + config: &'a Config, + ) -> FormatLines<'a> { + FormatLines { + name, + skipped_range, + last_was_space: false, + line_len: 0, + cur_line: 1, + newline_count: 0, + errors: vec![], + line_buffer: String::with_capacity(config.max_width() * 2), + current_line_contains_string_literal: false, + format_line: config.file_lines().contains_line(name, 1), + config, + } + } + + // Iterate over the chars in the file map. + fn iterate(&mut self, text: &mut String) { + for (kind, c) in CharClasses::new(text.chars()) { + if c == '\r' { + continue; + } + + if c == '\n' { + self.new_line(kind); + } else { + self.char(c, kind); + } + } + } + + fn new_line(&mut self, kind: FullCodeCharKind) { + if self.format_line { + // Check for (and record) trailing whitespace. + if self.last_was_space { + if self.should_report_error(kind, &ErrorKind::TrailingWhitespace) + && !self.is_skipped_line() + { + self.push_err( + ErrorKind::TrailingWhitespace, + kind.is_comment(), + kind.is_string(), + ); + } + self.line_len -= 1; + } + + // Check for any line width errors we couldn't correct. + let error_kind = ErrorKind::LineOverflow(self.line_len, self.config.max_width()); + if self.line_len > self.config.max_width() + && !self.is_skipped_line() + && self.should_report_error(kind, &error_kind) + { + let is_string = self.current_line_contains_string_literal; + self.push_err(error_kind, kind.is_comment(), is_string); + } + } + + self.line_len = 0; + self.cur_line += 1; + self.format_line = self + .config + .file_lines() + .contains_line(self.name, self.cur_line); + self.newline_count += 1; + self.last_was_space = false; + self.line_buffer.clear(); + self.current_line_contains_string_literal = false; + } + + fn char(&mut self, c: char, kind: FullCodeCharKind) { + self.newline_count = 0; + self.line_len += if c == '\t' { + self.config.tab_spaces() + } else { + 1 + }; + self.last_was_space = c.is_whitespace(); + self.line_buffer.push(c); + if kind.is_string() { + self.current_line_contains_string_literal = true; + } + } + + fn push_err(&mut self, kind: ErrorKind, is_comment: bool, is_string: bool) { + self.errors.push(FormattingError { + line: self.cur_line, + kind, + is_comment, + is_string, + line_buffer: self.line_buffer.clone(), + }); + } + + fn should_report_error(&self, char_kind: FullCodeCharKind, error_kind: &ErrorKind) -> bool { + let allow_error_report = if char_kind.is_comment() + || self.current_line_contains_string_literal + || error_kind.is_comment() + { + self.config.error_on_unformatted() + } else { + true + }; + + match error_kind { + ErrorKind::LineOverflow(..) => { + self.config.error_on_line_overflow() && allow_error_report + } + ErrorKind::TrailingWhitespace | ErrorKind::LostComment => allow_error_report, + _ => true, + } + } + + /// Returns `true` if the line with the given line number was skipped by `#[rustfmt::skip]`. + fn is_skipped_line(&self) -> bool { + self.skipped_range + .iter() + .any(|&(lo, hi)| lo <= self.cur_line && self.cur_line <= hi) + } +} + +fn should_emit_verbose(forbid_verbose_output: bool, config: &Config, f: F) +where + F: Fn(), +{ + if config.verbose() == Verbosity::Verbose && !forbid_verbose_output { + f(); + } +} diff --git a/src/formatting/generated.rs b/src/formatting/generated.rs new file mode 100644 index 000000000000..58f43f17ee15 --- /dev/null +++ b/src/formatting/generated.rs @@ -0,0 +1,7 @@ +/// Returns `true` if the given span is a part of generated files. +pub(super) fn is_generated_file(original_snippet: &str) -> bool { + original_snippet + .lines() + .take(5) // looking for marker only in the beginning of the file + .any(|line| line.contains("@generated")) +} diff --git a/src/formatting/newline_style.rs b/src/formatting/newline_style.rs new file mode 100644 index 000000000000..97c4fc16d6f5 --- /dev/null +++ b/src/formatting/newline_style.rs @@ -0,0 +1,250 @@ +use crate::NewlineStyle; + +/// Apply this newline style to the formatted text. When the style is set +/// to `Auto`, the `raw_input_text` is used to detect the existing line +/// endings. +/// +/// If the style is set to `Auto` and `raw_input_text` contains no +/// newlines, the `Native` style will be used. +pub(crate) fn apply_newline_style( + newline_style: NewlineStyle, + formatted_text: &mut String, + raw_input_text: &str, +) { + *formatted_text = match effective_newline_style(newline_style, raw_input_text) { + EffectiveNewlineStyle::Windows => convert_to_windows_newlines(formatted_text), + EffectiveNewlineStyle::Unix => convert_to_unix_newlines(formatted_text), + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +enum EffectiveNewlineStyle { + Windows, + Unix, +} + +fn effective_newline_style( + newline_style: NewlineStyle, + raw_input_text: &str, +) -> EffectiveNewlineStyle { + match newline_style { + NewlineStyle::Auto => auto_detect_newline_style(raw_input_text), + NewlineStyle::Native => native_newline_style(), + NewlineStyle::Windows => EffectiveNewlineStyle::Windows, + NewlineStyle::Unix => EffectiveNewlineStyle::Unix, + } +} + +const LINE_FEED: char = '\n'; +const CARRIAGE_RETURN: char = '\r'; +const WINDOWS_NEWLINE: &str = "\r\n"; +const UNIX_NEWLINE: &str = "\n"; + +fn auto_detect_newline_style(raw_input_text: &str) -> EffectiveNewlineStyle { + let first_line_feed_pos = raw_input_text.chars().position(|ch| ch == LINE_FEED); + match first_line_feed_pos { + Some(first_line_feed_pos) => { + let char_before_line_feed_pos = first_line_feed_pos.saturating_sub(1); + let char_before_line_feed = raw_input_text.chars().nth(char_before_line_feed_pos); + match char_before_line_feed { + Some(CARRIAGE_RETURN) => EffectiveNewlineStyle::Windows, + _ => EffectiveNewlineStyle::Unix, + } + } + None => native_newline_style(), + } +} + +fn native_newline_style() -> EffectiveNewlineStyle { + if cfg!(windows) { + EffectiveNewlineStyle::Windows + } else { + EffectiveNewlineStyle::Unix + } +} + +fn convert_to_windows_newlines(formatted_text: &String) -> String { + let mut transformed = String::with_capacity(2 * formatted_text.capacity()); + let mut chars = formatted_text.chars().peekable(); + while let Some(current_char) = chars.next() { + let next_char = chars.peek(); + match current_char { + LINE_FEED => transformed.push_str(WINDOWS_NEWLINE), + CARRIAGE_RETURN if next_char == Some(&LINE_FEED) => {} + current_char => transformed.push(current_char), + } + } + transformed +} + +fn convert_to_unix_newlines(formatted_text: &str) -> String { + formatted_text.replace(WINDOWS_NEWLINE, UNIX_NEWLINE) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn auto_detects_unix_newlines() { + assert_eq!( + EffectiveNewlineStyle::Unix, + auto_detect_newline_style("One\nTwo\nThree") + ); + } + + #[test] + fn auto_detects_windows_newlines() { + assert_eq!( + EffectiveNewlineStyle::Windows, + auto_detect_newline_style("One\r\nTwo\r\nThree") + ); + } + + #[test] + fn auto_detects_windows_newlines_with_multibyte_char_on_first_line() { + assert_eq!( + EffectiveNewlineStyle::Windows, + auto_detect_newline_style("A 🎢 of a first line\r\nTwo\r\nThree") + ); + } + + #[test] + fn falls_back_to_native_newlines_if_no_newlines_are_found() { + let expected_newline_style = if cfg!(windows) { + EffectiveNewlineStyle::Windows + } else { + EffectiveNewlineStyle::Unix + }; + assert_eq!( + expected_newline_style, + auto_detect_newline_style("One Two Three") + ); + } + + #[test] + fn auto_detects_and_applies_unix_newlines() { + let formatted_text = "One\nTwo\nThree"; + let raw_input_text = "One\nTwo\nThree"; + + let mut out = String::from(formatted_text); + apply_newline_style(NewlineStyle::Auto, &mut out, raw_input_text); + assert_eq!("One\nTwo\nThree", &out, "auto should detect 'lf'"); + } + + #[test] + fn auto_detects_and_applies_windows_newlines() { + let formatted_text = "One\nTwo\nThree"; + let raw_input_text = "One\r\nTwo\r\nThree"; + + let mut out = String::from(formatted_text); + apply_newline_style(NewlineStyle::Auto, &mut out, raw_input_text); + assert_eq!("One\r\nTwo\r\nThree", &out, "auto should detect 'crlf'"); + } + + #[test] + fn auto_detects_and_applies_native_newlines() { + let formatted_text = "One\nTwo\nThree"; + let raw_input_text = "One Two Three"; + + let mut out = String::from(formatted_text); + apply_newline_style(NewlineStyle::Auto, &mut out, raw_input_text); + + if cfg!(windows) { + assert_eq!( + "One\r\nTwo\r\nThree", &out, + "auto-native-windows should detect 'crlf'" + ); + } else { + assert_eq!( + "One\nTwo\nThree", &out, + "auto-native-unix should detect 'lf'" + ); + } + } + + #[test] + fn applies_unix_newlines() { + test_newlines_are_applied_correctly( + "One\r\nTwo\nThree", + "One\nTwo\nThree", + NewlineStyle::Unix, + ); + } + + #[test] + fn applying_unix_newlines_changes_nothing_for_unix_newlines() { + let formatted_text = "One\nTwo\nThree"; + test_newlines_are_applied_correctly(formatted_text, formatted_text, NewlineStyle::Unix); + } + + #[test] + fn applies_unix_newlines_to_string_with_unix_and_windows_newlines() { + test_newlines_are_applied_correctly( + "One\r\nTwo\r\nThree\nFour", + "One\nTwo\nThree\nFour", + NewlineStyle::Unix, + ); + } + + #[test] + fn applies_windows_newlines_to_string_with_unix_and_windows_newlines() { + test_newlines_are_applied_correctly( + "One\nTwo\nThree\r\nFour", + "One\r\nTwo\r\nThree\r\nFour", + NewlineStyle::Windows, + ); + } + + #[test] + fn applying_windows_newlines_changes_nothing_for_windows_newlines() { + let formatted_text = "One\r\nTwo\r\nThree"; + test_newlines_are_applied_correctly(formatted_text, formatted_text, NewlineStyle::Windows); + } + + #[test] + fn keeps_carriage_returns_when_applying_windows_newlines_to_str_with_unix_newlines() { + test_newlines_are_applied_correctly( + "One\nTwo\nThree\rDrei", + "One\r\nTwo\r\nThree\rDrei", + NewlineStyle::Windows, + ); + } + + #[test] + fn keeps_carriage_returns_when_applying_unix_newlines_to_str_with_unix_newlines() { + test_newlines_are_applied_correctly( + "One\nTwo\nThree\rDrei", + "One\nTwo\nThree\rDrei", + NewlineStyle::Unix, + ); + } + + #[test] + fn keeps_carriage_returns_when_applying_windows_newlines_to_str_with_windows_newlines() { + test_newlines_are_applied_correctly( + "One\r\nTwo\r\nThree\rDrei", + "One\r\nTwo\r\nThree\rDrei", + NewlineStyle::Windows, + ); + } + + #[test] + fn keeps_carriage_returns_when_applying_unix_newlines_to_str_with_windows_newlines() { + test_newlines_are_applied_correctly( + "One\r\nTwo\r\nThree\rDrei", + "One\nTwo\nThree\rDrei", + NewlineStyle::Unix, + ); + } + + fn test_newlines_are_applied_correctly( + input: &str, + expected: &str, + newline_style: NewlineStyle, + ) { + let mut out = String::from(input); + apply_newline_style(newline_style, &mut out, input); + assert_eq!(expected, &out); + } +} diff --git a/src/git-rustfmt/main.rs b/src/git-rustfmt/main.rs new file mode 100644 index 000000000000..579778edbe74 --- /dev/null +++ b/src/git-rustfmt/main.rs @@ -0,0 +1,192 @@ +#[macro_use] +extern crate log; + +use std::env; +use std::io::stdout; +use std::path::{Path, PathBuf}; +use std::process::Command; +use std::str::FromStr; + +use getopts::{Matches, Options}; +use rustfmt_nightly as rustfmt; + +use crate::rustfmt::{load_config, CliOptions, FormatReportFormatterBuilder, Input, Session}; + +fn prune_files(files: Vec<&str>) -> Vec<&str> { + let prefixes: Vec<_> = files + .iter() + .filter(|f| f.ends_with("mod.rs") || f.ends_with("lib.rs")) + .map(|f| &f[..f.len() - 6]) + .collect(); + + let mut pruned_prefixes = vec![]; + for p1 in prefixes { + if p1.starts_with("src/bin/") || pruned_prefixes.iter().all(|p2| !p1.starts_with(p2)) { + pruned_prefixes.push(p1); + } + } + debug!("prefixes: {:?}", pruned_prefixes); + + files + .into_iter() + .filter(|f| { + if f.ends_with("mod.rs") || f.ends_with("lib.rs") || f.starts_with("src/bin/") { + return true; + } + pruned_prefixes.iter().all(|pp| !f.starts_with(pp)) + }) + .collect() +} + +fn git_diff(commits: &str) -> String { + let mut cmd = Command::new("git"); + cmd.arg("diff"); + if commits != "0" { + cmd.arg(format!("HEAD~{}", commits)); + } + let output = cmd.output().expect("Couldn't execute `git diff`"); + String::from_utf8_lossy(&output.stdout).into_owned() +} + +fn get_files(input: &str) -> Vec<&str> { + input + .lines() + .filter(|line| line.starts_with("+++ b/") && line.ends_with(".rs")) + .map(|line| &line[6..]) + .collect() +} + +fn fmt_files(files: &[&str]) -> i32 { + let (config, _) = + load_config::(Some(Path::new(".")), None).expect("couldn't load config"); + + let mut exit_code = 0; + let mut out = stdout(); + let mut session = Session::new(config, Some(&mut out)); + for file in files { + let report = session.format(Input::File(PathBuf::from(file))).unwrap(); + if report.has_warnings() { + eprintln!("{}", FormatReportFormatterBuilder::new(&report).build()); + } + if !session.has_no_errors() { + exit_code = 1; + } + } + exit_code +} + +struct NullOptions; + +impl CliOptions for NullOptions { + fn apply_to(self, _: &mut rustfmt::Config) { + unreachable!(); + } + fn config_path(&self) -> Option<&Path> { + unreachable!(); + } +} + +fn uncommitted_files() -> Vec { + let mut cmd = Command::new("git"); + cmd.arg("ls-files"); + cmd.arg("--others"); + cmd.arg("--modified"); + cmd.arg("--exclude-standard"); + let output = cmd.output().expect("Couldn't execute Git"); + let stdout = String::from_utf8_lossy(&output.stdout); + stdout + .lines() + .filter(|s| s.ends_with(".rs")) + .map(std::borrow::ToOwned::to_owned) + .collect() +} + +fn check_uncommitted() { + let uncommitted = uncommitted_files(); + debug!("uncommitted files: {:?}", uncommitted); + if !uncommitted.is_empty() { + println!("Found untracked changes:"); + for f in &uncommitted { + println!(" {}", f); + } + println!("Commit your work, or run with `-u`."); + println!("Exiting."); + std::process::exit(1); + } +} + +fn make_opts() -> Options { + let mut opts = Options::new(); + opts.optflag("h", "help", "show this message"); + opts.optflag("c", "check", "check only, don't format (unimplemented)"); + opts.optflag("u", "uncommitted", "format uncommitted files"); + opts +} + +struct Config { + commits: String, + uncommitted: bool, +} + +impl Config { + fn from_args(matches: &Matches, opts: &Options) -> Config { + // `--help` display help message and quit + if matches.opt_present("h") { + let message = format!( + "\nusage: {} [options]\n\n\ + commits: number of commits to format, default: 1", + env::args_os().next().unwrap().to_string_lossy() + ); + println!("{}", opts.usage(&message)); + std::process::exit(0); + } + + let mut config = Config { + commits: "1".to_owned(), + uncommitted: false, + }; + + if matches.opt_present("c") { + unimplemented!(); + } + + if matches.opt_present("u") { + config.uncommitted = true; + } + + if matches.free.len() > 1 { + panic!("unknown arguments, use `-h` for usage"); + } + if matches.free.len() == 1 { + let commits = matches.free[0].trim(); + if u32::from_str(commits).is_err() { + panic!("Couldn't parse number of commits"); + } + config.commits = commits.to_owned(); + } + + config + } +} + +fn main() { + env_logger::Builder::from_env("RUSTFMT_LOG").init(); + + let opts = make_opts(); + let matches = opts + .parse(env::args().skip(1)) + .expect("Couldn't parse command line"); + let config = Config::from_args(&matches, &opts); + + if !config.uncommitted { + check_uncommitted(); + } + + let stdout = git_diff(&config.commits); + let files = get_files(&stdout); + debug!("files: {:?}", files); + let files = prune_files(files); + debug!("pruned files: {:?}", files); + let exit_code = fmt_files(&files); + std::process::exit(exit_code); +} diff --git a/src/ignore_path.rs b/src/ignore_path.rs new file mode 100644 index 000000000000..d955949496a6 --- /dev/null +++ b/src/ignore_path.rs @@ -0,0 +1,52 @@ +use ignore::{self, gitignore}; + +use crate::config::{FileName, IgnoreList}; + +pub(crate) struct IgnorePathSet { + ignore_set: gitignore::Gitignore, +} + +impl IgnorePathSet { + pub(crate) fn from_ignore_list(ignore_list: &IgnoreList) -> Result { + let mut ignore_builder = gitignore::GitignoreBuilder::new(ignore_list.rustfmt_toml_path()); + + for ignore_path in ignore_list { + ignore_builder.add_line(None, ignore_path.to_str().unwrap())?; + } + + Ok(IgnorePathSet { + ignore_set: ignore_builder.build()?, + }) + } + + pub(crate) fn is_match(&self, file_name: &FileName) -> bool { + match file_name { + FileName::Stdin => false, + FileName::Real(p) => self + .ignore_set + .matched_path_or_any_parents(p, false) + .is_ignore(), + } + } +} + +#[cfg(test)] +mod test { + 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(); + + assert!(ignore_path_set.is_match(&FileName::Real(PathBuf::from("src/foo.rs")))); + assert!(ignore_path_set.is_match(&FileName::Real(PathBuf::from("bar_dir/baz.rs")))); + assert!(!ignore_path_set.is_match(&FileName::Real(PathBuf::from("src/bar.rs")))); + } +} diff --git a/src/imports.rs b/src/imports.rs new file mode 100644 index 000000000000..339e5cef5af9 --- /dev/null +++ b/src/imports.rs @@ -0,0 +1,1506 @@ +use std::borrow::Cow; +use std::cmp::Ordering; +use std::fmt; + +use core::hash::{Hash, Hasher}; + +use itertools::Itertools; + +use rustc_ast::ast::{self, UseTreeKind}; +use rustc_span::{ + symbol::{self, sym}, + BytePos, Span, DUMMY_SP, +}; + +use crate::comment::combine_strs_with_missing_comments; +use crate::config::lists::*; +use crate::config::ImportGranularity; +use crate::config::{Edition, IndentStyle, Version}; +use crate::lists::{ + definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator, +}; +use crate::rewrite::{Rewrite, RewriteContext}; +use crate::shape::Shape; +use crate::source_map::SpanUtils; +use crate::spanned::Spanned; +use crate::utils::{is_same_visibility, mk_sp, rewrite_ident}; +use crate::visitor::FmtVisitor; + +/// Returns a name imported by a `use` declaration. +/// E.g., returns `Ordering` for `std::cmp::Ordering` and `self` for `std::cmp::self`. +pub(crate) fn path_to_imported_ident(path: &ast::Path) -> symbol::Ident { + path.segments.last().unwrap().ident +} + +impl<'a> FmtVisitor<'a> { + pub(crate) fn format_import(&mut self, item: &ast::Item, tree: &ast::UseTree) { + let span = item.span(); + let shape = self.shape(); + let rw = UseTree::from_ast( + &self.get_context(), + tree, + None, + Some(item.vis.clone()), + Some(item.span.lo()), + Some(item.attrs.clone()), + ) + .rewrite_top_level(&self.get_context(), shape); + match rw { + Some(ref s) if s.is_empty() => { + // Format up to last newline + let prev_span = mk_sp(self.last_pos, source!(self, span).lo()); + let trimmed_snippet = self.snippet(prev_span).trim_end(); + let span_end = self.last_pos + BytePos(trimmed_snippet.len() as u32); + self.format_missing(span_end); + // We have an excessive newline from the removed import. + if self.buffer.ends_with('\n') { + self.buffer.pop(); + self.line_number -= 1; + } + self.last_pos = source!(self, span).hi(); + } + Some(ref s) => { + self.format_missing_with_indent(source!(self, span).lo()); + self.push_str(s); + self.last_pos = source!(self, span).hi(); + } + None => { + self.format_missing_with_indent(source!(self, span).lo()); + self.format_missing(source!(self, span).hi()); + } + } + } +} + +// Ordering of imports + +// We order imports by translating to our own representation and then sorting. +// The Rust AST data structures are really bad for this. Rustfmt applies a bunch +// of normalisations to imports and since we want to sort based on the result +// of these (and to maintain idempotence) we must apply the same normalisations +// to the data structures for sorting. +// +// We sort `self` and `super` before other imports, then identifier imports, +// then glob imports, then lists of imports. We do not take aliases into account +// when ordering unless the imports are identical except for the alias (rare in +// practice). + +// FIXME(#2531): we should unify the comparison code here with the formatting +// code elsewhere since we are essentially string-ifying twice. Furthermore, by +// parsing to our own format on comparison, we repeat a lot of work when +// sorting. + +// FIXME we do a lot of allocation to make our own representation. +#[derive(Clone, Eq, Hash, PartialEq)] +pub(crate) enum UseSegmentKind { + Ident(String, Option), + Slf(Option), + Super(Option), + Crate(Option), + Glob, + List(Vec), +} + +#[derive(Clone, Eq, PartialEq)] +pub(crate) struct UseSegment { + pub(crate) kind: UseSegmentKind, + pub(crate) version: Version, +} + +#[derive(Clone)] +pub(crate) struct UseTree { + pub(crate) path: Vec, + pub(crate) span: Span, + // Comment information within nested use tree. + pub(crate) list_item: Option, + // Additional fields for top level use items. + // Should we have another struct for top-level use items rather than reusing this? + visibility: Option, + attrs: Option, +} + +impl PartialEq for UseTree { + fn eq(&self, other: &UseTree) -> bool { + self.path == other.path + } +} +impl Eq for UseTree {} + +impl Spanned for UseTree { + fn span(&self) -> Span { + let lo = if let Some(ref attrs) = self.attrs { + attrs.iter().next().map_or(self.span.lo(), |a| a.span.lo()) + } else { + self.span.lo() + }; + mk_sp(lo, self.span.hi()) + } +} + +impl UseSegment { + // Clone a version of self with any top-level alias removed. + fn remove_alias(&self) -> UseSegment { + let kind = match self.kind { + UseSegmentKind::Ident(ref s, _) => UseSegmentKind::Ident(s.clone(), None), + UseSegmentKind::Slf(_) => UseSegmentKind::Slf(None), + UseSegmentKind::Super(_) => UseSegmentKind::Super(None), + UseSegmentKind::Crate(_) => UseSegmentKind::Crate(None), + _ => return self.clone(), + }; + UseSegment { + kind, + version: self.version, + } + } + + // Check if self == other with their aliases removed. + fn equal_except_alias(&self, other: &Self) -> bool { + match (&self.kind, &other.kind) { + (UseSegmentKind::Ident(ref s1, _), UseSegmentKind::Ident(ref s2, _)) => s1 == s2, + (UseSegmentKind::Slf(_), UseSegmentKind::Slf(_)) + | (UseSegmentKind::Super(_), UseSegmentKind::Super(_)) + | (UseSegmentKind::Crate(_), UseSegmentKind::Crate(_)) + | (UseSegmentKind::Glob, UseSegmentKind::Glob) => true, + (UseSegmentKind::List(ref list1), UseSegmentKind::List(ref list2)) => list1 == list2, + _ => false, + } + } + + fn get_alias(&self) -> Option<&str> { + match &self.kind { + UseSegmentKind::Ident(_, a) + | UseSegmentKind::Slf(a) + | UseSegmentKind::Super(a) + | UseSegmentKind::Crate(a) => a.as_deref(), + _ => None, + } + } + + fn from_path_segment( + context: &RewriteContext<'_>, + path_seg: &ast::PathSegment, + modsep: bool, + ) -> Option { + let name = rewrite_ident(context, path_seg.ident); + if name.is_empty() || name == "{{root}}" { + return None; + } + let kind = match name { + "self" => UseSegmentKind::Slf(None), + "super" => UseSegmentKind::Super(None), + "crate" => UseSegmentKind::Crate(None), + _ => { + let mod_sep = if modsep { "::" } else { "" }; + UseSegmentKind::Ident(format!("{}{}", mod_sep, name), None) + } + }; + + Some(UseSegment { + kind, + version: context.config.version(), + }) + } + + fn contains_comment(&self) -> bool { + if let UseSegmentKind::List(list) = &self.kind { + list.iter().any(|subtree| subtree.contains_comment()) + } else { + false + } + } +} + +pub(crate) fn normalize_use_trees_with_granularity( + use_trees: Vec, + import_granularity: ImportGranularity, +) -> Vec { + let merge_by = match import_granularity { + ImportGranularity::Item => return flatten_use_trees(use_trees, ImportGranularity::Item), + ImportGranularity::Preserve => return use_trees, + ImportGranularity::Crate => SharedPrefix::Crate, + ImportGranularity::Module => SharedPrefix::Module, + ImportGranularity::One => SharedPrefix::One, + }; + + let mut result = Vec::with_capacity(use_trees.len()); + for use_tree in use_trees { + if use_tree.contains_comment() || use_tree.attrs.is_some() { + result.push(use_tree); + continue; + } + + for mut flattened in use_tree.flatten(import_granularity) { + 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); + } + } + } + result +} + +fn flatten_use_trees( + use_trees: Vec, + import_granularity: ImportGranularity, +) -> Vec { + // Return non-sorted single occurrence of the use-trees text string; + // order is by first occurrence of the use-tree. + use_trees + .into_iter() + .flat_map(|tree| tree.flatten(import_granularity)) + .map(UseTree::nest_trailing_self) + .unique() + .collect() +} + +impl fmt::Debug for UseTree { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl fmt::Debug for UseSegment { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.kind, f) + } +} + +impl fmt::Display for UseSegment { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.kind, f) + } +} + +impl Hash for UseSegment { + fn hash(&self, state: &mut H) { + self.kind.hash(state); + } +} + +impl fmt::Debug for UseSegmentKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl fmt::Display for UseSegmentKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + UseSegmentKind::Glob => write!(f, "*"), + UseSegmentKind::Ident(ref s, Some(ref alias)) => write!(f, "{} as {}", s, alias), + UseSegmentKind::Ident(ref s, None) => write!(f, "{}", s), + UseSegmentKind::Slf(..) => write!(f, "self"), + UseSegmentKind::Super(..) => write!(f, "super"), + UseSegmentKind::Crate(..) => write!(f, "crate"), + UseSegmentKind::List(ref list) => { + write!(f, "{{")?; + for (i, item) in list.iter().enumerate() { + if i != 0 { + write!(f, ", ")?; + } + write!(f, "{}", item)?; + } + write!(f, "}}") + } + } + } +} +impl fmt::Display for UseTree { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for (i, segment) in self.path.iter().enumerate() { + if i != 0 { + write!(f, "::")?; + } + write!(f, "{}", segment)?; + } + Ok(()) + } +} + +impl UseTree { + // Rewrite use tree with `use ` and a trailing `;`. + pub(crate) fn rewrite_top_level( + &self, + context: &RewriteContext<'_>, + shape: Shape, + ) -> Option { + let vis = self.visibility.as_ref().map_or(Cow::from(""), |vis| { + crate::utils::format_visibility(context, vis) + }); + let use_str = self + .rewrite(context, shape.offset_left(vis.len())?) + .map(|s| { + if s.is_empty() { + s + } else { + format!("{}use {};", vis, s) + } + })?; + match self.attrs { + Some(ref attrs) if !attrs.is_empty() => { + let attr_str = attrs.rewrite(context, shape)?; + let lo = attrs.last().as_ref()?.span.hi(); + let hi = self.span.lo(); + let span = mk_sp(lo, hi); + + let allow_extend = if attrs.len() == 1 { + let line_len = attr_str.len() + 1 + use_str.len(); + !attrs.first().unwrap().is_doc_comment() + && context.config.inline_attribute_width() >= line_len + } else { + false + }; + + combine_strs_with_missing_comments( + context, + &attr_str, + &use_str, + span, + shape, + allow_extend, + ) + } + _ => Some(use_str), + } + } + + // FIXME: Use correct span? + // The given span is essentially incorrect, since we are reconstructing + // use-statements. This should not be a problem, though, since we have + // already tried to extract comment and observed that there are no comment + // around the given use item, and the span will not be used afterward. + fn from_path(path: Vec, span: Span) -> UseTree { + UseTree { + path, + span, + list_item: None, + visibility: None, + attrs: None, + } + } + + pub(crate) fn from_ast_with_normalization( + context: &RewriteContext<'_>, + item: &ast::Item, + ) -> Option { + match item.kind { + ast::ItemKind::Use(ref use_tree) => Some( + UseTree::from_ast( + context, + use_tree, + None, + Some(item.vis.clone()), + Some(item.span.lo()), + if item.attrs.is_empty() { + None + } else { + Some(item.attrs.clone()) + }, + ) + .normalize(), + ), + _ => None, + } + } + + fn from_ast( + context: &RewriteContext<'_>, + a: &ast::UseTree, + list_item: Option, + visibility: Option, + opt_lo: Option, + attrs: Option, + ) -> UseTree { + let span = if let Some(lo) = opt_lo { + mk_sp(lo, a.span.hi()) + } else { + a.span + }; + let mut result = UseTree { + path: vec![], + span, + list_item, + visibility, + attrs, + }; + + let leading_modsep = + context.config.edition() >= Edition::Edition2018 && a.prefix.is_global(); + + let mut modsep = leading_modsep; + + for p in &a.prefix.segments { + if let Some(use_segment) = UseSegment::from_path_segment(context, p, modsep) { + result.path.push(use_segment); + modsep = false; + } + } + + let version = context.config.version(); + + match a.kind { + UseTreeKind::Glob => { + // in case of a global path and the glob starts at the root, e.g., "::*" + if a.prefix.segments.len() == 1 && leading_modsep { + let kind = UseSegmentKind::Ident("".to_owned(), None); + result.path.push(UseSegment { kind, version }); + } + result.path.push(UseSegment { + kind: UseSegmentKind::Glob, + version, + }); + } + UseTreeKind::Nested(ref list) => { + // Extract comments between nested use items. + // This needs to be done before sorting use items. + let items = itemize_list( + context.snippet_provider, + list.iter().map(|(tree, _)| tree), + "}", + ",", + |tree| tree.span.lo(), + |tree| tree.span.hi(), + |_| Some("".to_owned()), // We only need comments for now. + context.snippet_provider.span_after(a.span, "{"), + a.span.hi(), + false, + ); + + // in case of a global path and the nested list starts at the root, + // e.g., "::{foo, bar}" + if a.prefix.segments.len() == 1 && leading_modsep { + let kind = UseSegmentKind::Ident("".to_owned(), None); + result.path.push(UseSegment { kind, version }); + } + let kind = UseSegmentKind::List( + list.iter() + .zip(items) + .map(|(t, list_item)| { + Self::from_ast(context, &t.0, Some(list_item), None, None, None) + }) + .collect(), + ); + result.path.push(UseSegment { kind, version }); + } + UseTreeKind::Simple(ref rename) => { + // If the path has leading double colons and is composed of only 2 segments, then we + // bypass the call to path_to_imported_ident which would get only the ident and + // lose the path root, e.g., `that` in `::that`. + // The span of `a.prefix` contains the leading colons. + let name = if a.prefix.segments.len() == 2 && leading_modsep { + context.snippet(a.prefix.span).to_owned() + } else { + rewrite_ident(context, path_to_imported_ident(&a.prefix)).to_owned() + }; + let alias = rename.and_then(|ident| { + if ident.name == sym::underscore_imports { + // for impl-only-use + Some("_".to_owned()) + } else if ident == path_to_imported_ident(&a.prefix) { + None + } else { + Some(rewrite_ident(context, ident).to_owned()) + } + }); + let kind = match name.as_ref() { + "self" => UseSegmentKind::Slf(alias), + "super" => UseSegmentKind::Super(alias), + "crate" => UseSegmentKind::Crate(alias), + _ => UseSegmentKind::Ident(name, alias), + }; + + let segment = UseSegment { kind, version }; + + // `name` is already in result. + result.path.pop(); + result.path.push(segment); + } + } + result + } + + // Do the adjustments that rustfmt does elsewhere to use paths. + pub(crate) fn normalize(mut self) -> UseTree { + let mut last = self.path.pop().expect("Empty use tree?"); + // Hack around borrow checker. + let mut normalize_sole_list = false; + let mut aliased_self = false; + + // Remove foo::{} or self without attributes. + match last.kind { + _ if self.attrs.is_some() => (), + UseSegmentKind::List(ref list) if list.is_empty() => { + self.path = vec![]; + return self; + } + UseSegmentKind::Slf(None) if self.path.is_empty() && self.visibility.is_some() => { + self.path = vec![]; + return self; + } + _ => (), + } + + // Normalise foo::self -> foo. + if let UseSegmentKind::Slf(None) = last.kind { + if !self.path.is_empty() { + return self; + } + } + + // Normalise foo::self as bar -> foo as bar. + if let UseSegmentKind::Slf(_) = last.kind { + if let Some(UseSegment { + kind: UseSegmentKind::Ident(_, None), + .. + }) = self.path.last() + { + aliased_self = true; + } + } + + let mut done = false; + if aliased_self { + match self.path.last_mut() { + Some(UseSegment { + kind: UseSegmentKind::Ident(_, ref mut old_rename), + .. + }) => { + assert!(old_rename.is_none()); + if let UseSegmentKind::Slf(Some(rename)) = last.clone().kind { + *old_rename = Some(rename); + done = true; + } + } + _ => unreachable!(), + } + } + + if done { + return self; + } + + // Normalise foo::{bar} -> foo::bar + if let UseSegmentKind::List(ref list) = last.kind { + if list.len() == 1 && list[0].to_string() != "self" { + normalize_sole_list = true; + } + } + + if normalize_sole_list { + match last.kind { + UseSegmentKind::List(list) => { + for seg in &list[0].path { + self.path.push(seg.clone()); + } + return self.normalize(); + } + _ => unreachable!(), + } + } + + // Recursively normalize elements of a list use (including sorting the list). + if let UseSegmentKind::List(list) = last.kind { + let mut list = list.into_iter().map(UseTree::normalize).collect::>(); + list.sort(); + last = UseSegment { + kind: UseSegmentKind::List(list), + version: last.version, + }; + } + + self.path.push(last); + self + } + + fn has_comment(&self) -> bool { + self.list_item.as_ref().map_or(false, ListItem::has_comment) + } + + fn contains_comment(&self) -> bool { + self.has_comment() || self.path.iter().any(|path| path.contains_comment()) + } + + fn same_visibility(&self, other: &UseTree) -> bool { + match (&self.visibility, &other.visibility) { + ( + Some(ast::Visibility { + kind: ast::VisibilityKind::Inherited, + .. + }), + None, + ) + | ( + None, + Some(ast::Visibility { + kind: ast::VisibilityKind::Inherited, + .. + }), + ) + | (None, None) => true, + (Some(ref a), Some(ref b)) => is_same_visibility(a, b), + _ => false, + } + } + + fn share_prefix(&self, other: &UseTree, shared_prefix: SharedPrefix) -> bool { + if self.path.is_empty() + || other.path.is_empty() + || self.attrs.is_some() + || self.contains_comment() + || !self.same_visibility(other) + { + false + } else { + match shared_prefix { + SharedPrefix::Crate => self.path[0] == other.path[0], + SharedPrefix::Module => { + self.path[..self.path.len() - 1] == other.path[..other.path.len() - 1] + } + SharedPrefix::One => true, + } + } + } + + fn flatten(self, import_granularity: ImportGranularity) -> Vec { + if self.path.is_empty() || self.contains_comment() { + return vec![self]; + } + match &self.path.clone().last().unwrap().kind { + UseSegmentKind::List(list) => { + if list.len() == 1 && list[0].path.len() == 1 { + if let UseSegmentKind::Slf(..) = list[0].path[0].kind { + return vec![self]; + }; + } + let prefix = &self.path[..self.path.len() - 1]; + let mut result = vec![]; + for nested_use_tree in list { + for flattend in &mut nested_use_tree.clone().flatten(import_granularity) { + let mut new_path = prefix.to_vec(); + new_path.append(&mut flattend.path); + result.push(UseTree { + path: new_path, + span: self.span, + list_item: None, + visibility: self.visibility.clone(), + // only retain attributes for `ImportGranularity::Item` + attrs: match import_granularity { + ImportGranularity::Item => self.attrs.clone(), + _ => None, + }, + }); + } + } + + result + } + _ => vec![self], + } + } + + fn merge(&mut self, other: &UseTree, merge_by: SharedPrefix) { + let mut prefix = 0; + for (a, b) in self.path.iter().zip(other.path.iter()) { + // only discard the alias at the root of the tree + if (prefix == 0 && a.equal_except_alias(b)) || a == b { + prefix += 1; + } else { + break; + } + } + if let Some(new_path) = merge_rest(&self.path, &other.path, prefix, merge_by) { + self.path = new_path; + 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 { + kind: UseSegmentKind::Slf(..), + .. + }) = self.path.last() + { + let self_segment = self.path.pop().unwrap(); + let version = self_segment.version; + let kind = UseSegmentKind::List(vec![UseTree::from_path(vec![self_segment], DUMMY_SP)]); + self.path.push(UseSegment { kind, version }); + } + self + } +} + +fn merge_rest( + a: &[UseSegment], + b: &[UseSegment], + mut len: usize, + merge_by: SharedPrefix, +) -> Option> { + if a.len() == len && b.len() == len { + return None; + } + if a.len() != len && b.len() != len { + let version = a[len].version; + if let UseSegmentKind::List(ref list) = a[len].kind { + let mut list = list.clone(); + merge_use_trees_inner( + &mut list, + UseTree::from_path(b[len..].to_vec(), DUMMY_SP), + merge_by, + ); + let mut new_path = b[..len].to_vec(); + let kind = UseSegmentKind::List(list); + new_path.push(UseSegment { kind, version }); + return Some(new_path); + } + } else if len == 1 { + let (common, rest) = if a.len() == len { + (&a[0], &b[1..]) + } else { + (&b[0], &a[1..]) + }; + let kind = UseSegmentKind::Slf(common.get_alias().map(ToString::to_string)); + let version = a[0].version; + let mut list = vec![UseTree::from_path( + vec![UseSegment { kind, version }], + DUMMY_SP, + )]; + match rest { + [ + UseSegment { + kind: UseSegmentKind::List(rest_list), + .. + }, + ] => list.extend(rest_list.clone()), + _ => list.push(UseTree::from_path(rest.to_vec(), DUMMY_SP)), + } + return Some(vec![ + b[0].clone(), + UseSegment { + kind: UseSegmentKind::List(list), + version, + }, + ]); + } else { + len -= 1; + } + let mut list = vec![ + UseTree::from_path(a[len..].to_vec(), DUMMY_SP), + UseTree::from_path(b[len..].to_vec(), DUMMY_SP), + ]; + list.sort(); + let mut new_path = b[..len].to_vec(); + let kind = UseSegmentKind::List(list); + let version = a[0].version; + new_path.push(UseSegment { kind, version }); + Some(new_path) +} + +fn merge_use_trees_inner(trees: &mut Vec, use_tree: UseTree, merge_by: SharedPrefix) { + struct SimilarTree<'a> { + similarity: usize, + path_len: usize, + tree: &'a mut UseTree, + } + + let similar_trees = trees.iter_mut().filter_map(|tree| { + if tree.share_prefix(&use_tree, merge_by) { + // In the case of `SharedPrefix::One`, `similarity` is used for deciding with which + // tree `use_tree` should be merge. + // In other cases `similarity` won't be used, so set it to `0` as a dummy value. + let similarity = if merge_by == SharedPrefix::One { + tree.path + .iter() + .zip(&use_tree.path) + .take_while(|(a, b)| a.equal_except_alias(b)) + .count() + } else { + 0 + }; + + let path_len = tree.path.len(); + Some(SimilarTree { + similarity, + tree, + path_len, + }) + } else { + None + } + }); + + if use_tree.path.len() == 1 && merge_by == SharedPrefix::Crate { + if let Some(tree) = similar_trees.min_by_key(|tree| tree.path_len) { + if tree.path_len == 1 { + return; + } + } + } else if merge_by == SharedPrefix::One { + if let Some(sim_tree) = similar_trees.max_by_key(|tree| tree.similarity) { + if sim_tree.similarity > 0 { + sim_tree.tree.merge(&use_tree, merge_by); + return; + } + } + } else if let Some(sim_tree) = similar_trees.max_by_key(|tree| tree.path_len) { + if sim_tree.path_len > 1 { + sim_tree.tree.merge(&use_tree, merge_by); + return; + } + } + trees.push(use_tree); + trees.sort(); +} + +impl Hash for UseTree { + fn hash(&self, state: &mut H) { + self.path.hash(state); + } +} + +impl PartialOrd for UseSegment { + fn partial_cmp(&self, other: &UseSegment) -> Option { + Some(self.cmp(other)) + } +} +impl PartialOrd for UseTree { + fn partial_cmp(&self, other: &UseTree) -> Option { + Some(self.cmp(other)) + } +} +impl Ord for UseSegment { + fn cmp(&self, other: &UseSegment) -> Ordering { + use self::UseSegmentKind::*; + + fn is_upper_snake_case(s: &str) -> bool { + s.chars() + .all(|c| c.is_uppercase() || c == '_' || c.is_numeric()) + } + + match (&self.kind, &other.kind) { + (Slf(ref a), Slf(ref b)) + | (Super(ref a), Super(ref b)) + | (Crate(ref a), Crate(ref b)) => match (a, b) { + (Some(sa), Some(sb)) => { + if self.version == Version::Two { + sa.trim_start_matches("r#").cmp(sb.trim_start_matches("r#")) + } else { + a.cmp(b) + } + } + (_, _) => a.cmp(b), + }, + (Glob, Glob) => Ordering::Equal, + (Ident(ref pia, ref aa), Ident(ref pib, ref ab)) => { + let (ia, ib) = if self.version == Version::Two { + (pia.trim_start_matches("r#"), pib.trim_start_matches("r#")) + } else { + (pia.as_str(), pib.as_str()) + }; + // snake_case < CamelCase < UPPER_SNAKE_CASE + if ia.starts_with(char::is_uppercase) && ib.starts_with(char::is_lowercase) { + return Ordering::Greater; + } + if ia.starts_with(char::is_lowercase) && ib.starts_with(char::is_uppercase) { + return Ordering::Less; + } + if is_upper_snake_case(ia) && !is_upper_snake_case(ib) { + return Ordering::Greater; + } + if !is_upper_snake_case(ia) && is_upper_snake_case(ib) { + return Ordering::Less; + } + let ident_ord = ia.cmp(ib); + if ident_ord != Ordering::Equal { + return ident_ord; + } + match (aa, ab) { + (None, Some(_)) => Ordering::Less, + (Some(_), None) => Ordering::Greater, + (Some(aas), Some(abs)) => { + if self.version == Version::Two { + aas.trim_start_matches("r#") + .cmp(abs.trim_start_matches("r#")) + } else { + aas.cmp(abs) + } + } + (None, None) => Ordering::Equal, + } + } + (List(ref a), List(ref b)) => { + for (a, b) in a.iter().zip(b.iter()) { + let ord = a.cmp(b); + if ord != Ordering::Equal { + return ord; + } + } + + a.len().cmp(&b.len()) + } + (Slf(_), _) => Ordering::Less, + (_, Slf(_)) => Ordering::Greater, + (Super(_), _) => Ordering::Less, + (_, Super(_)) => Ordering::Greater, + (Crate(_), _) => Ordering::Less, + (_, Crate(_)) => Ordering::Greater, + (Ident(..), _) => Ordering::Less, + (_, Ident(..)) => Ordering::Greater, + (Glob, _) => Ordering::Less, + (_, Glob) => Ordering::Greater, + } + } +} +impl Ord for UseTree { + fn cmp(&self, other: &UseTree) -> Ordering { + for (a, b) in self.path.iter().zip(other.path.iter()) { + let ord = a.cmp(b); + // The comparison without aliases is a hack to avoid situations like + // comparing `a::b` to `a as c` - where the latter should be ordered + // first since it is shorter. + if ord != Ordering::Equal && a.remove_alias().cmp(&b.remove_alias()) != Ordering::Equal + { + return ord; + } + } + + self.path.len().cmp(&other.path.len()) + } +} + +fn rewrite_nested_use_tree( + context: &RewriteContext<'_>, + use_tree_list: &[UseTree], + shape: Shape, +) -> Option { + let mut list_items = Vec::with_capacity(use_tree_list.len()); + let nested_shape = match context.config.imports_indent() { + IndentStyle::Block => shape + .block_indent(context.config.tab_spaces()) + .with_max_width(context.config) + .sub_width(1)?, + IndentStyle::Visual => shape.visual_indent(0), + }; + for use_tree in use_tree_list { + if let Some(mut list_item) = use_tree.list_item.clone() { + list_item.item = use_tree.rewrite(context, nested_shape); + list_items.push(list_item); + } else { + list_items.push(ListItem::from_str(use_tree.rewrite(context, nested_shape)?)); + } + } + let has_nested_list = use_tree_list.iter().any(|use_segment| { + use_segment.path.last().map_or(false, |last_segment| { + matches!(last_segment.kind, UseSegmentKind::List(..)) + }) + }); + + let remaining_width = if has_nested_list { + 0 + } else { + shape.width.saturating_sub(2) + }; + + let tactic = definitive_tactic( + &list_items, + context.config.imports_layout(), + Separator::Comma, + remaining_width, + ); + + let ends_with_newline = context.config.imports_indent() == IndentStyle::Block + && tactic != DefinitiveListTactic::Horizontal; + let trailing_separator = if ends_with_newline { + context.config.trailing_comma() + } else { + SeparatorTactic::Never + }; + let fmt = ListFormatting::new(nested_shape, context.config) + .tactic(tactic) + .trailing_separator(trailing_separator) + .ends_with_newline(ends_with_newline) + .preserve_newline(true) + .nested(has_nested_list); + + let list_str = write_list(&list_items, &fmt)?; + + let result = if (list_str.contains('\n') || list_str.len() > remaining_width) + && context.config.imports_indent() == IndentStyle::Block + { + format!( + "{{\n{}{}\n{}}}", + nested_shape.indent.to_string(context.config), + list_str, + shape.indent.to_string(context.config) + ) + } else { + format!("{{{}}}", list_str) + }; + + Some(result) +} + +impl Rewrite for UseSegment { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + Some(match self.kind { + UseSegmentKind::Ident(ref ident, Some(ref rename)) => { + format!("{} as {}", ident, rename) + } + UseSegmentKind::Ident(ref ident, None) => ident.clone(), + UseSegmentKind::Slf(Some(ref rename)) => format!("self as {}", rename), + UseSegmentKind::Slf(None) => "self".to_owned(), + UseSegmentKind::Super(Some(ref rename)) => format!("super as {}", rename), + UseSegmentKind::Super(None) => "super".to_owned(), + UseSegmentKind::Crate(Some(ref rename)) => format!("crate as {}", rename), + UseSegmentKind::Crate(None) => "crate".to_owned(), + UseSegmentKind::Glob => "*".to_owned(), + UseSegmentKind::List(ref use_tree_list) => rewrite_nested_use_tree( + context, + use_tree_list, + // 1 = "{" and "}" + shape.offset_left(1)?.sub_width(1)?, + )?, + }) + } +} + +impl Rewrite for UseTree { + // This does NOT format attributes and visibility or add a trailing `;`. + fn rewrite(&self, context: &RewriteContext<'_>, mut shape: Shape) -> Option { + let mut result = String::with_capacity(256); + let mut iter = self.path.iter().peekable(); + while let Some(segment) = iter.next() { + let segment_str = segment.rewrite(context, shape)?; + result.push_str(&segment_str); + if iter.peek().is_some() { + result.push_str("::"); + // 2 = "::" + shape = shape.offset_left(2 + segment_str.len())?; + } + } + Some(result) + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +enum SharedPrefix { + Crate, + Module, + One, +} + +#[cfg(test)] +mod test { + use super::*; + use rustc_span::DUMMY_SP; + + // Parse the path part of an import. This parser is not robust and is only + // suitable for use in a test harness. + fn parse_use_tree(s: &str) -> UseTree { + use std::iter::Peekable; + use std::mem::swap; + use std::str::Chars; + + struct Parser<'a> { + input: Peekable>, + version: Version, + } + + impl<'a> Parser<'a> { + fn bump(&mut self) { + self.input.next().unwrap(); + } + + fn eat(&mut self, c: char) { + assert_eq!(self.input.next().unwrap(), c); + } + + fn push_segment( + &self, + result: &mut Vec, + buf: &mut String, + alias_buf: &mut Option, + ) { + let version = self.version; + if !buf.is_empty() { + let mut alias = None; + swap(alias_buf, &mut alias); + + match buf.as_ref() { + "self" => { + let kind = UseSegmentKind::Slf(alias); + result.push(UseSegment { kind, version }); + *buf = String::new(); + *alias_buf = None; + } + "super" => { + let kind = UseSegmentKind::Super(alias); + result.push(UseSegment { kind, version }); + *buf = String::new(); + *alias_buf = None; + } + "crate" => { + let kind = UseSegmentKind::Crate(alias); + result.push(UseSegment { kind, version }); + *buf = String::new(); + *alias_buf = None; + } + _ => { + let mut name = String::new(); + swap(buf, &mut name); + let kind = UseSegmentKind::Ident(name, alias); + result.push(UseSegment { kind, version }); + } + } + } + } + + fn parse_in_list(&mut self) -> UseTree { + let mut result = vec![]; + let mut buf = String::new(); + let mut alias_buf = None; + while let Some(&c) = self.input.peek() { + match c { + '{' => { + assert!(buf.is_empty()); + self.bump(); + let kind = UseSegmentKind::List(self.parse_list()); + result.push(UseSegment { + kind, + version: self.version, + }); + self.eat('}'); + } + '*' => { + assert!(buf.is_empty()); + self.bump(); + let kind = UseSegmentKind::Glob; + result.push(UseSegment { + kind, + version: self.version, + }); + } + ':' => { + self.bump(); + self.eat(':'); + self.push_segment(&mut result, &mut buf, &mut alias_buf); + } + '}' | ',' => { + self.push_segment(&mut result, &mut buf, &mut alias_buf); + return UseTree { + path: result, + span: DUMMY_SP, + list_item: None, + visibility: None, + attrs: None, + }; + } + ' ' => { + self.bump(); + self.eat('a'); + self.eat('s'); + self.eat(' '); + alias_buf = Some(String::new()); + } + c => { + self.bump(); + if let Some(ref mut buf) = alias_buf { + buf.push(c); + } else { + buf.push(c); + } + } + } + } + self.push_segment(&mut result, &mut buf, &mut alias_buf); + UseTree { + path: result, + span: DUMMY_SP, + list_item: None, + visibility: None, + attrs: None, + } + } + + fn parse_list(&mut self) -> Vec { + let mut result = vec![]; + loop { + match self.input.peek().unwrap() { + ',' | ' ' => self.bump(), + '}' => { + return result; + } + _ => result.push(self.parse_in_list()), + } + } + } + } + + let mut parser = Parser { + input: s.chars().peekable(), + version: Version::One, + }; + parser.parse_in_list() + } + + macro_rules! parse_use_trees { + ($($s:expr),* $(,)*) => { + vec![ + $(parse_use_tree($s),)* + ] + } + } + + macro_rules! test_merge { + ($by:ident, [$($input:expr),* $(,)*], [$($output:expr),* $(,)*]) => { + assert_eq!( + normalize_use_trees_with_granularity( + parse_use_trees!($($input,)*), + ImportGranularity::$by, + ), + parse_use_trees!($($output,)*), + ); + } + } + + #[test] + fn test_use_tree_merge_crate() { + test_merge!( + Crate, + ["a::b::{c, d}", "a::b::{e, f}"], + ["a::b::{c, d, e, f}"] + ); + test_merge!(Crate, ["a::b::c", "a::b"], ["a::{b, b::c}"]); + test_merge!(Crate, ["a::b", "a::b"], ["a::b"]); + test_merge!(Crate, ["a", "a::b", "a::b::c"], ["a::{self, b, b::c}"]); + test_merge!( + Crate, + ["a", "a::b", "a::b::c", "a::b::c::d"], + ["a::{self, b, b::{c, c::d}}"] + ); + test_merge!( + Crate, + ["a", "a::b", "a::b::c", "a::b"], + ["a::{self, b, b::c}"] + ); + test_merge!( + Crate, + ["a::{b::{self, c}, d::e}", "a::d::f"], + ["a::{b::{self, c}, d::{e, f}}"] + ); + test_merge!( + Crate, + ["a::d::f", "a::{b::{self, c}, d::e}"], + ["a::{b::{self, c}, d::{e, f}}"] + ); + test_merge!( + Crate, + ["a::{c, d, b}", "a::{d, e, b, a, f}", "a::{f, g, c}"], + ["a::{a, b, c, d, e, f, g}"] + ); + test_merge!( + Crate, + ["a::{self}", "b::{self as foo}"], + ["a::{self}", "b::{self as foo}"] + ); + } + + #[test] + fn test_use_tree_merge_module() { + test_merge!( + Module, + ["foo::b", "foo::{a, c, d::e}"], + ["foo::{a, b, c}", "foo::d::e"] + ); + + test_merge!( + Module, + ["foo::{a::b, a::c, d::e, d::f}"], + ["foo::a::{b, c}", "foo::d::{e, f}"] + ); + } + + #[test] + fn test_use_tree_merge_one() { + test_merge!(One, ["a", "b"], ["{a, b}"]); + + test_merge!(One, ["a::{aa, ab}", "b", "a"], ["{a::{self, aa, ab}, b}"]); + + test_merge!(One, ["a as x", "b as y"], ["{a as x, b as y}"]); + + test_merge!( + One, + ["a::{aa as xa, ab}", "b", "a"], + ["{a::{self, aa as xa, ab}, b}"] + ); + + test_merge!( + One, + ["a", "a::{aa, ab::{aba, abb}}"], + ["a::{self, aa, ab::{aba, abb}}"] + ); + + test_merge!(One, ["a", "b::{ba, *}"], ["{a, b::{ba, *}}"]); + + test_merge!(One, ["a", "b", "a::aa"], ["{a::{self, aa}, b}"]); + + test_merge!( + One, + ["a::aa::aaa", "a::ac::aca", "a::aa::*"], + ["a::{aa::{aaa, *}, ac::aca}"] + ); + + test_merge!( + One, + ["a", "b::{ba, bb}", "a::{aa::*, ab::aba}"], + ["{a::{self, aa::*, ab::aba}, b::{ba, bb}}"] + ); + + test_merge!( + One, + ["b", "a::ac::{aca, acb}", "a::{aa::*, ab}"], + ["{a::{aa::*, ab, ac::{aca, acb}}, b}"] + ); + } + + #[test] + fn test_flatten_use_trees() { + assert_eq!( + flatten_use_trees( + parse_use_trees!["foo::{a::{b, c}, d::e}"], + ImportGranularity::Item + ), + parse_use_trees!["foo::a::b", "foo::a::c", "foo::d::e"] + ); + + assert_eq!( + flatten_use_trees( + parse_use_trees!["foo::{self, a, b::{c, d}, e::*}"], + ImportGranularity::Item + ), + parse_use_trees![ + "foo::{self}", + "foo::a", + "foo::b::c", + "foo::b::d", + "foo::e::*" + ] + ); + } + + #[test] + fn test_use_tree_flatten() { + assert_eq!( + parse_use_tree("a::b::{c, d, e, f}").flatten(ImportGranularity::Item), + parse_use_trees!("a::b::c", "a::b::d", "a::b::e", "a::b::f",) + ); + + assert_eq!( + parse_use_tree("a::b::{c::{d, e, f}, g, h::{i, j, k}}") + .flatten(ImportGranularity::Item), + parse_use_trees![ + "a::b::c::d", + "a::b::c::e", + "a::b::c::f", + "a::b::g", + "a::b::h::i", + "a::b::h::j", + "a::b::h::k", + ] + ); + } + + #[test] + fn test_use_tree_normalize() { + assert_eq!(parse_use_tree("a::self").normalize(), parse_use_tree("a")); + assert_eq!( + parse_use_tree("a::self as foo").normalize(), + parse_use_tree("a as foo") + ); + assert_eq!( + parse_use_tree("a::{self}").normalize(), + parse_use_tree("a::{self}") + ); + assert_eq!(parse_use_tree("a::{b}").normalize(), parse_use_tree("a::b")); + assert_eq!( + parse_use_tree("a::{b, c::self}").normalize(), + parse_use_tree("a::{b, c}") + ); + assert_eq!( + parse_use_tree("a::{b as bar, c::self}").normalize(), + parse_use_tree("a::{b as bar, c}") + ); + } + + #[test] + fn test_use_tree_ord() { + assert!(parse_use_tree("a").normalize() < parse_use_tree("aa").normalize()); + assert!(parse_use_tree("a").normalize() < parse_use_tree("a::a").normalize()); + assert!(parse_use_tree("a").normalize() < parse_use_tree("*").normalize()); + assert!(parse_use_tree("a").normalize() < parse_use_tree("{a, b}").normalize()); + assert!(parse_use_tree("*").normalize() < parse_use_tree("{a, b}").normalize()); + + assert!( + parse_use_tree("aaaaaaaaaaaaaaa::{bb, cc, dddddddd}").normalize() + < parse_use_tree("aaaaaaaaaaaaaaa::{bb, cc, ddddddddd}").normalize() + ); + assert!( + parse_use_tree("serde::de::{Deserialize}").normalize() + < parse_use_tree("serde_json").normalize() + ); + assert!(parse_use_tree("a::b::c").normalize() < parse_use_tree("a::b::*").normalize()); + assert!( + parse_use_tree("foo::{Bar, Baz}").normalize() + < parse_use_tree("{Bar, Baz}").normalize() + ); + + assert!( + parse_use_tree("foo::{qux as bar}").normalize() + < parse_use_tree("foo::{self as bar}").normalize() + ); + assert!( + parse_use_tree("foo::{qux as bar}").normalize() + < parse_use_tree("foo::{baz, qux as bar}").normalize() + ); + assert!( + parse_use_tree("foo::{self as bar, baz}").normalize() + < parse_use_tree("foo::{baz, qux as bar}").normalize() + ); + + assert!(parse_use_tree("foo").normalize() < parse_use_tree("Foo").normalize()); + assert!(parse_use_tree("foo").normalize() < parse_use_tree("foo::Bar").normalize()); + + assert!( + parse_use_tree("std::cmp::{d, c, b, a}").normalize() + < 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/items.rs b/src/items.rs new file mode 100644 index 000000000000..d5bc38303e00 --- /dev/null +++ b/src/items.rs @@ -0,0 +1,3456 @@ +// Formatting top-level items - functions, structs, enums, traits, impls. + +use std::borrow::Cow; +use std::cmp::{max, min, Ordering}; + +use regex::Regex; +use rustc_ast::visit; +use rustc_ast::{ast, ptr}; +use rustc_span::{symbol, BytePos, Span, DUMMY_SP}; + +use crate::attr::filter_inline_attrs; +use crate::comment::{ + combine_strs_with_missing_comments, contains_comment, is_last_comment_block, + recover_comment_removed, recover_missing_comment_in_span, rewrite_missing_comment, + FindUncommented, +}; +use crate::config::lists::*; +use crate::config::{BraceStyle, Config, IndentStyle, Version}; +use crate::expr::{ + is_empty_block, is_simple_block_stmt, rewrite_assign_rhs, rewrite_assign_rhs_with, + rewrite_assign_rhs_with_comments, rewrite_else_kw_with_comments, rewrite_let_else_block, + RhsAssignKind, RhsTactics, +}; +use crate::lists::{definitive_tactic, itemize_list, write_list, ListFormatting, Separator}; +use crate::macros::{rewrite_macro, MacroPosition}; +use crate::overflow; +use crate::rewrite::{Rewrite, RewriteContext}; +use crate::shape::{Indent, Shape}; +use crate::source_map::{LineRangeUtils, SpanUtils}; +use crate::spanned::Spanned; +use crate::stmt::Stmt; +use crate::types::opaque_ty; +use crate::utils::*; +use crate::vertical::rewrite_with_alignment; +use crate::visitor::FmtVisitor; + +const DEFAULT_VISIBILITY: ast::Visibility = ast::Visibility { + kind: ast::VisibilityKind::Inherited, + span: DUMMY_SP, + tokens: None, +}; + +fn type_annotation_separator(config: &Config) -> &str { + colon_spaces(config) +} + +// Statements of the form +// let pat: ty = init; or let pat: ty = init else { .. }; +impl Rewrite for ast::Local { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + debug!( + "Local::rewrite {:?} {} {:?}", + self, shape.width, shape.indent + ); + + skip_out_of_file_lines_range!(context, self.span); + + if contains_skip(&self.attrs) { + return None; + } + + let attrs_str = self.attrs.rewrite(context, shape)?; + let mut result = if attrs_str.is_empty() { + "let ".to_owned() + } else { + combine_strs_with_missing_comments( + context, + &attrs_str, + "let ", + mk_sp( + self.attrs.last().map(|a| a.span.hi()).unwrap(), + self.span.lo(), + ), + shape, + false, + )? + }; + + // 4 = "let ".len() + let pat_shape = shape.offset_left(4)?; + // 1 = ; + let pat_shape = pat_shape.sub_width(1)?; + let pat_str = self.pat.rewrite(context, pat_shape)?; + result.push_str(&pat_str); + + // String that is placed within the assignment pattern and expression. + let infix = { + let mut infix = String::with_capacity(32); + + if let Some(ref ty) = self.ty { + let separator = type_annotation_separator(context.config); + let ty_shape = if pat_str.contains('\n') { + shape.with_max_width(context.config) + } else { + shape + } + .offset_left(last_line_width(&result) + separator.len())? + // 2 = ` =` + .sub_width(2)?; + + let rewrite = ty.rewrite(context, ty_shape)?; + + infix.push_str(separator); + infix.push_str(&rewrite); + } + + if self.kind.init().is_some() { + infix.push_str(" ="); + } + + infix + }; + + result.push_str(&infix); + + if let Some((init, else_block)) = self.kind.init_else_opt() { + // 1 = trailing semicolon; + let nested_shape = shape.sub_width(1)?; + + result = rewrite_assign_rhs( + context, + result, + init, + &RhsAssignKind::Expr(&init.kind, init.span), + nested_shape, + )?; + + if let Some(block) = else_block { + let else_kw_span = init.span.between(block.span); + let force_newline_else = pat_str.contains('\n') + || !same_line_else_kw_and_brace(&result, context, else_kw_span, nested_shape); + let else_kw = rewrite_else_kw_with_comments( + force_newline_else, + true, + context, + else_kw_span, + shape, + ); + result.push_str(&else_kw); + + // At this point we've written `let {pat} = {expr} else' into the buffer, and we + // want to calculate up front if there's room to write the divergent block on the + // same line. The available space varies based on indentation so we clamp the width + // on the smaller of `shape.width` and `single_line_let_else_max_width`. + let max_width = + std::cmp::min(shape.width, context.config.single_line_let_else_max_width()); + + // If available_space hits zero we know for sure this will be a multi-lined block + let available_space = max_width.saturating_sub(result.len()); + + let allow_single_line = !force_newline_else + && available_space > 0 + && allow_single_line_let_else_block(&result, block); + + let mut rw_else_block = + rewrite_let_else_block(block, allow_single_line, context, shape)?; + + let single_line_else = !rw_else_block.contains('\n'); + // +1 for the trailing `;` + let else_block_exceeds_width = rw_else_block.len() + 1 > available_space; + + if allow_single_line && single_line_else && else_block_exceeds_width { + // writing this on one line would exceed the available width + // so rewrite the else block over multiple lines. + rw_else_block = rewrite_let_else_block(block, false, context, shape)?; + } + + result.push_str(&rw_else_block); + }; + } + + result.push(';'); + Some(result) + } +} + +/// When the initializer expression is multi-lined, then the else keyword and opening brace of the +/// block ( i.e. "else {") should be put on the same line as the end of the initializer expression +/// if all the following are true: +/// +/// 1. The initializer expression ends with one or more closing parentheses, square brackets, +/// or braces +/// 2. There is nothing else on that line +/// 3. That line is not indented beyond the indent on the first line of the let keyword +fn same_line_else_kw_and_brace( + init_str: &str, + context: &RewriteContext<'_>, + else_kw_span: Span, + init_shape: Shape, +) -> bool { + if !init_str.contains('\n') { + // initializer expression is single lined. The "else {" can only be placed on the same line + // as the initializer expression if there is enough room for it. + // 7 = ` else {` + return init_shape.width.saturating_sub(init_str.len()) >= 7; + } + + // 1. The initializer expression ends with one or more `)`, `]`, `}`. + if !init_str.ends_with([')', ']', '}']) { + return false; + } + + // 2. There is nothing else on that line + // For example, there are no comments + let else_kw_snippet = context.snippet(else_kw_span).trim(); + if else_kw_snippet != "else" { + return false; + } + + // 3. The last line of the initializer expression is not indented beyond the `let` keyword + let indent = init_shape.indent.to_string(context.config); + init_str + .lines() + .last() + .expect("initializer expression is multi-lined") + .strip_prefix(indent.as_ref()) + .map_or(false, |l| !l.starts_with(char::is_whitespace)) +} + +fn allow_single_line_let_else_block(result: &str, block: &ast::Block) -> bool { + if result.contains('\n') { + return false; + } + + if block.stmts.len() <= 1 { + return true; + } + + false +} + +// FIXME convert to using rewrite style rather than visitor +// FIXME format modules in this style +#[allow(dead_code)] +#[derive(Debug)] +struct Item<'a> { + unsafety: ast::Unsafe, + abi: Cow<'static, str>, + vis: Option<&'a ast::Visibility>, + body: Vec>, + span: Span, +} + +impl<'a> Item<'a> { + fn from_foreign_mod(fm: &'a ast::ForeignMod, span: Span, config: &Config) -> Item<'a> { + Item { + unsafety: fm.unsafety, + abi: format_extern( + ast::Extern::from_abi(fm.abi, DUMMY_SP), + config.force_explicit_abi(), + true, + ), + vis: None, + body: fm + .items + .iter() + .map(|i| BodyElement::ForeignItem(i)) + .collect(), + span, + } + } +} + +#[derive(Debug)] +enum BodyElement<'a> { + // Stmt(&'a ast::Stmt), + // Field(&'a ast::ExprField), + // Variant(&'a ast::Variant), + // Item(&'a ast::Item), + ForeignItem(&'a ast::ForeignItem), +} + +/// Represents a fn's signature. +pub(crate) struct FnSig<'a> { + decl: &'a ast::FnDecl, + generics: &'a ast::Generics, + ext: ast::Extern, + is_async: Cow<'a, ast::Async>, + constness: ast::Const, + defaultness: ast::Defaultness, + unsafety: ast::Unsafe, + visibility: &'a ast::Visibility, +} + +impl<'a> FnSig<'a> { + pub(crate) fn from_method_sig( + method_sig: &'a ast::FnSig, + generics: &'a ast::Generics, + visibility: &'a ast::Visibility, + ) -> FnSig<'a> { + FnSig { + unsafety: method_sig.header.unsafety, + is_async: Cow::Borrowed(&method_sig.header.asyncness), + constness: method_sig.header.constness, + defaultness: ast::Defaultness::Final, + ext: method_sig.header.ext, + decl: &*method_sig.decl, + generics, + visibility, + } + } + + pub(crate) fn from_fn_kind( + fn_kind: &'a visit::FnKind<'_>, + decl: &'a ast::FnDecl, + defaultness: ast::Defaultness, + ) -> FnSig<'a> { + match *fn_kind { + visit::FnKind::Fn(fn_ctxt, _, fn_sig, vis, generics, _) => match fn_ctxt { + visit::FnCtxt::Assoc(..) => { + let mut fn_sig = FnSig::from_method_sig(fn_sig, generics, vis); + fn_sig.defaultness = defaultness; + fn_sig + } + _ => FnSig { + decl, + generics, + ext: fn_sig.header.ext, + constness: fn_sig.header.constness, + is_async: Cow::Borrowed(&fn_sig.header.asyncness), + defaultness, + unsafety: fn_sig.header.unsafety, + visibility: vis, + }, + }, + _ => unreachable!(), + } + } + + fn to_str(&self, context: &RewriteContext<'_>) -> String { + let mut result = String::with_capacity(128); + // Vis defaultness constness unsafety abi. + result.push_str(&*format_visibility(context, self.visibility)); + result.push_str(format_defaultness(self.defaultness)); + result.push_str(format_constness(self.constness)); + result.push_str(format_async(&self.is_async)); + result.push_str(format_unsafety(self.unsafety)); + result.push_str(&format_extern( + self.ext, + context.config.force_explicit_abi(), + false, + )); + result + } +} + +impl<'a> FmtVisitor<'a> { + fn format_item(&mut self, item: &Item<'_>) { + self.buffer.push_str(format_unsafety(item.unsafety)); + self.buffer.push_str(&item.abi); + + let snippet = self.snippet(item.span); + let brace_pos = snippet.find_uncommented("{").unwrap(); + + self.push_str("{"); + if !item.body.is_empty() || contains_comment(&snippet[brace_pos..]) { + // FIXME: this skips comments between the extern keyword and the opening + // brace. + self.last_pos = item.span.lo() + BytePos(brace_pos as u32 + 1); + self.block_indent = self.block_indent.block_indent(self.config); + + if !item.body.is_empty() { + for item in &item.body { + self.format_body_element(item); + } + } + + self.format_missing_no_indent(item.span.hi() - BytePos(1)); + self.block_indent = self.block_indent.block_unindent(self.config); + let indent_str = self.block_indent.to_string(self.config); + self.push_str(&indent_str); + } + + self.push_str("}"); + self.last_pos = item.span.hi(); + } + + fn format_body_element(&mut self, element: &BodyElement<'_>) { + match *element { + BodyElement::ForeignItem(item) => self.format_foreign_item(item), + } + } + + pub(crate) fn format_foreign_mod(&mut self, fm: &ast::ForeignMod, span: Span) { + let item = Item::from_foreign_mod(fm, span, self.config); + self.format_item(&item); + } + + fn format_foreign_item(&mut self, item: &ast::ForeignItem) { + let rewrite = item.rewrite(&self.get_context(), self.shape()); + let hi = item.span.hi(); + let span = if item.attrs.is_empty() { + item.span + } else { + mk_sp(item.attrs[0].span.lo(), hi) + }; + self.push_rewrite(span, rewrite); + self.last_pos = hi; + } + + pub(crate) fn rewrite_fn_before_block( + &mut self, + indent: Indent, + ident: symbol::Ident, + fn_sig: &FnSig<'_>, + span: Span, + ) -> Option<(String, FnBraceStyle)> { + let context = self.get_context(); + + let mut fn_brace_style = newline_for_brace(self.config, &fn_sig.generics.where_clause); + let (result, _, force_newline_brace) = + rewrite_fn_base(&context, indent, ident, fn_sig, span, fn_brace_style)?; + + // 2 = ` {` + if self.config.brace_style() == BraceStyle::AlwaysNextLine + || force_newline_brace + || last_line_width(&result) + 2 > self.shape().width + { + fn_brace_style = FnBraceStyle::NextLine + } + + Some((result, fn_brace_style)) + } + + pub(crate) fn rewrite_required_fn( + &mut self, + indent: Indent, + ident: symbol::Ident, + sig: &ast::FnSig, + vis: &ast::Visibility, + generics: &ast::Generics, + span: Span, + ) -> Option { + // Drop semicolon or it will be interpreted as comment. + let span = mk_sp(span.lo(), span.hi() - BytePos(1)); + let context = self.get_context(); + + let (mut result, ends_with_comment, _) = rewrite_fn_base( + &context, + indent, + ident, + &FnSig::from_method_sig(sig, generics, vis), + span, + FnBraceStyle::None, + )?; + + // If `result` ends with a comment, then remember to add a newline + if ends_with_comment { + result.push_str(&indent.to_string_with_newline(context.config)); + } + + // Re-attach semicolon + result.push(';'); + + Some(result) + } + + pub(crate) fn single_line_fn( + &self, + fn_str: &str, + block: &ast::Block, + inner_attrs: Option<&[ast::Attribute]>, + ) -> Option { + if fn_str.contains('\n') || inner_attrs.map_or(false, |a| !a.is_empty()) { + return None; + } + + let context = self.get_context(); + + if self.config.empty_item_single_line() + && is_empty_block(&context, block, None) + && self.block_indent.width() + fn_str.len() + 3 <= self.config.max_width() + && !last_line_contains_single_line_comment(fn_str) + { + return Some(format!("{} {{}}", fn_str)); + } + + if !self.config.fn_single_line() || !is_simple_block_stmt(&context, block, None) { + return None; + } + + let res = Stmt::from_ast_node(block.stmts.first()?, true) + .rewrite(&self.get_context(), self.shape())?; + + let width = self.block_indent.width() + fn_str.len() + res.len() + 5; + if !res.contains('\n') && width <= self.config.max_width() { + Some(format!("{} {{ {} }}", fn_str, res)) + } else { + None + } + } + + pub(crate) fn visit_static(&mut self, static_parts: &StaticParts<'_>) { + let rewrite = rewrite_static(&self.get_context(), static_parts, self.block_indent); + self.push_rewrite(static_parts.span, rewrite); + } + + pub(crate) fn visit_struct(&mut self, struct_parts: &StructParts<'_>) { + let is_tuple = match struct_parts.def { + ast::VariantData::Tuple(..) => true, + _ => false, + }; + let rewrite = format_struct(&self.get_context(), struct_parts, self.block_indent, None) + .map(|s| if is_tuple { s + ";" } else { s }); + self.push_rewrite(struct_parts.span, rewrite); + } + + pub(crate) fn visit_enum( + &mut self, + ident: symbol::Ident, + vis: &ast::Visibility, + enum_def: &ast::EnumDef, + generics: &ast::Generics, + span: Span, + ) { + let enum_header = + format_header(&self.get_context(), "enum ", ident, vis, self.block_indent); + self.push_str(&enum_header); + + let enum_snippet = self.snippet(span); + let brace_pos = enum_snippet.find_uncommented("{").unwrap(); + let body_start = span.lo() + BytePos(brace_pos as u32 + 1); + let generics_str = format_generics( + &self.get_context(), + generics, + self.config.brace_style(), + if enum_def.variants.is_empty() { + BracePos::ForceSameLine + } else { + BracePos::Auto + }, + self.block_indent, + // make a span that starts right after `enum Foo` + mk_sp(ident.span.hi(), body_start), + last_line_width(&enum_header), + ) + .unwrap(); + self.push_str(&generics_str); + + self.last_pos = body_start; + + match self.format_variant_list(enum_def, body_start, span.hi()) { + Some(ref s) if enum_def.variants.is_empty() => self.push_str(s), + rw => { + self.push_rewrite(mk_sp(body_start, span.hi()), rw); + self.block_indent = self.block_indent.block_unindent(self.config); + } + } + } + + // Format the body of an enum definition + fn format_variant_list( + &mut self, + enum_def: &ast::EnumDef, + body_lo: BytePos, + body_hi: BytePos, + ) -> Option { + if enum_def.variants.is_empty() { + let mut buffer = String::with_capacity(128); + // 1 = "}" + let span = mk_sp(body_lo, body_hi - BytePos(1)); + format_empty_struct_or_tuple( + &self.get_context(), + span, + self.block_indent, + &mut buffer, + "", + "}", + ); + return Some(buffer); + } + let mut result = String::with_capacity(1024); + let original_offset = self.block_indent; + self.block_indent = self.block_indent.block_indent(self.config); + + // If enum variants have discriminants, try to vertically align those, + // provided the discrims are not shifted too much to the right + let align_threshold: usize = self.config.enum_discrim_align_threshold(); + let discr_ident_lens: Vec = enum_def + .variants + .iter() + .filter(|var| var.disr_expr.is_some()) + .map(|var| rewrite_ident(&self.get_context(), var.ident).len()) + .collect(); + // cut the list at the point of longest discrim shorter than the threshold + // All of the discrims under the threshold will get padded, and all above - left as is. + let pad_discrim_ident_to = *discr_ident_lens + .iter() + .filter(|&l| *l <= align_threshold) + .max() + .unwrap_or(&0); + + let itemize_list_with = |one_line_width: usize| { + itemize_list( + self.snippet_provider, + enum_def.variants.iter(), + "}", + ",", + |f| { + if !f.attrs.is_empty() { + f.attrs[0].span.lo() + } else { + f.span.lo() + } + }, + |f| f.span.hi(), + |f| self.format_variant(f, one_line_width, pad_discrim_ident_to), + body_lo, + body_hi, + false, + ) + .collect() + }; + let mut items: Vec<_> = itemize_list_with(self.config.struct_variant_width()); + + // If one of the variants use multiple lines, use multi-lined formatting for all variants. + let has_multiline_variant = items.iter().any(|item| item.inner_as_ref().contains('\n')); + let has_single_line_variant = items.iter().any(|item| !item.inner_as_ref().contains('\n')); + if has_multiline_variant && has_single_line_variant { + items = itemize_list_with(0); + } + + let shape = self.shape().sub_width(2)?; + let fmt = ListFormatting::new(shape, self.config) + .trailing_separator(self.config.trailing_comma()) + .preserve_newline(true); + + let list = write_list(&items, &fmt)?; + result.push_str(&list); + result.push_str(&original_offset.to_string_with_newline(self.config)); + result.push('}'); + Some(result) + } + + // Variant of an enum. + fn format_variant( + &self, + field: &ast::Variant, + one_line_width: usize, + pad_discrim_ident_to: usize, + ) -> Option { + if contains_skip(&field.attrs) { + let lo = field.attrs[0].span.lo(); + let span = mk_sp(lo, field.span.hi()); + return Some(self.snippet(span).to_owned()); + } + + let context = self.get_context(); + // 1 = ',' + let shape = self.shape().sub_width(1)?; + let attrs_str = field.attrs.rewrite(&context, shape)?; + let lo = field + .attrs + .last() + .map_or(field.span.lo(), |attr| attr.span.hi()); + let span = mk_sp(lo, field.span.lo()); + + let variant_body = match field.data { + ast::VariantData::Tuple(..) | ast::VariantData::Struct(..) => format_struct( + &context, + &StructParts::from_variant(field, &context), + self.block_indent, + Some(one_line_width), + )?, + ast::VariantData::Unit(..) => rewrite_ident(&context, field.ident).to_owned(), + }; + + let variant_body = if let Some(ref expr) = field.disr_expr { + let lhs = format!("{:1$} =", variant_body, pad_discrim_ident_to); + let ex = &*expr.value; + rewrite_assign_rhs_with( + &context, + lhs, + ex, + shape, + &RhsAssignKind::Expr(&ex.kind, ex.span), + RhsTactics::AllowOverflow, + )? + } else { + variant_body + }; + + combine_strs_with_missing_comments(&context, &attrs_str, &variant_body, span, shape, false) + } + + fn visit_impl_items(&mut self, items: &[ptr::P]) { + if self.get_context().config.reorder_impl_items() { + type TyOpt = Option>; + use crate::ast::AssocItemKind::*; + let is_type = |ty: &TyOpt| opaque_ty(ty).is_none(); + let is_opaque = |ty: &TyOpt| opaque_ty(ty).is_some(); + let both_type = |l: &TyOpt, r: &TyOpt| is_type(l) && is_type(r); + let both_opaque = |l: &TyOpt, r: &TyOpt| is_opaque(l) && is_opaque(r); + let need_empty_line = |a: &ast::AssocItemKind, b: &ast::AssocItemKind| match (a, b) { + (Type(lty), Type(rty)) + if both_type(<y.ty, &rty.ty) || both_opaque(<y.ty, &rty.ty) => + { + false + } + (Const(..), Const(..)) => false, + _ => true, + }; + + // Create visitor for each items, then reorder them. + let mut buffer = vec![]; + for item in items { + self.visit_impl_item(item); + buffer.push((self.buffer.clone(), item.clone())); + self.buffer.clear(); + } + + buffer.sort_by(|(_, a), (_, b)| match (&a.kind, &b.kind) { + (Type(lty), Type(rty)) + if both_type(<y.ty, &rty.ty) || both_opaque(<y.ty, &rty.ty) => + { + a.ident.as_str().cmp(b.ident.as_str()) + } + (Const(..), Const(..)) | (MacCall(..), MacCall(..)) => { + a.ident.as_str().cmp(b.ident.as_str()) + } + (Fn(..), Fn(..)) => a.span.lo().cmp(&b.span.lo()), + (Type(ty), _) if is_type(&ty.ty) => Ordering::Less, + (_, Type(ty)) if is_type(&ty.ty) => Ordering::Greater, + (Type(..), _) => Ordering::Less, + (_, Type(..)) => Ordering::Greater, + (Const(..), _) => Ordering::Less, + (_, Const(..)) => Ordering::Greater, + (MacCall(..), _) => Ordering::Less, + (_, MacCall(..)) => Ordering::Greater, + }); + let mut prev_kind = None; + for (buf, item) in buffer { + // Make sure that there are at least a single empty line between + // different impl items. + if prev_kind + .as_ref() + .map_or(false, |prev_kind| need_empty_line(prev_kind, &item.kind)) + { + self.push_str("\n"); + } + let indent_str = self.block_indent.to_string_with_newline(self.config); + self.push_str(&indent_str); + self.push_str(buf.trim()); + prev_kind = Some(item.kind.clone()); + } + } else { + for item in items { + self.visit_impl_item(item); + } + } + } +} + +pub(crate) fn format_impl( + context: &RewriteContext<'_>, + item: &ast::Item, + iimpl: &ast::Impl, + offset: Indent, +) -> Option { + let ast::Impl { + generics, + self_ty, + items, + .. + } = iimpl; + let mut result = String::with_capacity(128); + let ref_and_type = format_impl_ref_and_type(context, item, iimpl, offset)?; + let sep = offset.to_string_with_newline(context.config); + result.push_str(&ref_and_type); + + let where_budget = if result.contains('\n') { + context.config.max_width() + } else { + context.budget(last_line_width(&result)) + }; + + let mut option = WhereClauseOption::snuggled(&ref_and_type); + let snippet = context.snippet(item.span); + let open_pos = snippet.find_uncommented("{")? + 1; + if !contains_comment(&snippet[open_pos..]) + && items.is_empty() + && generics.where_clause.predicates.len() == 1 + && !result.contains('\n') + { + option.suppress_comma(); + option.snuggle(); + option.allow_single_line(); + } + + let missing_span = mk_sp(self_ty.span.hi(), item.span.hi()); + let where_span_end = context.snippet_provider.opt_span_before(missing_span, "{"); + let where_clause_str = rewrite_where_clause( + context, + &generics.where_clause.predicates, + generics.where_clause.span, + context.config.brace_style(), + Shape::legacy(where_budget, offset.block_only()), + false, + "{", + where_span_end, + self_ty.span.hi(), + option, + )?; + + // If there is no where-clause, we may have missing comments between the trait name and + // the opening brace. + if generics.where_clause.predicates.is_empty() { + if let Some(hi) = where_span_end { + match recover_missing_comment_in_span( + mk_sp(self_ty.span.hi(), hi), + Shape::indented(offset, context.config), + context, + last_line_width(&result), + ) { + Some(ref missing_comment) if !missing_comment.is_empty() => { + result.push_str(missing_comment); + } + _ => (), + } + } + } + + if is_impl_single_line(context, items.as_slice(), &result, &where_clause_str, item)? { + result.push_str(&where_clause_str); + if where_clause_str.contains('\n') || last_line_contains_single_line_comment(&result) { + // if the where_clause contains extra comments AND + // there is only one where-clause predicate + // recover the suppressed comma in single line where_clause formatting + if generics.where_clause.predicates.len() == 1 { + result.push(','); + } + result.push_str(&format!("{}{{{}}}", sep, sep)); + } else { + result.push_str(" {}"); + } + return Some(result); + } + + result.push_str(&where_clause_str); + + let need_newline = last_line_contains_single_line_comment(&result) || result.contains('\n'); + match context.config.brace_style() { + _ if need_newline => result.push_str(&sep), + BraceStyle::AlwaysNextLine => result.push_str(&sep), + BraceStyle::PreferSameLine => result.push(' '), + BraceStyle::SameLineWhere => { + if !where_clause_str.is_empty() { + result.push_str(&sep); + } else { + result.push(' '); + } + } + } + + result.push('{'); + // this is an impl body snippet(impl SampleImpl { /* here */ }) + let lo = max(self_ty.span.hi(), generics.where_clause.span.hi()); + let snippet = context.snippet(mk_sp(lo, item.span.hi())); + let open_pos = snippet.find_uncommented("{")? + 1; + + if !items.is_empty() || contains_comment(&snippet[open_pos..]) { + let mut visitor = FmtVisitor::from_context(context); + let item_indent = offset.block_only().block_indent(context.config); + visitor.block_indent = item_indent; + visitor.last_pos = lo + BytePos(open_pos as u32); + + visitor.visit_attrs(&item.attrs, ast::AttrStyle::Inner); + visitor.visit_impl_items(items); + + visitor.format_missing(item.span.hi() - BytePos(1)); + + let inner_indent_str = visitor.block_indent.to_string_with_newline(context.config); + let outer_indent_str = offset.block_only().to_string_with_newline(context.config); + + result.push_str(&inner_indent_str); + result.push_str(visitor.buffer.trim()); + result.push_str(&outer_indent_str); + } else if need_newline || !context.config.empty_item_single_line() { + result.push_str(&sep); + } + + result.push('}'); + + Some(result) +} + +fn is_impl_single_line( + context: &RewriteContext<'_>, + items: &[ptr::P], + result: &str, + where_clause_str: &str, + item: &ast::Item, +) -> Option { + let snippet = context.snippet(item.span); + let open_pos = snippet.find_uncommented("{")? + 1; + + Some( + context.config.empty_item_single_line() + && items.is_empty() + && !result.contains('\n') + && result.len() + where_clause_str.len() <= context.config.max_width() + && !contains_comment(&snippet[open_pos..]), + ) +} + +fn format_impl_ref_and_type( + context: &RewriteContext<'_>, + item: &ast::Item, + iimpl: &ast::Impl, + offset: Indent, +) -> Option { + let ast::Impl { + unsafety, + polarity, + defaultness, + constness, + ref generics, + of_trait: ref trait_ref, + ref self_ty, + .. + } = *iimpl; + let mut result = String::with_capacity(128); + + result.push_str(&format_visibility(context, &item.vis)); + result.push_str(format_defaultness(defaultness)); + result.push_str(format_unsafety(unsafety)); + + let shape = if context.config.version() == Version::Two { + Shape::indented(offset + last_line_width(&result), context.config) + } else { + generics_shape_from_config( + context.config, + Shape::indented(offset + last_line_width(&result), context.config), + 0, + )? + }; + let generics_str = rewrite_generics(context, "impl", generics, shape)?; + result.push_str(&generics_str); + result.push_str(format_constness_right(constness)); + + let polarity_str = match polarity { + ast::ImplPolarity::Negative(_) => "!", + ast::ImplPolarity::Positive => "", + }; + + let polarity_overhead; + let trait_ref_overhead; + if let Some(ref trait_ref) = *trait_ref { + let result_len = last_line_width(&result); + result.push_str(&rewrite_trait_ref( + context, + trait_ref, + offset, + polarity_str, + result_len, + )?); + polarity_overhead = 0; // already written + trait_ref_overhead = " for".len(); + } else { + polarity_overhead = polarity_str.len(); + trait_ref_overhead = 0; + } + + // Try to put the self type in a single line. + let curly_brace_overhead = if generics.where_clause.predicates.is_empty() { + // If there is no where-clause adapt budget for type formatting to take space and curly + // brace into account. + match context.config.brace_style() { + BraceStyle::AlwaysNextLine => 0, + _ => 2, + } + } else { + 0 + }; + let used_space = + last_line_width(&result) + polarity_overhead + trait_ref_overhead + curly_brace_overhead; + // 1 = space before the type. + let budget = context.budget(used_space + 1); + if let Some(self_ty_str) = self_ty.rewrite(context, Shape::legacy(budget, offset)) { + if !self_ty_str.contains('\n') { + if trait_ref.is_some() { + result.push_str(" for "); + } else { + result.push(' '); + result.push_str(polarity_str); + } + result.push_str(&self_ty_str); + return Some(result); + } + } + + // Couldn't fit the self type on a single line, put it on a new line. + result.push('\n'); + // Add indentation of one additional tab. + let new_line_offset = offset.block_indent(context.config); + result.push_str(&new_line_offset.to_string(context.config)); + if trait_ref.is_some() { + result.push_str("for "); + } else { + result.push_str(polarity_str); + } + let budget = context.budget(last_line_width(&result) + polarity_overhead); + let type_offset = match context.config.indent_style() { + IndentStyle::Visual => new_line_offset + trait_ref_overhead, + IndentStyle::Block => new_line_offset, + }; + result.push_str(&*self_ty.rewrite(context, Shape::legacy(budget, type_offset))?); + Some(result) +} + +fn rewrite_trait_ref( + context: &RewriteContext<'_>, + trait_ref: &ast::TraitRef, + offset: Indent, + polarity_str: &str, + result_len: usize, +) -> Option { + // 1 = space between generics and trait_ref + let used_space = 1 + polarity_str.len() + result_len; + let shape = Shape::indented(offset + used_space, context.config); + if let Some(trait_ref_str) = trait_ref.rewrite(context, shape) { + if !trait_ref_str.contains('\n') { + return Some(format!(" {}{}", polarity_str, trait_ref_str)); + } + } + // We could not make enough space for trait_ref, so put it on new line. + let offset = offset.block_indent(context.config); + let shape = Shape::indented(offset, context.config); + let trait_ref_str = trait_ref.rewrite(context, shape)?; + Some(format!( + "{}{}{}", + offset.to_string_with_newline(context.config), + polarity_str, + trait_ref_str + )) +} + +pub(crate) struct StructParts<'a> { + prefix: &'a str, + ident: symbol::Ident, + vis: &'a ast::Visibility, + def: &'a ast::VariantData, + generics: Option<&'a ast::Generics>, + span: Span, +} + +impl<'a> StructParts<'a> { + fn format_header(&self, context: &RewriteContext<'_>, offset: Indent) -> String { + format_header(context, self.prefix, self.ident, self.vis, offset) + } + + fn from_variant(variant: &'a ast::Variant, context: &RewriteContext<'_>) -> Self { + StructParts { + prefix: "", + ident: variant.ident, + vis: &DEFAULT_VISIBILITY, + def: &variant.data, + generics: None, + span: enum_variant_span(variant, context), + } + } + + pub(crate) fn from_item(item: &'a ast::Item) -> Self { + let (prefix, def, generics) = match item.kind { + ast::ItemKind::Struct(ref def, ref generics) => ("struct ", def, generics), + ast::ItemKind::Union(ref def, ref generics) => ("union ", def, generics), + _ => unreachable!(), + }; + StructParts { + prefix, + ident: item.ident, + vis: &item.vis, + def, + generics: Some(generics), + span: item.span, + } + } +} + +fn enum_variant_span(variant: &ast::Variant, context: &RewriteContext<'_>) -> Span { + use ast::VariantData::*; + if let Some(ref anon_const) = variant.disr_expr { + let span_before_consts = variant.span.until(anon_const.value.span); + let hi = match &variant.data { + Struct(..) => context + .snippet_provider + .span_after_last(span_before_consts, "}"), + Tuple(..) => context + .snippet_provider + .span_after_last(span_before_consts, ")"), + Unit(..) => variant.ident.span.hi(), + }; + mk_sp(span_before_consts.lo(), hi) + } else { + variant.span + } +} + +fn format_struct( + context: &RewriteContext<'_>, + struct_parts: &StructParts<'_>, + offset: Indent, + one_line_width: Option, +) -> Option { + match *struct_parts.def { + ast::VariantData::Unit(..) => format_unit_struct(context, struct_parts, offset), + ast::VariantData::Tuple(ref fields, _) => { + format_tuple_struct(context, struct_parts, fields, offset) + } + ast::VariantData::Struct(ref fields, _) => { + format_struct_struct(context, struct_parts, fields, offset, one_line_width) + } + } +} + +pub(crate) fn format_trait( + context: &RewriteContext<'_>, + item: &ast::Item, + offset: Indent, +) -> Option { + if let ast::ItemKind::Trait(trait_kind) = &item.kind { + let ast::Trait { + is_auto, + unsafety, + ref generics, + ref bounds, + ref items, + } = **trait_kind; + let mut result = String::with_capacity(128); + let header = format!( + "{}{}{}trait ", + format_visibility(context, &item.vis), + format_unsafety(unsafety), + format_auto(is_auto), + ); + result.push_str(&header); + + let body_lo = context.snippet_provider.span_after(item.span, "{"); + + let shape = Shape::indented(offset, context.config).offset_left(result.len())?; + let generics_str = + rewrite_generics(context, rewrite_ident(context, item.ident), generics, shape)?; + result.push_str(&generics_str); + + // FIXME(#2055): rustfmt fails to format when there are comments between trait bounds. + if !bounds.is_empty() { + let ident_hi = context + .snippet_provider + .span_after(item.span, item.ident.as_str()); + let bound_hi = bounds.last().unwrap().span().hi(); + let snippet = context.snippet(mk_sp(ident_hi, bound_hi)); + if contains_comment(snippet) { + return None; + } + + result = rewrite_assign_rhs_with( + context, + result + ":", + bounds, + shape, + &RhsAssignKind::Bounds, + RhsTactics::ForceNextLineWithoutIndent, + )?; + } + + // Rewrite where-clause. + if !generics.where_clause.predicates.is_empty() { + let where_on_new_line = context.config.indent_style() != IndentStyle::Block; + + let where_budget = context.budget(last_line_width(&result)); + let pos_before_where = if bounds.is_empty() { + generics.where_clause.span.lo() + } else { + bounds[bounds.len() - 1].span().hi() + }; + let option = WhereClauseOption::snuggled(&generics_str); + let where_clause_str = rewrite_where_clause( + context, + &generics.where_clause.predicates, + generics.where_clause.span, + context.config.brace_style(), + Shape::legacy(where_budget, offset.block_only()), + where_on_new_line, + "{", + None, + pos_before_where, + option, + )?; + // If the where-clause cannot fit on the same line, + // put the where-clause on a new line + if !where_clause_str.contains('\n') + && last_line_width(&result) + where_clause_str.len() + offset.width() + > context.config.comment_width() + { + let width = offset.block_indent + context.config.tab_spaces() - 1; + let where_indent = Indent::new(0, width); + result.push_str(&where_indent.to_string_with_newline(context.config)); + } + result.push_str(&where_clause_str); + } else { + let item_snippet = context.snippet(item.span); + if let Some(lo) = item_snippet.find('/') { + // 1 = `{` + let comment_hi = if generics.params.len() > 0 { + generics.span.lo() - BytePos(1) + } else { + body_lo - BytePos(1) + }; + let comment_lo = item.span.lo() + BytePos(lo as u32); + if comment_lo < comment_hi { + match recover_missing_comment_in_span( + mk_sp(comment_lo, comment_hi), + Shape::indented(offset, context.config), + context, + last_line_width(&result), + ) { + Some(ref missing_comment) if !missing_comment.is_empty() => { + result.push_str(missing_comment); + } + _ => (), + } + } + } + } + + let block_span = mk_sp(generics.where_clause.span.hi(), item.span.hi()); + let snippet = context.snippet(block_span); + let open_pos = snippet.find_uncommented("{")? + 1; + + match context.config.brace_style() { + _ if last_line_contains_single_line_comment(&result) + || last_line_width(&result) + 2 > context.budget(offset.width()) => + { + result.push_str(&offset.to_string_with_newline(context.config)); + } + _ if context.config.empty_item_single_line() + && items.is_empty() + && !result.contains('\n') + && !contains_comment(&snippet[open_pos..]) => + { + result.push_str(" {}"); + return Some(result); + } + BraceStyle::AlwaysNextLine => { + result.push_str(&offset.to_string_with_newline(context.config)); + } + BraceStyle::PreferSameLine => result.push(' '), + BraceStyle::SameLineWhere => { + if result.contains('\n') + || (!generics.where_clause.predicates.is_empty() && !items.is_empty()) + { + result.push_str(&offset.to_string_with_newline(context.config)); + } else { + result.push(' '); + } + } + } + result.push('{'); + + let outer_indent_str = offset.block_only().to_string_with_newline(context.config); + + if !items.is_empty() || contains_comment(&snippet[open_pos..]) { + let mut visitor = FmtVisitor::from_context(context); + visitor.block_indent = offset.block_only().block_indent(context.config); + visitor.last_pos = block_span.lo() + BytePos(open_pos as u32); + + for item in items { + visitor.visit_trait_item(item); + } + + visitor.format_missing(item.span.hi() - BytePos(1)); + + let inner_indent_str = visitor.block_indent.to_string_with_newline(context.config); + + result.push_str(&inner_indent_str); + result.push_str(visitor.buffer.trim()); + result.push_str(&outer_indent_str); + } else if result.contains('\n') { + result.push_str(&outer_indent_str); + } + + result.push('}'); + Some(result) + } else { + unreachable!(); + } +} + +pub(crate) struct TraitAliasBounds<'a> { + generic_bounds: &'a ast::GenericBounds, + generics: &'a ast::Generics, +} + +impl<'a> Rewrite for TraitAliasBounds<'a> { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + let generic_bounds_str = self.generic_bounds.rewrite(context, shape)?; + + let mut option = WhereClauseOption::new(true, WhereClauseSpace::None); + option.allow_single_line(); + + let where_str = rewrite_where_clause( + context, + &self.generics.where_clause.predicates, + self.generics.where_clause.span, + context.config.brace_style(), + shape, + false, + ";", + None, + self.generics.where_clause.span.lo(), + option, + )?; + + let fits_single_line = !generic_bounds_str.contains('\n') + && !where_str.contains('\n') + && generic_bounds_str.len() + where_str.len() < shape.width; + let space = if generic_bounds_str.is_empty() || where_str.is_empty() { + Cow::from("") + } else if fits_single_line { + Cow::from(" ") + } else { + shape.indent.to_string_with_newline(context.config) + }; + + Some(format!("{}{}{}", generic_bounds_str, space, where_str)) + } +} + +pub(crate) fn format_trait_alias( + context: &RewriteContext<'_>, + ident: symbol::Ident, + vis: &ast::Visibility, + generics: &ast::Generics, + generic_bounds: &ast::GenericBounds, + shape: Shape, +) -> Option { + let alias = rewrite_ident(context, ident); + // 6 = "trait ", 2 = " =" + let g_shape = shape.offset_left(6)?.sub_width(2)?; + let generics_str = rewrite_generics(context, alias, generics, g_shape)?; + let vis_str = format_visibility(context, vis); + let lhs = format!("{}trait {} =", vis_str, generics_str); + // 1 = ";" + let trait_alias_bounds = TraitAliasBounds { + generic_bounds, + generics, + }; + rewrite_assign_rhs( + context, + lhs, + &trait_alias_bounds, + &RhsAssignKind::Bounds, + shape.sub_width(1)?, + ) + .map(|s| s + ";") +} + +fn format_unit_struct( + context: &RewriteContext<'_>, + p: &StructParts<'_>, + offset: Indent, +) -> Option { + let header_str = format_header(context, p.prefix, p.ident, p.vis, offset); + let generics_str = if let Some(generics) = p.generics { + let hi = context.snippet_provider.span_before_last(p.span, ";"); + format_generics( + context, + generics, + context.config.brace_style(), + BracePos::None, + offset, + // make a span that starts right after `struct Foo` + mk_sp(p.ident.span.hi(), hi), + last_line_width(&header_str), + )? + } else { + String::new() + }; + Some(format!("{}{};", header_str, generics_str)) +} + +pub(crate) fn format_struct_struct( + context: &RewriteContext<'_>, + struct_parts: &StructParts<'_>, + fields: &[ast::FieldDef], + offset: Indent, + one_line_width: Option, +) -> Option { + let mut result = String::with_capacity(1024); + let span = struct_parts.span; + + let header_str = struct_parts.format_header(context, offset); + result.push_str(&header_str); + + let header_hi = struct_parts.ident.span.hi(); + 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.where_clause.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( + context, + g, + context.config.brace_style(), + if fields.is_empty() { + BracePos::ForceSameLine + } else { + BracePos::Auto + }, + offset, + // make a span that starts right after `struct Foo` + mk_sp(header_hi, body_lo), + last_line_width(&result), + )?, + None => { + // 3 = ` {}`, 2 = ` {`. + let overhead = if fields.is_empty() { 3 } else { 2 }; + if (context.config.brace_style() == BraceStyle::AlwaysNextLine && !fields.is_empty()) + || context.config.max_width() < overhead + result.len() + { + format!("\n{}{{", offset.block_only().to_string(context.config)) + } else { + " {".to_owned() + } + } + }; + // 1 = `}` + let overhead = if fields.is_empty() { 1 } else { 0 }; + let total_width = result.len() + generics_str.len() + overhead; + if !generics_str.is_empty() + && !generics_str.contains('\n') + && total_width > context.config.max_width() + { + result.push('\n'); + result.push_str(&offset.to_string(context.config)); + result.push_str(generics_str.trim_start()); + } else { + result.push_str(&generics_str); + } + + if fields.is_empty() { + let inner_span = mk_sp(body_lo, span.hi() - BytePos(1)); + format_empty_struct_or_tuple(context, inner_span, offset, &mut result, "", "}"); + return Some(result); + } + + // 3 = ` ` and ` }` + let one_line_budget = context.budget(result.len() + 3 + offset.width()); + let one_line_budget = + one_line_width.map_or(0, |one_line_width| min(one_line_width, one_line_budget)); + + let items_str = rewrite_with_alignment( + fields, + context, + Shape::indented(offset.block_indent(context.config), context.config).sub_width(1)?, + mk_sp(body_lo, span.hi()), + one_line_budget, + )?; + + if !items_str.contains('\n') + && !result.contains('\n') + && items_str.len() <= one_line_budget + && !last_line_contains_single_line_comment(&items_str) + { + Some(format!("{} {} }}", result, items_str)) + } else { + Some(format!( + "{}\n{}{}\n{}}}", + result, + offset + .block_indent(context.config) + .to_string(context.config), + items_str, + offset.to_string(context.config) + )) + } +} + +fn get_bytepos_after_visibility(vis: &ast::Visibility, default_span: Span) -> BytePos { + match vis.kind { + ast::VisibilityKind::Restricted { .. } => vis.span.hi(), + _ => default_span.lo(), + } +} + +// Format tuple or struct without any fields. We need to make sure that the comments +// inside the delimiters are preserved. +fn format_empty_struct_or_tuple( + context: &RewriteContext<'_>, + span: Span, + offset: Indent, + result: &mut String, + opener: &str, + closer: &str, +) { + // 3 = " {}" or "();" + let used_width = last_line_used_width(result, offset.width()) + 3; + if used_width > context.config.max_width() { + result.push_str(&offset.to_string_with_newline(context.config)) + } + result.push_str(opener); + + // 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) => { + 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 is_multi_line || last_line_contains_single_line_comment(s) { + result.push_str(&offset.to_string_with_newline(context.config)); + } + } + None => result.push_str(context.snippet(span)), + } + result.push_str(closer); +} + +fn format_tuple_struct( + context: &RewriteContext<'_>, + struct_parts: &StructParts<'_>, + fields: &[ast::FieldDef], + offset: Indent, +) -> Option { + let mut result = String::with_capacity(1024); + let span = struct_parts.span; + + let header_str = struct_parts.format_header(context, offset); + result.push_str(&header_str); + + let body_lo = if fields.is_empty() { + let lo = get_bytepos_after_visibility(struct_parts.vis, span); + context + .snippet_provider + .span_after(mk_sp(lo, span.hi()), "(") + } else { + fields[0].span.lo() + }; + let body_hi = if fields.is_empty() { + context + .snippet_provider + .span_after(mk_sp(body_lo, span.hi()), ")") + } else { + // This is a dirty hack to work around a missing `)` from the span of the last field. + let last_arg_span = fields[fields.len() - 1].span; + context + .snippet_provider + .opt_span_after(mk_sp(last_arg_span.hi(), span.hi()), ")") + .unwrap_or_else(|| last_arg_span.hi()) + }; + + let where_clause_str = match struct_parts.generics { + Some(generics) => { + let budget = context.budget(last_line_width(&header_str)); + let shape = Shape::legacy(budget, offset); + let generics_str = rewrite_generics(context, "", generics, shape)?; + result.push_str(&generics_str); + + let where_budget = context.budget(last_line_width(&result)); + let option = WhereClauseOption::new(true, WhereClauseSpace::Newline); + rewrite_where_clause( + context, + &generics.where_clause.predicates, + generics.where_clause.span, + context.config.brace_style(), + Shape::legacy(where_budget, offset.block_only()), + false, + ";", + None, + body_hi, + option, + )? + } + None => "".to_owned(), + }; + + if fields.is_empty() { + let body_hi = context + .snippet_provider + .span_before(mk_sp(body_lo, span.hi()), ")"); + let inner_span = mk_sp(body_lo, body_hi); + format_empty_struct_or_tuple(context, inner_span, offset, &mut result, "(", ")"); + } else { + let shape = Shape::indented(offset, context.config).sub_width(1)?; + let lo = if let Some(generics) = struct_parts.generics { + generics.span.hi() + } else { + struct_parts.ident.span.hi() + }; + result = overflow::rewrite_with_parens( + context, + &result, + fields.iter(), + shape, + mk_sp(lo, span.hi()), + context.config.fn_call_width(), + None, + )?; + } + + if !where_clause_str.is_empty() + && !where_clause_str.contains('\n') + && (result.contains('\n') + || offset.block_indent + result.len() + where_clause_str.len() + 1 + > context.config.max_width()) + { + // We need to put the where-clause on a new line, but we didn't + // know that earlier, so the where-clause will not be indented properly. + result.push('\n'); + result.push_str( + &(offset.block_only() + (context.config.tab_spaces() - 1)).to_string(context.config), + ); + } + result.push_str(&where_clause_str); + + Some(result) +} + +pub(crate) enum ItemVisitorKind<'a> { + Item(&'a ast::Item), + AssocTraitItem(&'a ast::AssocItem), + AssocImplItem(&'a ast::AssocItem), + ForeignItem(&'a ast::ForeignItem), +} + +struct TyAliasRewriteInfo<'c, 'g>( + &'c RewriteContext<'c>, + Indent, + &'g ast::Generics, + (ast::TyAliasWhereClause, ast::TyAliasWhereClause), + usize, + symbol::Ident, + Span, +); + +pub(crate) fn rewrite_type_alias<'a, 'b>( + ty_alias_kind: &ast::TyAlias, + context: &RewriteContext<'a>, + indent: Indent, + visitor_kind: &ItemVisitorKind<'b>, + span: Span, +) -> Option { + use ItemVisitorKind::*; + + let ast::TyAlias { + defaultness, + ref generics, + ref bounds, + ref ty, + where_clauses, + where_predicates_split, + } = *ty_alias_kind; + let ty_opt = ty.as_ref(); + let (ident, vis) = match visitor_kind { + Item(i) => (i.ident, &i.vis), + AssocTraitItem(i) | AssocImplItem(i) => (i.ident, &i.vis), + ForeignItem(i) => (i.ident, &i.vis), + }; + let rw_info = &TyAliasRewriteInfo( + context, + indent, + generics, + where_clauses, + where_predicates_split, + ident, + span, + ); + let op_ty = opaque_ty(ty); + // Type Aliases are formatted slightly differently depending on the context + // in which they appear, whether they are opaque, and whether they are associated. + // https://rustc-dev-guide.rust-lang.org/opaque-types-type-alias-impl-trait.html + // https://github.com/rust-dev-tools/fmt-rfcs/blob/master/guide/items.md#type-aliases + match (visitor_kind, &op_ty) { + (Item(_) | AssocTraitItem(_) | ForeignItem(_), Some(op_bounds)) => { + let op = OpaqueType { bounds: op_bounds }; + rewrite_ty(rw_info, Some(bounds), Some(&op), vis) + } + (Item(_) | AssocTraitItem(_) | ForeignItem(_), None) => { + rewrite_ty(rw_info, Some(bounds), ty_opt, vis) + } + (AssocImplItem(_), _) => { + let result = if let Some(op_bounds) = op_ty { + let op = OpaqueType { bounds: op_bounds }; + rewrite_ty(rw_info, Some(bounds), Some(&op), &DEFAULT_VISIBILITY) + } else { + rewrite_ty(rw_info, Some(bounds), ty_opt, vis) + }?; + match defaultness { + ast::Defaultness::Default(..) => Some(format!("default {}", result)), + _ => Some(result), + } + } + } +} + +fn rewrite_ty( + rw_info: &TyAliasRewriteInfo<'_, '_>, + generic_bounds_opt: Option<&ast::GenericBounds>, + rhs: Option<&R>, + vis: &ast::Visibility, +) -> Option { + let mut result = String::with_capacity(128); + let TyAliasRewriteInfo( + context, + indent, + generics, + where_clauses, + where_predicates_split, + ident, + span, + ) = *rw_info; + let (before_where_predicates, after_where_predicates) = generics + .where_clause + .predicates + .split_at(where_predicates_split); + if !after_where_predicates.is_empty() { + return None; + } + result.push_str(&format!("{}type ", format_visibility(context, vis))); + let ident_str = rewrite_ident(context, ident); + + if generics.params.is_empty() { + result.push_str(ident_str) + } else { + // 2 = `= ` + let g_shape = Shape::indented(indent, context.config) + .offset_left(result.len())? + .sub_width(2)?; + let generics_str = rewrite_generics(context, ident_str, generics, g_shape)?; + result.push_str(&generics_str); + } + + if let Some(bounds) = generic_bounds_opt { + if !bounds.is_empty() { + // 2 = `: ` + let shape = Shape::indented(indent, context.config).offset_left(result.len() + 2)?; + let type_bounds = bounds.rewrite(context, shape).map(|s| format!(": {}", s))?; + result.push_str(&type_bounds); + } + } + + let where_budget = context.budget(last_line_width(&result)); + let mut option = WhereClauseOption::snuggled(&result); + if rhs.is_none() { + option.suppress_comma(); + } + let where_clause_str = rewrite_where_clause( + context, + before_where_predicates, + where_clauses.0.1, + context.config.brace_style(), + Shape::legacy(where_budget, indent), + false, + "=", + None, + generics.span.hi(), + option, + )?; + result.push_str(&where_clause_str); + + if let Some(ty) = rhs { + // If there's a where clause, add a newline before the assignment. Otherwise just add a + // space. + let has_where = !before_where_predicates.is_empty(); + if has_where { + result.push_str(&indent.to_string_with_newline(context.config)); + } else { + result.push(' '); + } + + let comment_span = context + .snippet_provider + .opt_span_before(span, "=") + .map(|op_lo| mk_sp(where_clauses.0.1.hi(), op_lo)); + + let lhs = match comment_span { + Some(comment_span) + if contains_comment(context.snippet_provider.span_to_snippet(comment_span)?) => + { + let comment_shape = if has_where { + Shape::indented(indent, context.config) + } else { + Shape::indented(indent, context.config) + .block_left(context.config.tab_spaces())? + }; + + combine_strs_with_missing_comments( + context, + result.trim_end(), + "=", + comment_span, + comment_shape, + true, + )? + } + _ => format!("{}=", result), + }; + + // 1 = `;` + let shape = Shape::indented(indent, context.config).sub_width(1)?; + rewrite_assign_rhs(context, lhs, &*ty, &RhsAssignKind::Ty, shape).map(|s| s + ";") + } else { + Some(format!("{};", result)) + } +} + +fn type_annotation_spacing(config: &Config) -> (&str, &str) { + ( + if config.space_before_colon() { " " } else { "" }, + if config.space_after_colon() { " " } else { "" }, + ) +} + +pub(crate) fn rewrite_struct_field_prefix( + context: &RewriteContext<'_>, + field: &ast::FieldDef, +) -> Option { + let vis = format_visibility(context, &field.vis); + let type_annotation_spacing = type_annotation_spacing(context.config); + Some(match field.ident { + Some(name) => format!( + "{}{}{}:", + vis, + rewrite_ident(context, name), + type_annotation_spacing.0 + ), + None => vis.to_string(), + }) +} + +impl Rewrite for ast::FieldDef { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + rewrite_struct_field(context, self, shape, 0) + } +} + +pub(crate) fn rewrite_struct_field( + context: &RewriteContext<'_>, + field: &ast::FieldDef, + shape: Shape, + lhs_max_width: usize, +) -> Option { + if contains_skip(&field.attrs) { + return Some(context.snippet(field.span()).to_owned()); + } + + let type_annotation_spacing = type_annotation_spacing(context.config); + let prefix = rewrite_struct_field_prefix(context, field)?; + + let attrs_str = field.attrs.rewrite(context, shape)?; + let attrs_extendable = field.ident.is_none() && is_attributes_extendable(&attrs_str); + let missing_span = if field.attrs.is_empty() { + mk_sp(field.span.lo(), field.span.lo()) + } else { + mk_sp(field.attrs.last().unwrap().span.hi(), field.span.lo()) + }; + let mut spacing = String::from(if field.ident.is_some() { + type_annotation_spacing.1 + } else { + "" + }); + // Try to put everything on a single line. + let attr_prefix = combine_strs_with_missing_comments( + context, + &attrs_str, + &prefix, + missing_span, + shape, + attrs_extendable, + )?; + let overhead = trimmed_last_line_width(&attr_prefix); + let lhs_offset = lhs_max_width.saturating_sub(overhead); + for _ in 0..lhs_offset { + spacing.push(' '); + } + // In this extreme case we will be missing a space between an attribute and a field. + if prefix.is_empty() && !attrs_str.is_empty() && attrs_extendable && spacing.is_empty() { + spacing.push(' '); + } + let orig_ty = shape + .offset_left(overhead + spacing.len()) + .and_then(|ty_shape| field.ty.rewrite(context, ty_shape)); + if let Some(ref ty) = orig_ty { + if !ty.contains('\n') && !contains_comment(context.snippet(missing_span)) { + return Some(attr_prefix + &spacing + ty); + } + } + + let is_prefix_empty = prefix.is_empty(); + // We must use multiline. We are going to put attributes and a field on different lines. + let field_str = rewrite_assign_rhs(context, prefix, &*field.ty, &RhsAssignKind::Ty, shape)?; + // Remove a leading white-space from `rewrite_assign_rhs()` when rewriting a tuple struct. + let field_str = if is_prefix_empty { + field_str.trim_start() + } else { + &field_str + }; + combine_strs_with_missing_comments(context, &attrs_str, field_str, missing_span, shape, false) +} + +pub(crate) struct StaticParts<'a> { + prefix: &'a str, + vis: &'a ast::Visibility, + ident: symbol::Ident, + ty: &'a ast::Ty, + mutability: ast::Mutability, + expr_opt: Option<&'a ptr::P>, + defaultness: Option, + span: Span, +} + +impl<'a> StaticParts<'a> { + pub(crate) fn from_item(item: &'a ast::Item) -> Self { + let (defaultness, prefix, ty, mutability, expr) = match &item.kind { + ast::ItemKind::Static(s) => (None, "static", &s.ty, s.mutability, &s.expr), + ast::ItemKind::Const(c) => ( + Some(c.defaultness), + "const", + &c.ty, + ast::Mutability::Not, + &c.expr, + ), + _ => unreachable!(), + }; + StaticParts { + prefix, + vis: &item.vis, + ident: item.ident, + ty, + mutability, + expr_opt: expr.as_ref(), + defaultness, + span: item.span, + } + } + + pub(crate) fn from_trait_item(ti: &'a ast::AssocItem) -> Self { + let (defaultness, ty, expr_opt) = match &ti.kind { + ast::AssocItemKind::Const(c) => (c.defaultness, &c.ty, &c.expr), + _ => unreachable!(), + }; + StaticParts { + prefix: "const", + vis: &ti.vis, + ident: ti.ident, + ty, + mutability: ast::Mutability::Not, + expr_opt: expr_opt.as_ref(), + defaultness: Some(defaultness), + span: ti.span, + } + } + + pub(crate) fn from_impl_item(ii: &'a ast::AssocItem) -> Self { + let (defaultness, ty, expr) = match &ii.kind { + ast::AssocItemKind::Const(c) => (c.defaultness, &c.ty, &c.expr), + _ => unreachable!(), + }; + StaticParts { + prefix: "const", + vis: &ii.vis, + ident: ii.ident, + ty, + mutability: ast::Mutability::Not, + expr_opt: expr.as_ref(), + defaultness: Some(defaultness), + span: ii.span, + } + } +} + +fn rewrite_static( + context: &RewriteContext<'_>, + static_parts: &StaticParts<'_>, + offset: Indent, +) -> Option { + let colon = colon_spaces(context.config); + let mut prefix = format!( + "{}{}{} {}{}{}", + format_visibility(context, static_parts.vis), + static_parts.defaultness.map_or("", format_defaultness), + static_parts.prefix, + format_mutability(static_parts.mutability), + rewrite_ident(context, static_parts.ident), + colon, + ); + // 2 = " =".len() + let ty_shape = + Shape::indented(offset.block_only(), context.config).offset_left(prefix.len() + 2)?; + let ty_str = match static_parts.ty.rewrite(context, ty_shape) { + Some(ty_str) => ty_str, + None => { + if prefix.ends_with(' ') { + prefix.pop(); + } + let nested_indent = offset.block_indent(context.config); + let nested_shape = Shape::indented(nested_indent, context.config); + let ty_str = static_parts.ty.rewrite(context, nested_shape)?; + format!( + "{}{}", + nested_indent.to_string_with_newline(context.config), + ty_str + ) + } + }; + + if let Some(expr) = static_parts.expr_opt { + let comments_lo = context.snippet_provider.span_after(static_parts.span, "="); + let expr_lo = expr.span.lo(); + let comments_span = mk_sp(comments_lo, expr_lo); + + let lhs = format!("{}{} =", prefix, ty_str); + + // 1 = ; + let remaining_width = context.budget(offset.block_indent + 1); + rewrite_assign_rhs_with_comments( + context, + &lhs, + &**expr, + Shape::legacy(remaining_width, offset.block_only()), + &RhsAssignKind::Expr(&expr.kind, expr.span), + RhsTactics::Default, + comments_span, + true, + ) + .and_then(|res| recover_comment_removed(res, static_parts.span, context)) + .map(|s| if s.ends_with(';') { s } else { s + ";" }) + } else { + Some(format!("{}{};", prefix, ty_str)) + } +} + +// FIXME(calebcartwright) - This is a hack around a bug in the handling of TyKind::ImplTrait. +// This should be removed once that bug is resolved, with the type alias formatting using the +// defined Ty for the RHS directly. +// https://github.com/rust-lang/rustfmt/issues/4373 +// https://github.com/rust-lang/rustfmt/issues/5027 +struct OpaqueType<'a> { + bounds: &'a ast::GenericBounds, +} + +impl<'a> Rewrite for OpaqueType<'a> { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + let shape = shape.offset_left(5)?; // `impl ` + self.bounds + .rewrite(context, shape) + .map(|s| format!("impl {}", s)) + } +} + +impl Rewrite for ast::FnRetTy { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + match *self { + ast::FnRetTy::Default(_) => Some(String::new()), + ast::FnRetTy::Ty(ref ty) => { + if context.config.version() == Version::One + || context.config.indent_style() == IndentStyle::Visual + { + let inner_width = shape.width.checked_sub(3)?; + return ty + .rewrite(context, Shape::legacy(inner_width, shape.indent + 3)) + .map(|r| format!("-> {}", r)); + } + + ty.rewrite(context, shape.offset_left(3)?) + .map(|s| format!("-> {}", s)) + } + } + } +} + +fn is_empty_infer(ty: &ast::Ty, pat_span: Span) -> bool { + match ty.kind { + ast::TyKind::Infer => ty.span.hi() == pat_span.hi(), + _ => false, + } +} + +/// Recover any missing comments between the param and the type. +/// +/// # Returns +/// +/// A 2-len tuple with the comment before the colon in first position, and the comment after the +/// colon in second position. +fn get_missing_param_comments( + context: &RewriteContext<'_>, + pat_span: Span, + ty_span: Span, + shape: Shape, +) -> (String, String) { + let missing_comment_span = mk_sp(pat_span.hi(), ty_span.lo()); + + let span_before_colon = { + let missing_comment_span_hi = context + .snippet_provider + .span_before(missing_comment_span, ":"); + mk_sp(pat_span.hi(), missing_comment_span_hi) + }; + let span_after_colon = { + let missing_comment_span_lo = context + .snippet_provider + .span_after(missing_comment_span, ":"); + mk_sp(missing_comment_span_lo, ty_span.lo()) + }; + + let comment_before_colon = rewrite_missing_comment(span_before_colon, shape, context) + .filter(|comment| !comment.is_empty()) + .map_or(String::new(), |comment| format!(" {}", comment)); + let comment_after_colon = rewrite_missing_comment(span_after_colon, shape, context) + .filter(|comment| !comment.is_empty()) + .map_or(String::new(), |comment| format!("{} ", comment)); + (comment_before_colon, comment_after_colon) +} + +impl Rewrite for ast::Param { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + let param_attrs_result = self + .attrs + .rewrite(context, Shape::legacy(shape.width, shape.indent))?; + // N.B. Doc comments aren't typically valid syntax, but could appear + // in the presence of certain macros - https://github.com/rust-lang/rustfmt/issues/4936 + let (span, has_multiple_attr_lines, has_doc_comments) = if !self.attrs.is_empty() { + let num_attrs = self.attrs.len(); + ( + mk_sp(self.attrs[num_attrs - 1].span.hi(), self.pat.span.lo()), + param_attrs_result.contains('\n'), + self.attrs.iter().any(|a| a.is_doc_comment()), + ) + } else { + (mk_sp(self.span.lo(), self.span.lo()), false, false) + }; + + if let Some(ref explicit_self) = self.to_self() { + rewrite_explicit_self( + context, + explicit_self, + ¶m_attrs_result, + span, + shape, + has_multiple_attr_lines, + ) + } else if is_named_param(self) { + let param_name = &self + .pat + .rewrite(context, Shape::legacy(shape.width, shape.indent))?; + let mut result = combine_strs_with_missing_comments( + context, + ¶m_attrs_result, + param_name, + span, + shape, + !has_multiple_attr_lines && !has_doc_comments, + )?; + + if !is_empty_infer(&*self.ty, self.pat.span) { + let (before_comment, after_comment) = + get_missing_param_comments(context, self.pat.span, self.ty.span, shape); + result.push_str(&before_comment); + result.push_str(colon_spaces(context.config)); + result.push_str(&after_comment); + let overhead = last_line_width(&result); + let max_width = shape.width.checked_sub(overhead)?; + if let Some(ty_str) = self + .ty + .rewrite(context, Shape::legacy(max_width, shape.indent)) + { + 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, + &prev_str, + param_name, + span, + shape, + !has_multiple_attr_lines, + )?; + result.push_str(&before_comment); + result.push_str(colon_spaces(context.config)); + result.push_str(&after_comment); + let overhead = last_line_width(&result); + let max_width = shape.width.checked_sub(overhead)?; + let ty_str = self + .ty + .rewrite(context, Shape::legacy(max_width, shape.indent))?; + result.push_str(&ty_str); + } + } + + Some(result) + } else { + self.ty.rewrite(context, shape) + } + } +} + +fn rewrite_explicit_self( + context: &RewriteContext<'_>, + explicit_self: &ast::ExplicitSelf, + param_attrs: &str, + span: Span, + shape: Shape, + has_multiple_attr_lines: bool, +) -> Option { + match explicit_self.node { + ast::SelfKind::Region(lt, m) => { + let mut_str = format_mutability(m); + match lt { + Some(ref l) => { + let lifetime_str = l.rewrite( + context, + Shape::legacy(context.config.max_width(), Indent::empty()), + )?; + Some(combine_strs_with_missing_comments( + context, + param_attrs, + &format!("&{} {}self", lifetime_str, mut_str), + span, + shape, + !has_multiple_attr_lines, + )?) + } + None => Some(combine_strs_with_missing_comments( + context, + param_attrs, + &format!("&{}self", mut_str), + span, + shape, + !has_multiple_attr_lines, + )?), + } + } + ast::SelfKind::Explicit(ref ty, mutability) => { + let type_str = ty.rewrite( + context, + Shape::legacy(context.config.max_width(), Indent::empty()), + )?; + + Some(combine_strs_with_missing_comments( + context, + param_attrs, + &format!("{}self: {}", format_mutability(mutability), type_str), + span, + shape, + !has_multiple_attr_lines, + )?) + } + ast::SelfKind::Value(mutability) => Some(combine_strs_with_missing_comments( + context, + param_attrs, + &format!("{}self", format_mutability(mutability)), + span, + shape, + !has_multiple_attr_lines, + )?), + } +} + +pub(crate) fn span_lo_for_param(param: &ast::Param) -> BytePos { + if param.attrs.is_empty() { + if is_named_param(param) { + param.pat.span.lo() + } else { + param.ty.span.lo() + } + } else { + param.attrs[0].span.lo() + } +} + +pub(crate) fn span_hi_for_param(context: &RewriteContext<'_>, param: &ast::Param) -> BytePos { + match param.ty.kind { + ast::TyKind::Infer if context.snippet(param.ty.span) == "_" => param.ty.span.hi(), + ast::TyKind::Infer if is_named_param(param) => param.pat.span.hi(), + _ => param.ty.span.hi(), + } +} + +pub(crate) fn is_named_param(param: &ast::Param) -> bool { + if let ast::PatKind::Ident(_, ident, _) = param.pat.kind { + ident.name != symbol::kw::Empty + } else { + true + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub(crate) enum FnBraceStyle { + SameLine, + NextLine, + None, +} + +// Return type is (result, force_new_line_for_brace) +fn rewrite_fn_base( + context: &RewriteContext<'_>, + indent: Indent, + ident: symbol::Ident, + fn_sig: &FnSig<'_>, + span: Span, + fn_brace_style: FnBraceStyle, +) -> Option<(String, bool, bool)> { + let mut force_new_line_for_brace = false; + + let where_clause = &fn_sig.generics.where_clause; + + let mut result = String::with_capacity(1024); + result.push_str(&fn_sig.to_str(context)); + + // fn foo + result.push_str("fn "); + + // Generics. + let overhead = if let FnBraceStyle::SameLine = fn_brace_style { + // 4 = `() {` + 4 + } else { + // 2 = `()` + 2 + }; + let used_width = last_line_used_width(&result, indent.width()); + let one_line_budget = context.budget(used_width + overhead); + let shape = Shape { + width: one_line_budget, + indent, + offset: used_width, + }; + let fd = fn_sig.decl; + let generics_str = rewrite_generics( + context, + rewrite_ident(context, ident), + &fn_sig.generics, + shape, + )?; + result.push_str(&generics_str); + + let snuggle_angle_bracket = generics_str + .lines() + .last() + .map_or(false, |l| l.trim_start().len() == 1); + + // Note that the width and indent don't really matter, we'll re-layout the + // return type later anyway. + let ret_str = fd + .output + .rewrite(context, Shape::indented(indent, context.config))?; + + let multi_line_ret_str = ret_str.contains('\n'); + let ret_str_len = if multi_line_ret_str { 0 } else { ret_str.len() }; + + // Params. + let (one_line_budget, multi_line_budget, mut param_indent) = compute_budgets_for_params( + context, + &result, + indent, + ret_str_len, + fn_brace_style, + multi_line_ret_str, + )?; + + debug!( + "rewrite_fn_base: one_line_budget: {}, multi_line_budget: {}, param_indent: {:?}", + one_line_budget, multi_line_budget, param_indent + ); + + result.push('('); + // Check if vertical layout was forced. + if one_line_budget == 0 + && !snuggle_angle_bracket + && context.config.indent_style() == IndentStyle::Visual + { + result.push_str(¶m_indent.to_string_with_newline(context.config)); + } + + let params_end = if fd.inputs.is_empty() { + context + .snippet_provider + .span_after(mk_sp(fn_sig.generics.span.hi(), span.hi()), ")") + } else { + let last_span = mk_sp(fd.inputs[fd.inputs.len() - 1].span().hi(), span.hi()); + context.snippet_provider.span_after(last_span, ")") + }; + let params_span = mk_sp( + context + .snippet_provider + .span_after(mk_sp(fn_sig.generics.span.hi(), span.hi()), "("), + params_end, + ); + let param_str = rewrite_params( + context, + &fd.inputs, + one_line_budget, + multi_line_budget, + indent, + param_indent, + params_span, + fd.c_variadic(), + )?; + + let put_params_in_block = match context.config.indent_style() { + IndentStyle::Block => param_str.contains('\n') || param_str.len() > one_line_budget, + _ => false, + } && !fd.inputs.is_empty(); + + let mut params_last_line_contains_comment = false; + let mut no_params_and_over_max_width = false; + + if put_params_in_block { + param_indent = indent.block_indent(context.config); + result.push_str(¶m_indent.to_string_with_newline(context.config)); + result.push_str(¶m_str); + result.push_str(&indent.to_string_with_newline(context.config)); + result.push(')'); + } else { + result.push_str(¶m_str); + let used_width = last_line_used_width(&result, indent.width()) + first_line_width(&ret_str); + // Put the closing brace on the next line if it overflows the max width. + // 1 = `)` + let closing_paren_overflow_max_width = + fd.inputs.is_empty() && used_width + 1 > context.config.max_width(); + // If the last line of params contains comment, we cannot put the closing paren + // on the same line. + params_last_line_contains_comment = param_str + .lines() + .last() + .map_or(false, |last_line| last_line.contains("//")); + + if context.config.version() == Version::Two { + if closing_paren_overflow_max_width { + result.push(')'); + result.push_str(&indent.to_string_with_newline(context.config)); + no_params_and_over_max_width = true; + } else if params_last_line_contains_comment { + result.push_str(&indent.to_string_with_newline(context.config)); + result.push(')'); + no_params_and_over_max_width = true; + } else { + result.push(')'); + } + } else { + if closing_paren_overflow_max_width || params_last_line_contains_comment { + result.push_str(&indent.to_string_with_newline(context.config)); + } + result.push(')'); + } + } + + // Return type. + if let ast::FnRetTy::Ty(..) = fd.output { + let ret_should_indent = match context.config.indent_style() { + // If our params are block layout then we surely must have space. + IndentStyle::Block if put_params_in_block || fd.inputs.is_empty() => false, + _ if params_last_line_contains_comment => false, + _ if result.contains('\n') || multi_line_ret_str => true, + _ => { + // If the return type would push over the max width, then put the return type on + // a new line. With the +1 for the signature length an additional space between + // the closing parenthesis of the param and the arrow '->' is considered. + let mut sig_length = result.len() + indent.width() + ret_str_len + 1; + + // If there is no where-clause, take into account the space after the return type + // and the brace. + if where_clause.predicates.is_empty() { + sig_length += 2; + } + + sig_length > context.config.max_width() + } + }; + let ret_shape = if ret_should_indent { + if context.config.version() == Version::One + || context.config.indent_style() == IndentStyle::Visual + { + let indent = if param_str.is_empty() { + // Aligning with non-existent params looks silly. + force_new_line_for_brace = true; + indent + 4 + } else { + // FIXME: we might want to check that using the param indent + // doesn't blow our budget, and if it does, then fallback to + // the where-clause indent. + param_indent + }; + + result.push_str(&indent.to_string_with_newline(context.config)); + Shape::indented(indent, context.config) + } else { + let mut ret_shape = Shape::indented(indent, context.config); + if param_str.is_empty() { + // Aligning with non-existent params looks silly. + force_new_line_for_brace = true; + ret_shape = if context.use_block_indent() { + ret_shape.offset_left(4).unwrap_or(ret_shape) + } else { + ret_shape.indent = ret_shape.indent + 4; + ret_shape + }; + } + + result.push_str(&ret_shape.indent.to_string_with_newline(context.config)); + ret_shape + } + } else { + if context.config.version() == Version::Two { + if !param_str.is_empty() || !no_params_and_over_max_width { + result.push(' '); + } + } else { + result.push(' '); + } + + let ret_shape = Shape::indented(indent, context.config); + ret_shape + .offset_left(last_line_width(&result)) + .unwrap_or(ret_shape) + }; + + if multi_line_ret_str || ret_should_indent { + // Now that we know the proper indent and width, we need to + // re-layout the return type. + let ret_str = fd.output.rewrite(context, ret_shape)?; + result.push_str(&ret_str); + } else { + result.push_str(&ret_str); + } + + // Comment between return type and the end of the decl. + let snippet_lo = fd.output.span().hi(); + if where_clause.predicates.is_empty() { + let snippet_hi = span.hi(); + let snippet = context.snippet(mk_sp(snippet_lo, snippet_hi)); + // Try to preserve the layout of the original snippet. + let original_starts_with_newline = snippet + .find(|c| c != ' ') + .map_or(false, |i| starts_with_newline(&snippet[i..])); + let original_ends_with_newline = snippet + .rfind(|c| c != ' ') + .map_or(false, |i| snippet[i..].ends_with('\n')); + let snippet = snippet.trim(); + if !snippet.is_empty() { + result.push(if original_starts_with_newline { + '\n' + } else { + ' ' + }); + result.push_str(snippet); + if original_ends_with_newline { + force_new_line_for_brace = true; + } + } + } + } + + let pos_before_where = match fd.output { + ast::FnRetTy::Default(..) => params_span.hi(), + ast::FnRetTy::Ty(ref ty) => ty.span.hi(), + }; + + let is_params_multi_lined = param_str.contains('\n'); + + let space = if put_params_in_block && ret_str.is_empty() { + WhereClauseSpace::Space + } else { + WhereClauseSpace::Newline + }; + let mut option = WhereClauseOption::new(fn_brace_style == FnBraceStyle::None, space); + if is_params_multi_lined { + option.veto_single_line(); + } + let where_clause_str = rewrite_where_clause( + context, + &where_clause.predicates, + where_clause.span, + context.config.brace_style(), + Shape::indented(indent, context.config), + true, + "{", + Some(span.hi()), + pos_before_where, + option, + )?; + // If there are neither where-clause nor return type, we may be missing comments between + // params and `{`. + if where_clause_str.is_empty() { + if let ast::FnRetTy::Default(ret_span) = fd.output { + match recover_missing_comment_in_span( + mk_sp(params_span.hi(), ret_span.hi()), + shape, + context, + last_line_width(&result), + ) { + Some(ref missing_comment) if !missing_comment.is_empty() => { + result.push_str(missing_comment); + force_new_line_for_brace = true; + } + _ => (), + } + } + } + + result.push_str(&where_clause_str); + + let ends_with_comment = last_line_contains_single_line_comment(&result); + force_new_line_for_brace |= ends_with_comment; + force_new_line_for_brace |= + is_params_multi_lined && context.config.where_single_line() && !where_clause_str.is_empty(); + Some((result, ends_with_comment, force_new_line_for_brace)) +} + +/// Kind of spaces to put before `where`. +#[derive(Copy, Clone)] +enum WhereClauseSpace { + /// A single space. + Space, + /// A new line. + Newline, + /// Nothing. + None, +} + +#[derive(Copy, Clone)] +struct WhereClauseOption { + suppress_comma: bool, // Force no trailing comma + snuggle: WhereClauseSpace, + allow_single_line: bool, // Try single line where-clause instead of vertical layout + veto_single_line: bool, // Disallow a single-line where-clause. +} + +impl WhereClauseOption { + fn new(suppress_comma: bool, snuggle: WhereClauseSpace) -> WhereClauseOption { + WhereClauseOption { + suppress_comma, + snuggle, + allow_single_line: false, + veto_single_line: false, + } + } + + fn snuggled(current: &str) -> WhereClauseOption { + WhereClauseOption { + suppress_comma: false, + snuggle: if last_line_width(current) == 1 { + WhereClauseSpace::Space + } else { + WhereClauseSpace::Newline + }, + allow_single_line: false, + veto_single_line: false, + } + } + + fn suppress_comma(&mut self) { + self.suppress_comma = true + } + + fn allow_single_line(&mut self) { + self.allow_single_line = true + } + + fn snuggle(&mut self) { + self.snuggle = WhereClauseSpace::Space + } + + fn veto_single_line(&mut self) { + self.veto_single_line = true; + } +} + +fn rewrite_params( + context: &RewriteContext<'_>, + params: &[ast::Param], + one_line_budget: usize, + multi_line_budget: usize, + indent: Indent, + param_indent: Indent, + span: Span, + variadic: bool, +) -> Option { + if params.is_empty() { + let comment = context + .snippet(mk_sp( + span.lo(), + // to remove ')' + span.hi() - BytePos(1), + )) + .trim(); + return Some(comment.to_owned()); + } + let param_items: Vec<_> = itemize_list( + context.snippet_provider, + params.iter(), + ")", + ",", + |param| span_lo_for_param(param), + |param| param.ty.span.hi(), + |param| { + param + .rewrite(context, Shape::legacy(multi_line_budget, param_indent)) + .or_else(|| Some(context.snippet(param.span()).to_owned())) + }, + span.lo(), + span.hi(), + false, + ) + .collect(); + + let tactic = definitive_tactic( + ¶m_items, + context + .config + .fn_params_layout() + .to_list_tactic(param_items.len()), + Separator::Comma, + one_line_budget, + ); + let budget = match tactic { + DefinitiveListTactic::Horizontal => one_line_budget, + _ => multi_line_budget, + }; + let indent = match context.config.indent_style() { + IndentStyle::Block => indent.block_indent(context.config), + IndentStyle::Visual => param_indent, + }; + let trailing_separator = if variadic { + SeparatorTactic::Never + } else { + match context.config.indent_style() { + IndentStyle::Block => context.config.trailing_comma(), + IndentStyle::Visual => SeparatorTactic::Never, + } + }; + let fmt = ListFormatting::new(Shape::legacy(budget, indent), context.config) + .tactic(tactic) + .trailing_separator(trailing_separator) + .ends_with_newline(tactic.ends_with_newline(context.config.indent_style())) + .preserve_newline(true); + write_list(¶m_items, &fmt) +} + +fn compute_budgets_for_params( + context: &RewriteContext<'_>, + result: &str, + indent: Indent, + ret_str_len: usize, + fn_brace_style: FnBraceStyle, + force_vertical_layout: bool, +) -> Option<(usize, usize, Indent)> { + debug!( + "compute_budgets_for_params {} {:?}, {}, {:?}", + result.len(), + indent, + ret_str_len, + fn_brace_style, + ); + // Try keeping everything on the same line. + if !result.contains('\n') && !force_vertical_layout { + // 2 = `()`, 3 = `() `, space is before ret_string. + let overhead = if ret_str_len == 0 { 2 } else { 3 }; + let mut used_space = indent.width() + result.len() + ret_str_len + overhead; + match fn_brace_style { + FnBraceStyle::None => used_space += 1, // 1 = `;` + FnBraceStyle::SameLine => used_space += 2, // 2 = `{}` + FnBraceStyle::NextLine => (), + } + let one_line_budget = context.budget(used_space); + + if one_line_budget > 0 { + // 4 = "() {".len() + let (indent, multi_line_budget) = match context.config.indent_style() { + IndentStyle::Block => { + let indent = indent.block_indent(context.config); + (indent, context.budget(indent.width() + 1)) + } + IndentStyle::Visual => { + let indent = indent + result.len() + 1; + let multi_line_overhead = match fn_brace_style { + FnBraceStyle::SameLine => 4, + _ => 2, + } + indent.width(); + (indent, context.budget(multi_line_overhead)) + } + }; + + return Some((one_line_budget, multi_line_budget, indent)); + } + } + + // Didn't work. we must force vertical layout and put params on a newline. + let new_indent = indent.block_indent(context.config); + let used_space = match context.config.indent_style() { + // 1 = `,` + IndentStyle::Block => new_indent.width() + 1, + // Account for `)` and possibly ` {`. + IndentStyle::Visual => new_indent.width() + if ret_str_len == 0 { 1 } else { 3 }, + }; + Some((0, context.budget(used_space), new_indent)) +} + +fn newline_for_brace(config: &Config, where_clause: &ast::WhereClause) -> FnBraceStyle { + let predicate_count = where_clause.predicates.len(); + + if config.where_single_line() && predicate_count == 1 { + return FnBraceStyle::SameLine; + } + let brace_style = config.brace_style(); + + let use_next_line = brace_style == BraceStyle::AlwaysNextLine + || (brace_style == BraceStyle::SameLineWhere && predicate_count > 0); + if use_next_line { + FnBraceStyle::NextLine + } else { + FnBraceStyle::SameLine + } +} + +fn rewrite_generics( + context: &RewriteContext<'_>, + ident: &str, + generics: &ast::Generics, + shape: Shape, +) -> Option { + // FIXME: convert bounds to where-clauses where they get too big or if + // there is a where-clause at all. + + if generics.params.is_empty() { + return Some(ident.to_owned()); + } + + let params = generics.params.iter(); + overflow::rewrite_with_angle_brackets(context, ident, params, shape, generics.span) +} + +fn generics_shape_from_config(config: &Config, shape: Shape, offset: usize) -> Option { + match config.indent_style() { + IndentStyle::Visual => shape.visual_indent(1 + offset).sub_width(offset + 2), + IndentStyle::Block => { + // 1 = "," + shape + .block() + .block_indent(config.tab_spaces()) + .with_max_width(config) + .sub_width(1) + } + } +} + +fn rewrite_where_clause_rfc_style( + context: &RewriteContext<'_>, + predicates: &[ast::WherePredicate], + where_span: Span, + shape: Shape, + terminator: &str, + span_end: Option, + span_end_before_where: BytePos, + where_clause_option: WhereClauseOption, +) -> Option { + let (where_keyword, allow_single_line) = rewrite_where_keyword( + context, + predicates, + where_span, + shape, + span_end_before_where, + where_clause_option, + )?; + + // 1 = `,` + let clause_shape = shape + .block() + .with_max_width(context.config) + .block_left(context.config.tab_spaces())? + .sub_width(1)?; + let force_single_line = context.config.where_single_line() + && predicates.len() == 1 + && !where_clause_option.veto_single_line; + + let preds_str = rewrite_bounds_on_where_clause( + context, + predicates, + clause_shape, + terminator, + span_end, + where_clause_option, + force_single_line, + )?; + + // 6 = `where ` + let clause_sep = + if allow_single_line && !preds_str.contains('\n') && 6 + preds_str.len() <= shape.width + || force_single_line + { + Cow::from(" ") + } else { + clause_shape.indent.to_string_with_newline(context.config) + }; + + Some(format!("{}{}{}", where_keyword, clause_sep, preds_str)) +} + +/// Rewrite `where` and comment around it. +fn rewrite_where_keyword( + context: &RewriteContext<'_>, + predicates: &[ast::WherePredicate], + where_span: Span, + shape: Shape, + span_end_before_where: BytePos, + where_clause_option: WhereClauseOption, +) -> Option<(String, bool)> { + let block_shape = shape.block().with_max_width(context.config); + // 1 = `,` + let clause_shape = block_shape + .block_left(context.config.tab_spaces())? + .sub_width(1)?; + + let comment_separator = |comment: &str, shape: Shape| { + if comment.is_empty() { + Cow::from("") + } else { + shape.indent.to_string_with_newline(context.config) + } + }; + + let (span_before, span_after) = + missing_span_before_after_where(span_end_before_where, predicates, where_span); + let (comment_before, comment_after) = + rewrite_comments_before_after_where(context, span_before, span_after, shape)?; + + let starting_newline = match where_clause_option.snuggle { + WhereClauseSpace::Space if comment_before.is_empty() => Cow::from(" "), + WhereClauseSpace::None => Cow::from(""), + _ => block_shape.indent.to_string_with_newline(context.config), + }; + + let newline_before_where = comment_separator(&comment_before, shape); + let newline_after_where = comment_separator(&comment_after, clause_shape); + let result = format!( + "{}{}{}where{}{}", + starting_newline, comment_before, newline_before_where, newline_after_where, comment_after + ); + let allow_single_line = where_clause_option.allow_single_line + && comment_before.is_empty() + && comment_after.is_empty(); + + Some((result, allow_single_line)) +} + +/// Rewrite bounds on a where clause. +fn rewrite_bounds_on_where_clause( + context: &RewriteContext<'_>, + predicates: &[ast::WherePredicate], + shape: Shape, + terminator: &str, + span_end: Option, + where_clause_option: WhereClauseOption, + force_single_line: bool, +) -> Option { + let span_start = predicates[0].span().lo(); + // If we don't have the start of the next span, then use the end of the + // predicates, but that means we miss comments. + let len = predicates.len(); + let end_of_preds = predicates[len - 1].span().hi(); + let span_end = span_end.unwrap_or(end_of_preds); + let items = itemize_list( + context.snippet_provider, + predicates.iter(), + terminator, + ",", + |pred| pred.span().lo(), + |pred| pred.span().hi(), + |pred| pred.rewrite(context, shape), + span_start, + span_end, + false, + ); + let comma_tactic = if where_clause_option.suppress_comma || force_single_line { + SeparatorTactic::Never + } else { + context.config.trailing_comma() + }; + + // shape should be vertical only and only if we have `force_single_line` option enabled + // and the number of items of the where-clause is equal to 1 + let shape_tactic = if force_single_line { + DefinitiveListTactic::Horizontal + } else { + DefinitiveListTactic::Vertical + }; + + let fmt = ListFormatting::new(shape, context.config) + .tactic(shape_tactic) + .trailing_separator(comma_tactic) + .preserve_newline(true); + write_list(&items.collect::>(), &fmt) +} + +fn rewrite_where_clause( + context: &RewriteContext<'_>, + predicates: &[ast::WherePredicate], + where_span: Span, + brace_style: BraceStyle, + shape: Shape, + on_new_line: bool, + terminator: &str, + span_end: Option, + span_end_before_where: BytePos, + where_clause_option: WhereClauseOption, +) -> Option { + if predicates.is_empty() { + return Some(String::new()); + } + + if context.config.indent_style() == IndentStyle::Block { + return rewrite_where_clause_rfc_style( + context, + predicates, + where_span, + shape, + terminator, + span_end, + span_end_before_where, + where_clause_option, + ); + } + + let extra_indent = Indent::new(context.config.tab_spaces(), 0); + + let offset = match context.config.indent_style() { + IndentStyle::Block => shape.indent + extra_indent.block_indent(context.config), + // 6 = "where ".len() + IndentStyle::Visual => shape.indent + extra_indent + 6, + }; + // FIXME: if indent_style != Visual, then the budgets below might + // be out by a char or two. + + let budget = context.config.max_width() - offset.width(); + let span_start = predicates[0].span().lo(); + // If we don't have the start of the next span, then use the end of the + // predicates, but that means we miss comments. + let len = predicates.len(); + let end_of_preds = predicates[len - 1].span().hi(); + let span_end = span_end.unwrap_or(end_of_preds); + let items = itemize_list( + context.snippet_provider, + predicates.iter(), + terminator, + ",", + |pred| pred.span().lo(), + |pred| pred.span().hi(), + |pred| pred.rewrite(context, Shape::legacy(budget, offset)), + span_start, + span_end, + false, + ); + let item_vec = items.collect::>(); + // FIXME: we don't need to collect here + let tactic = definitive_tactic(&item_vec, ListTactic::Vertical, Separator::Comma, budget); + + let mut comma_tactic = context.config.trailing_comma(); + // Kind of a hack because we don't usually have trailing commas in where-clauses. + if comma_tactic == SeparatorTactic::Vertical || where_clause_option.suppress_comma { + comma_tactic = SeparatorTactic::Never; + } + + let fmt = ListFormatting::new(Shape::legacy(budget, offset), context.config) + .tactic(tactic) + .trailing_separator(comma_tactic) + .ends_with_newline(tactic.ends_with_newline(context.config.indent_style())) + .preserve_newline(true); + let preds_str = write_list(&item_vec, &fmt)?; + + let end_length = if terminator == "{" { + // If the brace is on the next line we don't need to count it otherwise it needs two + // characters " {" + match brace_style { + BraceStyle::AlwaysNextLine | BraceStyle::SameLineWhere => 0, + BraceStyle::PreferSameLine => 2, + } + } else if terminator == "=" { + 2 + } else { + terminator.len() + }; + if on_new_line + || preds_str.contains('\n') + || shape.indent.width() + " where ".len() + preds_str.len() + end_length > shape.width + { + Some(format!( + "\n{}where {}", + (shape.indent + extra_indent).to_string(context.config), + preds_str + )) + } else { + Some(format!(" where {}", preds_str)) + } +} + +fn missing_span_before_after_where( + before_item_span_end: BytePos, + predicates: &[ast::WherePredicate], + where_span: Span, +) -> (Span, Span) { + let missing_span_before = mk_sp(before_item_span_end, where_span.lo()); + // 5 = `where` + let pos_after_where = where_span.lo() + BytePos(5); + let missing_span_after = mk_sp(pos_after_where, predicates[0].span().lo()); + (missing_span_before, missing_span_after) +} + +fn rewrite_comments_before_after_where( + context: &RewriteContext<'_>, + span_before_where: Span, + span_after_where: Span, + shape: Shape, +) -> Option<(String, String)> { + let before_comment = rewrite_missing_comment(span_before_where, shape, context)?; + let after_comment = rewrite_missing_comment( + span_after_where, + shape.block_indent(context.config.tab_spaces()), + context, + )?; + Some((before_comment, after_comment)) +} + +fn format_header( + context: &RewriteContext<'_>, + item_name: &str, + ident: symbol::Ident, + vis: &ast::Visibility, + offset: Indent, +) -> String { + let mut result = String::with_capacity(128); + let shape = Shape::indented(offset, context.config); + + result.push_str(format_visibility(context, vis).trim()); + + // Check for a missing comment between the visibility and the item name. + let after_vis = vis.span.hi(); + if let Some(before_item_name) = context + .snippet_provider + .opt_span_before(mk_sp(vis.span.lo(), ident.span.hi()), item_name.trim()) + { + let missing_span = mk_sp(after_vis, before_item_name); + if let Some(result_with_comment) = combine_strs_with_missing_comments( + context, + &result, + item_name, + missing_span, + shape, + /* allow_extend */ true, + ) { + result = result_with_comment; + } + } + + result.push_str(rewrite_ident(context, ident)); + + result +} + +#[derive(PartialEq, Eq, Clone, Copy)] +enum BracePos { + None, + Auto, + ForceSameLine, +} + +fn format_generics( + context: &RewriteContext<'_>, + generics: &ast::Generics, + brace_style: BraceStyle, + brace_pos: BracePos, + offset: Indent, + span: Span, + used_width: usize, +) -> Option { + let shape = Shape::legacy(context.budget(used_width + offset.width()), offset); + let mut result = rewrite_generics(context, "", generics, shape)?; + + // If the generics are not parameterized then generics.span.hi() == 0, + // so we use span.lo(), which is the position after `struct Foo`. + let span_end_before_where = if !generics.params.is_empty() { + generics.span.hi() + } else { + span.lo() + }; + let (same_line_brace, missed_comments) = if !generics.where_clause.predicates.is_empty() { + let budget = context.budget(last_line_used_width(&result, offset.width())); + let mut option = WhereClauseOption::snuggled(&result); + if brace_pos == BracePos::None { + option.suppress_comma = true; + } + let where_clause_str = rewrite_where_clause( + context, + &generics.where_clause.predicates, + generics.where_clause.span, + brace_style, + Shape::legacy(budget, offset.block_only()), + true, + "{", + Some(span.hi()), + span_end_before_where, + option, + )?; + result.push_str(&where_clause_str); + ( + brace_pos == BracePos::ForceSameLine || brace_style == BraceStyle::PreferSameLine, + // missed comments are taken care of in #rewrite_where_clause + None, + ) + } else { + ( + brace_pos == BracePos::ForceSameLine + || (result.contains('\n') && brace_style == BraceStyle::PreferSameLine + || brace_style != BraceStyle::AlwaysNextLine) + || trimmed_last_line_width(&result) == 1, + rewrite_missing_comment( + mk_sp( + span_end_before_where, + if brace_pos == BracePos::None { + span.hi() + } else { + context.snippet_provider.span_before(span, "{") + }, + ), + shape, + context, + ), + ) + }; + // add missing comments + let missed_line_comments = missed_comments + .filter(|missed_comments| !missed_comments.is_empty()) + .map_or(false, |missed_comments| { + let is_block = is_last_comment_block(&missed_comments); + let sep = if is_block { " " } else { "\n" }; + result.push_str(sep); + result.push_str(&missed_comments); + !is_block + }); + if brace_pos == BracePos::None { + return Some(result); + } + let total_used_width = last_line_used_width(&result, used_width); + let remaining_budget = context.budget(total_used_width); + // If the same line brace if forced, it indicates that we are rewriting an item with empty body, + // and hence we take the closer into account as well for one line budget. + // We assume that the closer has the same length as the opener. + let overhead = if brace_pos == BracePos::ForceSameLine { + // 3 = ` {}` + 3 + } else { + // 2 = ` {` + 2 + }; + let forbid_same_line_brace = missed_line_comments || overhead > remaining_budget; + if !forbid_same_line_brace && same_line_brace { + result.push(' '); + } else { + result.push('\n'); + result.push_str(&offset.block_only().to_string(context.config)); + } + result.push('{'); + + Some(result) +} + +impl Rewrite for ast::ForeignItem { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + let attrs_str = self.attrs.rewrite(context, shape)?; + // Drop semicolon or it will be interpreted as comment. + // FIXME: this may be a faulty span from libsyntax. + let span = mk_sp(self.span.lo(), self.span.hi() - BytePos(1)); + + let item_str = match self.kind { + ast::ForeignItemKind::Fn(ref fn_kind) => { + let ast::Fn { + defaultness, + ref sig, + ref generics, + ref body, + } = **fn_kind; + if let Some(ref body) = body { + let mut visitor = FmtVisitor::from_context(context); + visitor.block_indent = shape.indent; + visitor.last_pos = self.span.lo(); + let inner_attrs = inner_attributes(&self.attrs); + let fn_ctxt = visit::FnCtxt::Foreign; + visitor.visit_fn( + visit::FnKind::Fn( + fn_ctxt, + self.ident, + sig, + &self.vis, + generics, + Some(body), + ), + &sig.decl, + self.span, + defaultness, + Some(&inner_attrs), + ); + Some(visitor.buffer.to_owned()) + } else { + rewrite_fn_base( + context, + shape.indent, + self.ident, + &FnSig::from_method_sig(sig, generics, &self.vis), + span, + FnBraceStyle::None, + ) + .map(|(s, _, _)| format!("{};", s)) + } + } + ast::ForeignItemKind::Static(ref ty, mutability, _) => { + // FIXME(#21): we're dropping potential comments in between the + // function kw here. + let vis = format_visibility(context, &self.vis); + let mut_str = format_mutability(mutability); + let prefix = format!( + "{}static {}{}:", + vis, + mut_str, + rewrite_ident(context, self.ident) + ); + // 1 = ; + rewrite_assign_rhs( + context, + prefix, + &**ty, + &RhsAssignKind::Ty, + shape.sub_width(1)?, + ) + .map(|s| s + ";") + } + ast::ForeignItemKind::TyAlias(ref ty_alias) => { + let (kind, span) = (&ItemVisitorKind::ForeignItem(self), self.span); + rewrite_type_alias(ty_alias, context, shape.indent, kind, span) + } + ast::ForeignItemKind::MacCall(ref mac) => { + rewrite_macro(mac, None, context, shape, MacroPosition::Item) + } + }?; + + let missing_span = if self.attrs.is_empty() { + mk_sp(self.span.lo(), self.span.lo()) + } else { + mk_sp(self.attrs[self.attrs.len() - 1].span.hi(), self.span.lo()) + }; + combine_strs_with_missing_comments( + context, + &attrs_str, + &item_str, + missing_span, + shape, + false, + ) + } +} + +/// Rewrite the attributes of an item. +fn rewrite_attrs( + context: &RewriteContext<'_>, + item: &ast::Item, + item_str: &str, + shape: Shape, +) -> Option { + let attrs = filter_inline_attrs(&item.attrs, item.span()); + let attrs_str = attrs.rewrite(context, shape)?; + + let missed_span = if attrs.is_empty() { + mk_sp(item.span.lo(), item.span.lo()) + } else { + mk_sp(attrs[attrs.len() - 1].span.hi(), item.span.lo()) + }; + + let allow_extend = if attrs.len() == 1 { + let line_len = attrs_str.len() + 1 + item_str.len(); + !attrs.first().unwrap().is_doc_comment() + && context.config.inline_attribute_width() >= line_len + } else { + false + }; + + combine_strs_with_missing_comments( + context, + &attrs_str, + item_str, + missed_span, + shape, + allow_extend, + ) +} + +/// Rewrite an inline mod. +/// The given shape is used to format the mod's attributes. +pub(crate) fn rewrite_mod( + context: &RewriteContext<'_>, + item: &ast::Item, + attrs_shape: Shape, +) -> Option { + let mut result = String::with_capacity(32); + result.push_str(&*format_visibility(context, &item.vis)); + result.push_str("mod "); + result.push_str(rewrite_ident(context, item.ident)); + result.push(';'); + rewrite_attrs(context, item, &result, attrs_shape) +} + +/// Rewrite `extern crate foo;`. +/// The given shape is used to format the extern crate's attributes. +pub(crate) fn rewrite_extern_crate( + context: &RewriteContext<'_>, + item: &ast::Item, + attrs_shape: Shape, +) -> Option { + assert!(is_extern_crate(item)); + let new_str = context.snippet(item.span); + let item_str = if contains_comment(new_str) { + new_str.to_owned() + } else { + let no_whitespace = &new_str.split_whitespace().collect::>().join(" "); + String::from(&*Regex::new(r"\s;").unwrap().replace(no_whitespace, ";")) + }; + rewrite_attrs(context, item, &item_str, attrs_shape) +} + +/// Returns `true` for `mod foo;`, false for `mod foo { .. }`. +pub(crate) fn is_mod_decl(item: &ast::Item) -> bool { + !matches!( + item.kind, + ast::ItemKind::Mod(_, ast::ModKind::Loaded(_, ast::Inline::Yes, _)) + ) +} + +pub(crate) fn is_use_item(item: &ast::Item) -> bool { + matches!(item.kind, ast::ItemKind::Use(_)) +} + +pub(crate) fn is_extern_crate(item: &ast::Item) -> bool { + matches!(item.kind, ast::ItemKind::ExternCrate(..)) +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 000000000000..567628f63bdf --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,662 @@ +#![feature(rustc_private)] +#![deny(rust_2018_idioms)] +#![warn(unreachable_pub)] +#![recursion_limit = "256"] +#![allow(clippy::match_like_matches_macro)] +#![allow(unreachable_pub)] + +#[cfg(test)] +#[macro_use] +extern crate lazy_static; +#[macro_use] +extern crate log; + +// N.B. these crates are loaded from the sysroot, so they need extern crate. +extern crate rustc_ast; +extern crate rustc_ast_pretty; +extern crate rustc_builtin_macros; +extern crate rustc_data_structures; +extern crate rustc_errors; +extern crate rustc_expand; +extern crate rustc_parse; +extern crate rustc_session; +extern crate rustc_span; +extern crate thin_vec; + +// Necessary to pull in object code as the rest of the rustc crates are shipped only as rmeta +// files. +#[allow(unused_extern_crates)] +extern crate rustc_driver; + +use std::cell::RefCell; +use std::collections::HashMap; +use std::fmt; +use std::io::{self, Write}; +use std::mem; +use std::panic; +use std::path::PathBuf; +use std::rc::Rc; + +use rustc_ast::ast; +use rustc_span::symbol; +use thiserror::Error; + +use crate::comment::LineClasses; +use crate::emitter::Emitter; +use crate::formatting::{FormatErrorMap, FormattingError, ReportedErrors, SourceFile}; +use crate::modules::ModuleResolutionError; +use crate::parse::parser::DirectoryOwnership; +use crate::shape::Indent; +use crate::utils::indent_next_line; + +pub use crate::config::{ + load_config, CliOptions, Color, Config, Edition, EmitMode, FileLines, FileName, NewlineStyle, + Range, Verbosity, +}; + +pub use crate::format_report_formatter::{FormatReportFormatter, FormatReportFormatterBuilder}; + +pub use crate::rustfmt_diff::{ModifiedChunk, ModifiedLines}; + +#[macro_use] +mod utils; + +mod attr; +mod chains; +mod closures; +mod comment; +pub(crate) mod config; +mod coverage; +mod emitter; +mod expr; +mod format_report_formatter; +pub(crate) mod formatting; +mod ignore_path; +mod imports; +mod items; +mod lists; +mod macros; +mod matches; +mod missed_spans; +pub(crate) mod modules; +mod overflow; +mod pairs; +mod parse; +mod patterns; +mod release_channel; +mod reorder; +mod rewrite; +pub(crate) mod rustfmt_diff; +mod shape; +mod skip; +pub(crate) mod source_file; +pub(crate) mod source_map; +mod spanned; +mod stmt; +mod string; +#[cfg(test)] +mod test; +mod types; +mod vertical; +pub(crate) mod visitor; + +/// The various errors that can occur during formatting. Note that not all of +/// these can currently be propagated to clients. +#[derive(Error, Debug)] +pub enum ErrorKind { + /// Line has exceeded character limit (found, maximum). + #[error( + "line formatted, but exceeded maximum width \ + (maximum: {1} (see `max_width` option), found: {0})" + )] + LineOverflow(usize, usize), + /// Line ends in whitespace. + #[error("left behind trailing whitespace")] + TrailingWhitespace, + /// Used deprecated skip attribute. + #[error("`rustfmt_skip` is deprecated; use `rustfmt::skip`")] + DeprecatedAttr, + /// Used a rustfmt:: attribute other than skip or skip::macros. + #[error("invalid attribute")] + BadAttr, + /// An io error during reading or writing. + #[error("io error: {0}")] + IoError(io::Error), + /// Error during module resolution. + #[error("{0}")] + ModuleResolutionError(#[from] ModuleResolutionError), + /// Parse error occurred when parsing the input. + #[error("parse error")] + ParseError, + /// The user mandated a version and the current version of Rustfmt does not + /// satisfy that requirement. + #[error("version mismatch")] + VersionMismatch, + /// If we had formatted the given node, then we would have lost a comment. + #[error("not formatted because a comment would be lost")] + LostComment, + /// Invalid glob pattern in `ignore` configuration option. + #[error("Invalid glob pattern found in ignore list: {0}")] + InvalidGlobPattern(ignore::Error), +} + +impl ErrorKind { + fn is_comment(&self) -> bool { + matches!(self, ErrorKind::LostComment) + } +} + +impl From for ErrorKind { + fn from(e: io::Error) -> ErrorKind { + ErrorKind::IoError(e) + } +} + +/// Result of formatting a snippet of code along with ranges of lines that didn't get formatted, +/// i.e., that got returned as they were originally. +#[derive(Debug)] +struct FormattedSnippet { + snippet: String, + non_formatted_ranges: Vec<(usize, usize)>, +} + +impl FormattedSnippet { + /// In case the snippet needed to be wrapped in a function, this shifts down the ranges of + /// non-formatted code. + fn unwrap_code_block(&mut self) { + self.non_formatted_ranges + .iter_mut() + .for_each(|(low, high)| { + *low -= 1; + *high -= 1; + }); + } + + /// Returns `true` if the line n did not get formatted. + fn is_line_non_formatted(&self, n: usize) -> bool { + self.non_formatted_ranges + .iter() + .any(|(low, high)| *low <= n && n <= *high) + } +} + +/// Reports on any issues that occurred during a run of Rustfmt. +/// +/// Can be reported to the user using the `Display` impl on [`FormatReportFormatter`]. +#[derive(Clone)] +pub struct FormatReport { + // Maps stringified file paths to their associated formatting errors. + internal: Rc>, + non_formatted_ranges: Vec<(usize, usize)>, +} + +impl FormatReport { + fn new() -> FormatReport { + FormatReport { + internal: Rc::new(RefCell::new((HashMap::new(), ReportedErrors::default()))), + non_formatted_ranges: Vec::new(), + } + } + + fn add_non_formatted_ranges(&mut self, mut ranges: Vec<(usize, usize)>) { + self.non_formatted_ranges.append(&mut ranges); + } + + fn append(&self, f: FileName, mut v: Vec) { + self.track_errors(&v); + self.internal + .borrow_mut() + .0 + .entry(f) + .and_modify(|fe| fe.append(&mut v)) + .or_insert(v); + } + + fn track_errors(&self, new_errors: &[FormattingError]) { + let errs = &mut self.internal.borrow_mut().1; + if !new_errors.is_empty() { + errs.has_formatting_errors = true; + } + if errs.has_operational_errors && errs.has_check_errors && errs.has_unformatted_code_errors + { + return; + } + for err in new_errors { + match err.kind { + ErrorKind::LineOverflow(..) => { + errs.has_operational_errors = true; + } + ErrorKind::TrailingWhitespace => { + errs.has_operational_errors = true; + errs.has_unformatted_code_errors = true; + } + ErrorKind::LostComment => { + errs.has_unformatted_code_errors = true; + } + ErrorKind::DeprecatedAttr | ErrorKind::BadAttr | ErrorKind::VersionMismatch => { + errs.has_check_errors = true; + } + _ => {} + } + } + } + + fn add_diff(&mut self) { + self.internal.borrow_mut().1.has_diff = true; + } + + fn add_macro_format_failure(&mut self) { + self.internal.borrow_mut().1.has_macro_format_failure = true; + } + + fn add_parsing_error(&mut self) { + self.internal.borrow_mut().1.has_parsing_errors = true; + } + + fn warning_count(&self) -> usize { + self.internal + .borrow() + .0 + .iter() + .map(|(_, errors)| errors.len()) + .sum() + } + + /// Whether any warnings or errors are present in the report. + pub fn has_warnings(&self) -> bool { + self.internal.borrow().1.has_formatting_errors + } + + /// Print the report to a terminal using colours and potentially other + /// fancy output. + #[deprecated(note = "Use FormatReportFormatter with colors enabled instead")] + pub fn fancy_print( + &self, + mut t: Box>, + ) -> Result<(), term::Error> { + writeln!( + t, + "{}", + FormatReportFormatterBuilder::new(self) + .enable_colors(true) + .build() + )?; + Ok(()) + } +} + +/// Deprecated - Use FormatReportFormatter instead +// https://github.com/rust-lang/rust/issues/78625 +// https://github.com/rust-lang/rust/issues/39935 +impl fmt::Display for FormatReport { + // Prints all the formatting errors. + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + write!(fmt, "{}", FormatReportFormatterBuilder::new(self).build())?; + Ok(()) + } +} + +/// Format the given snippet. The snippet is expected to be *complete* code. +/// When we cannot parse the given snippet, this function returns `None`. +fn format_snippet(snippet: &str, config: &Config, is_macro_def: bool) -> Option { + let mut config = config.clone(); + panic::catch_unwind(|| { + let mut out: Vec = Vec::with_capacity(snippet.len() * 2); + config.set().emit_mode(config::EmitMode::Stdout); + config.set().verbose(Verbosity::Quiet); + config.set().hide_parse_errors(true); + if is_macro_def { + config.set().error_on_unformatted(true); + } + + let (formatting_error, result) = { + let input = Input::Text(snippet.into()); + let mut session = Session::new(config, Some(&mut out)); + let result = session.format_input_inner(input, is_macro_def); + ( + session.errors.has_macro_format_failure + || session.out.as_ref().unwrap().is_empty() && !snippet.is_empty() + || result.is_err() + || (is_macro_def && session.has_unformatted_code_errors()), + result, + ) + }; + if formatting_error { + None + } else { + String::from_utf8(out).ok().map(|snippet| FormattedSnippet { + snippet, + non_formatted_ranges: result.unwrap().non_formatted_ranges, + }) + } + }) + // Discard panics encountered while formatting the snippet + // The ? operator is needed to remove the extra Option + .ok()? +} + +/// Format the given code block. Mainly targeted for code block in comment. +/// The code block may be incomplete (i.e., parser may be unable to parse it). +/// To avoid panic in parser, we wrap the code block with a dummy function. +/// The returned code block does **not** end with newline. +fn format_code_block( + code_snippet: &str, + config: &Config, + is_macro_def: bool, +) -> Option { + const FN_MAIN_PREFIX: &str = "fn main() {\n"; + + fn enclose_in_main_block(s: &str, config: &Config) -> String { + let indent = Indent::from_width(config, config.tab_spaces()); + let mut result = String::with_capacity(s.len() * 2); + result.push_str(FN_MAIN_PREFIX); + let mut need_indent = true; + for (kind, line) in LineClasses::new(s) { + if need_indent { + result.push_str(&indent.to_string(config)); + } + result.push_str(&line); + result.push('\n'); + need_indent = indent_next_line(kind, &line, config); + } + result.push('}'); + result + } + + // Wrap the given code block with `fn main()` if it does not have one. + let snippet = enclose_in_main_block(code_snippet, config); + let mut result = String::with_capacity(snippet.len()); + let mut is_first = true; + + // While formatting the code, ignore the config's newline style setting and always use "\n" + // instead of "\r\n" for the newline characters. This is ok because the output here is + // not directly outputted by rustfmt command, but used by the comment formatter's input. + // We have output-file-wide "\n" ==> "\r\n" conversion process after here if it's necessary. + let mut config_with_unix_newline = config.clone(); + config_with_unix_newline + .set() + .newline_style(NewlineStyle::Unix); + let mut formatted = format_snippet(&snippet, &config_with_unix_newline, is_macro_def)?; + // Remove wrapping main block + formatted.unwrap_code_block(); + + // Trim "fn main() {" on the first line and "}" on the last line, + // then unindent the whole code block. + let block_len = formatted + .snippet + .rfind('}') + .unwrap_or_else(|| formatted.snippet.len()); + let mut is_indented = true; + let indent_str = Indent::from_width(config, config.tab_spaces()).to_string(config); + for (kind, ref line) in LineClasses::new(&formatted.snippet[FN_MAIN_PREFIX.len()..block_len]) { + if !is_first { + result.push('\n'); + } else { + is_first = false; + } + let trimmed_line = if !is_indented { + line + } else if line.len() > config.max_width() { + // If there are lines that are larger than max width, we cannot tell + // whether we have succeeded but have some comments or strings that + // are too long, or we have failed to format code block. We will be + // conservative and just return `None` in this case. + return None; + } else if line.len() > indent_str.len() { + // Make sure that the line has leading whitespaces. + if line.starts_with(indent_str.as_ref()) { + let offset = if config.hard_tabs() { + 1 + } else { + config.tab_spaces() + }; + &line[offset..] + } else { + line + } + } else { + line + }; + result.push_str(trimmed_line); + is_indented = indent_next_line(kind, line, config); + } + Some(FormattedSnippet { + snippet: result, + non_formatted_ranges: formatted.non_formatted_ranges, + }) +} + +/// A session is a run of rustfmt across a single or multiple inputs. +pub struct Session<'b, T: Write> { + pub config: Config, + pub out: Option<&'b mut T>, + pub(crate) errors: ReportedErrors, + source_file: SourceFile, + emitter: Box, +} + +impl<'b, T: Write + 'b> Session<'b, T> { + pub fn new(config: Config, mut out: Option<&'b mut T>) -> Session<'b, T> { + let emitter = create_emitter(&config); + + if let Some(ref mut out) = out { + let _ = emitter.emit_header(out); + } + + Session { + config, + out, + emitter, + errors: ReportedErrors::default(), + source_file: SourceFile::new(), + } + } + + /// The main entry point for Rustfmt. Formats the given input according to the + /// given config. `out` is only necessary if required by the configuration. + pub fn format(&mut self, input: Input) -> Result { + self.format_input_inner(input, false) + } + + pub fn override_config(&mut self, mut config: Config, f: F) -> U + where + F: FnOnce(&mut Session<'b, T>) -> U, + { + mem::swap(&mut config, &mut self.config); + let result = f(self); + mem::swap(&mut config, &mut self.config); + result + } + + pub fn add_operational_error(&mut self) { + self.errors.has_operational_errors = true; + } + + pub fn has_operational_errors(&self) -> bool { + self.errors.has_operational_errors + } + + pub fn has_parsing_errors(&self) -> bool { + self.errors.has_parsing_errors + } + + pub fn has_formatting_errors(&self) -> bool { + self.errors.has_formatting_errors + } + + pub fn has_check_errors(&self) -> bool { + self.errors.has_check_errors + } + + pub fn has_diff(&self) -> bool { + self.errors.has_diff + } + + pub fn has_unformatted_code_errors(&self) -> bool { + self.errors.has_unformatted_code_errors + } + + pub fn has_no_errors(&self) -> bool { + !(self.has_operational_errors() + || self.has_parsing_errors() + || self.has_formatting_errors() + || self.has_check_errors() + || self.has_diff() + || self.has_unformatted_code_errors() + || self.errors.has_macro_format_failure) + } +} + +pub(crate) fn create_emitter<'a>(config: &Config) -> Box { + match config.emit_mode() { + EmitMode::Files if config.make_backup() => { + Box::new(emitter::FilesWithBackupEmitter::default()) + } + EmitMode::Files => Box::new(emitter::FilesEmitter::new( + config.print_misformatted_file_names(), + )), + EmitMode::Stdout | EmitMode::Coverage => { + Box::new(emitter::StdoutEmitter::new(config.verbose())) + } + EmitMode::Json => Box::new(emitter::JsonEmitter::default()), + EmitMode::ModifiedLines => Box::new(emitter::ModifiedLinesEmitter::default()), + EmitMode::Checkstyle => Box::new(emitter::CheckstyleEmitter::default()), + EmitMode::Diff => Box::new(emitter::DiffEmitter::new(config.clone())), + } +} + +impl<'b, T: Write + 'b> Drop for Session<'b, T> { + fn drop(&mut self) { + if let Some(ref mut out) = self.out { + let _ = self.emitter.emit_footer(out); + } + } +} + +#[derive(Debug)] +pub enum Input { + File(PathBuf), + Text(String), +} + +impl Input { + fn file_name(&self) -> FileName { + match *self { + Input::File(ref file) => FileName::Real(file.clone()), + Input::Text(..) => FileName::Stdin, + } + } + + fn to_directory_ownership(&self) -> Option { + match self { + Input::File(ref file) => { + // If there exists a directory with the same name as an input, + // then the input should be parsed as a sub module. + let file_stem = file.file_stem()?; + if file.parent()?.to_path_buf().join(file_stem).is_dir() { + Some(DirectoryOwnership::Owned { + relative: file_stem.to_str().map(symbol::Ident::from_str), + }) + } else { + None + } + } + _ => None, + } + } +} + +#[cfg(test)] +mod unit_tests { + use super::*; + + #[test] + fn test_no_panic_on_format_snippet_and_format_code_block() { + // `format_snippet()` and `format_code_block()` should not panic + // even when we cannot parse the given snippet. + let snippet = "let"; + assert!(format_snippet(snippet, &Config::default(), false).is_none()); + assert!(format_code_block(snippet, &Config::default(), false).is_none()); + } + + fn test_format_inner(formatter: F, input: &str, expected: &str) -> bool + where + F: Fn(&str, &Config, bool) -> Option, + { + let output = formatter(input, &Config::default(), false); + output.is_some() && output.unwrap().snippet == expected + } + + #[test] + fn test_format_snippet() { + let snippet = "fn main() { println!(\"hello, world\"); }"; + #[cfg(not(windows))] + let expected = "fn main() {\n \ + println!(\"hello, world\");\n\ + }\n"; + #[cfg(windows)] + let expected = "fn main() {\r\n \ + println!(\"hello, world\");\r\n\ + }\r\n"; + assert!(test_format_inner(format_snippet, snippet, expected)); + } + + #[test] + fn test_format_code_block_fail() { + #[rustfmt::skip] + let code_block = "this_line_is_100_characters_long_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx(x, y, z);"; + assert!(format_code_block(code_block, &Config::default(), false).is_none()); + } + + #[test] + fn test_format_code_block() { + // simple code block + let code_block = "let x=3;"; + let expected = "let x = 3;"; + assert!(test_format_inner(format_code_block, code_block, expected)); + + // more complex code block, taken from chains.rs. + let code_block = +"let (nested_shape, extend) = if !parent_rewrite_contains_newline && is_continuable(&parent) { +( +chain_indent(context, shape.add_offset(parent_rewrite.len())), +context.config.indent_style() == IndentStyle::Visual || is_small_parent, +) +} else if is_block_expr(context, &parent, &parent_rewrite) { +match context.config.indent_style() { +// Try to put the first child on the same line with parent's last line +IndentStyle::Block => (parent_shape.block_indent(context.config.tab_spaces()), true), +// The parent is a block, so align the rest of the chain with the closing +// brace. +IndentStyle::Visual => (parent_shape, false), +} +} else { +( +chain_indent(context, shape.add_offset(parent_rewrite.len())), +false, +) +}; +"; + let expected = +"let (nested_shape, extend) = if !parent_rewrite_contains_newline && is_continuable(&parent) { + ( + chain_indent(context, shape.add_offset(parent_rewrite.len())), + context.config.indent_style() == IndentStyle::Visual || is_small_parent, + ) +} else if is_block_expr(context, &parent, &parent_rewrite) { + match context.config.indent_style() { + // Try to put the first child on the same line with parent's last line + IndentStyle::Block => (parent_shape.block_indent(context.config.tab_spaces()), true), + // The parent is a block, so align the rest of the chain with the closing + // brace. + IndentStyle::Visual => (parent_shape, false), + } +} else { + ( + chain_indent(context, shape.add_offset(parent_rewrite.len())), + false, + ) +};"; + assert!(test_format_inner(format_code_block, code_block, expected)); + } +} diff --git a/src/lists.rs b/src/lists.rs new file mode 100644 index 000000000000..a878e6cf9b2f --- /dev/null +++ b/src/lists.rs @@ -0,0 +1,943 @@ +//! Format list-like expressions and items. + +use std::cmp; +use std::iter::Peekable; + +use rustc_span::BytePos; + +use crate::comment::{find_comment_end, rewrite_comment, FindUncommented}; +use crate::config::lists::*; +use crate::config::{Config, IndentStyle}; +use crate::rewrite::RewriteContext; +use crate::shape::{Indent, Shape}; +use crate::utils::{ + count_newlines, first_line_width, last_line_width, mk_sp, starts_with_newline, + unicode_str_width, +}; +use crate::visitor::SnippetProvider; + +pub(crate) struct ListFormatting<'a> { + tactic: DefinitiveListTactic, + separator: &'a str, + trailing_separator: SeparatorTactic, + separator_place: SeparatorPlace, + shape: Shape, + // Non-expressions, e.g., items, will have a new line at the end of the list. + // Important for comment styles. + ends_with_newline: bool, + // Remove newlines between list elements for expressions. + preserve_newline: bool, + // Nested import lists get some special handling for the "Mixed" list type + nested: bool, + // Whether comments should be visually aligned. + align_comments: bool, + config: &'a Config, +} + +impl<'a> ListFormatting<'a> { + pub(crate) fn new(shape: Shape, config: &'a Config) -> Self { + ListFormatting { + tactic: DefinitiveListTactic::Vertical, + separator: ",", + trailing_separator: SeparatorTactic::Never, + separator_place: SeparatorPlace::Back, + shape, + ends_with_newline: true, + preserve_newline: false, + nested: false, + align_comments: true, + config, + } + } + + pub(crate) fn tactic(mut self, tactic: DefinitiveListTactic) -> Self { + self.tactic = tactic; + self + } + + pub(crate) fn separator(mut self, separator: &'a str) -> Self { + self.separator = separator; + self + } + + pub(crate) fn trailing_separator(mut self, trailing_separator: SeparatorTactic) -> Self { + self.trailing_separator = trailing_separator; + self + } + + pub(crate) fn separator_place(mut self, separator_place: SeparatorPlace) -> Self { + self.separator_place = separator_place; + self + } + + pub(crate) fn ends_with_newline(mut self, ends_with_newline: bool) -> Self { + self.ends_with_newline = ends_with_newline; + self + } + + pub(crate) fn preserve_newline(mut self, preserve_newline: bool) -> Self { + self.preserve_newline = preserve_newline; + self + } + + pub(crate) fn nested(mut self, nested: bool) -> Self { + self.nested = nested; + self + } + + pub(crate) fn align_comments(mut self, align_comments: bool) -> Self { + self.align_comments = align_comments; + self + } + + pub(crate) fn needs_trailing_separator(&self) -> bool { + match self.trailing_separator { + // We always put separator in front. + SeparatorTactic::Always => true, + SeparatorTactic::Vertical => self.tactic == DefinitiveListTactic::Vertical, + SeparatorTactic::Never => { + self.tactic == DefinitiveListTactic::Vertical && self.separator_place.is_front() + } + } + } +} + +impl AsRef for ListItem { + fn as_ref(&self) -> &ListItem { + self + } +} + +#[derive(PartialEq, Eq, Debug, Copy, Clone)] +pub(crate) enum ListItemCommentStyle { + // Try to keep the comment on the same line with the item. + SameLine, + // Put the comment on the previous or the next line of the item. + DifferentLine, + // No comment available. + None, +} + +#[derive(Debug, Clone)] +pub(crate) struct ListItem { + // None for comments mean that they are not present. + pub(crate) pre_comment: Option, + pub(crate) pre_comment_style: ListItemCommentStyle, + // Item should include attributes and doc comments. None indicates a failed + // rewrite. + pub(crate) item: Option, + pub(crate) post_comment: Option, + // Whether there is extra whitespace before this item. + pub(crate) new_lines: bool, +} + +impl ListItem { + pub(crate) fn empty() -> ListItem { + ListItem { + pre_comment: None, + pre_comment_style: ListItemCommentStyle::None, + item: None, + post_comment: None, + new_lines: false, + } + } + + pub(crate) fn inner_as_ref(&self) -> &str { + self.item.as_ref().map_or("", |s| s) + } + + pub(crate) fn is_different_group(&self) -> bool { + self.inner_as_ref().contains('\n') + || self.pre_comment.is_some() + || self + .post_comment + .as_ref() + .map_or(false, |s| s.contains('\n')) + } + + pub(crate) fn is_multiline(&self) -> bool { + self.inner_as_ref().contains('\n') + || self + .pre_comment + .as_ref() + .map_or(false, |s| s.contains('\n')) + || self + .post_comment + .as_ref() + .map_or(false, |s| s.contains('\n')) + } + + pub(crate) fn has_single_line_comment(&self) -> bool { + self.pre_comment + .as_ref() + .map_or(false, |comment| comment.trim_start().starts_with("//")) + || self + .post_comment + .as_ref() + .map_or(false, |comment| comment.trim_start().starts_with("//")) + } + + pub(crate) fn has_comment(&self) -> bool { + self.pre_comment.is_some() || self.post_comment.is_some() + } + + pub(crate) fn from_str>(s: S) -> ListItem { + ListItem { + pre_comment: None, + pre_comment_style: ListItemCommentStyle::None, + item: Some(s.into()), + post_comment: None, + new_lines: false, + } + } + + // Returns `true` if the item causes something to be written. + fn is_substantial(&self) -> bool { + fn empty(s: &Option) -> bool { + !matches!(*s, Some(ref s) if !s.is_empty()) + } + + !(empty(&self.pre_comment) && empty(&self.item) && empty(&self.post_comment)) + } +} + +/// The type of separator for lists. +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub(crate) enum Separator { + Comma, + VerticalBar, +} + +impl Separator { + pub(crate) fn len(self) -> usize { + match self { + // 2 = `, ` + Separator::Comma => 2, + // 3 = ` | ` + Separator::VerticalBar => 3, + } + } +} + +pub(crate) fn definitive_tactic( + items: I, + tactic: ListTactic, + sep: Separator, + width: usize, +) -> DefinitiveListTactic +where + I: IntoIterator + Clone, + T: AsRef, +{ + let pre_line_comments = items + .clone() + .into_iter() + .any(|item| item.as_ref().has_single_line_comment()); + + let limit = match tactic { + _ if pre_line_comments => return DefinitiveListTactic::Vertical, + ListTactic::Horizontal => return DefinitiveListTactic::Horizontal, + ListTactic::Vertical => return DefinitiveListTactic::Vertical, + ListTactic::LimitedHorizontalVertical(limit) => ::std::cmp::min(width, limit), + ListTactic::Mixed | ListTactic::HorizontalVertical => width, + }; + + let (sep_count, total_width) = calculate_width(items.clone()); + let total_sep_len = sep.len() * sep_count.saturating_sub(1); + let real_total = total_width + total_sep_len; + + if real_total <= limit && !items.into_iter().any(|item| item.as_ref().is_multiline()) { + DefinitiveListTactic::Horizontal + } else { + match tactic { + ListTactic::Mixed => DefinitiveListTactic::Mixed, + _ => DefinitiveListTactic::Vertical, + } + } +} + +// Format a list of commented items into a string. +pub(crate) fn write_list(items: I, formatting: &ListFormatting<'_>) -> Option +where + I: IntoIterator + Clone, + T: AsRef, +{ + let tactic = formatting.tactic; + let sep_len = formatting.separator.len(); + + // Now that we know how we will layout, we can decide for sure if there + // will be a trailing separator. + let mut trailing_separator = formatting.needs_trailing_separator(); + let mut result = String::with_capacity(128); + let cloned_items = items.clone(); + let mut iter = items.into_iter().enumerate().peekable(); + let mut item_max_width: Option = None; + let sep_place = + SeparatorPlace::from_tactic(formatting.separator_place, tactic, formatting.separator); + let mut prev_item_had_post_comment = false; + let mut prev_item_is_nested_import = false; + + let mut line_len = 0; + let indent_str = &formatting.shape.indent.to_string(formatting.config); + while let Some((i, item)) = iter.next() { + let item = item.as_ref(); + let inner_item = item.item.as_ref()?; + let first = i == 0; + let last = iter.peek().is_none(); + let mut separate = match sep_place { + SeparatorPlace::Front => !first, + SeparatorPlace::Back => !last || trailing_separator, + }; + let item_sep_len = if separate { sep_len } else { 0 }; + + // Item string may be multi-line. Its length (used for block comment alignment) + // should be only the length of the last line. + let item_last_line = if item.is_multiline() { + inner_item.lines().last().unwrap_or("") + } else { + inner_item.as_ref() + }; + let mut item_last_line_width = unicode_str_width(item_last_line) + item_sep_len; + if item_last_line.starts_with(&**indent_str) { + item_last_line_width -= unicode_str_width(indent_str); + } + + if !item.is_substantial() { + continue; + } + + match tactic { + DefinitiveListTactic::Horizontal if !first => { + result.push(' '); + } + DefinitiveListTactic::SpecialMacro(num_args_before) => { + if i == 0 { + // Nothing + } else if i < num_args_before { + result.push(' '); + } else if i <= num_args_before + 1 { + result.push('\n'); + result.push_str(indent_str); + } else { + result.push(' '); + } + } + DefinitiveListTactic::Vertical + if !first && !inner_item.is_empty() && !result.is_empty() => + { + result.push('\n'); + result.push_str(indent_str); + } + DefinitiveListTactic::Mixed => { + let total_width = total_item_width(item) + item_sep_len; + + // 1 is space between separator and item. + if (line_len > 0 && line_len + 1 + total_width > formatting.shape.width) + || prev_item_had_post_comment + || (formatting.nested + && (prev_item_is_nested_import || (!first && inner_item.contains("::")))) + { + result.push('\n'); + result.push_str(indent_str); + line_len = 0; + if formatting.ends_with_newline { + trailing_separator = true; + } + } else if line_len > 0 { + result.push(' '); + line_len += 1; + } + + if last && formatting.ends_with_newline { + separate = formatting.trailing_separator != SeparatorTactic::Never; + } + + line_len += total_width; + } + _ => {} + } + + // Pre-comments + if let Some(ref comment) = item.pre_comment { + // Block style in non-vertical mode. + let block_mode = tactic == DefinitiveListTactic::Horizontal; + // Width restriction is only relevant in vertical mode. + let comment = + rewrite_comment(comment, block_mode, formatting.shape, formatting.config)?; + result.push_str(&comment); + + if !inner_item.is_empty() { + use DefinitiveListTactic::*; + if matches!(tactic, Vertical | Mixed | SpecialMacro(_)) { + // We cannot keep pre-comments on the same line if the comment is normalized. + let keep_comment = if formatting.config.normalize_comments() + || item.pre_comment_style == ListItemCommentStyle::DifferentLine + { + false + } else { + // We will try to keep the comment on the same line with the item here. + // 1 = ` ` + let total_width = total_item_width(item) + item_sep_len + 1; + total_width <= formatting.shape.width + }; + if keep_comment { + result.push(' '); + } else { + result.push('\n'); + result.push_str(indent_str); + // This is the width of the item (without comments). + line_len = item.item.as_ref().map_or(0, |s| unicode_str_width(s)); + } + } else { + result.push(' ') + } + } + item_max_width = None; + } + + if separate && sep_place.is_front() && !first { + result.push_str(formatting.separator.trim()); + result.push(' '); + } + result.push_str(inner_item); + + // Post-comments + if tactic == DefinitiveListTactic::Horizontal && item.post_comment.is_some() { + let comment = item.post_comment.as_ref().unwrap(); + let formatted_comment = rewrite_comment( + comment, + true, + Shape::legacy(formatting.shape.width, Indent::empty()), + formatting.config, + )?; + + result.push(' '); + result.push_str(&formatted_comment); + } + + if separate && sep_place.is_back() { + result.push_str(formatting.separator); + } + + if tactic != DefinitiveListTactic::Horizontal && item.post_comment.is_some() { + let comment = item.post_comment.as_ref().unwrap(); + let overhead = last_line_width(&result) + first_line_width(comment.trim()); + + let rewrite_post_comment = |item_max_width: &mut Option| { + if item_max_width.is_none() && !last && !inner_item.contains('\n') { + *item_max_width = Some(max_width_of_item_with_post_comment( + &cloned_items, + i, + overhead, + formatting.config.max_width(), + )); + } + let overhead = if starts_with_newline(comment) { + 0 + } else if let Some(max_width) = *item_max_width { + max_width + 2 + } else { + // 1 = space between item and comment. + item_last_line_width + 1 + }; + let width = formatting.shape.width.checked_sub(overhead).unwrap_or(1); + let offset = formatting.shape.indent + overhead; + let comment_shape = Shape::legacy(width, offset); + + let block_style = if !formatting.ends_with_newline && last { + true + } else if starts_with_newline(comment) { + false + } else { + comment.trim().contains('\n') || unicode_str_width(comment.trim()) > width + }; + + rewrite_comment( + comment.trim_start(), + block_style, + comment_shape, + formatting.config, + ) + }; + + let mut formatted_comment = rewrite_post_comment(&mut item_max_width)?; + + if !starts_with_newline(comment) { + if formatting.align_comments { + let mut comment_alignment = + post_comment_alignment(item_max_width, unicode_str_width(inner_item)); + if first_line_width(&formatted_comment) + + last_line_width(&result) + + comment_alignment + + 1 + > formatting.config.max_width() + { + item_max_width = None; + formatted_comment = rewrite_post_comment(&mut item_max_width)?; + comment_alignment = + post_comment_alignment(item_max_width, unicode_str_width(inner_item)); + } + for _ in 0..=comment_alignment { + result.push(' '); + } + } + // An additional space for the missing trailing separator (or + // if we skipped alignment above). + if !formatting.align_comments + || (last + && item_max_width.is_some() + && !separate + && !formatting.separator.is_empty()) + { + result.push(' '); + } + } else { + result.push('\n'); + result.push_str(indent_str); + } + if formatted_comment.contains('\n') { + item_max_width = None; + } + result.push_str(&formatted_comment); + } else { + item_max_width = None; + } + + if formatting.preserve_newline + && !last + && tactic == DefinitiveListTactic::Vertical + && item.new_lines + { + item_max_width = None; + result.push('\n'); + } + + prev_item_had_post_comment = item.post_comment.is_some(); + prev_item_is_nested_import = inner_item.contains("::"); + } + + Some(result) +} + +fn max_width_of_item_with_post_comment( + items: &I, + i: usize, + overhead: usize, + max_budget: usize, +) -> usize +where + I: IntoIterator + Clone, + T: AsRef, +{ + let mut max_width = 0; + let mut first = true; + for item in items.clone().into_iter().skip(i) { + let item = item.as_ref(); + let inner_item_width = unicode_str_width(item.inner_as_ref()); + if !first + && (item.is_different_group() + || item.post_comment.is_none() + || inner_item_width + overhead > max_budget) + { + return max_width; + } + if max_width < inner_item_width { + max_width = inner_item_width; + } + if item.new_lines { + return max_width; + } + first = false; + } + max_width +} + +fn post_comment_alignment(item_max_width: Option, inner_item_width: usize) -> usize { + item_max_width.unwrap_or(0).saturating_sub(inner_item_width) +} + +pub(crate) struct ListItems<'a, I, F1, F2, F3> +where + I: Iterator, +{ + snippet_provider: &'a SnippetProvider, + inner: Peekable, + get_lo: F1, + get_hi: F2, + get_item_string: F3, + prev_span_end: BytePos, + next_span_start: BytePos, + terminator: &'a str, + separator: &'a str, + leave_last: bool, +} + +pub(crate) fn extract_pre_comment(pre_snippet: &str) -> (Option, ListItemCommentStyle) { + let trimmed_pre_snippet = pre_snippet.trim(); + // Both start and end are checked to support keeping a block comment inline with + // the item, even if there are preceding line comments, while still supporting + // a snippet that starts with a block comment but also contains one or more + // trailing single line comments. + // https://github.com/rust-lang/rustfmt/issues/3025 + // https://github.com/rust-lang/rustfmt/pull/3048 + // https://github.com/rust-lang/rustfmt/issues/3839 + let starts_with_block_comment = trimmed_pre_snippet.starts_with("/*"); + let ends_with_block_comment = trimmed_pre_snippet.ends_with("*/"); + let starts_with_single_line_comment = trimmed_pre_snippet.starts_with("//"); + if ends_with_block_comment { + let comment_end = pre_snippet.rfind(|c| c == '/').unwrap(); + if pre_snippet[comment_end..].contains('\n') { + ( + Some(trimmed_pre_snippet.to_owned()), + ListItemCommentStyle::DifferentLine, + ) + } else { + ( + Some(trimmed_pre_snippet.to_owned()), + ListItemCommentStyle::SameLine, + ) + } + } else if starts_with_single_line_comment || starts_with_block_comment { + ( + Some(trimmed_pre_snippet.to_owned()), + ListItemCommentStyle::DifferentLine, + ) + } else { + (None, ListItemCommentStyle::None) + } +} + +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(',') + && (!post_snippet.trim().starts_with("//") || post_snippet.trim().contains('\n')) + { + post_snippet[..(post_snippet.len() - 1)].trim_matches(white_space) + } else { + post_snippet + }; + // FIXME(#3441): post_snippet includes 'const' now + // it should not include here + let removed_newline_snippet = post_snippet_trimmed.trim(); + if !post_snippet_trimmed.is_empty() + && (removed_newline_snippet.starts_with("//") || removed_newline_snippet.starts_with("/*")) + { + Some(post_snippet_trimmed.to_owned()) + } else { + None + } +} + +pub(crate) fn get_comment_end( + post_snippet: &str, + separator: &str, + terminator: &str, + is_last: bool, +) -> usize { + if is_last { + return post_snippet + .find_uncommented(terminator) + .unwrap_or_else(|| post_snippet.len()); + } + + let mut block_open_index = post_snippet.find("/*"); + // check if it really is a block comment (and not `//*` or a nested comment) + if let Some(i) = block_open_index { + match post_snippet.find('/') { + Some(j) if j < i => block_open_index = None, + _ if post_snippet[..i].ends_with('/') => block_open_index = None, + _ => (), + } + } + let newline_index = post_snippet.find('\n'); + if let Some(separator_index) = post_snippet.find_uncommented(separator) { + match (block_open_index, newline_index) { + // Separator before comment, with the next item on same line. + // Comment belongs to next item. + (Some(i), None) if i > separator_index => separator_index + 1, + // Block-style post-comment before the separator. + (Some(i), None) => cmp::max( + find_comment_end(&post_snippet[i..]).unwrap() + i, + separator_index + 1, + ), + // Block-style post-comment. Either before or after the separator. + (Some(i), Some(j)) if i < j => cmp::max( + find_comment_end(&post_snippet[i..]).unwrap() + i, + separator_index + 1, + ), + // Potential *single* line comment. + (_, Some(j)) if j > separator_index => j + 1, + _ => post_snippet.len(), + } + } else if let Some(newline_index) = newline_index { + // Match arms may not have trailing comma. In any case, for match arms, + // we will assume that the post comment belongs to the next arm if they + // do not end with trailing comma. + newline_index + 1 + } else { + 0 + } +} + +// Account for extra whitespace between items. This is fiddly +// because of the way we divide pre- and post- comments. +pub(crate) fn has_extra_newline(post_snippet: &str, comment_end: usize) -> bool { + if post_snippet.is_empty() || comment_end == 0 { + return false; + } + + let len_last = post_snippet[..comment_end] + .chars() + .last() + .unwrap() + .len_utf8(); + // Everything from the separator to the next item. + let test_snippet = &post_snippet[comment_end - len_last..]; + let first_newline = test_snippet + .find('\n') + .unwrap_or_else(|| test_snippet.len()); + // From the end of the first line of comments. + let test_snippet = &test_snippet[first_newline..]; + let first = test_snippet + .find(|c: char| !c.is_whitespace()) + .unwrap_or_else(|| test_snippet.len()); + // From the end of the first line of comments to the next non-whitespace char. + let test_snippet = &test_snippet[..first]; + + // There were multiple line breaks which got trimmed to nothing. + count_newlines(test_snippet) > 1 +} + +impl<'a, T, I, F1, F2, F3> Iterator for ListItems<'a, I, F1, F2, F3> +where + I: Iterator, + F1: Fn(&T) -> BytePos, + F2: Fn(&T) -> BytePos, + F3: Fn(&T) -> Option, +{ + type Item = ListItem; + + fn next(&mut self) -> Option { + self.inner.next().map(|item| { + // Pre-comment + let pre_snippet = self + .snippet_provider + .span_to_snippet(mk_sp(self.prev_span_end, (self.get_lo)(&item))) + .unwrap_or(""); + let (pre_comment, pre_comment_style) = extract_pre_comment(pre_snippet); + + // Post-comment + let next_start = match self.inner.peek() { + Some(next_item) => (self.get_lo)(next_item), + None => self.next_span_start, + }; + let post_snippet = self + .snippet_provider + .span_to_snippet(mk_sp((self.get_hi)(&item), next_start)) + .unwrap_or(""); + 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, is_last); + + self.prev_span_end = (self.get_hi)(&item) + BytePos(comment_end as u32); + + ListItem { + pre_comment, + pre_comment_style, + item: if self.inner.peek().is_none() && self.leave_last { + None + } else { + (self.get_item_string)(&item) + }, + post_comment, + new_lines, + } + }) + } +} + +#[allow(clippy::too_many_arguments)] +// Creates an iterator over a list's items with associated comments. +pub(crate) fn itemize_list<'a, T, I, F1, F2, F3>( + snippet_provider: &'a SnippetProvider, + inner: I, + terminator: &'a str, + separator: &'a str, + get_lo: F1, + get_hi: F2, + get_item_string: F3, + prev_span_end: BytePos, + next_span_start: BytePos, + leave_last: bool, +) -> ListItems<'a, I, F1, F2, F3> +where + I: Iterator, + F1: Fn(&T) -> BytePos, + F2: Fn(&T) -> BytePos, + F3: Fn(&T) -> Option, +{ + ListItems { + snippet_provider, + inner: inner.peekable(), + get_lo, + get_hi, + get_item_string, + prev_span_end, + next_span_start, + terminator, + separator, + leave_last, + } +} + +/// Returns the count and total width of the list items. +fn calculate_width(items: I) -> (usize, usize) +where + I: IntoIterator, + T: AsRef, +{ + items + .into_iter() + .map(|item| total_item_width(item.as_ref())) + .fold((0, 0), |acc, l| (acc.0 + 1, acc.1 + l)) +} + +pub(crate) fn total_item_width(item: &ListItem) -> usize { + comment_len(item.pre_comment.as_ref().map(|x| &(*x)[..])) + + comment_len(item.post_comment.as_ref().map(|x| &(*x)[..])) + + item.item.as_ref().map_or(0, |s| unicode_str_width(s)) +} + +fn comment_len(comment: Option<&str>) -> usize { + match comment { + Some(s) => { + let text_len = s.trim().len(); + if text_len > 0 { + // We'll put " /*" before and " */" after inline comments. + text_len + 6 + } else { + text_len + } + } + None => 0, + } +} + +// Compute horizontal and vertical shapes for a struct-lit-like thing. +pub(crate) fn struct_lit_shape( + shape: Shape, + context: &RewriteContext<'_>, + prefix_width: usize, + suffix_width: usize, +) -> Option<(Option, Shape)> { + let v_shape = match context.config.indent_style() { + IndentStyle::Visual => shape + .visual_indent(0) + .shrink_left(prefix_width)? + .sub_width(suffix_width)?, + IndentStyle::Block => { + let shape = shape.block_indent(context.config.tab_spaces()); + Shape { + width: context.budget(shape.indent.width()), + ..shape + } + } + }; + let shape_width = shape.width.checked_sub(prefix_width + suffix_width); + if let Some(w) = shape_width { + let shape_width = cmp::min(w, context.config.struct_lit_width()); + Some((Some(Shape::legacy(shape_width, shape.indent)), v_shape)) + } else { + Some((None, v_shape)) + } +} + +// Compute the tactic for the internals of a struct-lit-like thing. +pub(crate) fn struct_lit_tactic( + h_shape: Option, + context: &RewriteContext<'_>, + items: &[ListItem], +) -> DefinitiveListTactic { + if let Some(h_shape) = h_shape { + let prelim_tactic = match (context.config.indent_style(), items.len()) { + (IndentStyle::Visual, 1) => ListTactic::HorizontalVertical, + _ if context.config.struct_lit_single_line() => ListTactic::HorizontalVertical, + _ => ListTactic::Vertical, + }; + definitive_tactic(items, prelim_tactic, Separator::Comma, h_shape.width) + } else { + DefinitiveListTactic::Vertical + } +} + +// Given a tactic and possible shapes for horizontal and vertical layout, +// come up with the actual shape to use. +pub(crate) fn shape_for_tactic( + tactic: DefinitiveListTactic, + h_shape: Option, + v_shape: Shape, +) -> Shape { + match tactic { + DefinitiveListTactic::Horizontal => h_shape.unwrap(), + _ => v_shape, + } +} + +// Create a ListFormatting object for formatting the internals of a +// struct-lit-like thing, that is a series of fields. +pub(crate) fn struct_lit_formatting<'a>( + shape: Shape, + tactic: DefinitiveListTactic, + context: &'a RewriteContext<'_>, + force_no_trailing_comma: bool, +) -> ListFormatting<'a> { + let ends_with_newline = context.config.indent_style() != IndentStyle::Visual + && tactic == DefinitiveListTactic::Vertical; + ListFormatting { + tactic, + separator: ",", + trailing_separator: if force_no_trailing_comma { + SeparatorTactic::Never + } else { + context.config.trailing_comma() + }, + separator_place: SeparatorPlace::Back, + shape, + ends_with_newline, + preserve_newline: true, + nested: false, + align_comments: true, + config: context.config, + } +} diff --git a/src/macros.rs b/src/macros.rs new file mode 100644 index 000000000000..e9a298a27693 --- /dev/null +++ b/src/macros.rs @@ -0,0 +1,1415 @@ +// Format list-like macro invocations. These are invocations whose token trees +// can be interpreted as expressions and separated by commas. +// Note that these token trees do not actually have to be interpreted as +// expressions by the compiler. An example of an invocation we would reformat is +// foo!( x, y, z ). The token x may represent an identifier in the code, but we +// interpreted as an expression. +// Macro uses which are not-list like, such as bar!(key => val), will not be +// reformatted. +// List-like invocations with parentheses will be formatted as function calls, +// and those with brackets will be formatted as array literals. + +use std::collections::HashMap; +use std::panic::{catch_unwind, AssertUnwindSafe}; + +use rustc_ast::token::{BinOpToken, Delimiter, Token, TokenKind}; +use rustc_ast::tokenstream::{TokenStream, TokenTree, TokenTreeCursor}; +use rustc_ast::{ast, ptr}; +use rustc_ast_pretty::pprust; +use rustc_span::{ + symbol::{self, kw}, + BytePos, Span, Symbol, DUMMY_SP, +}; + +use crate::comment::{ + contains_comment, CharClasses, FindUncommented, FullCodeCharKind, LineClasses, +}; +use crate::config::lists::*; +use crate::expr::{rewrite_array, rewrite_assign_rhs, RhsAssignKind}; +use crate::lists::{itemize_list, write_list, ListFormatting}; +use crate::overflow; +use crate::parse::macros::lazy_static::parse_lazy_static; +use crate::parse::macros::{parse_expr, parse_macro_args, ParsedMacroArgs}; +use crate::rewrite::{Rewrite, RewriteContext}; +use crate::shape::{Indent, Shape}; +use crate::source_map::SpanUtils; +use crate::spanned::Spanned; +use crate::utils::{ + filtered_str_fits, format_visibility, indent_next_line, is_empty_line, mk_sp, + remove_trailing_white_spaces, rewrite_ident, trim_left_preserve_layout, NodeIdExt, +}; +use crate::visitor::FmtVisitor; + +const FORCED_BRACKET_MACROS: &[&str] = &["vec!"]; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum MacroPosition { + Item, + Statement, + Expression, + Pat, +} + +#[derive(Debug)] +pub(crate) enum MacroArg { + Expr(ptr::P), + Ty(ptr::P), + Pat(ptr::P), + Item(ptr::P), + Keyword(symbol::Ident, Span), +} + +impl MacroArg { + pub(crate) fn is_item(&self) -> bool { + match self { + MacroArg::Item(..) => true, + _ => false, + } + } +} + +impl Rewrite for ast::Item { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + let mut visitor = crate::visitor::FmtVisitor::from_context(context); + visitor.block_indent = shape.indent; + visitor.last_pos = self.span().lo(); + visitor.visit_item(self); + Some(visitor.buffer.to_owned()) + } +} + +impl Rewrite for MacroArg { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + match *self { + MacroArg::Expr(ref expr) => expr.rewrite(context, shape), + MacroArg::Ty(ref ty) => ty.rewrite(context, shape), + MacroArg::Pat(ref pat) => pat.rewrite(context, shape), + MacroArg::Item(ref item) => item.rewrite(context, shape), + MacroArg::Keyword(ident, _) => Some(ident.name.to_string()), + } + } +} + +/// Rewrite macro name without using pretty-printer if possible. +fn rewrite_macro_name( + context: &RewriteContext<'_>, + path: &ast::Path, + extra_ident: Option, +) -> String { + let name = if path.segments.len() == 1 { + // Avoid using pretty-printer in the common case. + format!("{}!", rewrite_ident(context, path.segments[0].ident)) + } else { + format!("{}!", pprust::path_to_string(path)) + }; + match extra_ident { + Some(ident) if ident.name != kw::Empty => format!("{} {}", name, ident), + _ => name, + } +} + +// Use this on failing to format the macro call. +fn return_macro_parse_failure_fallback( + context: &RewriteContext<'_>, + indent: Indent, + position: MacroPosition, + span: Span, +) -> Option { + // Mark this as a failure however we format it + context.macro_rewrite_failure.replace(true); + + // Heuristically determine whether the last line of the macro uses "Block" style + // rather than using "Visual" style, or another indentation style. + let is_like_block_indent_style = context + .snippet(span) + .lines() + .last() + .map(|closing_line| { + closing_line + .trim() + .chars() + .all(|ch| matches!(ch, '}' | ')' | ']')) + }) + .unwrap_or(false); + if is_like_block_indent_style { + return trim_left_preserve_layout(context.snippet(span), indent, context.config); + } + + context.skipped_range.borrow_mut().push(( + context.parse_sess.line_of_byte_pos(span.lo()), + context.parse_sess.line_of_byte_pos(span.hi()), + )); + + // Return the snippet unmodified if the macro is not block-like + let mut snippet = context.snippet(span).to_owned(); + if position == MacroPosition::Item { + snippet.push(';'); + } + Some(snippet) +} + +pub(crate) fn rewrite_macro( + mac: &ast::MacCall, + extra_ident: Option, + context: &RewriteContext<'_>, + shape: Shape, + position: MacroPosition, +) -> Option { + let should_skip = context + .skip_context + .macros + .skip(context.snippet(mac.path.span)); + if should_skip { + None + } else { + let guard = context.enter_macro(); + let result = catch_unwind(AssertUnwindSafe(|| { + rewrite_macro_inner( + mac, + extra_ident, + context, + shape, + position, + guard.is_nested(), + ) + })); + match result { + Err(..) | Ok(None) => { + context.macro_rewrite_failure.replace(true); + None + } + Ok(rw) => rw, + } + } +} + +fn rewrite_macro_inner( + mac: &ast::MacCall, + extra_ident: Option, + context: &RewriteContext<'_>, + shape: Shape, + position: MacroPosition, + is_nested_macro: bool, +) -> Option { + if context.config.use_try_shorthand() { + if let Some(expr) = convert_try_mac(mac, context) { + context.leave_macro(); + return expr.rewrite(context, shape); + } + } + + let original_style = macro_style(mac, context); + + let macro_name = rewrite_macro_name(context, &mac.path, extra_ident); + let is_forced_bracket = FORCED_BRACKET_MACROS.contains(&¯o_name[..]); + + let style = if is_forced_bracket && !is_nested_macro { + Delimiter::Bracket + } else { + original_style + }; + + let ts = mac.args.tokens.clone(); + let has_comment = contains_comment(context.snippet(mac.span())); + if ts.is_empty() && !has_comment { + return match style { + Delimiter::Parenthesis if position == MacroPosition::Item => { + Some(format!("{}();", macro_name)) + } + Delimiter::Bracket if position == MacroPosition::Item => { + Some(format!("{}[];", macro_name)) + } + Delimiter::Parenthesis => Some(format!("{}()", macro_name)), + Delimiter::Bracket => Some(format!("{}[]", macro_name)), + Delimiter::Brace => Some(format!("{} {{}}", macro_name)), + _ => unreachable!(), + }; + } + // Format well-known macros which cannot be parsed as a valid AST. + if macro_name == "lazy_static!" && !has_comment { + if let success @ Some(..) = format_lazy_static(context, shape, ts.clone()) { + return success; + } + } + + let ParsedMacroArgs { + args: arg_vec, + vec_with_semi, + trailing_comma, + } = match parse_macro_args(context, ts, style, is_forced_bracket) { + Some(args) => args, + None => { + return return_macro_parse_failure_fallback( + context, + shape.indent, + position, + mac.span(), + ); + } + }; + + if !arg_vec.is_empty() && arg_vec.iter().all(MacroArg::is_item) { + return rewrite_macro_with_items( + context, + &arg_vec, + ¯o_name, + shape, + style, + position, + mac.span(), + ); + } + + match style { + Delimiter::Parenthesis => { + // Handle special case: `vec!(expr; expr)` + if vec_with_semi { + handle_vec_semi(context, shape, arg_vec, macro_name, style) + } else { + // Format macro invocation as function call, preserve the trailing + // comma because not all macros support them. + overflow::rewrite_with_parens( + context, + ¯o_name, + arg_vec.iter(), + shape, + mac.span(), + context.config.fn_call_width(), + if trailing_comma { + Some(SeparatorTactic::Always) + } else { + Some(SeparatorTactic::Never) + }, + ) + .map(|rw| match position { + MacroPosition::Item => format!("{};", rw), + _ => rw, + }) + } + } + Delimiter::Bracket => { + // Handle special case: `vec![expr; expr]` + if vec_with_semi { + handle_vec_semi(context, shape, arg_vec, macro_name, style) + } else { + // If we are rewriting `vec!` macro or other special macros, + // then we can rewrite this as a usual array literal. + // Otherwise, we must preserve the original existence of trailing comma. + let macro_name = ¯o_name.as_str(); + let mut force_trailing_comma = if trailing_comma { + Some(SeparatorTactic::Always) + } else { + Some(SeparatorTactic::Never) + }; + if FORCED_BRACKET_MACROS.contains(macro_name) && !is_nested_macro { + context.leave_macro(); + if context.use_block_indent() { + force_trailing_comma = Some(SeparatorTactic::Vertical); + }; + } + let rewrite = rewrite_array( + macro_name, + arg_vec.iter(), + mac.span(), + context, + shape, + force_trailing_comma, + Some(original_style), + )?; + let comma = match position { + MacroPosition::Item => ";", + _ => "", + }; + + Some(format!("{}{}", rewrite, comma)) + } + } + Delimiter::Brace => { + // For macro invocations with braces, always put a space between + // the `macro_name!` and `{ /* macro_body */ }` but skip modifying + // anything in between the braces (for now). + let snippet = context.snippet(mac.span()).trim_start_matches(|c| c != '{'); + match trim_left_preserve_layout(snippet, shape.indent, context.config) { + Some(macro_body) => Some(format!("{} {}", macro_name, macro_body)), + None => Some(format!("{} {}", macro_name, snippet)), + } + } + _ => unreachable!(), + } +} + +fn handle_vec_semi( + context: &RewriteContext<'_>, + shape: Shape, + arg_vec: Vec, + macro_name: String, + delim_token: Delimiter, +) -> Option { + let (left, right) = match delim_token { + Delimiter::Parenthesis => ("(", ")"), + Delimiter::Bracket => ("[", "]"), + _ => unreachable!(), + }; + + let mac_shape = shape.offset_left(macro_name.len())?; + // 8 = `vec![]` + `; ` or `vec!()` + `; ` + let total_overhead = 8; + let nested_shape = mac_shape.block_indent(context.config.tab_spaces()); + let lhs = arg_vec[0].rewrite(context, nested_shape)?; + let rhs = arg_vec[1].rewrite(context, nested_shape)?; + if !lhs.contains('\n') + && !rhs.contains('\n') + && lhs.len() + rhs.len() + total_overhead <= shape.width + { + // macro_name(lhs; rhs) or macro_name[lhs; rhs] + Some(format!("{}{}{}; {}{}", macro_name, left, lhs, rhs, right)) + } else { + // macro_name(\nlhs;\nrhs\n) or macro_name[\nlhs;\nrhs\n] + Some(format!( + "{}{}{}{};{}{}{}{}", + macro_name, + left, + nested_shape.indent.to_string_with_newline(context.config), + lhs, + nested_shape.indent.to_string_with_newline(context.config), + rhs, + shape.indent.to_string_with_newline(context.config), + right + )) + } +} + +pub(crate) fn rewrite_macro_def( + context: &RewriteContext<'_>, + shape: Shape, + indent: Indent, + def: &ast::MacroDef, + ident: symbol::Ident, + vis: &ast::Visibility, + span: Span, +) -> Option { + let snippet = Some(remove_trailing_white_spaces(context.snippet(span))); + if snippet.as_ref().map_or(true, |s| s.ends_with(';')) { + return snippet; + } + + let ts = def.body.tokens.clone(); + let mut parser = MacroParser::new(ts.into_trees()); + let parsed_def = match parser.parse() { + Some(def) => def, + None => return snippet, + }; + + let mut result = if def.macro_rules { + String::from("macro_rules!") + } else { + format!("{}macro", format_visibility(context, vis)) + }; + + result += " "; + result += rewrite_ident(context, ident); + + let multi_branch_style = def.macro_rules || parsed_def.branches.len() != 1; + + let arm_shape = if multi_branch_style { + shape + .block_indent(context.config.tab_spaces()) + .with_max_width(context.config) + } else { + shape + }; + + let branch_items = itemize_list( + context.snippet_provider, + parsed_def.branches.iter(), + "}", + ";", + |branch| branch.span.lo(), + |branch| branch.span.hi(), + |branch| match branch.rewrite(context, arm_shape, multi_branch_style) { + Some(v) => Some(v), + // if the rewrite returned None because a macro could not be rewritten, then return the + // original body + None if context.macro_rewrite_failure.get() => { + Some(context.snippet(branch.body).trim().to_string()) + } + None => None, + }, + context.snippet_provider.span_after(span, "{"), + span.hi(), + false, + ) + .collect::>(); + + let fmt = ListFormatting::new(arm_shape, context.config) + .separator(if def.macro_rules { ";" } else { "" }) + .trailing_separator(SeparatorTactic::Always) + .preserve_newline(true); + + if multi_branch_style { + result += " {"; + result += &arm_shape.indent.to_string_with_newline(context.config); + } + + match write_list(&branch_items, &fmt) { + Some(ref s) => result += s, + None => return snippet, + } + + if multi_branch_style { + result += &indent.to_string_with_newline(context.config); + result += "}"; + } + + Some(result) +} + +fn register_metavariable( + map: &mut HashMap, + result: &mut String, + name: &str, + dollar_count: usize, +) { + let mut new_name = "$".repeat(dollar_count - 1); + let mut old_name = "$".repeat(dollar_count); + + new_name.push('z'); + new_name.push_str(name); + old_name.push_str(name); + + result.push_str(&new_name); + map.insert(old_name, new_name); +} + +// Replaces `$foo` with `zfoo`. We must check for name overlap to ensure we +// aren't causing problems. +// This should also work for escaped `$` variables, where we leave earlier `$`s. +fn replace_names(input: &str) -> Option<(String, HashMap)> { + // Each substitution will require five or six extra bytes. + let mut result = String::with_capacity(input.len() + 64); + let mut substs = HashMap::new(); + let mut dollar_count = 0; + let mut cur_name = String::new(); + + for (kind, c) in CharClasses::new(input.chars()) { + if kind != FullCodeCharKind::Normal { + result.push(c); + } else if c == '$' { + dollar_count += 1; + } else if dollar_count == 0 { + result.push(c); + } else if !c.is_alphanumeric() && !cur_name.is_empty() { + // Terminates a name following one or more dollars. + register_metavariable(&mut substs, &mut result, &cur_name, dollar_count); + + result.push(c); + dollar_count = 0; + cur_name.clear(); + } else if c == '(' && cur_name.is_empty() { + // FIXME: Support macro def with repeat. + return None; + } else if c.is_alphanumeric() || c == '_' { + cur_name.push(c); + } + } + + if !cur_name.is_empty() { + register_metavariable(&mut substs, &mut result, &cur_name, dollar_count); + } + + debug!("replace_names `{}` {:?}", result, substs); + + Some((result, substs)) +} + +#[derive(Debug, Clone)] +enum MacroArgKind { + /// e.g., `$x: expr`. + MetaVariable(Symbol, String), + /// e.g., `$($foo: expr),*` + Repeat( + /// `()`, `[]` or `{}`. + Delimiter, + /// Inner arguments inside delimiters. + Vec, + /// Something after the closing delimiter and the repeat token, if available. + Option>, + /// The repeat token. This could be one of `*`, `+` or `?`. + Token, + ), + /// e.g., `[derive(Debug)]` + Delimited(Delimiter, Vec), + /// A possible separator. e.g., `,` or `;`. + Separator(String, String), + /// Other random stuff that does not fit to other kinds. + /// e.g., `== foo` in `($x: expr == foo)`. + Other(String, String), +} + +fn delim_token_to_str( + context: &RewriteContext<'_>, + delim_token: Delimiter, + shape: Shape, + use_multiple_lines: bool, + inner_is_empty: bool, +) -> (String, String) { + let (lhs, rhs) = match delim_token { + Delimiter::Parenthesis => ("(", ")"), + Delimiter::Bracket => ("[", "]"), + Delimiter::Brace => { + if inner_is_empty || use_multiple_lines { + ("{", "}") + } else { + ("{ ", " }") + } + } + Delimiter::Invisible => unreachable!(), + }; + if use_multiple_lines { + let indent_str = shape.indent.to_string_with_newline(context.config); + let nested_indent_str = shape + .indent + .block_indent(context.config) + .to_string_with_newline(context.config); + ( + format!("{}{}", lhs, nested_indent_str), + format!("{}{}", indent_str, rhs), + ) + } else { + (lhs.to_owned(), rhs.to_owned()) + } +} + +impl MacroArgKind { + fn starts_with_brace(&self) -> bool { + matches!( + *self, + MacroArgKind::Repeat(Delimiter::Brace, _, _, _) + | MacroArgKind::Delimited(Delimiter::Brace, _) + ) + } + + fn starts_with_dollar(&self) -> bool { + matches!( + *self, + MacroArgKind::Repeat(..) | MacroArgKind::MetaVariable(..) + ) + } + + fn ends_with_space(&self) -> bool { + matches!(*self, MacroArgKind::Separator(..)) + } + + fn has_meta_var(&self) -> bool { + match *self { + MacroArgKind::MetaVariable(..) => true, + MacroArgKind::Repeat(_, ref args, _, _) => args.iter().any(|a| a.kind.has_meta_var()), + _ => false, + } + } + + fn rewrite( + &self, + context: &RewriteContext<'_>, + shape: Shape, + use_multiple_lines: bool, + ) -> Option { + let rewrite_delimited_inner = |delim_tok, args| -> Option<(String, String, String)> { + let inner = wrap_macro_args(context, args, shape)?; + let (lhs, rhs) = delim_token_to_str(context, delim_tok, shape, false, inner.is_empty()); + if lhs.len() + inner.len() + rhs.len() <= shape.width { + return Some((lhs, inner, rhs)); + } + + let (lhs, rhs) = delim_token_to_str(context, delim_tok, shape, true, false); + let nested_shape = shape + .block_indent(context.config.tab_spaces()) + .with_max_width(context.config); + let inner = wrap_macro_args(context, args, nested_shape)?; + Some((lhs, inner, rhs)) + }; + + match *self { + MacroArgKind::MetaVariable(ty, ref name) => Some(format!("${}:{}", name, ty)), + MacroArgKind::Repeat(delim_tok, ref args, ref another, ref tok) => { + let (lhs, inner, rhs) = rewrite_delimited_inner(delim_tok, args)?; + let another = another + .as_ref() + .and_then(|a| a.rewrite(context, shape, use_multiple_lines)) + .unwrap_or_else(|| "".to_owned()); + let repeat_tok = pprust::token_to_string(tok); + + Some(format!("${}{}{}{}{}", lhs, inner, rhs, another, repeat_tok)) + } + MacroArgKind::Delimited(delim_tok, ref args) => { + rewrite_delimited_inner(delim_tok, args) + .map(|(lhs, inner, rhs)| format!("{}{}{}", lhs, inner, rhs)) + } + MacroArgKind::Separator(ref sep, ref prefix) => Some(format!("{}{} ", prefix, sep)), + MacroArgKind::Other(ref inner, ref prefix) => Some(format!("{}{}", prefix, inner)), + } + } +} + +#[derive(Debug, Clone)] +struct ParsedMacroArg { + kind: MacroArgKind, +} + +impl ParsedMacroArg { + fn rewrite( + &self, + context: &RewriteContext<'_>, + shape: Shape, + use_multiple_lines: bool, + ) -> Option { + self.kind.rewrite(context, shape, use_multiple_lines) + } +} + +/// Parses macro arguments on macro def. +struct MacroArgParser { + /// Either a name of the next metavariable, a separator, or junk. + buf: String, + /// The first token of the current buffer. + start_tok: Token, + /// `true` if we are parsing a metavariable or a repeat. + is_meta_var: bool, + /// The last token parsed. + last_tok: Token, + /// Holds the parsed arguments. + result: Vec, +} + +fn last_tok(tt: &TokenTree) -> Token { + match *tt { + TokenTree::Token(ref t, _) => t.clone(), + TokenTree::Delimited(delim_span, delim, _) => Token { + kind: TokenKind::CloseDelim(delim), + span: delim_span.close, + }, + } +} + +impl MacroArgParser { + fn new() -> MacroArgParser { + MacroArgParser { + buf: String::new(), + is_meta_var: false, + last_tok: Token { + kind: TokenKind::Eof, + span: DUMMY_SP, + }, + start_tok: Token { + kind: TokenKind::Eof, + span: DUMMY_SP, + }, + result: vec![], + } + } + + fn set_last_tok(&mut self, tok: &TokenTree) { + self.last_tok = last_tok(tok); + } + + fn add_separator(&mut self) { + let prefix = if self.need_space_prefix() { + " ".to_owned() + } else { + "".to_owned() + }; + self.result.push(ParsedMacroArg { + kind: MacroArgKind::Separator(self.buf.clone(), prefix), + }); + self.buf.clear(); + } + + fn add_other(&mut self) { + let prefix = if self.need_space_prefix() { + " ".to_owned() + } else { + "".to_owned() + }; + self.result.push(ParsedMacroArg { + kind: MacroArgKind::Other(self.buf.clone(), prefix), + }); + self.buf.clear(); + } + + fn add_meta_variable(&mut self, iter: &mut TokenTreeCursor) -> Option<()> { + match iter.next() { + Some(TokenTree::Token( + Token { + kind: TokenKind::Ident(name, _), + .. + }, + _, + )) => { + self.result.push(ParsedMacroArg { + kind: MacroArgKind::MetaVariable(name, self.buf.clone()), + }); + + self.buf.clear(); + self.is_meta_var = false; + Some(()) + } + _ => None, + } + } + + fn add_delimited(&mut self, inner: Vec, delim: Delimiter) { + self.result.push(ParsedMacroArg { + kind: MacroArgKind::Delimited(delim, inner), + }); + } + + // $($foo: expr),? + fn add_repeat( + &mut self, + inner: Vec, + delim: Delimiter, + iter: &mut TokenTreeCursor, + ) -> Option<()> { + let mut buffer = String::new(); + let mut first = true; + + // Parse '*', '+' or '?. + for tok in iter { + self.set_last_tok(&tok); + if first { + first = false; + } + + match tok { + TokenTree::Token( + Token { + kind: TokenKind::BinOp(BinOpToken::Plus), + .. + }, + _, + ) + | TokenTree::Token( + Token { + kind: TokenKind::Question, + .. + }, + _, + ) + | TokenTree::Token( + Token { + kind: TokenKind::BinOp(BinOpToken::Star), + .. + }, + _, + ) => { + break; + } + TokenTree::Token(ref t, _) => { + buffer.push_str(&pprust::token_to_string(t)); + } + _ => return None, + } + } + + // There could be some random stuff between ')' and '*', '+' or '?'. + let another = if buffer.trim().is_empty() { + None + } else { + Some(Box::new(ParsedMacroArg { + kind: MacroArgKind::Other(buffer, "".to_owned()), + })) + }; + + self.result.push(ParsedMacroArg { + kind: MacroArgKind::Repeat(delim, inner, another, self.last_tok.clone()), + }); + Some(()) + } + + fn update_buffer(&mut self, t: &Token) { + if self.buf.is_empty() { + self.start_tok = t.clone(); + } else { + let needs_space = match next_space(&self.last_tok.kind) { + SpaceState::Ident => ident_like(t), + SpaceState::Punctuation => !ident_like(t), + SpaceState::Always => true, + SpaceState::Never => false, + }; + if force_space_before(&t.kind) || needs_space { + self.buf.push(' '); + } + } + + self.buf.push_str(&pprust::token_to_string(t)); + } + + fn need_space_prefix(&self) -> bool { + if self.result.is_empty() { + return false; + } + + let last_arg = self.result.last().unwrap(); + if let MacroArgKind::MetaVariable(..) = last_arg.kind { + if ident_like(&self.start_tok) { + return true; + } + if self.start_tok.kind == TokenKind::Colon { + return true; + } + } + + if force_space_before(&self.start_tok.kind) { + return true; + } + + false + } + + /// Returns a collection of parsed macro def's arguments. + fn parse(mut self, tokens: TokenStream) -> Option> { + let mut iter = tokens.into_trees(); + + while let Some(tok) = iter.next() { + match tok { + TokenTree::Token( + Token { + kind: TokenKind::Dollar, + span, + }, + _, + ) => { + // We always want to add a separator before meta variables. + if !self.buf.is_empty() { + self.add_separator(); + } + + // Start keeping the name of this metavariable in the buffer. + self.is_meta_var = true; + self.start_tok = Token { + kind: TokenKind::Dollar, + span, + }; + } + TokenTree::Token( + Token { + kind: TokenKind::Colon, + .. + }, + _, + ) if self.is_meta_var => { + self.add_meta_variable(&mut iter)?; + } + TokenTree::Token(ref t, _) => self.update_buffer(t), + TokenTree::Delimited(_delimited_span, delimited, ref tts) => { + if !self.buf.is_empty() { + if next_space(&self.last_tok.kind) == SpaceState::Always { + self.add_separator(); + } else { + self.add_other(); + } + } + + // Parse the stuff inside delimiters. + let parser = MacroArgParser::new(); + let delimited_arg = parser.parse(tts.clone())?; + + if self.is_meta_var { + self.add_repeat(delimited_arg, delimited, &mut iter)?; + self.is_meta_var = false; + } else { + self.add_delimited(delimited_arg, delimited); + } + } + } + + self.set_last_tok(&tok); + } + + // We are left with some stuff in the buffer. Since there is nothing + // left to separate, add this as `Other`. + if !self.buf.is_empty() { + self.add_other(); + } + + Some(self.result) + } +} + +fn wrap_macro_args( + context: &RewriteContext<'_>, + args: &[ParsedMacroArg], + shape: Shape, +) -> Option { + wrap_macro_args_inner(context, args, shape, false) + .or_else(|| wrap_macro_args_inner(context, args, shape, true)) +} + +fn wrap_macro_args_inner( + context: &RewriteContext<'_>, + args: &[ParsedMacroArg], + shape: Shape, + use_multiple_lines: bool, +) -> Option { + let mut result = String::with_capacity(128); + let mut iter = args.iter().peekable(); + let indent_str = shape.indent.to_string_with_newline(context.config); + + while let Some(arg) = iter.next() { + result.push_str(&arg.rewrite(context, shape, use_multiple_lines)?); + + if use_multiple_lines + && (arg.kind.ends_with_space() || iter.peek().map_or(false, |a| a.kind.has_meta_var())) + { + if arg.kind.ends_with_space() { + result.pop(); + } + result.push_str(&indent_str); + } else if let Some(next_arg) = iter.peek() { + let space_before_dollar = + !arg.kind.ends_with_space() && next_arg.kind.starts_with_dollar(); + let space_before_brace = next_arg.kind.starts_with_brace(); + if space_before_dollar || space_before_brace { + result.push(' '); + } + } + } + + if !use_multiple_lines && result.len() >= shape.width { + None + } else { + Some(result) + } +} + +// This is a bit sketchy. The token rules probably need tweaking, but it works +// for some common cases. I hope the basic logic is sufficient. Note that the +// meaning of some tokens is a bit different here from usual Rust, e.g., `*` +// and `(`/`)` have special meaning. +// +// We always try and format on one line. +// FIXME: Use multi-line when every thing does not fit on one line. +fn format_macro_args( + context: &RewriteContext<'_>, + token_stream: TokenStream, + shape: Shape, +) -> Option { + if !context.config.format_macro_matchers() { + let span = span_for_token_stream(&token_stream); + return Some(match span { + Some(span) => context.snippet(span).to_owned(), + None => String::new(), + }); + } + let parsed_args = MacroArgParser::new().parse(token_stream)?; + wrap_macro_args(context, &parsed_args, shape) +} + +fn span_for_token_stream(token_stream: &TokenStream) -> Option { + token_stream.trees().next().map(|tt| tt.span()) +} + +// We should insert a space if the next token is a: +#[derive(Copy, Clone, PartialEq)] +enum SpaceState { + Never, + Punctuation, + Ident, // Or ident/literal-like thing. + Always, +} + +fn force_space_before(tok: &TokenKind) -> bool { + debug!("tok: force_space_before {:?}", tok); + + match tok { + TokenKind::Eq + | TokenKind::Lt + | TokenKind::Le + | TokenKind::EqEq + | TokenKind::Ne + | TokenKind::Ge + | TokenKind::Gt + | TokenKind::AndAnd + | TokenKind::OrOr + | TokenKind::Not + | TokenKind::Tilde + | TokenKind::BinOpEq(_) + | TokenKind::At + | TokenKind::RArrow + | TokenKind::LArrow + | TokenKind::FatArrow + | TokenKind::BinOp(_) + | TokenKind::Pound + | TokenKind::Dollar => true, + _ => false, + } +} + +fn ident_like(tok: &Token) -> bool { + matches!( + tok.kind, + TokenKind::Ident(..) | TokenKind::Literal(..) | TokenKind::Lifetime(_) + ) +} + +fn next_space(tok: &TokenKind) -> SpaceState { + debug!("next_space: {:?}", tok); + + match tok { + TokenKind::Not + | TokenKind::BinOp(BinOpToken::And) + | TokenKind::Tilde + | TokenKind::At + | TokenKind::Comma + | TokenKind::Dot + | TokenKind::DotDot + | TokenKind::DotDotDot + | TokenKind::DotDotEq + | TokenKind::Question => SpaceState::Punctuation, + + TokenKind::ModSep + | TokenKind::Pound + | TokenKind::Dollar + | TokenKind::OpenDelim(_) + | TokenKind::CloseDelim(_) => SpaceState::Never, + + TokenKind::Literal(..) | TokenKind::Ident(..) | TokenKind::Lifetime(_) => SpaceState::Ident, + + _ => SpaceState::Always, + } +} + +/// Tries to convert a macro use into a short hand try expression. Returns `None` +/// when the macro is not an instance of `try!` (or parsing the inner expression +/// failed). +pub(crate) fn convert_try_mac( + mac: &ast::MacCall, + context: &RewriteContext<'_>, +) -> Option { + let path = &pprust::path_to_string(&mac.path); + if path == "try" || path == "r#try" { + let ts = mac.args.tokens.clone(); + + Some(ast::Expr { + id: ast::NodeId::root(), // dummy value + kind: ast::ExprKind::Try(parse_expr(context, ts)?), + span: mac.span(), // incorrect span, but shouldn't matter too much + attrs: ast::AttrVec::new(), + tokens: None, + }) + } else { + None + } +} + +pub(crate) fn macro_style(mac: &ast::MacCall, context: &RewriteContext<'_>) -> Delimiter { + let snippet = context.snippet(mac.span()); + let paren_pos = snippet.find_uncommented("(").unwrap_or(usize::max_value()); + let bracket_pos = snippet.find_uncommented("[").unwrap_or(usize::max_value()); + let brace_pos = snippet.find_uncommented("{").unwrap_or(usize::max_value()); + + if paren_pos < bracket_pos && paren_pos < brace_pos { + Delimiter::Parenthesis + } else if bracket_pos < brace_pos { + Delimiter::Bracket + } else { + Delimiter::Brace + } +} + +// A very simple parser that just parses a macros 2.0 definition into its branches. +// Currently we do not attempt to parse any further than that. +struct MacroParser { + toks: TokenTreeCursor, +} + +impl MacroParser { + const fn new(toks: TokenTreeCursor) -> Self { + Self { toks } + } + + // (`(` ... `)` `=>` `{` ... `}`)* + fn parse(&mut self) -> Option { + let mut branches = vec![]; + while self.toks.look_ahead(1).is_some() { + branches.push(self.parse_branch()?); + } + + Some(Macro { branches }) + } + + // `(` ... `)` `=>` `{` ... `}` + fn parse_branch(&mut self) -> Option { + let tok = self.toks.next()?; + let (lo, args_paren_kind) = match tok { + TokenTree::Token(..) => return None, + TokenTree::Delimited(delimited_span, d, _) => (delimited_span.open.lo(), d), + }; + let args = TokenStream::new(vec![tok]); + match self.toks.next()? { + TokenTree::Token( + Token { + kind: TokenKind::FatArrow, + .. + }, + _, + ) => {} + _ => return None, + } + let (mut hi, body, whole_body) = match self.toks.next()? { + TokenTree::Token(..) => return None, + TokenTree::Delimited(delimited_span, ..) => { + let data = delimited_span.entire().data(); + ( + data.hi, + Span::new( + data.lo + BytePos(1), + data.hi - BytePos(1), + data.ctxt, + data.parent, + ), + delimited_span.entire(), + ) + } + }; + if let Some(TokenTree::Token( + Token { + kind: TokenKind::Semi, + span, + }, + _, + )) = self.toks.look_ahead(0) + { + hi = span.hi(); + self.toks.next(); + } + Some(MacroBranch { + span: mk_sp(lo, hi), + args_paren_kind, + args, + body, + whole_body, + }) + } +} + +// A parsed macros 2.0 macro definition. +struct Macro { + branches: Vec, +} + +// FIXME: it would be more efficient to use references to the token streams +// rather than clone them, if we can make the borrowing work out. +struct MacroBranch { + span: Span, + args_paren_kind: Delimiter, + args: TokenStream, + body: Span, + whole_body: Span, +} + +impl MacroBranch { + fn rewrite( + &self, + context: &RewriteContext<'_>, + shape: Shape, + multi_branch_style: bool, + ) -> Option { + // Only attempt to format function-like macros. + if self.args_paren_kind != Delimiter::Parenthesis { + // FIXME(#1539): implement for non-sugared macros. + return None; + } + + // 5 = " => {" + let mut result = format_macro_args(context, self.args.clone(), shape.sub_width(5)?)?; + + if multi_branch_style { + result += " =>"; + } + + if !context.config.format_macro_bodies() { + result += " "; + result += context.snippet(self.whole_body); + return Some(result); + } + + // The macro body is the most interesting part. It might end up as various + // AST nodes, but also has special variables (e.g, `$foo`) which can't be + // parsed as regular Rust code (and note that these can be escaped using + // `$$`). We'll try and format like an AST node, but we'll substitute + // variables for new names with the same length first. + + let old_body = context.snippet(self.body).trim(); + let (body_str, substs) = replace_names(old_body)?; + let has_block_body = old_body.starts_with('{'); + + let mut config = context.config.clone(); + config.set().hide_parse_errors(true); + + result += " {"; + + let body_indent = if has_block_body { + shape.indent + } else { + shape.indent.block_indent(&config) + }; + let new_width = config.max_width() - body_indent.width(); + config.set().max_width(new_width); + + // First try to format as items, then as statements. + let new_body_snippet = match crate::format_snippet(&body_str, &config, true) { + Some(new_body) => new_body, + None => { + let new_width = new_width + config.tab_spaces(); + config.set().max_width(new_width); + match crate::format_code_block(&body_str, &config, true) { + Some(new_body) => new_body, + None => return None, + } + } + }; + + if !filtered_str_fits(&new_body_snippet.snippet, config.max_width(), shape) { + return None; + } + + // Indent the body since it is in a block. + let indent_str = body_indent.to_string(&config); + let mut new_body = LineClasses::new(new_body_snippet.snippet.trim_end()) + .enumerate() + .fold( + (String::new(), true), + |(mut s, need_indent), (i, (kind, ref l))| { + if !is_empty_line(l) + && need_indent + && !new_body_snippet.is_line_non_formatted(i + 1) + { + s += &indent_str; + } + (s + l + "\n", indent_next_line(kind, l, &config)) + }, + ) + .0; + + // Undo our replacement of macro variables. + // FIXME: this could be *much* more efficient. + for (old, new) in &substs { + if old_body.contains(new) { + debug!("rewrite_macro_def: bailing matching variable: `{}`", new); + return None; + } + new_body = new_body.replace(new, old); + } + + if has_block_body { + result += new_body.trim(); + } else if !new_body.is_empty() { + result += "\n"; + result += &new_body; + result += &shape.indent.to_string(&config); + } + + result += "}"; + + Some(result) + } +} + +/// Format `lazy_static!` from . +/// +/// # Expected syntax +/// +/// ```text +/// lazy_static! { +/// [pub] static ref NAME_1: TYPE_1 = EXPR_1; +/// [pub] static ref NAME_2: TYPE_2 = EXPR_2; +/// ... +/// [pub] static ref NAME_N: TYPE_N = EXPR_N; +/// } +/// ``` +fn format_lazy_static( + context: &RewriteContext<'_>, + shape: Shape, + ts: TokenStream, +) -> Option { + let mut result = String::with_capacity(1024); + let nested_shape = shape + .block_indent(context.config.tab_spaces()) + .with_max_width(context.config); + + result.push_str("lazy_static! {"); + result.push_str(&nested_shape.indent.to_string_with_newline(context.config)); + + let parsed_elems = parse_lazy_static(context, ts)?; + let last = parsed_elems.len() - 1; + for (i, (vis, id, ty, expr)) in parsed_elems.iter().enumerate() { + // Rewrite as a static item. + let vis = crate::utils::format_visibility(context, vis); + let mut stmt = String::with_capacity(128); + stmt.push_str(&format!( + "{}static ref {}: {} =", + vis, + id, + ty.rewrite(context, nested_shape)? + )); + result.push_str(&rewrite_assign_rhs( + context, + stmt, + &*expr, + &RhsAssignKind::Expr(&expr.kind, expr.span), + nested_shape.sub_width(1)?, + )?); + result.push(';'); + if i != last { + result.push_str(&nested_shape.indent.to_string_with_newline(context.config)); + } + } + + result.push_str(&shape.indent.to_string_with_newline(context.config)); + result.push('}'); + + Some(result) +} + +fn rewrite_macro_with_items( + context: &RewriteContext<'_>, + items: &[MacroArg], + macro_name: &str, + shape: Shape, + style: Delimiter, + position: MacroPosition, + span: Span, +) -> Option { + let (opener, closer) = match style { + Delimiter::Parenthesis => ("(", ")"), + Delimiter::Bracket => ("[", "]"), + Delimiter::Brace => (" {", "}"), + _ => return None, + }; + let trailing_semicolon = match style { + Delimiter::Parenthesis | Delimiter::Bracket if position == MacroPosition::Item => ";", + _ => "", + }; + + let mut visitor = FmtVisitor::from_context(context); + visitor.block_indent = shape.indent.block_indent(context.config); + visitor.last_pos = context.snippet_provider.span_after(span, opener.trim()); + for item in items { + let item = match item { + MacroArg::Item(item) => item, + _ => return None, + }; + visitor.visit_item(item); + } + + let mut result = String::with_capacity(256); + result.push_str(macro_name); + result.push_str(opener); + result.push_str(&visitor.block_indent.to_string_with_newline(context.config)); + result.push_str(visitor.buffer.trim()); + result.push_str(&shape.indent.to_string_with_newline(context.config)); + result.push_str(closer); + result.push_str(trailing_semicolon); + Some(result) +} diff --git a/src/matches.rs b/src/matches.rs new file mode 100644 index 000000000000..aac5e59b8603 --- /dev/null +++ b/src/matches.rs @@ -0,0 +1,601 @@ +//! Format match expression. + +use std::iter::repeat; + +use rustc_ast::{ast, ptr}; +use rustc_span::{BytePos, Span}; + +use crate::comment::{combine_strs_with_missing_comments, rewrite_comment}; +use crate::config::lists::*; +use crate::config::{Config, ControlBraceStyle, IndentStyle, MatchArmLeadingPipe, Version}; +use crate::expr::{ + format_expr, is_empty_block, is_simple_block, is_unsafe_block, prefer_next_line, rewrite_cond, + ExprType, RhsTactics, +}; +use crate::lists::{itemize_list, write_list, ListFormatting}; +use crate::rewrite::{Rewrite, RewriteContext}; +use crate::shape::Shape; +use crate::source_map::SpanUtils; +use crate::spanned::Spanned; +use crate::utils::{ + contains_skip, extra_offset, first_line_width, inner_attributes, last_line_extendable, mk_sp, + semicolon_for_expr, trimmed_last_line_width, unicode_str_width, +}; + +/// A simple wrapper type against `ast::Arm`. Used inside `write_list()`. +struct ArmWrapper<'a> { + arm: &'a ast::Arm, + /// `true` if the arm is the last one in match expression. Used to decide on whether we should + /// add trailing comma to the match arm when `config.trailing_comma() == Never`. + is_last: bool, + /// Holds a byte position of `|` at the beginning of the arm pattern, if available. + beginning_vert: Option, +} + +impl<'a> ArmWrapper<'a> { + fn new(arm: &'a ast::Arm, is_last: bool, beginning_vert: Option) -> ArmWrapper<'a> { + ArmWrapper { + arm, + is_last, + beginning_vert, + } + } +} + +impl<'a> Spanned for ArmWrapper<'a> { + fn span(&self) -> Span { + if let Some(lo) = self.beginning_vert { + let lo = std::cmp::min(lo, self.arm.span().lo()); + mk_sp(lo, self.arm.span().hi()) + } else { + self.arm.span() + } + } +} + +impl<'a> Rewrite for ArmWrapper<'a> { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + rewrite_match_arm( + context, + self.arm, + shape, + self.is_last, + self.beginning_vert.is_some(), + ) + } +} + +pub(crate) fn rewrite_match( + context: &RewriteContext<'_>, + cond: &ast::Expr, + arms: &[ast::Arm], + shape: Shape, + span: Span, + attrs: &[ast::Attribute], +) -> Option { + // Do not take the rhs overhead from the upper expressions into account + // when rewriting match condition. + let cond_shape = Shape { + width: context.budget(shape.used_width()), + ..shape + }; + // 6 = `match ` + let cond_shape = match context.config.indent_style() { + IndentStyle::Visual => cond_shape.shrink_left(6)?, + IndentStyle::Block => cond_shape.offset_left(6)?, + }; + let cond_str = cond.rewrite(context, cond_shape)?; + let alt_block_sep = &shape.indent.to_string_with_newline(context.config); + let block_sep = match context.config.control_brace_style() { + ControlBraceStyle::AlwaysNextLine => alt_block_sep, + _ if last_line_extendable(&cond_str) => " ", + // 2 = ` {` + _ if cond_str.contains('\n') || cond_str.len() + 2 > cond_shape.width => alt_block_sep, + _ => " ", + }; + + let nested_indent_str = shape + .indent + .block_indent(context.config) + .to_string(context.config); + // Inner attributes. + let inner_attrs = &inner_attributes(attrs); + let inner_attrs_str = if inner_attrs.is_empty() { + String::new() + } else { + inner_attrs + .rewrite(context, shape) + .map(|s| format!("{}{}\n", nested_indent_str, s))? + }; + + let open_brace_pos = if inner_attrs.is_empty() { + let hi = if arms.is_empty() { + span.hi() + } else { + arms[0].span().lo() + }; + context + .snippet_provider + .span_after(mk_sp(cond.span.hi(), hi), "{") + } else { + inner_attrs[inner_attrs.len() - 1].span.hi() + }; + + if arms.is_empty() { + let snippet = context.snippet(mk_sp(open_brace_pos, span.hi() - BytePos(1))); + if snippet.trim().is_empty() { + Some(format!("match {} {{}}", cond_str)) + } else { + // Empty match with comments or inner attributes? We are not going to bother, sorry ;) + Some(context.snippet(span).to_owned()) + } + } else { + let span_after_cond = mk_sp(cond.span.hi(), span.hi()); + Some(format!( + "match {}{}{{\n{}{}{}\n{}}}", + cond_str, + block_sep, + inner_attrs_str, + nested_indent_str, + rewrite_match_arms(context, arms, shape, span_after_cond, open_brace_pos)?, + shape.indent.to_string(context.config), + )) + } +} + +fn arm_comma(config: &Config, body: &ast::Expr, is_last: bool) -> &'static str { + if is_last && config.trailing_comma() == SeparatorTactic::Never { + "" + } else if config.match_block_trailing_comma() { + "," + } else if let ast::ExprKind::Block(ref block, _) = body.kind { + if let ast::BlockCheckMode::Default = block.rules { + "" + } else { + "," + } + } else { + "," + } +} + +/// Collect a byte position of the beginning `|` for each arm, if available. +fn collect_beginning_verts( + context: &RewriteContext<'_>, + arms: &[ast::Arm], +) -> Vec> { + arms.iter() + .map(|a| { + context + .snippet(a.pat.span) + .starts_with('|') + .then(|| a.pat.span().lo()) + }) + .collect() +} + +fn rewrite_match_arms( + context: &RewriteContext<'_>, + arms: &[ast::Arm], + shape: Shape, + span: Span, + open_brace_pos: BytePos, +) -> Option { + let arm_shape = shape + .block_indent(context.config.tab_spaces()) + .with_max_width(context.config); + + let arm_len = arms.len(); + let is_last_iter = repeat(false) + .take(arm_len.saturating_sub(1)) + .chain(repeat(true)); + let beginning_verts = collect_beginning_verts(context, arms); + let items = itemize_list( + context.snippet_provider, + arms.iter() + .zip(is_last_iter) + .zip(beginning_verts.into_iter()) + .map(|((arm, is_last), beginning_vert)| ArmWrapper::new(arm, is_last, beginning_vert)), + "}", + "|", + |arm| arm.span().lo(), + |arm| arm.span().hi(), + |arm| arm.rewrite(context, arm_shape), + open_brace_pos, + span.hi(), + false, + ); + let arms_vec: Vec<_> = items.collect(); + // We will add/remove commas inside `arm.rewrite()`, and hence no separator here. + let fmt = ListFormatting::new(arm_shape, context.config) + .separator("") + .preserve_newline(true); + + write_list(&arms_vec, &fmt) +} + +fn rewrite_match_arm( + context: &RewriteContext<'_>, + arm: &ast::Arm, + shape: Shape, + is_last: bool, + has_leading_pipe: bool, +) -> Option { + let (missing_span, attrs_str) = if !arm.attrs.is_empty() { + if contains_skip(&arm.attrs) { + let (_, body) = flatten_arm_body(context, &arm.body, None); + // `arm.span()` does not include trailing comma, add it manually. + return Some(format!( + "{}{}", + context.snippet(arm.span()), + arm_comma(context.config, body, is_last), + )); + } + let missing_span = mk_sp(arm.attrs[arm.attrs.len() - 1].span.hi(), arm.pat.span.lo()); + (missing_span, arm.attrs.rewrite(context, shape)?) + } else { + (mk_sp(arm.span().lo(), arm.span().lo()), String::new()) + }; + + // Leading pipe offset + // 2 = `| ` + let (pipe_offset, pipe_str) = match context.config.match_arm_leading_pipes() { + MatchArmLeadingPipe::Never => (0, ""), + MatchArmLeadingPipe::Preserve if !has_leading_pipe => (0, ""), + MatchArmLeadingPipe::Preserve | MatchArmLeadingPipe::Always => (2, "| "), + }; + + // Patterns + // 5 = ` => {` + let pat_shape = shape.sub_width(5)?.offset_left(pipe_offset)?; + let pats_str = arm.pat.rewrite(context, pat_shape)?; + + // Guard + let block_like_pat = trimmed_last_line_width(&pats_str) <= context.config.tab_spaces(); + let new_line_guard = pats_str.contains('\n') && !block_like_pat; + let guard_str = rewrite_guard( + context, + &arm.guard, + shape, + trimmed_last_line_width(&pats_str), + new_line_guard, + )?; + + let lhs_str = combine_strs_with_missing_comments( + context, + &attrs_str, + &format!("{}{}{}", pipe_str, pats_str, guard_str), + missing_span, + shape, + false, + )?; + + let arrow_span = mk_sp(arm.pat.span.hi(), arm.body.span().lo()); + rewrite_match_body( + context, + &arm.body, + &lhs_str, + shape, + guard_str.contains('\n'), + arrow_span, + is_last, + ) +} + +fn stmt_is_expr_mac(stmt: &ast::Stmt) -> bool { + if let ast::StmtKind::Expr(expr) = &stmt.kind { + if let ast::ExprKind::MacCall(_) = &expr.kind { + return true; + } + } + false +} + +fn block_can_be_flattened<'a>( + context: &RewriteContext<'_>, + expr: &'a ast::Expr, +) -> Option<&'a ast::Block> { + match expr.kind { + ast::ExprKind::Block(ref block, _) + if !is_unsafe_block(block) + && !context.inside_macro() + && is_simple_block(context, block, Some(&expr.attrs)) + && !stmt_is_expr_mac(&block.stmts[0]) => + { + Some(&*block) + } + _ => None, + } +} + +// (extend, body) +// @extend: true if the arm body can be put next to `=>` +// @body: flattened body, if the body is block with a single expression +fn flatten_arm_body<'a>( + context: &'a RewriteContext<'_>, + body: &'a ast::Expr, + opt_shape: Option, +) -> (bool, &'a ast::Expr) { + let can_extend = + |expr| !context.config.force_multiline_blocks() && can_flatten_block_around_this(expr); + + if let Some(block) = block_can_be_flattened(context, body) { + if let ast::StmtKind::Expr(ref expr) = block.stmts[0].kind { + if let ast::ExprKind::Block(..) = expr.kind { + if expr.attrs.is_empty() { + flatten_arm_body(context, expr, None) + } else { + (true, body) + } + } else { + let cond_becomes_muti_line = opt_shape + .and_then(|shape| rewrite_cond(context, expr, shape)) + .map_or(false, |cond| cond.contains('\n')); + if cond_becomes_muti_line { + (false, &*body) + } else { + (can_extend(expr), &*expr) + } + } + } else { + (false, &*body) + } + } else { + (can_extend(body), &*body) + } +} + +fn rewrite_match_body( + context: &RewriteContext<'_>, + body: &ptr::P, + pats_str: &str, + shape: Shape, + has_guard: bool, + arrow_span: Span, + is_last: bool, +) -> Option { + let (extend, body) = flatten_arm_body( + context, + body, + shape.offset_left(extra_offset(pats_str, shape) + 4), + ); + let (is_block, is_empty_block) = if let ast::ExprKind::Block(ref block, _) = body.kind { + (true, is_empty_block(context, block, Some(&body.attrs))) + } else { + (false, false) + }; + + let comma = arm_comma(context.config, body, is_last); + let alt_block_sep = &shape.indent.to_string_with_newline(context.config); + + let combine_orig_body = |body_str: &str| { + let block_sep = match context.config.control_brace_style() { + ControlBraceStyle::AlwaysNextLine if is_block => alt_block_sep, + _ => " ", + }; + + Some(format!("{} =>{}{}{}", pats_str, block_sep, body_str, comma)) + }; + + let next_line_indent = if !is_block || is_empty_block { + shape.indent.block_indent(context.config) + } else { + shape.indent + }; + + let forbid_same_line = + (has_guard && pats_str.contains('\n') && !is_empty_block) || !body.attrs.is_empty(); + + // Look for comments between `=>` and the start of the body. + let arrow_comment = { + let arrow_snippet = context.snippet(arrow_span).trim(); + // search for the arrow starting from the end of the snippet since there may be a match + // expression within the guard + let arrow_index = arrow_snippet.rfind("=>").unwrap(); + // 2 = `=>` + let comment_str = arrow_snippet[arrow_index + 2..].trim(); + if comment_str.is_empty() { + String::new() + } else { + rewrite_comment(comment_str, false, shape, context.config)? + } + }; + + let combine_next_line_body = |body_str: &str| { + let nested_indent_str = next_line_indent.to_string_with_newline(context.config); + + if is_block { + let mut result = pats_str.to_owned(); + result.push_str(" =>"); + if !arrow_comment.is_empty() { + result.push_str(&nested_indent_str); + result.push_str(&arrow_comment); + } + result.push_str(&nested_indent_str); + result.push_str(body_str); + result.push_str(comma); + return Some(result); + } + + let indent_str = shape.indent.to_string_with_newline(context.config); + let (body_prefix, body_suffix) = + if context.config.match_arm_blocks() && !context.inside_macro() { + let comma = if context.config.match_block_trailing_comma() { + "," + } else { + "" + }; + let semicolon = if context.config.version() == Version::One { + "" + } else { + if semicolon_for_expr(context, body) { + ";" + } else { + "" + } + }; + ("{", format!("{}{}}}{}", semicolon, indent_str, comma)) + } else { + ("", String::from(",")) + }; + + let block_sep = match context.config.control_brace_style() { + ControlBraceStyle::AlwaysNextLine => format!("{}{}", alt_block_sep, body_prefix), + _ if body_prefix.is_empty() => "".to_owned(), + _ if forbid_same_line || !arrow_comment.is_empty() => { + format!("{}{}", alt_block_sep, body_prefix) + } + _ => format!(" {}", body_prefix), + } + &nested_indent_str; + + let mut result = pats_str.to_owned(); + result.push_str(" =>"); + if !arrow_comment.is_empty() { + result.push_str(&indent_str); + result.push_str(&arrow_comment); + } + result.push_str(&block_sep); + result.push_str(body_str); + result.push_str(&body_suffix); + Some(result) + }; + + // Let's try and get the arm body on the same line as the condition. + // 4 = ` => `.len() + let orig_body_shape = shape + .offset_left(extra_offset(pats_str, shape) + 4) + .and_then(|shape| shape.sub_width(comma.len())); + let orig_body = if forbid_same_line || !arrow_comment.is_empty() { + None + } else if let Some(body_shape) = orig_body_shape { + let rewrite = nop_block_collapse( + format_expr(body, ExprType::Statement, context, body_shape), + body_shape.width, + ); + + match rewrite { + Some(ref body_str) + if is_block + || (!body_str.contains('\n') + && unicode_str_width(body_str) <= body_shape.width) => + { + return combine_orig_body(body_str); + } + _ => rewrite, + } + } else { + None + }; + let orig_budget = orig_body_shape.map_or(0, |shape| shape.width); + + // Try putting body on the next line and see if it looks better. + let next_line_body_shape = Shape::indented(next_line_indent, context.config); + let next_line_body = nop_block_collapse( + format_expr(body, ExprType::Statement, context, next_line_body_shape), + next_line_body_shape.width, + ); + match (orig_body, next_line_body) { + (Some(ref orig_str), Some(ref next_line_str)) + if prefer_next_line(orig_str, next_line_str, RhsTactics::Default) => + { + combine_next_line_body(next_line_str) + } + (Some(ref orig_str), _) if extend && first_line_width(orig_str) <= orig_budget => { + combine_orig_body(orig_str) + } + (Some(ref orig_str), Some(ref next_line_str)) if orig_str.contains('\n') => { + combine_next_line_body(next_line_str) + } + (None, Some(ref next_line_str)) => combine_next_line_body(next_line_str), + (None, None) => None, + (Some(ref orig_str), _) => combine_orig_body(orig_str), + } +} + +// The `if ...` guard on a match arm. +fn rewrite_guard( + context: &RewriteContext<'_>, + guard: &Option>, + shape: Shape, + // The amount of space used up on this line for the pattern in + // the arm (excludes offset). + pattern_width: usize, + multiline_pattern: bool, +) -> Option { + if let Some(ref guard) = *guard { + // First try to fit the guard string on the same line as the pattern. + // 4 = ` if `, 5 = ` => {` + let cond_shape = shape + .offset_left(pattern_width + 4) + .and_then(|s| s.sub_width(5)); + if !multiline_pattern { + if let Some(cond_shape) = cond_shape { + if let Some(cond_str) = guard.rewrite(context, cond_shape) { + if !cond_str.contains('\n') || pattern_width <= context.config.tab_spaces() { + return Some(format!(" if {}", cond_str)); + } + } + } + } + + // Not enough space to put the guard after the pattern, try a newline. + // 3 = `if `, 5 = ` => {` + let cond_shape = Shape::indented(shape.indent.block_indent(context.config), context.config) + .offset_left(3) + .and_then(|s| s.sub_width(5)); + if let Some(cond_shape) = cond_shape { + if let Some(cond_str) = guard.rewrite(context, cond_shape) { + return Some(format!( + "{}if {}", + cond_shape.indent.to_string_with_newline(context.config), + cond_str + )); + } + } + + None + } else { + Some(String::new()) + } +} + +fn nop_block_collapse(block_str: Option, budget: usize) -> Option { + debug!("nop_block_collapse {:?} {}", block_str, budget); + block_str.map(|block_str| { + if block_str.starts_with('{') + && budget >= 2 + && (block_str[1..].find(|c: char| !c.is_whitespace()).unwrap() == block_str.len() - 2) + { + String::from("{}") + } else { + block_str + } + }) +} + +fn can_flatten_block_around_this(body: &ast::Expr) -> bool { + match body.kind { + // We do not allow `if` to stay on the same line, since we could easily mistake + // `pat => if cond { ... }` and `pat if cond => { ... }`. + ast::ExprKind::If(..) => false, + // We do not allow collapsing a block around expression with condition + // to avoid it being cluttered with match arm. + ast::ExprKind::ForLoop(..) | ast::ExprKind::While(..) => false, + ast::ExprKind::Loop(..) + | ast::ExprKind::Match(..) + | ast::ExprKind::Block(..) + | ast::ExprKind::Closure(..) + | ast::ExprKind::Array(..) + | ast::ExprKind::Call(..) + | ast::ExprKind::MethodCall(..) + | ast::ExprKind::MacCall(..) + | ast::ExprKind::Struct(..) + | ast::ExprKind::Tup(..) => true, + ast::ExprKind::AddrOf(_, _, ref expr) + | ast::ExprKind::Try(ref expr) + | ast::ExprKind::Unary(_, ref expr) + | ast::ExprKind::Index(ref expr, _) + | ast::ExprKind::Cast(ref expr, _) => can_flatten_block_around_this(expr), + _ => false, + } +} diff --git a/src/missed_spans.rs b/src/missed_spans.rs new file mode 100644 index 000000000000..28edcb784b40 --- /dev/null +++ b/src/missed_spans.rs @@ -0,0 +1,363 @@ +use rustc_span::{BytePos, Pos, Span}; + +use crate::comment::{is_last_comment_block, rewrite_comment, CodeCharKind, CommentCodeSlices}; +use crate::config::file_lines::FileLines; +use crate::config::FileName; +use crate::config::Version; +use crate::coverage::transform_missing_snippet; +use crate::shape::{Indent, Shape}; +use crate::source_map::LineRangeUtils; +use crate::utils::{count_lf_crlf, count_newlines, last_line_width, mk_sp}; +use crate::visitor::FmtVisitor; + +struct SnippetStatus { + /// An offset to the current line from the beginning of the original snippet. + line_start: usize, + /// A length of trailing whitespaces on the current line. + last_wspace: Option, + /// The current line number. + cur_line: usize, +} + +impl SnippetStatus { + fn new(cur_line: usize) -> Self { + SnippetStatus { + line_start: 0, + last_wspace: None, + cur_line, + } + } +} + +impl<'a> FmtVisitor<'a> { + fn output_at_start(&self) -> bool { + self.buffer.is_empty() + } + + pub(crate) fn format_missing(&mut self, end: BytePos) { + // HACK(topecongiro): we use `format_missing()` to extract a missing comment between + // a macro (or similar) and a trailing semicolon. Here we just try to avoid calling + // `format_missing_inner` in the common case where there is no such comment. + // This is a hack, ideally we should fix a possible bug in `format_missing_inner` + // or refactor `visit_mac` and `rewrite_macro`, but this should suffice to fix the + // issue (#2727). + let missing_snippet = self.snippet(mk_sp(self.last_pos, end)); + if missing_snippet.trim() == ";" { + self.push_str(";"); + self.last_pos = end; + return; + } + self.format_missing_inner(end, |this, last_snippet, _| this.push_str(last_snippet)) + } + + pub(crate) fn format_missing_with_indent(&mut self, end: BytePos) { + self.format_missing_indent(end, true) + } + + pub(crate) fn format_missing_no_indent(&mut self, end: BytePos) { + self.format_missing_indent(end, false) + } + + fn format_missing_indent(&mut self, end: BytePos, should_indent: bool) { + let config = self.config; + self.format_missing_inner(end, |this, last_snippet, snippet| { + this.push_str(last_snippet.trim_end()); + if last_snippet == snippet && !this.output_at_start() { + // No new lines in the snippet. + this.push_str("\n"); + } + if should_indent { + let indent = this.block_indent.to_string(config); + this.push_str(&indent); + } + }) + } + + fn format_missing_inner, &str, &str)>( + &mut self, + end: BytePos, + process_last_snippet: F, + ) { + let start = self.last_pos; + + if start == end { + // Do nothing if this is the beginning of the file. + if !self.output_at_start() { + process_last_snippet(self, "", ""); + } + return; + } + + assert!( + start < end, + "Request to format inverted span: {}", + self.parse_sess.span_to_debug_info(mk_sp(start, end)), + ); + + self.last_pos = end; + let span = mk_sp(start, end); + let snippet = self.snippet(span); + + // Do nothing for spaces in the beginning of the file + if start == BytePos(0) && end.0 as usize == snippet.len() && snippet.trim().is_empty() { + return; + } + + if snippet.trim().is_empty() && !out_of_file_lines_range!(self, span) { + // Keep vertical spaces within range. + self.push_vertical_spaces(count_newlines(snippet)); + process_last_snippet(self, "", snippet); + } else { + self.write_snippet(span, &process_last_snippet); + } + } + + fn push_vertical_spaces(&mut self, mut newline_count: usize) { + let offset = self.buffer.chars().rev().take_while(|c| *c == '\n').count(); + let newline_upper_bound = self.config.blank_lines_upper_bound() + 1; + let newline_lower_bound = self.config.blank_lines_lower_bound() + 1; + + if newline_count + offset > newline_upper_bound { + if offset >= newline_upper_bound { + newline_count = 0; + } else { + newline_count = newline_upper_bound - offset; + } + } else if newline_count + offset < newline_lower_bound { + if offset >= newline_lower_bound { + newline_count = 0; + } else { + newline_count = newline_lower_bound - offset; + } + } + + let blank_lines = "\n".repeat(newline_count); + self.push_str(&blank_lines); + } + + fn write_snippet(&mut self, span: Span, process_last_snippet: F) + where + F: Fn(&mut FmtVisitor<'_>, &str, &str), + { + // Get a snippet from the file start to the span's hi without allocating. + // We need it to determine what precedes the current comment. If the comment + // follows code on the same line, we won't touch it. + let big_span_lo = self.snippet_provider.start_pos(); + let big_snippet = self.snippet_provider.entire_snippet(); + let big_diff = (span.lo() - big_span_lo).to_usize(); + + let snippet = self.snippet(span); + + debug!("write_snippet `{}`", snippet); + + self.write_snippet_inner(big_snippet, snippet, big_diff, span, process_last_snippet); + } + + fn write_snippet_inner( + &mut self, + big_snippet: &str, + old_snippet: &str, + big_diff: usize, + span: Span, + process_last_snippet: F, + ) where + F: Fn(&mut FmtVisitor<'_>, &str, &str), + { + // Trim whitespace from the right hand side of each line. + // Annoyingly, the library functions for splitting by lines etc. are not + // quite right, so we must do it ourselves. + let line = self.parse_sess.line_of_byte_pos(span.lo()); + let file_name = &self.parse_sess.span_to_filename(span); + let mut status = SnippetStatus::new(line); + + let snippet = &*transform_missing_snippet(self.config, old_snippet); + + let slice_within_file_lines_range = + |file_lines: FileLines, cur_line, s| -> (usize, usize, bool) { + let (lf_count, crlf_count) = count_lf_crlf(s); + let newline_count = lf_count + crlf_count; + let within_file_lines_range = file_lines.contains_range( + file_name, + cur_line, + // if a newline character is at the end of the slice, then the number of + // newlines needs to be decreased by 1 so that the range checked against + // the file_lines is the visual range one would expect. + cur_line + newline_count - if s.ends_with('\n') { 1 } else { 0 }, + ); + (lf_count, crlf_count, within_file_lines_range) + }; + for (kind, offset, subslice) in CommentCodeSlices::new(snippet) { + debug!("{:?}: {:?}", kind, subslice); + + let (lf_count, crlf_count, within_file_lines_range) = + slice_within_file_lines_range(self.config.file_lines(), status.cur_line, subslice); + let newline_count = lf_count + crlf_count; + if CodeCharKind::Comment == kind && within_file_lines_range { + // 1: comment. + self.process_comment( + &mut status, + snippet, + &big_snippet[..(offset + big_diff)], + offset, + subslice, + ); + } else if subslice.trim().is_empty() && newline_count > 0 && within_file_lines_range { + // 2: blank lines. + self.push_vertical_spaces(newline_count); + status.cur_line += newline_count; + status.line_start = offset + lf_count + crlf_count * 2; + } else { + // 3: code which we failed to format or which is not within file-lines range. + self.process_missing_code(&mut status, snippet, subslice, offset, file_name); + } + } + + let last_snippet = &snippet[status.line_start..]; + let (_, _, within_file_lines_range) = + slice_within_file_lines_range(self.config.file_lines(), status.cur_line, last_snippet); + if within_file_lines_range { + process_last_snippet(self, last_snippet, snippet); + } else { + // just append what's left + self.push_str(last_snippet); + } + } + + fn process_comment( + &mut self, + status: &mut SnippetStatus, + snippet: &str, + big_snippet: &str, + offset: usize, + subslice: &str, + ) { + let last_char = big_snippet + .chars() + .rev() + .find(|rev_c| ![' ', '\t'].contains(rev_c)); + + let fix_indent = last_char.map_or(true, |rev_c| ['{', '\n'].contains(&rev_c)); + let mut on_same_line = false; + + let comment_indent = if fix_indent { + if let Some('{') = last_char { + self.push_str("\n"); + } + let indent_str = self.block_indent.to_string(self.config); + self.push_str(&indent_str); + self.block_indent + } else if self.config.version() == Version::Two && !snippet.starts_with('\n') { + // The comment appears on the same line as the previous formatted code. + // Assuming that comment is logically associated with that code, we want to keep it on + // the same level and avoid mixing it with possible other comment. + on_same_line = true; + self.push_str(" "); + self.block_indent + } else { + self.push_str(" "); + Indent::from_width(self.config, last_line_width(&self.buffer)) + }; + + let comment_width = ::std::cmp::min( + self.config.comment_width(), + self.config.max_width() - self.block_indent.width(), + ); + let comment_shape = Shape::legacy(comment_width, comment_indent); + + if on_same_line { + match subslice.find('\n') { + None => { + self.push_str(subslice); + } + Some(offset) if offset + 1 == subslice.len() => { + self.push_str(&subslice[..offset]); + } + Some(offset) => { + // keep first line as is: if it were too long and wrapped, it may get mixed + // with the other lines. + let first_line = &subslice[..offset]; + self.push_str(first_line); + self.push_str(&comment_indent.to_string_with_newline(self.config)); + + let other_lines = &subslice[offset + 1..]; + let comment_str = + rewrite_comment(other_lines, false, comment_shape, self.config) + .unwrap_or_else(|| String::from(other_lines)); + self.push_str(&comment_str); + } + } + } else { + let comment_str = rewrite_comment(subslice, false, comment_shape, self.config) + .unwrap_or_else(|| String::from(subslice)); + self.push_str(&comment_str); + } + + status.last_wspace = None; + status.line_start = offset + subslice.len(); + + // Add a newline: + // - if there isn't one already + // - otherwise, only if the last line is a line comment + if status.line_start <= snippet.len() { + match snippet[status.line_start..] + .chars() + // skip trailing whitespaces + .find(|c| !(*c == ' ' || *c == '\t')) + { + Some('\n') | Some('\r') => { + if !is_last_comment_block(subslice) { + self.push_str("\n"); + } + } + _ => self.push_str("\n"), + } + } + + status.cur_line += count_newlines(subslice); + } + + fn process_missing_code( + &mut self, + status: &mut SnippetStatus, + snippet: &str, + subslice: &str, + offset: usize, + file_name: &FileName, + ) { + for (mut i, c) in subslice.char_indices() { + i += offset; + + if c == '\n' { + let skip_this_line = !self + .config + .file_lines() + .contains_line(file_name, status.cur_line); + if skip_this_line { + status.last_wspace = None; + } + + if let Some(lw) = status.last_wspace { + self.push_str(&snippet[status.line_start..lw]); + self.push_str("\n"); + status.last_wspace = None; + } else { + self.push_str(&snippet[status.line_start..=i]); + } + + status.cur_line += 1; + status.line_start = i + 1; + } else if c.is_whitespace() && status.last_wspace.is_none() { + status.last_wspace = Some(i); + } else { + status.last_wspace = None; + } + } + + let remaining = snippet[status.line_start..subslice.len() + offset].trim(); + if !remaining.is_empty() { + self.push_str(&self.block_indent.to_string(self.config)); + self.push_str(remaining); + status.line_start = subslice.len() + offset; + } + } +} diff --git a/src/modules.rs b/src/modules.rs new file mode 100644 index 000000000000..af9a154a6aea --- /dev/null +++ b/src/modules.rs @@ -0,0 +1,578 @@ +use std::borrow::Cow; +use std::collections::BTreeMap; +use std::path::{Path, PathBuf}; + +use rustc_ast::ast; +use rustc_ast::visit::Visitor; +use rustc_span::symbol::{self, sym, Symbol}; +use rustc_span::Span; +use thin_vec::ThinVec; +use thiserror::Error; + +use crate::attr::MetaVisitor; +use crate::config::FileName; +use crate::items::is_mod_decl; +use crate::parse::parser::{ + Directory, DirectoryOwnership, ModError, ModulePathSuccess, Parser, ParserError, +}; +use crate::parse::session::ParseSess; +use crate::utils::{contains_skip, mk_sp}; + +mod visitor; + +type FileModMap<'ast> = BTreeMap>; + +/// Represents module with its inner attributes. +#[derive(Debug, Clone)] +pub(crate) struct Module<'a> { + ast_mod_kind: Option>, + pub(crate) items: Cow<'a, ThinVec>>, + inner_attr: ast::AttrVec, + pub(crate) span: Span, +} + +impl<'a> Module<'a> { + pub(crate) fn new( + mod_span: Span, + ast_mod_kind: Option>, + mod_items: Cow<'a, ThinVec>>, + mod_attrs: Cow<'a, ast::AttrVec>, + ) -> Self { + let inner_attr = mod_attrs + .iter() + .filter(|attr| attr.style == ast::AttrStyle::Inner) + .cloned() + .collect(); + Module { + items: mod_items, + inner_attr, + span: mod_span, + ast_mod_kind, + } + } + + pub(crate) fn attrs(&self) -> &[ast::Attribute] { + &self.inner_attr + } +} + +/// Maps each module to the corresponding file. +pub(crate) struct ModResolver<'ast, 'sess> { + parse_sess: &'sess ParseSess, + directory: Directory, + file_map: FileModMap<'ast>, + recursive: bool, +} + +/// Represents errors while trying to resolve modules. +#[derive(Debug, Error)] +#[error("failed to resolve mod `{module}`: {kind}")] +pub struct ModuleResolutionError { + pub(crate) module: String, + 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. + #[error("cannot parse {file}")] + ParseError { file: PathBuf }, + /// 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)] +enum SubModKind<'a, 'ast> { + /// `mod foo;` + External(PathBuf, DirectoryOwnership, Module<'ast>), + /// `mod foo;` with multiple sources. + MultiExternal(Vec<(PathBuf, DirectoryOwnership, Module<'ast>)>), + /// `mod foo {}` + Internal(&'a ast::Item), +} + +impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> { + /// Creates a new `ModResolver`. + pub(crate) fn new( + parse_sess: &'sess ParseSess, + directory_ownership: DirectoryOwnership, + recursive: bool, + ) -> Self { + ModResolver { + directory: Directory { + path: PathBuf::new(), + ownership: directory_ownership, + }, + file_map: BTreeMap::new(), + parse_sess, + recursive, + } + } + + /// Creates a map that maps a file name to the module in AST. + pub(crate) fn visit_crate( + mut self, + krate: &'ast ast::Crate, + ) -> Result, ModuleResolutionError> { + let root_filename = self.parse_sess.span_to_filename(krate.spans.inner_span); + self.directory.path = match root_filename { + FileName::Real(ref p) => p.parent().unwrap_or(Path::new("")).to_path_buf(), + _ => PathBuf::new(), + }; + + // Skip visiting sub modules when the input is from stdin. + if self.recursive { + self.visit_mod_from_ast(&krate.items)?; + } + + let snippet_provider = self.parse_sess.snippet_provider(krate.spans.inner_span); + + self.file_map.insert( + root_filename, + Module::new( + mk_sp(snippet_provider.start_pos(), snippet_provider.end_pos()), + None, + Cow::Borrowed(&krate.items), + Cow::Borrowed(&krate.attrs), + ), + ); + Ok(self.file_map) + } + + /// Visit `cfg_if` macro and look for module declarations. + fn visit_cfg_if(&mut self, item: Cow<'ast, ast::Item>) -> Result<(), ModuleResolutionError> { + let mut visitor = visitor::CfgIfVisitor::new(self.parse_sess); + visitor.visit_item(&item); + for module_item in visitor.mods() { + if let ast::ItemKind::Mod(_, ref sub_mod_kind) = module_item.item.kind { + self.visit_sub_mod( + &module_item.item, + Module::new( + module_item.item.span, + Some(Cow::Owned(sub_mod_kind.clone())), + Cow::Owned(ThinVec::new()), + Cow::Owned(ast::AttrVec::new()), + ), + )?; + } + } + Ok(()) + } + + /// Visit modules defined inside macro calls. + fn visit_mod_outside_ast( + &mut self, + items: ThinVec>, + ) -> Result<(), ModuleResolutionError> { + for item in items { + if is_cfg_if(&item) { + self.visit_cfg_if(Cow::Owned(item.into_inner()))?; + continue; + } + + if let ast::ItemKind::Mod(_, ref sub_mod_kind) = item.kind { + let span = item.span; + self.visit_sub_mod( + &item, + Module::new( + span, + Some(Cow::Owned(sub_mod_kind.clone())), + Cow::Owned(ThinVec::new()), + Cow::Owned(ast::AttrVec::new()), + ), + )?; + } + } + Ok(()) + } + + /// Visit modules from AST. + fn visit_mod_from_ast( + &mut self, + items: &'ast [rustc_ast::ptr::P], + ) -> Result<(), ModuleResolutionError> { + for item in items { + if is_cfg_if(item) { + self.visit_cfg_if(Cow::Borrowed(item))?; + } + + if let ast::ItemKind::Mod(_, ref sub_mod_kind) = item.kind { + let span = item.span; + self.visit_sub_mod( + item, + Module::new( + span, + Some(Cow::Borrowed(sub_mod_kind)), + Cow::Owned(ThinVec::new()), + Cow::Borrowed(&item.attrs), + ), + )?; + } + } + Ok(()) + } + + fn visit_sub_mod( + &mut self, + item: &'c ast::Item, + sub_mod: Module<'ast>, + ) -> Result<(), ModuleResolutionError> { + let old_directory = self.directory.clone(); + let sub_mod_kind = self.peek_sub_mod(item, &sub_mod)?; + if let Some(sub_mod_kind) = sub_mod_kind { + self.insert_sub_mod(sub_mod_kind.clone())?; + self.visit_sub_mod_inner(sub_mod, sub_mod_kind)?; + } + self.directory = old_directory; + Ok(()) + } + + /// Inspect the given sub-module which we are about to visit and returns its kind. + fn peek_sub_mod( + &self, + item: &'c ast::Item, + sub_mod: &Module<'ast>, + ) -> Result>, ModuleResolutionError> { + if contains_skip(&item.attrs) { + return Ok(None); + } + + if is_mod_decl(item) { + // mod foo; + // Look for an extern file. + self.find_external_module(item.ident, &item.attrs, sub_mod) + } else { + // An internal module (`mod foo { /* ... */ }`); + Ok(Some(SubModKind::Internal(item))) + } + } + + fn insert_sub_mod( + &mut self, + sub_mod_kind: SubModKind<'c, 'ast>, + ) -> Result<(), ModuleResolutionError> { + match sub_mod_kind { + SubModKind::External(mod_path, _, sub_mod) => { + self.file_map + .entry(FileName::Real(mod_path)) + .or_insert(sub_mod); + } + SubModKind::MultiExternal(mods) => { + for (mod_path, _, sub_mod) in mods { + self.file_map + .entry(FileName::Real(mod_path)) + .or_insert(sub_mod); + } + } + _ => (), + } + Ok(()) + } + + fn visit_sub_mod_inner( + &mut self, + sub_mod: Module<'ast>, + sub_mod_kind: SubModKind<'c, 'ast>, + ) -> Result<(), ModuleResolutionError> { + match sub_mod_kind { + SubModKind::External(mod_path, directory_ownership, sub_mod) => { + let directory = Directory { + path: mod_path.parent().unwrap().to_path_buf(), + ownership: directory_ownership, + }; + self.visit_sub_mod_after_directory_update(sub_mod, Some(directory)) + } + SubModKind::Internal(item) => { + self.push_inline_mod_directory(item.ident, &item.attrs); + self.visit_sub_mod_after_directory_update(sub_mod, None) + } + SubModKind::MultiExternal(mods) => { + for (mod_path, directory_ownership, sub_mod) in mods { + let directory = Directory { + path: mod_path.parent().unwrap().to_path_buf(), + ownership: directory_ownership, + }; + self.visit_sub_mod_after_directory_update(sub_mod, Some(directory))?; + } + Ok(()) + } + } + } + + fn visit_sub_mod_after_directory_update( + &mut self, + sub_mod: Module<'ast>, + directory: Option, + ) -> Result<(), ModuleResolutionError> { + if let Some(directory) = directory { + self.directory = directory; + } + match (sub_mod.ast_mod_kind, sub_mod.items) { + (Some(Cow::Borrowed(ast::ModKind::Loaded(items, _, _))), _) => { + self.visit_mod_from_ast(items) + } + (Some(Cow::Owned(ast::ModKind::Loaded(items, _, _))), _) | (_, Cow::Owned(items)) => { + self.visit_mod_outside_ast(items) + } + (_, _) => Ok(()), + } + } + + /// Find a file path in the filesystem which corresponds to the given module. + fn find_external_module( + &self, + mod_name: symbol::Ident, + attrs: &[ast::Attribute], + sub_mod: &Module<'ast>, + ) -> Result>, ModuleResolutionError> { + let relative = match self.directory.ownership { + DirectoryOwnership::Owned { relative } => relative, + DirectoryOwnership::UnownedViaBlock => None, + }; + if let Some(path) = Parser::submod_path_from_attr(attrs, &self.directory.path) { + if self.parse_sess.is_file_parsed(&path) { + return Ok(None); + } + return match Parser::parse_file_as_module(self.parse_sess, &path, sub_mod.span) { + Ok((ref attrs, _, _)) if contains_skip(attrs) => Ok(None), + Ok((attrs, items, span)) => Ok(Some(SubModKind::External( + path, + DirectoryOwnership::Owned { relative: None }, + Module::new( + span, + Some(Cow::Owned(ast::ModKind::Unloaded)), + Cow::Owned(items), + Cow::Owned(attrs), + ), + ))), + Err(ParserError::ParseError) => Err(ModuleResolutionError { + module: mod_name.to_string(), + kind: ModuleResolutionErrorKind::ParseError { file: path }, + }), + Err(..) => Err(ModuleResolutionError { + module: mod_name.to_string(), + kind: ModuleResolutionErrorKind::NotFound { file: path }, + }), + }; + } + + // Look for nested path, like `#[cfg_attr(feature = "foo", path = "bar.rs")]`. + let mut mods_outside_ast = self.find_mods_outside_of_ast(attrs, sub_mod); + + match self + .parse_sess + .default_submod_path(mod_name, relative, &self.directory.path) + { + Ok(ModulePathSuccess { + file_path, + dir_ownership, + .. + }) => { + let outside_mods_empty = mods_outside_ast.is_empty(); + let should_insert = !mods_outside_ast + .iter() + .any(|(outside_path, _, _)| outside_path == &file_path); + if self.parse_sess.is_file_parsed(&file_path) { + if outside_mods_empty { + return Ok(None); + } else { + if should_insert { + mods_outside_ast.push((file_path, dir_ownership, sub_mod.clone())); + } + return Ok(Some(SubModKind::MultiExternal(mods_outside_ast))); + } + } + match Parser::parse_file_as_module(self.parse_sess, &file_path, sub_mod.span) { + Ok((ref attrs, _, _)) if contains_skip(attrs) => Ok(None), + Ok((attrs, items, span)) if outside_mods_empty => { + Ok(Some(SubModKind::External( + file_path, + dir_ownership, + Module::new( + span, + Some(Cow::Owned(ast::ModKind::Unloaded)), + Cow::Owned(items), + Cow::Owned(attrs), + ), + ))) + } + Ok((attrs, items, span)) => { + mods_outside_ast.push(( + file_path.clone(), + dir_ownership, + Module::new( + span, + Some(Cow::Owned(ast::ModKind::Unloaded)), + Cow::Owned(items), + Cow::Owned(attrs), + ), + )); + if should_insert { + mods_outside_ast.push((file_path, dir_ownership, sub_mod.clone())); + } + Ok(Some(SubModKind::MultiExternal(mods_outside_ast))) + } + Err(ParserError::ParseError) => Err(ModuleResolutionError { + module: mod_name.to_string(), + kind: ModuleResolutionErrorKind::ParseError { file: file_path }, + }), + Err(..) if outside_mods_empty => Err(ModuleResolutionError { + module: mod_name.to_string(), + kind: ModuleResolutionErrorKind::NotFound { file: file_path }, + }), + Err(..) => { + if should_insert { + mods_outside_ast.push((file_path, dir_ownership, sub_mod.clone())); + } + Ok(Some(SubModKind::MultiExternal(mods_outside_ast))) + } + } + } + Err(mod_err) if !mods_outside_ast.is_empty() => { + if let ModError::ParserError(e) = mod_err { + e.cancel(); + } + Ok(Some(SubModKind::MultiExternal(mods_outside_ast))) + } + 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(), + }, + }), + }, + } + } + + fn push_inline_mod_directory(&mut self, id: symbol::Ident, attrs: &[ast::Attribute]) { + if let Some(path) = find_path_value(attrs) { + 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. + // + // For example, a `mod z { ... }` inside `x/y.rs` should set the current + // directory path to `/x/y/z`, not `/x/z` with a relative offset of `y`. + if let DirectoryOwnership::Owned { relative } = &mut self.directory.ownership { + 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); + } + } + + fn find_mods_outside_of_ast( + &self, + attrs: &[ast::Attribute], + sub_mod: &Module<'ast>, + ) -> Vec<(PathBuf, DirectoryOwnership, Module<'ast>)> { + // Filter nested path, like `#[cfg_attr(feature = "foo", path = "bar.rs")]`. + let mut path_visitor = visitor::PathVisitor::default(); + for attr in attrs.iter() { + if let Some(meta) = attr.meta() { + path_visitor.visit_meta_item(&meta) + } + } + let mut result = vec![]; + for path in path_visitor.paths() { + let mut actual_path = self.directory.path.clone(); + actual_path.push(&path); + if !actual_path.exists() { + continue; + } + if self.parse_sess.is_file_parsed(&actual_path) { + // If the specified file is already parsed, then we just use that. + result.push(( + actual_path, + DirectoryOwnership::Owned { relative: None }, + sub_mod.clone(), + )); + continue; + } + let (attrs, items, span) = + match Parser::parse_file_as_module(self.parse_sess, &actual_path, sub_mod.span) { + Ok((ref attrs, _, _)) if contains_skip(attrs) => continue, + Ok(m) => m, + Err(..) => continue, + }; + + result.push(( + actual_path, + DirectoryOwnership::Owned { relative: None }, + Module::new( + span, + Some(Cow::Owned(ast::ModKind::Unloaded)), + Cow::Owned(items), + Cow::Owned(attrs), + ), + )) + } + result + } +} + +fn path_value(attr: &ast::Attribute) -> Option { + if attr.has_name(sym::path) { + attr.value_str() + } else { + None + } +} + +// N.B., even when there are multiple `#[path = ...]` attributes, we just need to +// examine the first one, since rustc ignores the second and the subsequent ones +// as unused attributes. +fn find_path_value(attrs: &[ast::Attribute]) -> Option { + attrs.iter().flat_map(path_value).next() +} + +fn is_cfg_if(item: &ast::Item) -> bool { + match item.kind { + ast::ItemKind::MacCall(ref mac) => { + if let Some(first_segment) = mac.path.segments.first() { + if first_segment.ident.name == Symbol::intern("cfg_if") { + return true; + } + } + false + } + _ => false, + } +} diff --git a/src/modules/visitor.rs b/src/modules/visitor.rs new file mode 100644 index 000000000000..48431693332a --- /dev/null +++ b/src/modules/visitor.rs @@ -0,0 +1,112 @@ +use rustc_ast::ast; +use rustc_ast::visit::Visitor; +use rustc_span::Symbol; + +use crate::attr::MetaVisitor; +use crate::parse::macros::cfg_if::parse_cfg_if; +use crate::parse::session::ParseSess; + +pub(crate) struct ModItem { + pub(crate) item: ast::Item, +} + +/// Traverse `cfg_if!` macro and fetch modules. +pub(crate) struct CfgIfVisitor<'a> { + parse_sess: &'a ParseSess, + mods: Vec, +} + +impl<'a> CfgIfVisitor<'a> { + pub(crate) fn new(parse_sess: &'a ParseSess) -> CfgIfVisitor<'a> { + CfgIfVisitor { + mods: vec![], + parse_sess, + } + } + + pub(crate) fn mods(self) -> Vec { + self.mods + } +} + +impl<'a, 'ast: 'a> Visitor<'ast> for CfgIfVisitor<'a> { + fn visit_mac_call(&mut self, mac: &'ast ast::MacCall) { + match self.visit_mac_inner(mac) { + Ok(()) => (), + Err(e) => debug!("{}", e), + } + } +} + +impl<'a, 'ast: 'a> CfgIfVisitor<'a> { + fn visit_mac_inner(&mut self, mac: &'ast ast::MacCall) -> Result<(), &'static str> { + // Support both: + // ``` + // extern crate cfg_if; + // cfg_if::cfg_if! {..} + // ``` + // And: + // ``` + // #[macro_use] + // extern crate cfg_if; + // cfg_if! {..} + // ``` + match mac.path.segments.first() { + Some(first_segment) => { + if first_segment.ident.name != Symbol::intern("cfg_if") { + return Err("Expected cfg_if"); + } + } + None => { + return Err("Expected cfg_if"); + } + }; + + let items = parse_cfg_if(self.parse_sess, mac)?; + self.mods + .append(&mut items.into_iter().map(|item| ModItem { item }).collect()); + + Ok(()) + } +} + +/// Extracts `path = "foo.rs"` from attributes. +#[derive(Default)] +pub(crate) struct PathVisitor { + /// A list of path defined in attributes. + paths: Vec, +} + +impl PathVisitor { + pub(crate) fn paths(self) -> Vec { + self.paths + } +} + +impl<'ast> MetaVisitor<'ast> for PathVisitor { + fn visit_meta_name_value( + &mut self, + meta_item: &'ast ast::MetaItem, + lit: &'ast ast::MetaItemLit, + ) { + if meta_item.has_name(Symbol::intern("path")) && lit.kind.is_str() { + self.paths.push(meta_item_lit_to_str(lit)); + } + } +} + +#[cfg(not(windows))] +fn meta_item_lit_to_str(lit: &ast::MetaItemLit) -> String { + match lit.kind { + ast::LitKind::Str(symbol, ..) => symbol.to_string(), + _ => unreachable!(), + } +} + +#[cfg(windows)] +fn meta_item_lit_to_str(lit: &ast::MetaItemLit) -> String { + match lit.kind { + ast::LitKind::Str(symbol, ..) => symbol.as_str().replace("/", "\\"), + _ => unreachable!(), + } +} diff --git a/src/overflow.rs b/src/overflow.rs new file mode 100644 index 000000000000..d81bf24dbd1c --- /dev/null +++ b/src/overflow.rs @@ -0,0 +1,789 @@ +//! Rewrite a list some items with overflow. + +use std::cmp::min; + +use itertools::Itertools; +use rustc_ast::token::Delimiter; +use rustc_ast::{ast, ptr}; +use rustc_span::Span; + +use crate::closures; +use crate::config::lists::*; +use crate::config::Version; +use crate::expr::{ + can_be_overflowed_expr, is_every_expr_simple, is_method_call, is_nested_call, is_simple_expr, + rewrite_cond, +}; +use crate::lists::{ + definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator, +}; +use crate::macros::MacroArg; +use crate::patterns::{can_be_overflowed_pat, TuplePatField}; +use crate::rewrite::{Rewrite, RewriteContext}; +use crate::shape::Shape; +use crate::source_map::SpanUtils; +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}; + +/// A list of `format!`-like macros, that take a long format string and a list of arguments to +/// format. +/// +/// Organized as a list of `(&str, usize)` tuples, giving the name of the macro and the number of +/// arguments before the format string (none for `format!("format", ...)`, one for `assert!(result, +/// "format", ...)`, two for `assert_eq!(left, right, "format", ...)`). +const SPECIAL_CASE_MACROS: &[(&str, usize)] = &[ + // format! like macros + // From the Rust Standard Library. + ("eprint!", 0), + ("eprintln!", 0), + ("format!", 0), + ("format_args!", 0), + ("print!", 0), + ("println!", 0), + ("panic!", 0), + ("unreachable!", 0), + // From the `log` crate. + ("debug!", 0), + ("error!", 0), + ("info!", 0), + ("warn!", 0), + // write! like macros + ("assert!", 1), + ("debug_assert!", 1), + ("write!", 1), + ("writeln!", 1), + // assert_eq! like macros + ("assert_eq!", 2), + ("assert_ne!", 2), + ("debug_assert_eq!", 2), + ("debug_assert_ne!", 2), +]; + +const SPECIAL_CASE_ATTR: &[(&str, usize)] = &[ + // From the `failure` crate. + ("fail", 0), +]; + +#[derive(Debug)] +pub(crate) enum OverflowableItem<'a> { + Expr(&'a ast::Expr), + GenericParam(&'a ast::GenericParam), + MacroArg(&'a MacroArg), + NestedMetaItem(&'a ast::NestedMetaItem), + SegmentParam(&'a SegmentParam<'a>), + FieldDef(&'a ast::FieldDef), + TuplePatField(&'a TuplePatField<'a>), + Ty(&'a ast::Ty), + Pat(&'a ast::Pat), +} + +impl<'a> Rewrite for OverflowableItem<'a> { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + self.map(|item| item.rewrite(context, shape)) + } +} + +impl<'a> Spanned for OverflowableItem<'a> { + fn span(&self) -> Span { + self.map(|item| item.span()) + } +} + +impl<'a> OverflowableItem<'a> { + fn has_attrs(&self) -> bool { + match self { + OverflowableItem::Expr(ast::Expr { attrs, .. }) + | OverflowableItem::GenericParam(ast::GenericParam { attrs, .. }) => !attrs.is_empty(), + OverflowableItem::FieldDef(ast::FieldDef { attrs, .. }) => !attrs.is_empty(), + OverflowableItem::MacroArg(MacroArg::Expr(expr)) => !expr.attrs.is_empty(), + OverflowableItem::MacroArg(MacroArg::Item(item)) => !item.attrs.is_empty(), + _ => false, + } + } + + pub(crate) fn map(&self, f: F) -> T + where + F: Fn(&dyn IntoOverflowableItem<'a>) -> T, + { + match self { + OverflowableItem::Expr(expr) => f(*expr), + OverflowableItem::GenericParam(gp) => f(*gp), + OverflowableItem::MacroArg(macro_arg) => f(*macro_arg), + OverflowableItem::NestedMetaItem(nmi) => f(*nmi), + OverflowableItem::SegmentParam(sp) => f(*sp), + OverflowableItem::FieldDef(sf) => f(*sf), + OverflowableItem::TuplePatField(pat) => f(*pat), + OverflowableItem::Ty(ty) => f(*ty), + OverflowableItem::Pat(pat) => f(*pat), + } + } + + pub(crate) fn is_simple(&self) -> bool { + match self { + OverflowableItem::Expr(expr) => is_simple_expr(expr), + OverflowableItem::MacroArg(MacroArg::Keyword(..)) => true, + OverflowableItem::MacroArg(MacroArg::Expr(expr)) => is_simple_expr(expr), + OverflowableItem::NestedMetaItem(nested_meta_item) => match nested_meta_item { + ast::NestedMetaItem::Lit(..) => true, + ast::NestedMetaItem::MetaItem(ref meta_item) => { + matches!(meta_item.kind, ast::MetaItemKind::Word) + } + }, + _ => false, + } + } + + pub(crate) fn is_expr(&self) -> bool { + matches!( + self, + OverflowableItem::Expr(..) | OverflowableItem::MacroArg(MacroArg::Expr(..)) + ) + } + + pub(crate) fn is_nested_call(&self) -> bool { + match self { + OverflowableItem::Expr(expr) => is_nested_call(expr), + OverflowableItem::MacroArg(MacroArg::Expr(expr)) => is_nested_call(expr), + _ => false, + } + } + + pub(crate) fn to_expr(&self) -> Option<&'a ast::Expr> { + match self { + OverflowableItem::Expr(expr) => Some(expr), + OverflowableItem::MacroArg(MacroArg::Expr(ref expr)) => Some(expr), + _ => None, + } + } + + pub(crate) fn can_be_overflowed(&self, context: &RewriteContext<'_>, len: usize) -> bool { + match self { + OverflowableItem::Expr(expr) => can_be_overflowed_expr(context, expr, len), + OverflowableItem::MacroArg(macro_arg) => match macro_arg { + MacroArg::Expr(ref expr) => can_be_overflowed_expr(context, expr, len), + MacroArg::Ty(ref ty) => can_be_overflowed_type(context, ty, len), + MacroArg::Pat(..) => false, + MacroArg::Item(..) => len == 1, + MacroArg::Keyword(..) => false, + }, + OverflowableItem::NestedMetaItem(nested_meta_item) if len == 1 => { + match nested_meta_item { + ast::NestedMetaItem::Lit(..) => false, + ast::NestedMetaItem::MetaItem(..) => true, + } + } + OverflowableItem::SegmentParam(SegmentParam::Type(ty)) => { + can_be_overflowed_type(context, ty, len) + } + OverflowableItem::TuplePatField(pat) => can_be_overflowed_pat(context, pat, len), + OverflowableItem::Ty(ty) => can_be_overflowed_type(context, ty, len), + _ => false, + } + } + + fn special_cases(&self) -> &'static [(&'static str, usize)] { + match self { + OverflowableItem::MacroArg(..) => SPECIAL_CASE_MACROS, + OverflowableItem::NestedMetaItem(..) => SPECIAL_CASE_ATTR, + _ => &[], + } + } +} + +pub(crate) trait IntoOverflowableItem<'a>: Rewrite + Spanned { + fn into_overflowable_item(&'a self) -> OverflowableItem<'a>; +} + +impl<'a, T: 'a + IntoOverflowableItem<'a>> IntoOverflowableItem<'a> for ptr::P { + fn into_overflowable_item(&'a self) -> OverflowableItem<'a> { + (**self).into_overflowable_item() + } +} + +macro_rules! impl_into_overflowable_item_for_ast_node { + ($($ast_node:ident),*) => { + $( + impl<'a> IntoOverflowableItem<'a> for ast::$ast_node { + fn into_overflowable_item(&'a self) -> OverflowableItem<'a> { + OverflowableItem::$ast_node(self) + } + } + )* + } +} + +macro_rules! impl_into_overflowable_item_for_rustfmt_types { + ([$($ty:ident),*], [$($ty_with_lifetime:ident),*]) => { + $( + impl<'a> IntoOverflowableItem<'a> for $ty { + fn into_overflowable_item(&'a self) -> OverflowableItem<'a> { + OverflowableItem::$ty(self) + } + } + )* + $( + impl<'a> IntoOverflowableItem<'a> for $ty_with_lifetime<'a> { + fn into_overflowable_item(&'a self) -> OverflowableItem<'a> { + OverflowableItem::$ty_with_lifetime(self) + } + } + )* + } +} + +impl_into_overflowable_item_for_ast_node!(Expr, GenericParam, NestedMetaItem, FieldDef, Ty, Pat); +impl_into_overflowable_item_for_rustfmt_types!([MacroArg], [SegmentParam, TuplePatField]); + +pub(crate) fn into_overflowable_list<'a, T>( + iter: impl Iterator, +) -> impl Iterator> +where + T: 'a + IntoOverflowableItem<'a>, +{ + iter.map(|x| IntoOverflowableItem::into_overflowable_item(x)) +} + +pub(crate) fn rewrite_with_parens<'a, T: 'a + IntoOverflowableItem<'a>>( + context: &'a RewriteContext<'_>, + ident: &'a str, + items: impl Iterator, + shape: Shape, + span: Span, + item_max_width: usize, + force_separator_tactic: Option, +) -> Option { + Context::new( + context, + items, + ident, + shape, + span, + "(", + ")", + item_max_width, + force_separator_tactic, + None, + ) + .rewrite(shape) +} + +pub(crate) fn rewrite_with_angle_brackets<'a, T: 'a + IntoOverflowableItem<'a>>( + context: &'a RewriteContext<'_>, + ident: &'a str, + items: impl Iterator, + shape: Shape, + span: Span, +) -> Option { + Context::new( + context, + items, + ident, + shape, + span, + "<", + ">", + context.config.max_width(), + None, + None, + ) + .rewrite(shape) +} + +pub(crate) fn rewrite_with_square_brackets<'a, T: 'a + IntoOverflowableItem<'a>>( + context: &'a RewriteContext<'_>, + name: &'a str, + items: impl Iterator, + shape: Shape, + span: Span, + force_separator_tactic: Option, + delim_token: Option, +) -> Option { + let (lhs, rhs) = match delim_token { + Some(Delimiter::Parenthesis) => ("(", ")"), + Some(Delimiter::Brace) => ("{", "}"), + _ => ("[", "]"), + }; + Context::new( + context, + items, + name, + shape, + span, + lhs, + rhs, + context.config.array_width(), + force_separator_tactic, + Some(("[", "]")), + ) + .rewrite(shape) +} + +struct Context<'a> { + context: &'a RewriteContext<'a>, + items: Vec>, + ident: &'a str, + prefix: &'static str, + suffix: &'static str, + one_line_shape: Shape, + nested_shape: Shape, + span: Span, + item_max_width: usize, + one_line_width: usize, + force_separator_tactic: Option, + custom_delims: Option<(&'a str, &'a str)>, +} + +impl<'a> Context<'a> { + fn new>( + context: &'a RewriteContext<'_>, + items: impl Iterator, + ident: &'a str, + shape: Shape, + span: Span, + prefix: &'static str, + suffix: &'static str, + item_max_width: usize, + force_separator_tactic: Option, + custom_delims: Option<(&'a str, &'a str)>, + ) -> Context<'a> { + let used_width = extra_offset(ident, shape); + // 1 = `()` + let one_line_width = shape.width.saturating_sub(used_width + 2); + + // 1 = "(" or ")" + let one_line_shape = shape + .offset_left(last_line_width(ident) + 1) + .and_then(|shape| shape.sub_width(1)) + .unwrap_or(Shape { width: 0, ..shape }); + let nested_shape = shape_from_indent_style(context, shape, used_width + 2, used_width + 1); + Context { + context, + items: into_overflowable_list(items).collect(), + ident, + one_line_shape, + nested_shape, + span, + prefix, + suffix, + item_max_width, + one_line_width, + force_separator_tactic, + custom_delims, + } + } + + fn last_item(&self) -> Option<&OverflowableItem<'_>> { + self.items.last() + } + + fn items_span(&self) -> Span { + let span_lo = self + .context + .snippet_provider + .span_after(self.span, self.prefix); + mk_sp(span_lo, self.span.hi()) + } + + fn rewrite_last_item_with_overflow( + &self, + last_list_item: &mut ListItem, + shape: Shape, + ) -> Option { + let last_item = self.last_item()?; + let rewrite = match last_item { + OverflowableItem::Expr(expr) => { + match expr.kind { + // When overflowing the closure which consists of a single control flow + // expression, force to use block if its condition uses multi line. + ast::ExprKind::Closure(..) => { + // If the argument consists of multiple closures, we do not overflow + // the last closure. + if closures::args_have_many_closure(&self.items) { + None + } else { + closures::rewrite_last_closure(self.context, expr, shape) + } + } + + // When overflowing the expressions which consists of a control flow + // expression, avoid condition to use multi line. + ast::ExprKind::If(..) + | ast::ExprKind::ForLoop(..) + | ast::ExprKind::Loop(..) + | ast::ExprKind::While(..) + | ast::ExprKind::Match(..) => { + let multi_line = rewrite_cond(self.context, expr, shape) + .map_or(false, |cond| cond.contains('\n')); + + if multi_line { + None + } else { + expr.rewrite(self.context, shape) + } + } + + _ => expr.rewrite(self.context, shape), + } + } + item => item.rewrite(self.context, shape), + }; + + if let Some(rewrite) = rewrite { + // splitn(2, *).next().unwrap() is always safe. + let rewrite_first_line = Some(rewrite.splitn(2, '\n').next().unwrap().to_owned()); + last_list_item.item = rewrite_first_line; + Some(rewrite) + } else { + None + } + } + + fn default_tactic(&self, list_items: &[ListItem]) -> DefinitiveListTactic { + definitive_tactic( + list_items, + ListTactic::LimitedHorizontalVertical(self.item_max_width), + Separator::Comma, + self.one_line_width, + ) + } + + fn try_overflow_last_item(&self, list_items: &mut Vec) -> DefinitiveListTactic { + // 1 = "(" + let combine_arg_with_callee = self.items.len() == 1 + && self.items[0].is_expr() + && !self.items[0].has_attrs() + && self.ident.len() < self.context.config.tab_spaces(); + let overflow_last = combine_arg_with_callee || can_be_overflowed(self.context, &self.items); + + // Replace the last item with its first line to see if it fits with + // first arguments. + let placeholder = if overflow_last { + let old_value = self.context.force_one_line_chain.get(); + match self.last_item() { + Some(OverflowableItem::Expr(expr)) + if !combine_arg_with_callee && is_method_call(expr) => + { + self.context.force_one_line_chain.replace(true); + } + Some(OverflowableItem::MacroArg(MacroArg::Expr(expr))) + if !combine_arg_with_callee + && is_method_call(expr) + && self.context.config.version() == Version::Two => + { + self.context.force_one_line_chain.replace(true); + } + _ => (), + } + let result = last_item_shape( + &self.items, + list_items, + self.one_line_shape, + self.item_max_width, + ) + .and_then(|arg_shape| { + self.rewrite_last_item_with_overflow( + &mut list_items[self.items.len() - 1], + arg_shape, + ) + }); + self.context.force_one_line_chain.replace(old_value); + result + } else { + None + }; + + let mut tactic = definitive_tactic( + &*list_items, + ListTactic::LimitedHorizontalVertical(self.item_max_width), + Separator::Comma, + self.one_line_width, + ); + + // Replace the stub with the full overflowing last argument if the rewrite + // succeeded and its first line fits with the other arguments. + match (overflow_last, tactic, placeholder) { + (true, DefinitiveListTactic::Horizontal, Some(ref overflowed)) + if self.items.len() == 1 => + { + // When we are rewriting a nested function call, we restrict the + // budget for the inner function to avoid them being deeply nested. + // However, when the inner function has a prefix or a suffix + // (e.g., `foo() as u32`), this budget reduction may produce poorly + // formatted code, where a prefix or a suffix being left on its own + // line. Here we explicitlly check those cases. + if count_newlines(overflowed) == 1 { + let rw = self + .items + .last() + .and_then(|last_item| last_item.rewrite(self.context, self.nested_shape)); + let no_newline = rw.as_ref().map_or(false, |s| !s.contains('\n')); + if no_newline { + list_items[self.items.len() - 1].item = rw; + } else { + list_items[self.items.len() - 1].item = Some(overflowed.to_owned()); + } + } else { + list_items[self.items.len() - 1].item = Some(overflowed.to_owned()); + } + } + (true, DefinitiveListTactic::Horizontal, placeholder @ Some(..)) => { + list_items[self.items.len() - 1].item = placeholder; + } + _ if !self.items.is_empty() => { + list_items[self.items.len() - 1].item = self + .items + .last() + .and_then(|last_item| last_item.rewrite(self.context, self.nested_shape)); + + // Use horizontal layout for a function with a single argument as long as + // everything fits in a single line. + // `self.one_line_width == 0` means vertical layout is forced. + if self.items.len() == 1 + && self.one_line_width != 0 + && !list_items[0].has_comment() + && !list_items[0].inner_as_ref().contains('\n') + && crate::lists::total_item_width(&list_items[0]) <= self.one_line_width + { + tactic = DefinitiveListTactic::Horizontal; + } else { + tactic = self.default_tactic(list_items); + + if tactic == DefinitiveListTactic::Vertical { + if let Some((all_simple, num_args_before)) = + maybe_get_args_offset(self.ident, &self.items) + { + let one_line = all_simple + && definitive_tactic( + &list_items[..num_args_before], + ListTactic::HorizontalVertical, + Separator::Comma, + self.nested_shape.width, + ) == DefinitiveListTactic::Horizontal + && definitive_tactic( + &list_items[num_args_before + 1..], + ListTactic::HorizontalVertical, + Separator::Comma, + self.nested_shape.width, + ) == DefinitiveListTactic::Horizontal; + + if one_line { + tactic = DefinitiveListTactic::SpecialMacro(num_args_before); + }; + } else if is_every_expr_simple(&self.items) + && no_long_items( + list_items, + self.context.config.short_array_element_width_threshold(), + ) + { + tactic = DefinitiveListTactic::Mixed; + } + } + } + } + _ => (), + } + + tactic + } + + fn rewrite_items(&self) -> Option<(bool, String)> { + let span = self.items_span(); + debug!("items: {:?}", self.items); + + let items = itemize_list( + self.context.snippet_provider, + self.items.iter(), + self.suffix, + ",", + |item| item.span().lo(), + |item| item.span().hi(), + |item| item.rewrite(self.context, self.nested_shape), + span.lo(), + span.hi(), + true, + ); + let mut list_items: Vec<_> = items.collect(); + + debug!("items: {list_items:?}"); + + // Try letting the last argument overflow to the next line with block + // indentation. If its first line fits on one line with the other arguments, + // we format the function arguments horizontally. + let tactic = self.try_overflow_last_item(&mut list_items); + let trailing_separator = if let Some(tactic) = self.force_separator_tactic { + tactic + } else if !self.context.use_block_indent() { + SeparatorTactic::Never + } else { + self.context.config.trailing_comma() + }; + let ends_with_newline = match tactic { + DefinitiveListTactic::Vertical | DefinitiveListTactic::Mixed => { + self.context.use_block_indent() + } + _ => false, + }; + + let fmt = ListFormatting::new(self.nested_shape, self.context.config) + .tactic(tactic) + .trailing_separator(trailing_separator) + .ends_with_newline(ends_with_newline); + + write_list(&list_items, &fmt) + .map(|items_str| (tactic == DefinitiveListTactic::Horizontal, items_str)) + } + + fn wrap_items(&self, items_str: &str, shape: Shape, is_extendable: bool) -> String { + let shape = Shape { + width: shape.width.saturating_sub(last_line_width(self.ident)), + ..shape + }; + + let (prefix, suffix) = match self.custom_delims { + Some((lhs, rhs)) => (lhs, rhs), + _ => (self.prefix, self.suffix), + }; + + let extend_width = if items_str.is_empty() { + 2 + } else { + first_line_width(items_str) + 1 + }; + let nested_indent_str = self + .nested_shape + .indent + .to_string_with_newline(self.context.config); + let indent_str = shape + .block() + .indent + .to_string_with_newline(self.context.config); + let mut result = String::with_capacity( + self.ident.len() + items_str.len() + 2 + indent_str.len() + nested_indent_str.len(), + ); + result.push_str(self.ident); + result.push_str(prefix); + let force_single_line = if self.context.config.version() == Version::Two { + !self.context.use_block_indent() || (is_extendable && extend_width <= shape.width) + } else { + // 2 = `()` + let fits_one_line = items_str.len() + 2 <= shape.width; + !self.context.use_block_indent() + || (self.context.inside_macro() && !items_str.contains('\n') && fits_one_line) + || (is_extendable && extend_width <= shape.width) + }; + if force_single_line { + result.push_str(items_str); + } else { + if !items_str.is_empty() { + result.push_str(&nested_indent_str); + result.push_str(items_str); + } + result.push_str(&indent_str); + } + result.push_str(suffix); + result + } + + fn rewrite(&self, shape: Shape) -> Option { + let (extendable, items_str) = self.rewrite_items()?; + + // If we are using visual indent style and failed to format, retry with block indent. + if !self.context.use_block_indent() + && need_block_indent(&items_str, self.nested_shape) + && !extendable + { + self.context.use_block.replace(true); + let result = self.rewrite(shape); + self.context.use_block.replace(false); + return result; + } + + Some(self.wrap_items(&items_str, shape, extendable)) + } +} + +fn need_block_indent(s: &str, shape: Shape) -> bool { + s.lines().skip(1).any(|s| { + s.find(|c| !char::is_whitespace(c)) + .map_or(false, |w| w + 1 < shape.indent.width()) + }) +} + +fn can_be_overflowed(context: &RewriteContext<'_>, items: &[OverflowableItem<'_>]) -> bool { + items + .last() + .map_or(false, |x| x.can_be_overflowed(context, items.len())) +} + +/// Returns a shape for the last argument which is going to be overflowed. +fn last_item_shape( + lists: &[OverflowableItem<'_>], + items: &[ListItem], + shape: Shape, + args_max_width: usize, +) -> Option { + if items.len() == 1 && !lists.get(0)?.is_nested_call() { + return Some(shape); + } + let offset = items + .iter() + .dropping_back(1) + .map(|i| { + // 2 = ", " + 2 + i.inner_as_ref().len() + }) + .sum(); + Shape { + width: min(args_max_width, shape.width), + ..shape + } + .offset_left(offset) +} + +fn shape_from_indent_style( + context: &RewriteContext<'_>, + shape: Shape, + overhead: usize, + offset: usize, +) -> Shape { + let (shape, overhead) = if context.use_block_indent() { + let shape = shape + .block() + .block_indent(context.config.tab_spaces()) + .with_max_width(context.config); + (shape, 1) // 1 = "," + } else { + (shape.visual_indent(offset), overhead) + }; + Shape { + width: shape.width.saturating_sub(overhead), + ..shape + } +} + +fn no_long_items(list: &[ListItem], short_array_element_width_threshold: usize) -> bool { + list.iter() + .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. +pub(crate) fn maybe_get_args_offset( + callee_str: &str, + args: &[OverflowableItem<'_>], +) -> Option<(bool, usize)> { + if let Some(&(_, num_args_before)) = args + .get(0)? + .special_cases() + .iter() + .find(|&&(s, _)| s == callee_str) + { + let all_simple = args.len() > num_args_before + && is_every_expr_simple(&args[0..num_args_before]) + && is_every_expr_simple(&args[num_args_before + 1..]); + + Some((all_simple, num_args_before)) + } else { + None + } +} diff --git a/src/pairs.rs b/src/pairs.rs new file mode 100644 index 000000000000..d135da7e3591 --- /dev/null +++ b/src/pairs.rs @@ -0,0 +1,325 @@ +use rustc_ast::ast; + +use crate::config::lists::*; +use crate::config::IndentStyle; +use crate::rewrite::{Rewrite, RewriteContext}; +use crate::shape::Shape; +use crate::utils::{ + first_line_width, is_single_line, last_line_width, trimmed_last_line_width, wrap_str, +}; + +/// Sigils that decorate a binop pair. +#[derive(Clone, Copy)] +pub(crate) struct PairParts<'a> { + prefix: &'a str, + infix: &'a str, + suffix: &'a str, +} + +impl<'a> PairParts<'a> { + pub(crate) const fn new(prefix: &'a str, infix: &'a str, suffix: &'a str) -> Self { + Self { + prefix, + infix, + suffix, + } + } + pub(crate) fn infix(infix: &'a str) -> PairParts<'a> { + PairParts { + prefix: "", + infix, + suffix: "", + } + } +} + +// Flattens a tree of pairs into a list and tries to rewrite them all at once. +// FIXME would be nice to reuse the lists API for this, but because each separator +// can be different, we can't. +pub(crate) fn rewrite_all_pairs( + expr: &ast::Expr, + shape: Shape, + context: &RewriteContext<'_>, +) -> Option { + expr.flatten(context, shape).and_then(|list| { + // First we try formatting on one line. + rewrite_pairs_one_line(&list, shape, context) + .or_else(|| rewrite_pairs_multiline(&list, shape, context)) + }) +} + +// This may return a multi-line result since we allow the last expression to go +// multiline in a 'single line' formatting. +fn rewrite_pairs_one_line( + list: &PairList<'_, '_, T>, + shape: Shape, + context: &RewriteContext<'_>, +) -> Option { + assert!(list.list.len() >= 2, "Not a pair?"); + + let mut result = String::new(); + let base_shape = shape.block(); + + for ((_, rewrite), s) in list.list.iter().zip(list.separators.iter()) { + if let Some(rewrite) = rewrite { + if !is_single_line(rewrite) || result.len() > shape.width { + return None; + } + + result.push_str(rewrite); + result.push(' '); + result.push_str(s); + result.push(' '); + } else { + return None; + } + } + + let prefix_len = result.len(); + let last = list.list.last()?.0; + let cur_shape = base_shape.offset_left(last_line_width(&result))?; + let last_rewrite = last.rewrite(context, cur_shape)?; + result.push_str(&last_rewrite); + + if first_line_width(&result) > shape.width { + return None; + } + + // Check the last expression in the list. We sometimes let this expression + // go over multiple lines, but we check for some ugly conditions. + if !(is_single_line(&result) || last_rewrite.starts_with('{')) + && (last_rewrite.starts_with('(') || prefix_len > context.config.tab_spaces()) + { + return None; + } + + wrap_str(result, context.config.max_width(), shape) +} + +fn rewrite_pairs_multiline( + list: &PairList<'_, '_, T>, + shape: Shape, + context: &RewriteContext<'_>, +) -> Option { + let rhs_offset = shape.rhs_overhead(context.config); + let nested_shape = (match context.config.indent_style() { + IndentStyle::Visual => shape.visual_indent(0), + IndentStyle::Block => shape.block_indent(context.config.tab_spaces()), + }) + .with_max_width(context.config) + .sub_width(rhs_offset)?; + + let indent_str = nested_shape.indent.to_string_with_newline(context.config); + let mut result = String::new(); + + result.push_str(list.list[0].1.as_ref()?); + + for ((e, default_rw), s) in list.list[1..].iter().zip(list.separators.iter()) { + // The following test checks if we should keep two subexprs on the same + // line. We do this if not doing so would create an orphan and there is + // enough space to do so. + let offset = if result.contains('\n') { + 0 + } else { + shape.used_width() + }; + if last_line_width(&result) + offset <= nested_shape.used_width() { + // We must snuggle the next line onto the previous line to avoid an orphan. + if let Some(line_shape) = + shape.offset_left(s.len() + 2 + trimmed_last_line_width(&result)) + { + if let Some(rewrite) = e.rewrite(context, line_shape) { + result.push(' '); + result.push_str(s); + result.push(' '); + result.push_str(&rewrite); + continue; + } + } + } + + match context.config.binop_separator() { + SeparatorPlace::Back => { + result.push(' '); + result.push_str(s); + result.push_str(&indent_str); + } + SeparatorPlace::Front => { + result.push_str(&indent_str); + result.push_str(s); + result.push(' '); + } + } + + result.push_str(default_rw.as_ref()?); + } + Some(result) +} + +// Rewrites a single pair. +pub(crate) fn rewrite_pair( + lhs: &LHS, + rhs: &RHS, + pp: PairParts<'_>, + context: &RewriteContext<'_>, + shape: Shape, + separator_place: SeparatorPlace, +) -> Option +where + LHS: Rewrite, + RHS: Rewrite, +{ + let tab_spaces = context.config.tab_spaces(); + let lhs_overhead = match separator_place { + SeparatorPlace::Back => shape.used_width() + pp.prefix.len() + pp.infix.trim_end().len(), + SeparatorPlace::Front => shape.used_width(), + }; + let lhs_shape = Shape { + width: context.budget(lhs_overhead), + ..shape + }; + let lhs_result = lhs + .rewrite(context, lhs_shape) + .map(|lhs_str| format!("{}{}", pp.prefix, lhs_str))?; + + // Try to put both lhs and rhs on the same line. + let rhs_orig_result = shape + .offset_left(last_line_width(&lhs_result) + pp.infix.len()) + .and_then(|s| s.sub_width(pp.suffix.len())) + .and_then(|rhs_shape| rhs.rewrite(context, rhs_shape)); + if let Some(ref rhs_result) = rhs_orig_result { + // If the length of the lhs is equal to or shorter than the tab width or + // the rhs looks like block expression, we put the rhs on the same + // line with the lhs even if the rhs is multi-lined. + let allow_same_line = lhs_result.len() <= tab_spaces + || rhs_result + .lines() + .next() + .map(|first_line| first_line.ends_with('{')) + .unwrap_or(false); + if !rhs_result.contains('\n') || allow_same_line { + let one_line_width = last_line_width(&lhs_result) + + pp.infix.len() + + first_line_width(rhs_result) + + pp.suffix.len(); + if one_line_width <= shape.width { + return Some(format!( + "{}{}{}{}", + lhs_result, pp.infix, rhs_result, pp.suffix + )); + } + } + } + + // We have to use multiple lines. + // Re-evaluate the rhs because we have more space now: + let mut rhs_shape = match context.config.indent_style() { + IndentStyle::Visual => shape + .sub_width(pp.suffix.len() + pp.prefix.len())? + .visual_indent(pp.prefix.len()), + IndentStyle::Block => { + // Try to calculate the initial constraint on the right hand side. + let rhs_overhead = shape.rhs_overhead(context.config); + Shape::indented(shape.indent.block_indent(context.config), context.config) + .sub_width(rhs_overhead)? + } + }; + let infix = match separator_place { + SeparatorPlace::Back => pp.infix.trim_end(), + SeparatorPlace::Front => pp.infix.trim_start(), + }; + if separator_place == SeparatorPlace::Front { + rhs_shape = rhs_shape.offset_left(infix.len())?; + } + let rhs_result = rhs.rewrite(context, rhs_shape)?; + let indent_str = rhs_shape.indent.to_string_with_newline(context.config); + let infix_with_sep = match separator_place { + SeparatorPlace::Back => format!("{}{}", infix, indent_str), + SeparatorPlace::Front => format!("{}{}", indent_str, infix), + }; + Some(format!( + "{}{}{}{}", + lhs_result, infix_with_sep, rhs_result, pp.suffix + )) +} + +// A pair which forms a tree and can be flattened (e.g., binops). +trait FlattenPair: Rewrite + Sized { + fn flatten(&self, _: &RewriteContext<'_>, _: Shape) -> Option> { + None + } +} + +struct PairList<'a, 'b, T: Rewrite> { + list: Vec<(&'b T, Option)>, + separators: Vec<&'a str>, +} + +impl FlattenPair for ast::Expr { + fn flatten( + &self, + context: &RewriteContext<'_>, + shape: Shape, + ) -> Option> { + let top_op = match self.kind { + ast::ExprKind::Binary(op, _, _) => op.node, + _ => return None, + }; + + let default_rewrite = |node: &ast::Expr, sep: usize, is_first: bool| { + if is_first { + return node.rewrite(context, shape); + } + let nested_overhead = sep + 1; + let rhs_offset = shape.rhs_overhead(context.config); + let nested_shape = (match context.config.indent_style() { + IndentStyle::Visual => shape.visual_indent(0), + IndentStyle::Block => shape.block_indent(context.config.tab_spaces()), + }) + .with_max_width(context.config) + .sub_width(rhs_offset)?; + let default_shape = match context.config.binop_separator() { + SeparatorPlace::Back => nested_shape.sub_width(nested_overhead)?, + SeparatorPlace::Front => nested_shape.offset_left(nested_overhead)?, + }; + node.rewrite(context, default_shape) + }; + + // Turn a tree of binop expressions into a list using a depth-first, + // in-order traversal. + let mut stack = vec![]; + let mut list = vec![]; + let mut separators = vec![]; + let mut node = self; + loop { + match node.kind { + ast::ExprKind::Binary(op, ref lhs, _) if op.node == top_op => { + stack.push(node); + node = lhs; + } + _ => { + let op_len = separators.last().map_or(0, |s: &&str| s.len()); + let rw = default_rewrite(node, op_len, list.is_empty()); + list.push((node, rw)); + if let Some(pop) = stack.pop() { + match pop.kind { + ast::ExprKind::Binary(op, _, ref rhs) => { + separators.push(op.node.to_string()); + node = rhs; + } + _ => unreachable!(), + } + } else { + break; + } + } + } + } + + assert_eq!(list.len() - 1, separators.len()); + Some(PairList { list, separators }) + } +} + +impl FlattenPair for ast::Ty {} +impl FlattenPair for ast::Pat {} diff --git a/src/parse/macros/asm.rs b/src/parse/macros/asm.rs new file mode 100644 index 000000000000..01edfab36547 --- /dev/null +++ b/src/parse/macros/asm.rs @@ -0,0 +1,11 @@ +use rustc_ast::ast; +use rustc_builtin_macros::asm::{parse_asm_args, AsmArgs}; + +use crate::rewrite::RewriteContext; + +#[allow(dead_code)] +pub(crate) fn parse_asm(context: &RewriteContext<'_>, mac: &ast::MacCall) -> Option { + let ts = mac.args.tokens.clone(); + let mut parser = super::build_parser(context, ts); + parse_asm_args(&mut parser, context.parse_sess.inner(), mac.span(), false).ok() +} diff --git a/src/parse/macros/cfg_if.rs b/src/parse/macros/cfg_if.rs new file mode 100644 index 000000000000..cbc4c90b8f98 --- /dev/null +++ b/src/parse/macros/cfg_if.rs @@ -0,0 +1,97 @@ +use std::panic::{catch_unwind, AssertUnwindSafe}; + +use rustc_ast::ast; +use rustc_ast::token::{Delimiter, TokenKind}; +use rustc_parse::parser::ForceCollect; +use rustc_span::symbol::kw; + +use crate::parse::macros::build_stream_parser; +use crate::parse::session::ParseSess; + +pub(crate) fn parse_cfg_if<'a>( + sess: &'a ParseSess, + mac: &'a ast::MacCall, +) -> Result, &'static str> { + match catch_unwind(AssertUnwindSafe(|| parse_cfg_if_inner(sess, mac))) { + Ok(Ok(items)) => Ok(items), + Ok(err @ Err(_)) => err, + Err(..) => Err("failed to parse cfg_if!"), + } +} + +fn parse_cfg_if_inner<'a>( + sess: &'a ParseSess, + mac: &'a ast::MacCall, +) -> Result, &'static str> { + let ts = mac.args.tokens.clone(); + let mut parser = build_stream_parser(sess.inner(), ts); + + let mut items = vec![]; + let mut process_if_cfg = true; + + while parser.token.kind != TokenKind::Eof { + if process_if_cfg { + if !parser.eat_keyword(kw::If) { + return Err("Expected `if`"); + } + + if !matches!(parser.token.kind, TokenKind::Pound) { + return Err("Failed to parse attributes"); + } + + // Inner attributes are not actually syntactically permitted here, but we don't + // care about inner vs outer attributes in this position. Our purpose with this + // special case parsing of cfg_if macros is to ensure we can correctly resolve + // imported modules that may have a custom `path` defined. + // + // As such, we just need to advance the parser past the attribute and up to + // to the opening brace. + // See also https://github.com/rust-lang/rust/pull/79433 + parser + .parse_attribute(rustc_parse::parser::attr::InnerAttrPolicy::Permitted) + .map_err(|e| { + e.cancel(); + "Failed to parse attributes" + })?; + } + + if !parser.eat(&TokenKind::OpenDelim(Delimiter::Brace)) { + return Err("Expected an opening brace"); + } + + while parser.token != TokenKind::CloseDelim(Delimiter::Brace) + && parser.token.kind != TokenKind::Eof + { + let item = match parser.parse_item(ForceCollect::No) { + Ok(Some(item_ptr)) => item_ptr.into_inner(), + Ok(None) => continue, + Err(err) => { + err.cancel(); + parser.sess.span_diagnostic.reset_err_count(); + return Err( + "Expected item inside cfg_if block, but failed to parse it as an item", + ); + } + }; + if let ast::ItemKind::Mod(..) = item.kind { + items.push(item); + } + } + + if !parser.eat(&TokenKind::CloseDelim(Delimiter::Brace)) { + return Err("Expected a closing brace"); + } + + if parser.eat(&TokenKind::Eof) { + break; + } + + if !parser.eat_keyword(kw::Else) { + return Err("Expected `else`"); + } + + process_if_cfg = parser.token.is_keyword(kw::If); + } + + Ok(items) +} diff --git a/src/parse/macros/lazy_static.rs b/src/parse/macros/lazy_static.rs new file mode 100644 index 000000000000..a8c2feec453c --- /dev/null +++ b/src/parse/macros/lazy_static.rs @@ -0,0 +1,50 @@ +use rustc_ast::ast; +use rustc_ast::ptr::P; +use rustc_ast::token::TokenKind; +use rustc_ast::tokenstream::TokenStream; +use rustc_span::symbol::{self, kw}; + +use crate::rewrite::RewriteContext; + +pub(crate) fn parse_lazy_static( + context: &RewriteContext<'_>, + ts: TokenStream, +) -> Option, P)>> { + let mut result = vec![]; + let mut parser = super::build_parser(context, ts); + macro_rules! parse_or { + ($method:ident $(,)* $($arg:expr),* $(,)*) => { + match parser.$method($($arg,)*) { + Ok(val) => { + if parser.sess.span_diagnostic.has_errors().is_some() { + parser.sess.span_diagnostic.reset_err_count(); + return None; + } else { + val + } + } + Err(err) => { + err.cancel(); + parser.sess.span_diagnostic.reset_err_count(); + return None; + } + } + } + } + + while parser.token.kind != TokenKind::Eof { + // Parse a `lazy_static!` item. + let vis = parse_or!(parse_visibility, rustc_parse::parser::FollowedByType::No); + parser.eat_keyword(kw::Static); + parser.eat_keyword(kw::Ref); + let id = parse_or!(parse_ident); + parser.eat(&TokenKind::Colon); + let ty = parse_or!(parse_ty); + parser.eat(&TokenKind::Eq); + let expr = parse_or!(parse_expr); + parser.eat(&TokenKind::Semi); + result.push((vis, id, ty, expr)); + } + + Some(result) +} diff --git a/src/parse/macros/mod.rs b/src/parse/macros/mod.rs new file mode 100644 index 000000000000..67f3985926e2 --- /dev/null +++ b/src/parse/macros/mod.rs @@ -0,0 +1,229 @@ +use rustc_ast::token::{Delimiter, TokenKind}; +use rustc_ast::tokenstream::TokenStream; +use rustc_ast::{ast, ptr}; +use rustc_parse::parser::{ForceCollect, Parser}; +use rustc_parse::{stream_to_parser, MACRO_ARGUMENTS}; +use rustc_session::parse::ParseSess; +use rustc_span::symbol::{self, kw}; +use rustc_span::Symbol; + +use crate::macros::MacroArg; +use crate::rewrite::RewriteContext; + +pub(crate) mod asm; +pub(crate) mod cfg_if; +pub(crate) mod lazy_static; + +fn build_stream_parser<'a>(sess: &'a ParseSess, tokens: TokenStream) -> Parser<'a> { + stream_to_parser(sess, tokens, MACRO_ARGUMENTS) +} + +fn build_parser<'a>(context: &RewriteContext<'a>, tokens: TokenStream) -> Parser<'a> { + build_stream_parser(context.parse_sess.inner(), tokens) +} + +fn parse_macro_arg<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option { + macro_rules! parse_macro_arg { + ($macro_arg:ident, $parser:expr, $f:expr) => { + let mut cloned_parser = (*parser).clone(); + match $parser(&mut cloned_parser) { + Ok(x) => { + if parser.sess.span_diagnostic.has_errors().is_some() { + parser.sess.span_diagnostic.reset_err_count(); + } else { + // Parsing succeeded. + *parser = cloned_parser; + return Some(MacroArg::$macro_arg($f(x)?)); + } + } + Err(e) => { + e.cancel(); + parser.sess.span_diagnostic.reset_err_count(); + } + } + }; + } + + parse_macro_arg!( + Expr, + |parser: &mut rustc_parse::parser::Parser<'b>| parser.parse_expr(), + |x: ptr::P| Some(x) + ); + parse_macro_arg!( + Ty, + |parser: &mut rustc_parse::parser::Parser<'b>| parser.parse_ty(), + |x: ptr::P| Some(x) + ); + parse_macro_arg!( + Pat, + |parser: &mut rustc_parse::parser::Parser<'b>| parser.parse_pat_no_top_alt(None), + |x: ptr::P| Some(x) + ); + // `parse_item` returns `Option>`. + parse_macro_arg!( + Item, + |parser: &mut rustc_parse::parser::Parser<'b>| parser.parse_item(ForceCollect::No), + |x: Option>| x + ); + + None +} + +pub(crate) struct ParsedMacroArgs { + pub(crate) vec_with_semi: bool, + pub(crate) trailing_comma: bool, + pub(crate) args: Vec, +} + +fn check_keyword<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option { + for &keyword in RUST_KW.iter() { + if parser.token.is_keyword(keyword) + && parser.look_ahead(1, |t| { + t.kind == TokenKind::Eof || t.kind == TokenKind::Comma + }) + { + parser.bump(); + return Some(MacroArg::Keyword( + symbol::Ident::with_dummy_span(keyword), + parser.prev_token.span, + )); + } + } + None +} + +pub(crate) fn parse_macro_args( + context: &RewriteContext<'_>, + tokens: TokenStream, + style: Delimiter, + forced_bracket: bool, +) -> Option { + let mut parser = build_parser(context, tokens); + let mut args = Vec::new(); + let mut vec_with_semi = false; + let mut trailing_comma = false; + + if Delimiter::Brace != style { + loop { + if let Some(arg) = check_keyword(&mut parser) { + args.push(arg); + } else if let Some(arg) = parse_macro_arg(&mut parser) { + args.push(arg); + } else { + return None; + } + + match parser.token.kind { + TokenKind::Eof => break, + TokenKind::Comma => (), + TokenKind::Semi => { + // Try to parse `vec![expr; expr]` + if forced_bracket { + parser.bump(); + if parser.token.kind != TokenKind::Eof { + match parse_macro_arg(&mut parser) { + Some(arg) => { + args.push(arg); + parser.bump(); + if parser.token.kind == TokenKind::Eof && args.len() == 2 { + vec_with_semi = true; + break; + } + } + None => { + return None; + } + } + } + } + return None; + } + _ if args.last().map_or(false, MacroArg::is_item) => continue, + _ => return None, + } + + parser.bump(); + + if parser.token.kind == TokenKind::Eof { + trailing_comma = true; + break; + } + } + } + + Some(ParsedMacroArgs { + vec_with_semi, + trailing_comma, + args, + }) +} + +pub(crate) fn parse_expr( + context: &RewriteContext<'_>, + tokens: TokenStream, +) -> Option> { + let mut parser = build_parser(context, tokens); + parser.parse_expr().ok() +} + +const RUST_KW: [Symbol; 59] = [ + kw::PathRoot, + kw::DollarCrate, + kw::Underscore, + kw::As, + kw::Box, + kw::Break, + kw::Const, + kw::Continue, + kw::Crate, + kw::Else, + kw::Enum, + kw::Extern, + kw::False, + kw::Fn, + kw::For, + kw::If, + kw::Impl, + kw::In, + kw::Let, + kw::Loop, + kw::Match, + kw::Mod, + kw::Move, + kw::Mut, + kw::Pub, + kw::Ref, + kw::Return, + kw::SelfLower, + kw::SelfUpper, + kw::Static, + kw::Struct, + kw::Super, + kw::Trait, + kw::True, + kw::Type, + kw::Unsafe, + kw::Use, + kw::Where, + kw::While, + kw::Abstract, + kw::Become, + kw::Do, + kw::Final, + kw::Macro, + kw::Override, + kw::Priv, + kw::Typeof, + kw::Unsized, + kw::Virtual, + kw::Yield, + kw::Dyn, + kw::Async, + kw::Try, + kw::UnderscoreLifetime, + kw::StaticLifetime, + kw::Auto, + kw::Catch, + kw::Default, + kw::Union, +]; diff --git a/src/parse/mod.rs b/src/parse/mod.rs new file mode 100644 index 000000000000..5e88826ea8cc --- /dev/null +++ b/src/parse/mod.rs @@ -0,0 +1,3 @@ +pub(crate) mod macros; +pub(crate) mod parser; +pub(crate) mod session; diff --git a/src/parse/parser.rs b/src/parse/parser.rs new file mode 100644 index 000000000000..6bc53159b38b --- /dev/null +++ b/src/parse/parser.rs @@ -0,0 +1,175 @@ +use std::panic::{catch_unwind, AssertUnwindSafe}; +use std::path::{Path, PathBuf}; + +use rustc_ast::token::TokenKind; +use rustc_ast::{ast, attr, ptr}; +use rustc_errors::Diagnostic; +use rustc_parse::{new_parser_from_file, parser::Parser as RawParser}; +use rustc_span::{sym, Span}; +use thin_vec::ThinVec; + +use crate::parse::session::ParseSess; +use crate::Input; + +pub(crate) type DirectoryOwnership = rustc_expand::module::DirOwnership; +pub(crate) type ModulePathSuccess = rustc_expand::module::ModulePathSuccess; +pub(crate) type ModError<'a> = rustc_expand::module::ModError<'a>; + +#[derive(Clone)] +pub(crate) struct Directory { + pub(crate) path: PathBuf, + pub(crate) ownership: DirectoryOwnership, +} + +/// A parser for Rust source code. +pub(crate) struct Parser<'a> { + parser: RawParser<'a>, +} + +/// A builder for the `Parser`. +#[derive(Default)] +pub(crate) struct ParserBuilder<'a> { + sess: Option<&'a ParseSess>, + input: Option, +} + +impl<'a> ParserBuilder<'a> { + pub(crate) fn input(mut self, input: Input) -> ParserBuilder<'a> { + self.input = Some(input); + self + } + + pub(crate) fn sess(mut self, sess: &'a ParseSess) -> ParserBuilder<'a> { + self.sess = Some(sess); + self + } + + pub(crate) fn build(self) -> Result, ParserError> { + let sess = self.sess.ok_or(ParserError::NoParseSess)?; + let input = self.input.ok_or(ParserError::NoInput)?; + + let parser = match Self::parser(sess.inner(), input) { + Ok(p) => p, + Err(db) => { + if let Some(diagnostics) = db { + sess.emit_diagnostics(diagnostics); + return Err(ParserError::ParserCreationError); + } + return Err(ParserError::ParsePanicError); + } + }; + + Ok(Parser { parser }) + } + + fn parser( + sess: &'a rustc_session::parse::ParseSess, + input: Input, + ) -> Result, Option>> { + match input { + Input::File(ref file) => catch_unwind(AssertUnwindSafe(move || { + new_parser_from_file(sess, file, None) + })) + .map_err(|_| None), + Input::Text(text) => rustc_parse::maybe_new_parser_from_source_str( + sess, + rustc_span::FileName::Custom("stdin".to_owned()), + text, + ) + .map_err(Some), + } + } +} + +#[derive(Debug, PartialEq)] +pub(crate) enum ParserError { + NoParseSess, + NoInput, + ParserCreationError, + ParseError, + ParsePanicError, +} + +impl<'a> Parser<'a> { + pub(crate) fn submod_path_from_attr(attrs: &[ast::Attribute], path: &Path) -> Option { + let path_sym = attr::first_attr_value_str_by_name(attrs, sym::path)?; + let path_str = path_sym.as_str(); + + // On windows, the base path might have the form + // `\\?\foo\bar` in which case it does not tolerate + // mixed `/` and `\` separators, so canonicalize + // `/` to `\`. + #[cfg(windows)] + let path_str = path_str.replace("/", "\\"); + + Some(path.join(path_str)) + } + + pub(crate) fn parse_file_as_module( + sess: &'a ParseSess, + path: &Path, + span: Span, + ) -> Result<(ast::AttrVec, ThinVec>, Span), ParserError> { + let result = catch_unwind(AssertUnwindSafe(|| { + let mut parser = new_parser_from_file(sess.inner(), path, Some(span)); + match parser.parse_mod(&TokenKind::Eof) { + Ok((a, i, spans)) => Some((a, i, spans.inner_span)), + Err(mut e) => { + e.emit(); + if sess.can_reset_errors() { + sess.reset_errors(); + } + None + } + } + })); + match result { + Ok(Some(m)) if !sess.has_errors() => Ok(m), + Ok(Some(m)) if sess.can_reset_errors() => { + sess.reset_errors(); + Ok(m) + } + Ok(_) => Err(ParserError::ParseError), + Err(..) if path.exists() => Err(ParserError::ParseError), + Err(_) => Err(ParserError::ParsePanicError), + } + } + + pub(crate) fn parse_crate( + input: Input, + sess: &'a ParseSess, + ) -> Result { + let krate = Parser::parse_crate_inner(input, sess)?; + if !sess.has_errors() { + return Ok(krate); + } + + if sess.can_reset_errors() { + sess.reset_errors(); + return Ok(krate); + } + + Err(ParserError::ParseError) + } + + fn parse_crate_inner(input: Input, sess: &'a ParseSess) -> Result { + ParserBuilder::default() + .input(input) + .sess(sess) + .build()? + .parse_crate_mod() + } + + fn parse_crate_mod(&mut self) -> Result { + let mut parser = AssertUnwindSafe(&mut self.parser); + + match catch_unwind(move || parser.parse_crate_mod()) { + Ok(Ok(k)) => Ok(k), + Ok(Err(mut db)) => { + db.emit(); + Err(ParserError::ParseError) + } + Err(_) => Err(ParserError::ParsePanicError), + } + } +} diff --git a/src/parse/session.rs b/src/parse/session.rs new file mode 100644 index 000000000000..81b5015dde33 --- /dev/null +++ b/src/parse/session.rs @@ -0,0 +1,538 @@ +use std::path::Path; +use std::sync::atomic::{AtomicBool, Ordering}; + +use rustc_data_structures::sync::{Lrc, Send}; +use rustc_errors::emitter::{Emitter, EmitterWriter}; +use rustc_errors::translation::Translate; +use rustc_errors::{ColorConfig, Diagnostic, Handler, Level as DiagnosticLevel, TerminalUrl}; +use rustc_session::parse::ParseSess as RawParseSess; +use rustc_span::{ + source_map::{FilePathMapping, SourceMap}, + symbol, BytePos, Span, +}; + +use crate::config::file_lines::LineRange; +use crate::config::options::Color; +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; +use crate::{Config, ErrorKind, FileName}; + +/// ParseSess holds structs necessary for constructing a parser. +pub(crate) struct ParseSess { + parse_sess: RawParseSess, + ignore_path_set: Lrc, + can_reset_errors: Lrc, +} + +/// Emitter which discards every error. +struct SilentEmitter; + +impl Translate for SilentEmitter { + fn fluent_bundle(&self) -> Option<&Lrc> { + None + } + + fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle { + panic!("silent emitter attempted to translate a diagnostic"); + } +} + +impl Emitter for SilentEmitter { + fn source_map(&self) -> Option<&Lrc> { + None + } + + fn emit_diagnostic(&mut self, _db: &Diagnostic) {} +} + +fn silent_emitter() -> Box { + Box::new(SilentEmitter {}) +} + +/// Emit errors against every files expect ones specified in the `ignore_path_set`. +struct SilentOnIgnoredFilesEmitter { + ignore_path_set: Lrc, + source_map: Lrc, + emitter: Box, + has_non_ignorable_parser_errors: bool, + can_reset: Lrc, +} + +impl SilentOnIgnoredFilesEmitter { + fn handle_non_ignoreable_error(&mut self, db: &Diagnostic) { + self.has_non_ignorable_parser_errors = true; + self.can_reset.store(false, Ordering::Release); + self.emitter.emit_diagnostic(db); + } +} + +impl Translate for SilentOnIgnoredFilesEmitter { + fn fluent_bundle(&self) -> Option<&Lrc> { + self.emitter.fluent_bundle() + } + + fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle { + self.emitter.fallback_fluent_bundle() + } +} + +impl Emitter for SilentOnIgnoredFilesEmitter { + fn source_map(&self) -> Option<&Lrc> { + None + } + + fn emit_diagnostic(&mut self, db: &Diagnostic) { + if db.level() == DiagnosticLevel::Fatal { + return self.handle_non_ignoreable_error(db); + } + if let Some(primary_span) = &db.span.primary_span() { + let file_name = self.source_map.span_to_filename(*primary_span); + if let rustc_span::FileName::Real(rustc_span::RealFileName::LocalPath(ref path)) = + file_name + { + if self + .ignore_path_set + .is_match(&FileName::Real(path.to_path_buf())) + { + if !self.has_non_ignorable_parser_errors { + self.can_reset.store(true, Ordering::Release); + } + return; + } + }; + } + self.handle_non_ignoreable_error(db); + } +} + +impl From for ColorConfig { + fn from(color: Color) -> Self { + match color { + Color::Auto => ColorConfig::Auto, + Color::Always => ColorConfig::Always, + Color::Never => ColorConfig::Never, + } + } +} + +fn default_handler( + source_map: Lrc, + ignore_path_set: Lrc, + can_reset: Lrc, + hide_parse_errors: bool, + color: Color, +) -> Handler { + let supports_color = term::stderr().map_or(false, |term| term.supports_color()); + let emit_color = if supports_color { + ColorConfig::from(color) + } else { + ColorConfig::Never + }; + + let emitter = if hide_parse_errors { + silent_emitter() + } else { + let fallback_bundle = rustc_errors::fallback_fluent_bundle( + rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), + false, + ); + Box::new(EmitterWriter::stderr( + emit_color, + Some(source_map.clone()), + None, + fallback_bundle, + false, + false, + None, + false, + false, + TerminalUrl::No, + )) + }; + Handler::with_emitter( + true, + None, + Box::new(SilentOnIgnoredFilesEmitter { + has_non_ignorable_parser_errors: false, + source_map, + emitter, + ignore_path_set, + can_reset, + }), + ) +} + +impl ParseSess { + pub(crate) fn new(config: &Config) -> Result { + let ignore_path_set = match IgnorePathSet::from_ignore_list(&config.ignore()) { + Ok(ignore_path_set) => Lrc::new(ignore_path_set), + Err(e) => return Err(ErrorKind::InvalidGlobPattern(e)), + }; + let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let can_reset_errors = Lrc::new(AtomicBool::new(false)); + + let handler = default_handler( + Lrc::clone(&source_map), + Lrc::clone(&ignore_path_set), + Lrc::clone(&can_reset_errors), + config.hide_parse_errors(), + config.color(), + ); + let parse_sess = RawParseSess::with_span_handler(handler, source_map); + + Ok(ParseSess { + parse_sess, + ignore_path_set, + can_reset_errors, + }) + } + + /// 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 directory. + 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).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 { + self.parse_sess + .source_map() + .get_source_file(&rustc_span::FileName::Real( + rustc_span::RealFileName::LocalPath(path.to_path_buf()), + )) + .is_some() + } + + pub(crate) fn ignore_file(&self, path: &FileName) -> bool { + self.ignore_path_set.as_ref().is_match(path) + } + + pub(crate) fn set_silent_emitter(&mut self) { + self.parse_sess.span_diagnostic = Handler::with_emitter(true, None, silent_emitter()); + } + + pub(crate) fn span_to_filename(&self, span: Span) -> FileName { + self.parse_sess.source_map().span_to_filename(span).into() + } + + pub(crate) fn span_to_file_contents(&self, span: Span) -> Lrc { + self.parse_sess + .source_map() + .lookup_source_file(span.data().lo) + } + + pub(crate) fn span_to_first_line_string(&self, span: Span) -> String { + let file_lines = self.parse_sess.source_map().span_to_lines(span).ok(); + + match file_lines { + Some(fl) => fl + .file + .get_line(fl.lines[0].line_index) + .map_or_else(String::new, |s| s.to_string()), + None => String::new(), + } + } + + pub(crate) fn line_of_byte_pos(&self, pos: BytePos) -> usize { + self.parse_sess.source_map().lookup_char_pos(pos).line + } + + // 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) + } + + pub(crate) fn inner(&self) -> &RawParseSess { + &self.parse_sess + } + + pub(crate) fn snippet_provider(&self, span: Span) -> SnippetProvider { + let source_file = self.parse_sess.source_map().lookup_char_pos(span.lo()).file; + SnippetProvider::new( + source_file.start_pos, + source_file.end_pos, + Lrc::clone(source_file.src.as_ref().unwrap()), + ) + } + + pub(crate) fn get_original_snippet(&self, file_name: &FileName) -> Option> { + self.parse_sess + .source_map() + .get_source_file(&file_name.into()) + .and_then(|source_file| source_file.src.clone()) + } +} + +// Methods that should be restricted within the parse module. +impl ParseSess { + pub(super) fn emit_diagnostics(&self, diagnostics: Vec) { + for mut diagnostic in diagnostics { + self.parse_sess + .span_diagnostic + .emit_diagnostic(&mut diagnostic); + } + } + + pub(super) fn can_reset_errors(&self) -> bool { + self.can_reset_errors.load(Ordering::Acquire) + } + + pub(super) fn has_errors(&self) -> bool { + self.parse_sess.span_diagnostic.has_errors().is_some() + } + + pub(super) fn reset_errors(&self) { + self.parse_sess.span_diagnostic.reset_err_count(); + } +} + +impl LineRangeUtils for ParseSess { + fn lookup_line_range(&self, span: Span) -> LineRange { + let snippet = self + .parse_sess + .source_map() + .span_to_snippet(span) + .unwrap_or_default(); + let lo = self.parse_sess.source_map().lookup_line(span.lo()).unwrap(); + let hi = self.parse_sess.source_map().lookup_line(span.hi()).unwrap(); + + debug_assert_eq!( + lo.sf.name, hi.sf.name, + "span crossed file boundary: lo: {:?}, hi: {:?}", + lo, hi + ); + + // in case the span starts with a newline, the line range is off by 1 without the + // adjustment below + let offset = 1 + if starts_with_newline(&snippet) { 1 } else { 0 }; + // Line numbers start at 1 + LineRange { + file: lo.sf.clone(), + lo: lo.line + offset, + hi: hi.line + offset, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use rustfmt_config_proc_macro::nightly_only_test; + + mod emitter { + use super::*; + use crate::config::IgnoreList; + use crate::utils::mk_sp; + use rustc_errors::MultiSpan; + use rustc_span::{FileName as SourceMapFileName, RealFileName}; + use std::path::PathBuf; + use std::sync::atomic::AtomicU32; + + struct TestEmitter { + num_emitted_errors: Lrc, + } + + impl Translate for TestEmitter { + fn fluent_bundle(&self) -> Option<&Lrc> { + None + } + + fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle { + panic!("test emitter attempted to translate a diagnostic"); + } + } + + impl Emitter for TestEmitter { + fn source_map(&self) -> Option<&Lrc> { + None + } + + fn emit_diagnostic(&mut self, _db: &Diagnostic) { + self.num_emitted_errors.fetch_add(1, Ordering::Release); + } + } + + fn build_diagnostic(level: DiagnosticLevel, span: Option) -> Diagnostic { + let mut diag = Diagnostic::new(level, ""); + diag.message.clear(); + if let Some(span) = span { + diag.span = span; + } + diag + } + + fn build_emitter( + num_emitted_errors: Lrc, + can_reset: Lrc, + source_map: Option>, + ignore_list: Option, + ) -> SilentOnIgnoredFilesEmitter { + let emitter_writer = TestEmitter { num_emitted_errors }; + let source_map = + source_map.unwrap_or_else(|| Lrc::new(SourceMap::new(FilePathMapping::empty()))); + let ignore_path_set = Lrc::new( + IgnorePathSet::from_ignore_list(&ignore_list.unwrap_or_default()).unwrap(), + ); + SilentOnIgnoredFilesEmitter { + has_non_ignorable_parser_errors: false, + source_map, + emitter: Box::new(emitter_writer), + ignore_path_set, + can_reset, + } + } + + fn get_ignore_list(config: &str) -> IgnoreList { + Config::from_toml(config, Path::new("")).unwrap().ignore() + } + + #[test] + fn handles_fatal_parse_error_in_ignored_file() { + let num_emitted_errors = Lrc::new(AtomicU32::new(0)); + let can_reset_errors = Lrc::new(AtomicBool::new(false)); + let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#); + let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let source = + String::from(r#"extern "system" fn jni_symbol!( funcName ) ( ... ) -> {} "#); + source_map.new_source_file( + SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))), + source, + ); + let mut emitter = build_emitter( + Lrc::clone(&num_emitted_errors), + Lrc::clone(&can_reset_errors), + Some(Lrc::clone(&source_map)), + Some(ignore_list), + ); + let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1))); + let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, Some(span)); + emitter.emit_diagnostic(&fatal_diagnostic); + assert_eq!(num_emitted_errors.load(Ordering::Acquire), 1); + assert_eq!(can_reset_errors.load(Ordering::Acquire), false); + } + + #[nightly_only_test] + #[test] + fn handles_recoverable_parse_error_in_ignored_file() { + let num_emitted_errors = Lrc::new(AtomicU32::new(0)); + let can_reset_errors = Lrc::new(AtomicBool::new(false)); + let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#); + let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let source = String::from(r#"pub fn bar() { 1x; }"#); + source_map.new_source_file( + SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))), + source, + ); + let mut emitter = build_emitter( + Lrc::clone(&num_emitted_errors), + Lrc::clone(&can_reset_errors), + Some(Lrc::clone(&source_map)), + Some(ignore_list), + ); + let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1))); + let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning(None), Some(span)); + emitter.emit_diagnostic(&non_fatal_diagnostic); + assert_eq!(num_emitted_errors.load(Ordering::Acquire), 0); + assert_eq!(can_reset_errors.load(Ordering::Acquire), true); + } + + #[nightly_only_test] + #[test] + fn handles_recoverable_parse_error_in_non_ignored_file() { + let num_emitted_errors = Lrc::new(AtomicU32::new(0)); + let can_reset_errors = Lrc::new(AtomicBool::new(false)); + let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let source = String::from(r#"pub fn bar() { 1x; }"#); + source_map.new_source_file( + SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))), + source, + ); + let mut emitter = build_emitter( + Lrc::clone(&num_emitted_errors), + Lrc::clone(&can_reset_errors), + Some(Lrc::clone(&source_map)), + None, + ); + let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1))); + let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning(None), Some(span)); + emitter.emit_diagnostic(&non_fatal_diagnostic); + assert_eq!(num_emitted_errors.load(Ordering::Acquire), 1); + assert_eq!(can_reset_errors.load(Ordering::Acquire), false); + } + + #[nightly_only_test] + #[test] + fn handles_mix_of_recoverable_parse_error() { + let num_emitted_errors = Lrc::new(AtomicU32::new(0)); + let can_reset_errors = Lrc::new(AtomicBool::new(false)); + let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#); + let bar_source = String::from(r#"pub fn bar() { 1x; }"#); + let foo_source = String::from(r#"pub fn foo() { 1x; }"#); + let fatal_source = + String::from(r#"extern "system" fn jni_symbol!( funcName ) ( ... ) -> {} "#); + source_map.new_source_file( + SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("bar.rs"))), + bar_source, + ); + source_map.new_source_file( + SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))), + foo_source, + ); + source_map.new_source_file( + SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("fatal.rs"))), + fatal_source, + ); + let mut emitter = build_emitter( + Lrc::clone(&num_emitted_errors), + Lrc::clone(&can_reset_errors), + Some(Lrc::clone(&source_map)), + Some(ignore_list), + ); + let bar_span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1))); + let foo_span = MultiSpan::from_span(mk_sp(BytePos(21), BytePos(22))); + let bar_diagnostic = build_diagnostic(DiagnosticLevel::Warning(None), Some(bar_span)); + let foo_diagnostic = build_diagnostic(DiagnosticLevel::Warning(None), Some(foo_span)); + let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, None); + emitter.emit_diagnostic(&bar_diagnostic); + emitter.emit_diagnostic(&foo_diagnostic); + emitter.emit_diagnostic(&fatal_diagnostic); + assert_eq!(num_emitted_errors.load(Ordering::Acquire), 2); + assert_eq!(can_reset_errors.load(Ordering::Acquire), false); + } + } +} diff --git a/src/patterns.rs b/src/patterns.rs new file mode 100644 index 000000000000..3f335172590e --- /dev/null +++ b/src/patterns.rs @@ -0,0 +1,536 @@ +use rustc_ast::ast::{ + self, BindingAnnotation, ByRef, Pat, PatField, PatKind, RangeEnd, RangeSyntax, +}; +use rustc_ast::ptr; +use rustc_span::{BytePos, Span}; + +use crate::comment::{combine_strs_with_missing_comments, FindUncommented}; +use crate::config::lists::*; +use crate::config::Version; +use crate::expr::{can_be_overflowed_expr, rewrite_unary_prefix, wrap_struct_field}; +use crate::lists::{ + definitive_tactic, itemize_list, shape_for_tactic, struct_lit_formatting, struct_lit_shape, + struct_lit_tactic, write_list, ListFormatting, ListItem, Separator, +}; +use crate::macros::{rewrite_macro, MacroPosition}; +use crate::overflow; +use crate::pairs::{rewrite_pair, PairParts}; +use crate::rewrite::{Rewrite, RewriteContext}; +use crate::shape::Shape; +use crate::source_map::SpanUtils; +use crate::spanned::Spanned; +use crate::types::{rewrite_path, PathContext}; +use crate::utils::{format_mutability, mk_sp, mk_sp_lo_plus_one, rewrite_ident}; + +/// Returns `true` if the given pattern is "short". +/// A short pattern is defined by the following grammar: +/// +/// `[small, ntp]`: +/// - single token +/// - `&[single-line, ntp]` +/// +/// `[small]`: +/// - `[small, ntp]` +/// - unary tuple constructor `([small, ntp])` +/// - `&[small]` +pub(crate) fn is_short_pattern(pat: &ast::Pat, pat_str: &str) -> bool { + // We also require that the pattern is reasonably 'small' with its literal width. + pat_str.len() <= 20 && !pat_str.contains('\n') && is_short_pattern_inner(pat) +} + +fn is_short_pattern_inner(pat: &ast::Pat) -> bool { + match pat.kind { + ast::PatKind::Rest | ast::PatKind::Wild | ast::PatKind::Lit(_) => true, + ast::PatKind::Ident(_, _, ref pat) => pat.is_none(), + ast::PatKind::Struct(..) + | ast::PatKind::MacCall(..) + | ast::PatKind::Slice(..) + | ast::PatKind::Path(..) + | ast::PatKind::Range(..) => false, + ast::PatKind::Tuple(ref subpats) => subpats.len() <= 1, + ast::PatKind::TupleStruct(_, ref path, ref subpats) => { + path.segments.len() <= 1 && subpats.len() <= 1 + } + ast::PatKind::Box(ref p) | ast::PatKind::Ref(ref p, _) | ast::PatKind::Paren(ref p) => { + is_short_pattern_inner(&*p) + } + PatKind::Or(ref pats) => pats.iter().all(|p| is_short_pattern_inner(p)), + } +} + +struct RangeOperand<'a>(&'a Option>); + +impl<'a> Rewrite for RangeOperand<'a> { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + match &self.0 { + None => Some("".to_owned()), + Some(ref exp) => exp.rewrite(context, shape), + } + } +} + +impl Rewrite for Pat { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + match self.kind { + PatKind::Or(ref pats) => { + let pat_strs = pats + .iter() + .map(|p| p.rewrite(context, shape)) + .collect::>>()?; + + let use_mixed_layout = pats + .iter() + .zip(pat_strs.iter()) + .all(|(pat, pat_str)| is_short_pattern(pat, pat_str)); + let items: Vec<_> = pat_strs.into_iter().map(ListItem::from_str).collect(); + let tactic = if use_mixed_layout { + DefinitiveListTactic::Mixed + } else { + definitive_tactic( + &items, + ListTactic::HorizontalVertical, + Separator::VerticalBar, + shape.width, + ) + }; + let fmt = ListFormatting::new(shape, context.config) + .tactic(tactic) + .separator(" |") + .separator_place(context.config.binop_separator()) + .ends_with_newline(false); + write_list(&items, &fmt) + } + PatKind::Box(ref pat) => rewrite_unary_prefix(context, "box ", &**pat, shape), + PatKind::Ident(BindingAnnotation(by_ref, mutability), ident, ref sub_pat) => { + let prefix = match by_ref { + ByRef::Yes => "ref", + ByRef::No => "", + }; + let mut_infix = format_mutability(mutability).trim(); + let id_str = rewrite_ident(context, ident); + let sub_pat = match *sub_pat { + Some(ref p) => { + // 2 - `@ `. + let width = shape + .width + .checked_sub(prefix.len() + mut_infix.len() + id_str.len() + 2)?; + let lo = context.snippet_provider.span_after(self.span, "@"); + combine_strs_with_missing_comments( + context, + "@", + &p.rewrite(context, Shape::legacy(width, shape.indent))?, + mk_sp(lo, p.span.lo()), + shape, + true, + )? + } + None => "".to_owned(), + }; + + // combine prefix and mut + let (first_lo, first) = if !prefix.is_empty() && !mut_infix.is_empty() { + let hi = context.snippet_provider.span_before(self.span, "mut"); + let lo = context.snippet_provider.span_after(self.span, "ref"); + ( + context.snippet_provider.span_after(self.span, "mut"), + combine_strs_with_missing_comments( + context, + prefix, + mut_infix, + mk_sp(lo, hi), + shape, + true, + )?, + ) + } else if !prefix.is_empty() { + ( + context.snippet_provider.span_after(self.span, "ref"), + prefix.to_owned(), + ) + } else if !mut_infix.is_empty() { + ( + context.snippet_provider.span_after(self.span, "mut"), + mut_infix.to_owned(), + ) + } else { + (self.span.lo(), "".to_owned()) + }; + + let next = if !sub_pat.is_empty() { + let hi = context.snippet_provider.span_before(self.span, "@"); + combine_strs_with_missing_comments( + context, + id_str, + &sub_pat, + mk_sp(ident.span.hi(), hi), + shape, + true, + )? + } else { + id_str.to_owned() + }; + + combine_strs_with_missing_comments( + context, + &first, + &next, + mk_sp(first_lo, ident.span.lo()), + shape, + true, + ) + } + PatKind::Wild => { + if 1 <= shape.width { + Some("_".to_owned()) + } else { + None + } + } + PatKind::Rest => { + if 1 <= shape.width { + Some("..".to_owned()) + } else { + None + } + } + PatKind::Range(ref lhs, ref rhs, ref end_kind) => { + let infix = match end_kind.node { + RangeEnd::Included(RangeSyntax::DotDotDot) => "...", + RangeEnd::Included(RangeSyntax::DotDotEq) => "..=", + RangeEnd::Excluded => "..", + }; + let infix = if context.config.spaces_around_ranges() { + let lhs_spacing = match lhs { + None => "", + Some(_) => " ", + }; + let rhs_spacing = match rhs { + None => "", + Some(_) => " ", + }; + format!("{}{}{}", lhs_spacing, infix, rhs_spacing) + } else { + infix.to_owned() + }; + rewrite_pair( + &RangeOperand(lhs), + &RangeOperand(rhs), + PairParts::infix(&infix), + context, + shape, + SeparatorPlace::Front, + ) + } + PatKind::Ref(ref pat, mutability) => { + let prefix = format!("&{}", format_mutability(mutability)); + rewrite_unary_prefix(context, &prefix, &**pat, shape) + } + PatKind::Tuple(ref items) => rewrite_tuple_pat(items, None, self.span, context, shape), + PatKind::Path(ref q_self, ref path) => { + rewrite_path(context, PathContext::Expr, q_self, path, shape) + } + PatKind::TupleStruct(ref q_self, ref path, ref pat_vec) => { + let path_str = rewrite_path(context, PathContext::Expr, q_self, path, shape)?; + rewrite_tuple_pat(pat_vec, Some(path_str), self.span, context, shape) + } + PatKind::Lit(ref expr) => expr.rewrite(context, shape), + PatKind::Slice(ref slice_pat) if context.config.version() == Version::One => { + let rw: Vec = slice_pat + .iter() + .map(|p| { + if let Some(rw) = p.rewrite(context, shape) { + rw + } else { + context.snippet(p.span).to_string() + } + }) + .collect(); + Some(format!("[{}]", rw.join(", "))) + } + PatKind::Slice(ref slice_pat) => overflow::rewrite_with_square_brackets( + context, + "", + slice_pat.iter(), + shape, + self.span, + None, + None, + ), + PatKind::Struct(ref qself, ref path, ref fields, ellipsis) => { + rewrite_struct_pat(qself, path, fields, ellipsis, self.span, context, shape) + } + PatKind::MacCall(ref mac) => { + rewrite_macro(mac, None, context, shape, MacroPosition::Pat) + } + PatKind::Paren(ref pat) => pat + .rewrite(context, shape.offset_left(1)?.sub_width(1)?) + .map(|inner_pat| format!("({})", inner_pat)), + } + } +} + +fn rewrite_struct_pat( + qself: &Option>, + path: &ast::Path, + fields: &[ast::PatField], + ellipsis: bool, + span: Span, + context: &RewriteContext<'_>, + shape: Shape, +) -> Option { + // 2 = ` {` + let path_shape = shape.sub_width(2)?; + let path_str = rewrite_path(context, PathContext::Expr, qself, path, path_shape)?; + + if fields.is_empty() && !ellipsis { + return Some(format!("{} {{}}", path_str)); + } + + let (ellipsis_str, terminator) = if ellipsis { (", ..", "..") } else { ("", "}") }; + + // 3 = ` { `, 2 = ` }`. + let (h_shape, v_shape) = + struct_lit_shape(shape, context, path_str.len() + 3, ellipsis_str.len() + 2)?; + + let items = itemize_list( + context.snippet_provider, + fields.iter(), + terminator, + ",", + |f| { + if f.attrs.is_empty() { + f.span.lo() + } else { + f.attrs.first().unwrap().span.lo() + } + }, + |f| f.span.hi(), + |f| f.rewrite(context, v_shape), + context.snippet_provider.span_after(span, "{"), + span.hi(), + false, + ); + let item_vec = items.collect::>(); + + let tactic = struct_lit_tactic(h_shape, context, &item_vec); + let nested_shape = shape_for_tactic(tactic, h_shape, v_shape); + let fmt = struct_lit_formatting(nested_shape, tactic, context, false); + + let mut fields_str = write_list(&item_vec, &fmt)?; + let one_line_width = h_shape.map_or(0, |shape| shape.width); + + let has_trailing_comma = fmt.needs_trailing_separator(); + + if ellipsis { + if fields_str.contains('\n') || fields_str.len() > one_line_width { + // Add a missing trailing comma. + if !has_trailing_comma { + fields_str.push(','); + } + fields_str.push('\n'); + fields_str.push_str(&nested_shape.indent.to_string(context.config)); + } else { + if !fields_str.is_empty() { + // there are preceding struct fields being matched on + if has_trailing_comma { + fields_str.push(' '); + } else { + fields_str.push_str(", "); + } + } + } + fields_str.push_str(".."); + } + + // ast::Pat doesn't have attrs so use &[] + let fields_str = wrap_struct_field(context, &[], &fields_str, shape, v_shape, one_line_width)?; + Some(format!("{} {{{}}}", path_str, fields_str)) +} + +impl Rewrite for PatField { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + let hi_pos = if let Some(last) = self.attrs.last() { + last.span.hi() + } else { + self.pat.span.lo() + }; + + let attrs_str = if self.attrs.is_empty() { + String::from("") + } else { + self.attrs.rewrite(context, shape)? + }; + + let pat_str = self.pat.rewrite(context, shape)?; + if self.is_shorthand { + combine_strs_with_missing_comments( + context, + &attrs_str, + &pat_str, + mk_sp(hi_pos, self.pat.span.lo()), + shape, + false, + ) + } else { + let nested_shape = shape.block_indent(context.config.tab_spaces()); + let id_str = rewrite_ident(context, self.ident); + let one_line_width = id_str.len() + 2 + pat_str.len(); + let pat_and_id_str = if one_line_width <= shape.width { + format!("{}: {}", id_str, pat_str) + } else { + format!( + "{}:\n{}{}", + id_str, + nested_shape.indent.to_string(context.config), + self.pat.rewrite(context, nested_shape)? + ) + }; + combine_strs_with_missing_comments( + context, + &attrs_str, + &pat_and_id_str, + mk_sp(hi_pos, self.pat.span.lo()), + nested_shape, + false, + ) + } + } +} + +#[derive(Debug)] +pub(crate) enum TuplePatField<'a> { + Pat(&'a ptr::P), + Dotdot(Span), +} + +impl<'a> Rewrite for TuplePatField<'a> { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + match *self { + TuplePatField::Pat(p) => p.rewrite(context, shape), + TuplePatField::Dotdot(_) => Some("..".to_string()), + } + } +} + +impl<'a> Spanned for TuplePatField<'a> { + fn span(&self) -> Span { + match *self { + TuplePatField::Pat(p) => p.span(), + TuplePatField::Dotdot(span) => span, + } + } +} + +impl<'a> TuplePatField<'a> { + fn is_dotdot(&self) -> bool { + match self { + TuplePatField::Pat(pat) => matches!(pat.kind, ast::PatKind::Rest), + TuplePatField::Dotdot(_) => true, + } + } +} + +pub(crate) fn can_be_overflowed_pat( + context: &RewriteContext<'_>, + pat: &TuplePatField<'_>, + len: usize, +) -> bool { + match *pat { + TuplePatField::Pat(pat) => match pat.kind { + ast::PatKind::Path(..) + | ast::PatKind::Tuple(..) + | ast::PatKind::Struct(..) + | ast::PatKind::TupleStruct(..) => context.use_block_indent() && len == 1, + ast::PatKind::Ref(ref p, _) | ast::PatKind::Box(ref p) => { + can_be_overflowed_pat(context, &TuplePatField::Pat(p), len) + } + ast::PatKind::Lit(ref expr) => can_be_overflowed_expr(context, expr, len), + _ => false, + }, + TuplePatField::Dotdot(..) => false, + } +} + +fn rewrite_tuple_pat( + pats: &[ptr::P], + path_str: Option, + span: Span, + context: &RewriteContext<'_>, + shape: Shape, +) -> Option { + if pats.is_empty() { + return Some(format!("{}()", path_str.unwrap_or_default())); + } + let mut pat_vec: Vec<_> = pats.iter().map(TuplePatField::Pat).collect(); + + let wildcard_suffix_len = count_wildcard_suffix_len(context, &pat_vec, span, shape); + let (pat_vec, span) = if context.config.condense_wildcard_suffixes() && wildcard_suffix_len >= 2 + { + let new_item_count = 1 + pat_vec.len() - wildcard_suffix_len; + let sp = pat_vec[new_item_count - 1].span(); + let snippet = context.snippet(sp); + let lo = sp.lo() + BytePos(snippet.find_uncommented("_").unwrap() as u32); + pat_vec[new_item_count - 1] = TuplePatField::Dotdot(mk_sp_lo_plus_one(lo)); + ( + &pat_vec[..new_item_count], + mk_sp(span.lo(), lo + BytePos(1)), + ) + } else { + (&pat_vec[..], span) + }; + + let is_last_pat_dotdot = pat_vec.last().map_or(false, |p| p.is_dotdot()); + let add_comma = path_str.is_none() && pat_vec.len() == 1 && !is_last_pat_dotdot; + let path_str = path_str.unwrap_or_default(); + + overflow::rewrite_with_parens( + context, + &path_str, + pat_vec.iter(), + shape, + span, + context.config.max_width(), + if add_comma { + Some(SeparatorTactic::Always) + } else { + None + }, + ) +} + +fn count_wildcard_suffix_len( + context: &RewriteContext<'_>, + patterns: &[TuplePatField<'_>], + span: Span, + shape: Shape, +) -> usize { + let mut suffix_len = 0; + + let items: Vec<_> = itemize_list( + context.snippet_provider, + patterns.iter(), + ")", + ",", + |item| item.span().lo(), + |item| item.span().hi(), + |item| item.rewrite(context, shape), + context.snippet_provider.span_after(span, "("), + span.hi() - BytePos(1), + false, + ) + .collect(); + + for item in items + .iter() + .rev() + .take_while(|i| matches!(i.item, Some(ref internal_string) if internal_string == "_")) + { + suffix_len += 1; + + if item.has_comment() { + break; + } + } + + suffix_len +} diff --git a/src/release_channel.rs b/src/release_channel.rs new file mode 100644 index 000000000000..948247b3c970 --- /dev/null +++ b/src/release_channel.rs @@ -0,0 +1,16 @@ +/// Checks if we're in a nightly build. +/// +/// The environment variable `CFG_RELEASE_CHANNEL` is set during the rustc bootstrap +/// to "stable", "beta", or "nightly" depending on what toolchain is being built. +/// If we are being built as part of the stable or beta toolchains, we want +/// to disable unstable configuration options. +/// +/// If we're being built by cargo (e.g., `cargo +nightly install rustfmt-nightly`), +/// `CFG_RELEASE_CHANNEL` is not set. As we only support being built against the +/// nightly compiler when installed from crates.io, default to nightly mode. +#[macro_export] +macro_rules! is_nightly_channel { + () => { + option_env!("CFG_RELEASE_CHANNEL").map_or(true, |c| c == "nightly" || c == "dev") + }; +} diff --git a/src/reorder.rs b/src/reorder.rs new file mode 100644 index 000000000000..3bddf4c1b6a4 --- /dev/null +++ b/src/reorder.rs @@ -0,0 +1,330 @@ +//! Reorder items. +//! +//! `mod`, `extern crate` and `use` declarations are reordered in alphabetical +//! order. Trait items are reordered in pre-determined order (associated types +//! and constants comes before methods). + +// FIXME(#2455): Reorder trait items. + +use std::cmp::{Ord, Ordering}; + +use rustc_ast::{ast, attr}; +use rustc_span::{symbol::sym, Span}; + +use crate::config::{Config, GroupImportsTactic}; +use crate::imports::{normalize_use_trees_with_granularity, UseSegmentKind, UseTree}; +use crate::items::{is_mod_decl, rewrite_extern_crate, rewrite_mod}; +use crate::lists::{itemize_list, write_list, ListFormatting, ListItem}; +use crate::rewrite::RewriteContext; +use crate::shape::Shape; +use crate::source_map::LineRangeUtils; +use crate::spanned::Spanned; +use crate::utils::{contains_skip, mk_sp}; +use crate::visitor::FmtVisitor; + +/// Choose the ordering between the given two items. +fn compare_items(a: &ast::Item, b: &ast::Item) -> Ordering { + match (&a.kind, &b.kind) { + (&ast::ItemKind::Mod(..), &ast::ItemKind::Mod(..)) => { + a.ident.as_str().cmp(b.ident.as_str()) + } + (&ast::ItemKind::ExternCrate(ref a_name), &ast::ItemKind::ExternCrate(ref b_name)) => { + // `extern crate foo as bar;` + // ^^^ Comparing this. + let a_orig_name = a_name.unwrap_or(a.ident.name); + let b_orig_name = b_name.unwrap_or(b.ident.name); + let result = a_orig_name.as_str().cmp(b_orig_name.as_str()); + if result != Ordering::Equal { + return result; + } + + // `extern crate foo as bar;` + // ^^^ Comparing this. + match (a_name, b_name) { + (Some(..), None) => Ordering::Greater, + (None, Some(..)) => Ordering::Less, + (None, None) => Ordering::Equal, + (Some(..), Some(..)) => a.ident.as_str().cmp(b.ident.as_str()), + } + } + _ => unreachable!(), + } +} + +fn wrap_reorderable_items( + context: &RewriteContext<'_>, + list_items: &[ListItem], + shape: Shape, +) -> Option { + let fmt = ListFormatting::new(shape, context.config) + .separator("") + .align_comments(false); + write_list(list_items, &fmt) +} + +fn rewrite_reorderable_item( + context: &RewriteContext<'_>, + item: &ast::Item, + shape: Shape, +) -> Option { + match item.kind { + ast::ItemKind::ExternCrate(..) => rewrite_extern_crate(context, item, shape), + ast::ItemKind::Mod(..) => rewrite_mod(context, item, shape), + _ => None, + } +} + +/// Rewrite a list of items with reordering and/or regrouping. Every item +/// in `items` must have the same `ast::ItemKind`. Whether reordering, regrouping, +/// or both are done is determined from the `context`. +fn rewrite_reorderable_or_regroupable_items( + context: &RewriteContext<'_>, + reorderable_items: &[&ast::Item], + shape: Shape, + span: Span, +) -> Option { + match reorderable_items[0].kind { + // FIXME: Remove duplicated code. + ast::ItemKind::Use(..) => { + let mut normalized_items: Vec<_> = reorderable_items + .iter() + .filter_map(|item| UseTree::from_ast_with_normalization(context, item)) + .collect(); + let cloned = normalized_items.clone(); + // Add comments before merging. + let list_items = itemize_list( + context.snippet_provider, + cloned.iter(), + "", + ";", + |item| item.span().lo(), + |item| item.span().hi(), + |_item| Some("".to_owned()), + span.lo(), + span.hi(), + false, + ); + for (item, list_item) in normalized_items.iter_mut().zip(list_items) { + item.list_item = Some(list_item.clone()); + } + normalized_items = normalize_use_trees_with_granularity( + normalized_items, + context.config.imports_granularity(), + ); + + let mut regrouped_items = match context.config.group_imports() { + GroupImportsTactic::Preserve | GroupImportsTactic::One => { + vec![normalized_items] + } + GroupImportsTactic::StdExternalCrate => group_imports(normalized_items), + }; + + if context.config.reorder_imports() { + regrouped_items.iter_mut().for_each(|items| items.sort()) + } + + // 4 = "use ", 1 = ";" + let nested_shape = shape.offset_left(4)?.sub_width(1)?; + let item_vec: Vec<_> = regrouped_items + .into_iter() + .filter(|use_group| !use_group.is_empty()) + .map(|use_group| { + let item_vec: Vec<_> = use_group + .into_iter() + .map(|use_tree| ListItem { + item: use_tree.rewrite_top_level(context, nested_shape), + ..use_tree.list_item.unwrap_or_else(ListItem::empty) + }) + .collect(); + wrap_reorderable_items(context, &item_vec, nested_shape) + }) + .collect::>>()?; + + let join_string = format!("\n\n{}", shape.indent.to_string(context.config)); + Some(item_vec.join(&join_string)) + } + _ => { + let list_items = itemize_list( + context.snippet_provider, + reorderable_items.iter(), + "", + ";", + |item| item.span().lo(), + |item| item.span().hi(), + |item| rewrite_reorderable_item(context, item, shape), + span.lo(), + span.hi(), + false, + ); + + let mut item_pair_vec: Vec<_> = list_items.zip(reorderable_items.iter()).collect(); + item_pair_vec.sort_by(|a, b| compare_items(a.1, b.1)); + let item_vec: Vec<_> = item_pair_vec.into_iter().map(|pair| pair.0).collect(); + + wrap_reorderable_items(context, &item_vec, shape) + } + } +} + +fn contains_macro_use_attr(item: &ast::Item) -> bool { + attr::contains_name(&item.attrs, sym::macro_use) +} + +/// Divides imports into three groups, corresponding to standard, external +/// and local imports. Sorts each subgroup. +fn group_imports(uts: Vec) -> Vec> { + let mut std_imports = Vec::new(); + let mut external_imports = Vec::new(); + let mut local_imports = Vec::new(); + + for ut in uts.into_iter() { + if ut.path.is_empty() { + external_imports.push(ut); + continue; + } + match &ut.path[0].kind { + UseSegmentKind::Ident(id, _) => match id.as_ref() { + "std" | "alloc" | "core" => std_imports.push(ut), + _ => external_imports.push(ut), + }, + UseSegmentKind::Slf(_) | UseSegmentKind::Super(_) | UseSegmentKind::Crate(_) => { + local_imports.push(ut) + } + // These are probably illegal here + UseSegmentKind::Glob | UseSegmentKind::List(_) => external_imports.push(ut), + } + } + + vec![std_imports, external_imports, local_imports] +} + +/// A simplified version of `ast::ItemKind`. +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +enum ReorderableItemKind { + ExternCrate, + Mod, + Use, + /// An item that cannot be reordered. Either has an unreorderable item kind + /// or an `macro_use` attribute. + Other, +} + +impl ReorderableItemKind { + fn from(item: &ast::Item) -> Self { + match item.kind { + _ if contains_macro_use_attr(item) | contains_skip(&item.attrs) => { + ReorderableItemKind::Other + } + ast::ItemKind::ExternCrate(..) => ReorderableItemKind::ExternCrate, + ast::ItemKind::Mod(..) if is_mod_decl(item) => ReorderableItemKind::Mod, + ast::ItemKind::Use(..) => ReorderableItemKind::Use, + _ => ReorderableItemKind::Other, + } + } + + fn is_same_item_kind(self, item: &ast::Item) -> bool { + ReorderableItemKind::from(item) == self + } + + fn is_reorderable(self, config: &Config) -> bool { + match self { + ReorderableItemKind::ExternCrate => config.reorder_imports(), + ReorderableItemKind::Mod => config.reorder_modules(), + ReorderableItemKind::Use => config.reorder_imports(), + ReorderableItemKind::Other => false, + } + } + + fn is_regroupable(self, config: &Config) -> bool { + match self { + ReorderableItemKind::ExternCrate + | ReorderableItemKind::Mod + | ReorderableItemKind::Other => false, + ReorderableItemKind::Use => config.group_imports() != GroupImportsTactic::Preserve, + } + } + + fn in_group(self, config: &Config) -> bool { + match self { + ReorderableItemKind::ExternCrate | ReorderableItemKind::Mod => true, + ReorderableItemKind::Use => config.group_imports() == GroupImportsTactic::Preserve, + ReorderableItemKind::Other => false, + } + } +} + +impl<'b, 'a: 'b> FmtVisitor<'a> { + /// Format items with the same item kind and reorder them, regroup them, or + /// both. If `in_group` is `true`, then the items separated by an empty line + /// will not be reordered together. + fn walk_reorderable_or_regroupable_items( + &mut self, + items: &[&ast::Item], + item_kind: ReorderableItemKind, + in_group: bool, + ) -> usize { + let mut last = self.parse_sess.lookup_line_range(items[0].span()); + let item_length = items + .iter() + .take_while(|ppi| { + item_kind.is_same_item_kind(&***ppi) + && (!in_group || { + let current = self.parse_sess.lookup_line_range(ppi.span()); + let in_same_group = current.lo < last.hi + 2; + last = current; + in_same_group + }) + }) + .count(); + let items = &items[..item_length]; + + let at_least_one_in_file_lines = items + .iter() + .any(|item| !out_of_file_lines_range!(self, item.span)); + + if at_least_one_in_file_lines && !items.is_empty() { + let lo = items.first().unwrap().span().lo(); + let hi = items.last().unwrap().span().hi(); + let span = mk_sp(lo, hi); + let rw = rewrite_reorderable_or_regroupable_items( + &self.get_context(), + items, + self.shape(), + span, + ); + self.push_rewrite(span, rw); + } else { + for item in items { + self.push_rewrite(item.span, None); + } + } + + item_length + } + + /// Visits and format the given items. Items are reordered If they are + /// consecutive and reorderable. + pub(crate) fn visit_items_with_reordering(&mut self, mut items: &[&ast::Item]) { + while !items.is_empty() { + // If the next item is a `use`, `extern crate` or `mod`, then extract it and any + // subsequent items that have the same item kind to be reordered within + // `walk_reorderable_items`. Otherwise, just format the next item for output. + let item_kind = ReorderableItemKind::from(items[0]); + if item_kind.is_reorderable(self.config) || item_kind.is_regroupable(self.config) { + let visited_items_num = self.walk_reorderable_or_regroupable_items( + items, + item_kind, + item_kind.in_group(self.config), + ); + let (_, rest) = items.split_at(visited_items_num); + items = rest; + } else { + // Reaching here means items were not reordered. There must be at least + // one item left in `items`, so calling `unwrap()` here is safe. + let (item, rest) = items.split_first().unwrap(); + self.visit_item(item); + items = rest; + } + } + } +} diff --git a/src/rewrite.rs b/src/rewrite.rs new file mode 100644 index 000000000000..4a3bd129d16f --- /dev/null +++ b/src/rewrite.rs @@ -0,0 +1,98 @@ +// A generic trait to abstract the rewriting of an element (of the AST). + +use std::cell::{Cell, RefCell}; +use std::rc::Rc; + +use rustc_ast::ptr; +use rustc_span::Span; + +use crate::config::{Config, IndentStyle}; +use crate::parse::session::ParseSess; +use crate::shape::Shape; +use crate::skip::SkipContext; +use crate::visitor::SnippetProvider; +use crate::FormatReport; + +pub(crate) trait Rewrite { + /// Rewrite self into shape. + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option; +} + +impl Rewrite for ptr::P { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + (**self).rewrite(context, shape) + } +} + +#[derive(Clone)] +pub(crate) struct RewriteContext<'a> { + pub(crate) parse_sess: &'a ParseSess, + pub(crate) config: &'a Config, + pub(crate) inside_macro: Rc>, + // Force block indent style even if we are using visual indent style. + pub(crate) use_block: Cell, + // When `is_if_else_block` is true, unindent the comment on top + // of the `else` or `else if`. + pub(crate) is_if_else_block: Cell, + // When rewriting chain, veto going multi line except the last element + pub(crate) force_one_line_chain: Cell, + pub(crate) snippet_provider: &'a SnippetProvider, + // Used for `format_snippet` + pub(crate) macro_rewrite_failure: Cell, + pub(crate) is_macro_def: bool, + pub(crate) report: FormatReport, + pub(crate) skip_context: SkipContext, + pub(crate) skipped_range: Rc>>, +} + +pub(crate) struct InsideMacroGuard { + is_nested_macro_context: bool, + inside_macro_ref: Rc>, +} + +impl InsideMacroGuard { + pub(crate) fn is_nested(&self) -> bool { + self.is_nested_macro_context + } +} + +impl Drop for InsideMacroGuard { + fn drop(&mut self) { + self.inside_macro_ref.replace(self.is_nested_macro_context); + } +} + +impl<'a> RewriteContext<'a> { + pub(crate) fn snippet(&self, span: Span) -> &str { + self.snippet_provider.span_to_snippet(span).unwrap() + } + + /// Returns `true` if we should use block indent style for rewriting function call. + pub(crate) fn use_block_indent(&self) -> bool { + self.config.indent_style() == IndentStyle::Block || self.use_block.get() + } + + pub(crate) fn budget(&self, used_width: usize) -> usize { + self.config.max_width().saturating_sub(used_width) + } + + pub(crate) fn inside_macro(&self) -> bool { + self.inside_macro.get() + } + + pub(crate) fn enter_macro(&self) -> InsideMacroGuard { + let is_nested_macro_context = self.inside_macro.replace(true); + InsideMacroGuard { + is_nested_macro_context, + inside_macro_ref: self.inside_macro.clone(), + } + } + + pub(crate) fn leave_macro(&self) { + self.inside_macro.replace(false); + } + + pub(crate) fn is_if_else_block(&self) -> bool { + self.is_if_else_block.get() + } +} diff --git a/src/rustfmt_diff.rs b/src/rustfmt_diff.rs new file mode 100644 index 000000000000..1724a0f87bf7 --- /dev/null +++ b/src/rustfmt_diff.rs @@ -0,0 +1,400 @@ +use std::collections::VecDeque; +use std::fmt; +use std::io; +use std::io::Write; + +use crate::config::{Color, Config, Verbosity}; + +#[derive(Debug, PartialEq)] +pub(crate) enum DiffLine { + Context(String), + Expected(String), + Resulting(String), +} + +#[derive(Debug, PartialEq)] +pub(crate) struct Mismatch { + /// The line number in the formatted version. + pub(crate) line_number: u32, + /// The line number in the original version. + pub(crate) line_number_orig: u32, + /// The set of lines (context and old/new) in the mismatch. + pub(crate) lines: Vec, +} + +impl Mismatch { + fn new(line_number: u32, line_number_orig: u32) -> Mismatch { + Mismatch { + line_number, + line_number_orig, + lines: Vec::new(), + } + } +} + +/// A single span of changed lines, with 0 or more removed lines +/// and a vector of 0 or more inserted lines. +#[derive(Debug, PartialEq, Eq)] +pub struct ModifiedChunk { + /// The first to be removed from the original text + pub line_number_orig: u32, + /// The number of lines which have been replaced + pub lines_removed: u32, + /// The new lines + pub lines: Vec, +} + +/// Set of changed sections of a file. +#[derive(Debug, PartialEq, Eq)] +pub struct ModifiedLines { + /// The set of changed chunks. + pub chunks: Vec, +} + +impl From> for ModifiedLines { + fn from(mismatches: Vec) -> ModifiedLines { + let chunks = mismatches.into_iter().map(|mismatch| { + let lines = mismatch.lines.iter(); + let num_removed = lines + .filter(|line| matches!(line, DiffLine::Resulting(_))) + .count(); + + let new_lines = mismatch.lines.into_iter().filter_map(|line| match line { + DiffLine::Context(_) | DiffLine::Resulting(_) => None, + DiffLine::Expected(str) => Some(str), + }); + + ModifiedChunk { + line_number_orig: mismatch.line_number_orig, + lines_removed: num_removed as u32, + lines: new_lines.collect(), + } + }); + + ModifiedLines { + chunks: chunks.collect(), + } + } +} + +// Converts a `Mismatch` into a serialized form, which just includes +// enough information to modify the original file. +// Each section starts with a line with three integers, space separated: +// lineno num_removed num_added +// followed by (`num_added`) lines of added text. The line numbers are +// relative to the original file. +impl fmt::Display for ModifiedLines { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for chunk in &self.chunks { + writeln!( + f, + "{} {} {}", + chunk.line_number_orig, + chunk.lines_removed, + chunk.lines.len() + )?; + + for line in &chunk.lines { + writeln!(f, "{}", line)?; + } + } + + Ok(()) + } +} + +// Allows to convert `Display`ed `ModifiedLines` back to the structural data. +impl std::str::FromStr for ModifiedLines { + type Err = (); + + fn from_str(s: &str) -> Result { + let mut chunks = vec![]; + + let mut lines = s.lines(); + while let Some(header) = lines.next() { + let mut header = header.split_whitespace(); + let (orig, rem, new_lines) = match (header.next(), header.next(), header.next()) { + (Some(orig), Some(removed), Some(added)) => (orig, removed, added), + _ => return Err(()), + }; + let (orig, rem, new_lines): (u32, u32, usize) = + match (orig.parse(), rem.parse(), new_lines.parse()) { + (Ok(a), Ok(b), Ok(c)) => (a, b, c), + _ => return Err(()), + }; + let lines = lines.by_ref().take(new_lines); + let lines: Vec<_> = lines.map(ToOwned::to_owned).collect(); + if lines.len() != new_lines { + return Err(()); + } + + chunks.push(ModifiedChunk { + line_number_orig: orig, + lines_removed: rem, + lines, + }); + } + + Ok(ModifiedLines { chunks }) + } +} + +// This struct handles writing output to stdout and abstracts away the logic +// of printing in color, if it's possible in the executing environment. +pub(crate) struct OutputWriter { + terminal: Option>>, +} + +impl OutputWriter { + // Create a new OutputWriter instance based on the caller's preference + // for colorized output and the capabilities of the terminal. + pub(crate) fn new(color: Color) -> Self { + if let Some(t) = term::stdout() { + if color.use_colored_tty() && t.supports_color() { + return OutputWriter { terminal: Some(t) }; + } + } + OutputWriter { terminal: None } + } + + // Write output in the optionally specified color. The output is written + // in the specified color if this OutputWriter instance contains a + // Terminal in its `terminal` field. + pub(crate) fn writeln(&mut self, msg: &str, color: Option) { + match &mut self.terminal { + Some(ref mut t) => { + if let Some(color) = color { + t.fg(color).unwrap(); + } + writeln!(t, "{}", msg).unwrap(); + if color.is_some() { + t.reset().unwrap(); + } + } + None => println!("{}", msg), + } + } +} + +// Produces a diff between the expected output and actual output of rustfmt. +pub(crate) fn make_diff(expected: &str, actual: &str, context_size: usize) -> Vec { + let mut line_number = 1; + let mut line_number_orig = 1; + let mut context_queue: VecDeque<&str> = VecDeque::with_capacity(context_size); + let mut lines_since_mismatch = context_size + 1; + let mut results = Vec::new(); + let mut mismatch = Mismatch::new(0, 0); + + for result in diff::lines(expected, actual) { + match result { + diff::Result::Left(str) => { + if lines_since_mismatch >= context_size && lines_since_mismatch > 0 { + results.push(mismatch); + mismatch = Mismatch::new( + line_number - context_queue.len() as u32, + line_number_orig - context_queue.len() as u32, + ); + } + + while let Some(line) = context_queue.pop_front() { + mismatch.lines.push(DiffLine::Context(line.to_owned())); + } + + mismatch.lines.push(DiffLine::Resulting(str.to_owned())); + line_number_orig += 1; + lines_since_mismatch = 0; + } + diff::Result::Right(str) => { + if lines_since_mismatch >= context_size && lines_since_mismatch > 0 { + results.push(mismatch); + mismatch = Mismatch::new( + line_number - context_queue.len() as u32, + line_number_orig - context_queue.len() as u32, + ); + } + + while let Some(line) = context_queue.pop_front() { + mismatch.lines.push(DiffLine::Context(line.to_owned())); + } + + mismatch.lines.push(DiffLine::Expected(str.to_owned())); + line_number += 1; + lines_since_mismatch = 0; + } + diff::Result::Both(str, _) => { + if context_queue.len() >= context_size { + let _ = context_queue.pop_front(); + } + + if lines_since_mismatch < context_size { + mismatch.lines.push(DiffLine::Context(str.to_owned())); + } else if context_size > 0 { + context_queue.push_back(str); + } + + line_number += 1; + line_number_orig += 1; + lines_since_mismatch += 1; + } + } + } + + results.push(mismatch); + results.remove(0); + + results +} + +pub(crate) fn print_diff(diff: Vec, get_section_title: F, config: &Config) +where + F: Fn(u32) -> String, +{ + let color = config.color(); + let line_terminator = if config.verbose() == Verbosity::Verbose { + "⏎" + } else { + "" + }; + + let mut writer = OutputWriter::new(color); + + for mismatch in diff { + let title = get_section_title(mismatch.line_number_orig); + writer.writeln(&title, None); + + for line in mismatch.lines { + match line { + DiffLine::Context(ref str) => { + writer.writeln(&format!(" {}{}", str, line_terminator), None) + } + DiffLine::Expected(ref str) => writer.writeln( + &format!("+{}{}", str, line_terminator), + Some(term::color::GREEN), + ), + DiffLine::Resulting(ref str) => writer.writeln( + &format!("-{}{}", str, line_terminator), + Some(term::color::RED), + ), + } + } + } +} + +#[cfg(test)] +mod test { + use super::DiffLine::*; + use super::{make_diff, Mismatch}; + use super::{ModifiedChunk, ModifiedLines}; + + #[test] + fn diff_simple() { + let src = "one\ntwo\nthree\nfour\nfive\n"; + let dest = "one\ntwo\ntrois\nfour\nfive\n"; + let diff = make_diff(src, dest, 1); + assert_eq!( + diff, + vec![Mismatch { + line_number: 2, + line_number_orig: 2, + lines: vec![ + Context("two".to_owned()), + Resulting("three".to_owned()), + Expected("trois".to_owned()), + Context("four".to_owned()), + ], + }] + ); + } + + #[test] + fn diff_simple2() { + let src = "one\ntwo\nthree\nfour\nfive\nsix\nseven\n"; + let dest = "one\ntwo\ntrois\nfour\ncinq\nsix\nseven\n"; + let diff = make_diff(src, dest, 1); + assert_eq!( + diff, + vec![ + Mismatch { + line_number: 2, + line_number_orig: 2, + lines: vec![ + Context("two".to_owned()), + Resulting("three".to_owned()), + Expected("trois".to_owned()), + Context("four".to_owned()), + ], + }, + Mismatch { + line_number: 5, + line_number_orig: 5, + lines: vec![ + Resulting("five".to_owned()), + Expected("cinq".to_owned()), + Context("six".to_owned()), + ], + }, + ] + ); + } + + #[test] + fn diff_zerocontext() { + let src = "one\ntwo\nthree\nfour\nfive\n"; + let dest = "one\ntwo\ntrois\nfour\nfive\n"; + let diff = make_diff(src, dest, 0); + assert_eq!( + diff, + vec![Mismatch { + line_number: 3, + line_number_orig: 3, + lines: vec![Resulting("three".to_owned()), Expected("trois".to_owned())], + }] + ); + } + + #[test] + fn diff_trailing_newline() { + let src = "one\ntwo\nthree\nfour\nfive"; + let dest = "one\ntwo\nthree\nfour\nfive\n"; + let diff = make_diff(src, dest, 1); + assert_eq!( + diff, + vec![Mismatch { + line_number: 5, + line_number_orig: 5, + lines: vec![Context("five".to_owned()), Expected("".to_owned())], + }] + ); + } + + #[test] + fn modified_lines_from_str() { + use std::str::FromStr; + + let src = "1 6 2\nfn some() {}\nfn main() {}\n25 3 1\n struct Test {}"; + let lines = ModifiedLines::from_str(src).unwrap(); + assert_eq!( + lines, + ModifiedLines { + chunks: vec![ + ModifiedChunk { + line_number_orig: 1, + lines_removed: 6, + lines: vec!["fn some() {}".to_owned(), "fn main() {}".to_owned(),] + }, + ModifiedChunk { + line_number_orig: 25, + lines_removed: 3, + lines: vec![" struct Test {}".to_owned()] + } + ] + } + ); + + let src = "1 5 3"; + assert_eq!(ModifiedLines::from_str(src), Err(())); + + let src = "1 5 3\na\nb"; + assert_eq!(ModifiedLines::from_str(src), Err(())); + } +} diff --git a/src/shape.rs b/src/shape.rs new file mode 100644 index 000000000000..4376fd12b526 --- /dev/null +++ b/src/shape.rs @@ -0,0 +1,373 @@ +use std::borrow::Cow; +use std::cmp::min; +use std::ops::{Add, Sub}; + +use crate::Config; + +#[derive(Copy, Clone, Debug)] +pub(crate) struct Indent { + // Width of the block indent, in characters. Must be a multiple of + // Config::tab_spaces. + pub(crate) block_indent: usize, + // Alignment in characters. + pub(crate) alignment: usize, +} + +// INDENT_BUFFER.len() = 81 +const INDENT_BUFFER_LEN: usize = 80; +const INDENT_BUFFER: &str = + "\n "; + +impl Indent { + pub(crate) fn new(block_indent: usize, alignment: usize) -> Indent { + Indent { + block_indent, + alignment, + } + } + + pub(crate) fn from_width(config: &Config, width: usize) -> Indent { + if config.hard_tabs() { + let tab_num = width / config.tab_spaces(); + let alignment = width % config.tab_spaces(); + Indent::new(config.tab_spaces() * tab_num, alignment) + } else { + Indent::new(width, 0) + } + } + + pub(crate) fn empty() -> Indent { + Indent::new(0, 0) + } + + pub(crate) fn block_only(&self) -> Indent { + Indent { + block_indent: self.block_indent, + alignment: 0, + } + } + + pub(crate) fn block_indent(mut self, config: &Config) -> Indent { + self.block_indent += config.tab_spaces(); + self + } + + pub(crate) fn block_unindent(mut self, config: &Config) -> Indent { + if self.block_indent < config.tab_spaces() { + Indent::new(self.block_indent, 0) + } else { + self.block_indent -= config.tab_spaces(); + self + } + } + + pub(crate) fn width(&self) -> usize { + self.block_indent + self.alignment + } + + pub(crate) fn to_string(&self, config: &Config) -> Cow<'static, str> { + self.to_string_inner(config, 1) + } + + pub(crate) fn to_string_with_newline(&self, config: &Config) -> Cow<'static, str> { + self.to_string_inner(config, 0) + } + + fn to_string_inner(&self, config: &Config, offset: usize) -> Cow<'static, str> { + let (num_tabs, num_spaces) = if config.hard_tabs() { + (self.block_indent / config.tab_spaces(), self.alignment) + } else { + (0, self.width()) + }; + let num_chars = num_tabs + num_spaces; + if num_tabs == 0 && num_chars + offset <= INDENT_BUFFER_LEN { + Cow::from(&INDENT_BUFFER[offset..=num_chars]) + } else { + let mut indent = String::with_capacity(num_chars + if offset == 0 { 1 } else { 0 }); + if offset == 0 { + indent.push('\n'); + } + for _ in 0..num_tabs { + indent.push('\t') + } + for _ in 0..num_spaces { + indent.push(' ') + } + Cow::from(indent) + } + } +} + +impl Add for Indent { + type Output = Indent; + + fn add(self, rhs: Indent) -> Indent { + Indent { + block_indent: self.block_indent + rhs.block_indent, + alignment: self.alignment + rhs.alignment, + } + } +} + +impl Sub for Indent { + type Output = Indent; + + fn sub(self, rhs: Indent) -> Indent { + Indent::new( + self.block_indent - rhs.block_indent, + self.alignment - rhs.alignment, + ) + } +} + +impl Add for Indent { + type Output = Indent; + + fn add(self, rhs: usize) -> Indent { + Indent::new(self.block_indent, self.alignment + rhs) + } +} + +impl Sub for Indent { + type Output = Indent; + + fn sub(self, rhs: usize) -> Indent { + Indent::new(self.block_indent, self.alignment - rhs) + } +} + +// 8096 is close enough to infinite for rustfmt. +const INFINITE_SHAPE_WIDTH: usize = 8096; + +#[derive(Copy, Clone, Debug)] +pub(crate) struct Shape { + pub(crate) width: usize, + // The current indentation of code. + pub(crate) indent: Indent, + // Indentation + any already emitted text on the first line of the current + // statement. + pub(crate) offset: usize, +} + +impl Shape { + /// `indent` is the indentation of the first line. The next lines + /// should begin with at least `indent` spaces (except backwards + /// indentation). The first line should not begin with indentation. + /// `width` is the maximum number of characters on the last line + /// (excluding `indent`). The width of other lines is not limited by + /// `width`. + /// Note that in reality, we sometimes use width for lines other than the + /// last (i.e., we are conservative). + // .......*-------* + // | | + // | *-* + // *-----| + // |<------------>| max width + // |<---->| indent + // |<--->| width + pub(crate) fn legacy(width: usize, indent: Indent) -> Shape { + Shape { + width, + indent, + offset: indent.alignment, + } + } + + pub(crate) fn indented(indent: Indent, config: &Config) -> Shape { + Shape { + width: config.max_width().saturating_sub(indent.width()), + indent, + offset: indent.alignment, + } + } + + pub(crate) fn with_max_width(&self, config: &Config) -> Shape { + Shape { + width: config.max_width().saturating_sub(self.indent.width()), + ..*self + } + } + + pub(crate) fn visual_indent(&self, extra_width: usize) -> Shape { + let alignment = self.offset + extra_width; + Shape { + width: self.width, + indent: Indent::new(self.indent.block_indent, alignment), + offset: alignment, + } + } + + pub(crate) fn block_indent(&self, extra_width: usize) -> Shape { + if self.indent.alignment == 0 { + Shape { + width: self.width, + indent: Indent::new(self.indent.block_indent + extra_width, 0), + offset: 0, + } + } else { + Shape { + width: self.width, + indent: self.indent + extra_width, + offset: self.indent.alignment + extra_width, + } + } + } + + pub(crate) fn block_left(&self, width: usize) -> Option { + self.block_indent(width).sub_width(width) + } + + pub(crate) fn add_offset(&self, extra_width: usize) -> Shape { + Shape { + offset: self.offset + extra_width, + ..*self + } + } + + pub(crate) fn block(&self) -> Shape { + Shape { + indent: self.indent.block_only(), + ..*self + } + } + + pub(crate) fn saturating_sub_width(&self, width: usize) -> Shape { + self.sub_width(width).unwrap_or(Shape { width: 0, ..*self }) + } + + pub(crate) fn sub_width(&self, width: usize) -> Option { + Some(Shape { + width: self.width.checked_sub(width)?, + ..*self + }) + } + + pub(crate) fn shrink_left(&self, width: usize) -> Option { + Some(Shape { + width: self.width.checked_sub(width)?, + indent: self.indent + width, + offset: self.offset + width, + }) + } + + pub(crate) fn offset_left(&self, width: usize) -> Option { + self.add_offset(width).sub_width(width) + } + + pub(crate) fn used_width(&self) -> usize { + self.indent.block_indent + self.offset + } + + pub(crate) fn rhs_overhead(&self, config: &Config) -> usize { + config + .max_width() + .saturating_sub(self.used_width() + self.width) + } + + pub(crate) fn comment(&self, config: &Config) -> Shape { + let width = min( + self.width, + config.comment_width().saturating_sub(self.indent.width()), + ); + Shape { width, ..*self } + } + + pub(crate) fn to_string_with_newline(&self, config: &Config) -> Cow<'static, str> { + let mut offset_indent = self.indent; + offset_indent.alignment = self.offset; + offset_indent.to_string_inner(config, 0) + } + + /// Creates a `Shape` with a virtually infinite width. + pub(crate) fn infinite_width(&self) -> Shape { + Shape { + width: INFINITE_SHAPE_WIDTH, + ..*self + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn indent_add_sub() { + let indent = Indent::new(4, 8) + Indent::new(8, 12); + assert_eq!(12, indent.block_indent); + assert_eq!(20, indent.alignment); + + let indent = indent - Indent::new(4, 4); + assert_eq!(8, indent.block_indent); + assert_eq!(16, indent.alignment); + } + + #[test] + fn indent_add_sub_alignment() { + let indent = Indent::new(4, 8) + 4; + assert_eq!(4, indent.block_indent); + assert_eq!(12, indent.alignment); + + let indent = indent - 4; + assert_eq!(4, indent.block_indent); + assert_eq!(8, indent.alignment); + } + + #[test] + fn indent_to_string_spaces() { + let config = Config::default(); + let indent = Indent::new(4, 8); + + // 12 spaces + assert_eq!(" ", indent.to_string(&config)); + } + + #[test] + fn indent_to_string_hard_tabs() { + let mut config = Config::default(); + config.set().hard_tabs(true); + let indent = Indent::new(8, 4); + + // 2 tabs + 4 spaces + assert_eq!("\t\t ", indent.to_string(&config)); + } + + #[test] + fn shape_visual_indent() { + let config = Config::default(); + let indent = Indent::new(4, 8); + let shape = Shape::legacy(config.max_width(), indent); + let shape = shape.visual_indent(20); + + assert_eq!(config.max_width(), shape.width); + assert_eq!(4, shape.indent.block_indent); + assert_eq!(28, shape.indent.alignment); + assert_eq!(28, shape.offset); + } + + #[test] + fn shape_block_indent_without_alignment() { + let config = Config::default(); + let indent = Indent::new(4, 0); + let shape = Shape::legacy(config.max_width(), indent); + let shape = shape.block_indent(20); + + assert_eq!(config.max_width(), shape.width); + assert_eq!(24, shape.indent.block_indent); + assert_eq!(0, shape.indent.alignment); + assert_eq!(0, shape.offset); + } + + #[test] + fn shape_block_indent_with_alignment() { + let config = Config::default(); + let indent = Indent::new(4, 8); + let shape = Shape::legacy(config.max_width(), indent); + let shape = shape.block_indent(20); + + assert_eq!(config.max_width(), shape.width); + assert_eq!(4, shape.indent.block_indent); + assert_eq!(28, shape.indent.alignment); + assert_eq!(28, shape.offset); + } +} diff --git a/src/skip.rs b/src/skip.rs new file mode 100644 index 000000000000..68f85b2ade48 --- /dev/null +++ b/src/skip.rs @@ -0,0 +1,127 @@ +//! Module that contains skip related stuffs. + +use rustc_ast::ast; +use rustc_ast_pretty::pprust; +use std::collections::HashSet; + +/// Track which blocks of code are to be skipped when formatting. +/// +/// You can update it by: +/// +/// - attributes slice +/// - manually feeding values into the underlying contexts +/// +/// Query this context to know if you need to skip a block. +#[derive(Default, Clone)] +pub(crate) struct SkipContext { + pub(crate) macros: SkipNameContext, + pub(crate) attributes: SkipNameContext, +} + +impl SkipContext { + pub(crate) fn update_with_attrs(&mut self, attrs: &[ast::Attribute]) { + self.macros.extend(get_skip_names("macros", attrs)); + self.attributes.extend(get_skip_names("attributes", attrs)); + } + + pub(crate) fn update(&mut self, other: SkipContext) { + let SkipContext { macros, attributes } = other; + self.macros.update(macros); + self.attributes.update(attributes); + } +} + +/// Track which names to skip. +/// +/// Query this context with a string to know whether to skip it. +#[derive(Clone)] +pub(crate) enum SkipNameContext { + All, + Values(HashSet), +} + +impl Default for SkipNameContext { + fn default() -> Self { + Self::Values(Default::default()) + } +} + +impl Extend for SkipNameContext { + fn extend>(&mut self, iter: T) { + match self { + Self::All => {} + Self::Values(values) => values.extend(iter), + } + } +} + +impl SkipNameContext { + pub(crate) fn update(&mut self, other: Self) { + match (self, other) { + // If we're already skipping everything, nothing more can be added + (Self::All, _) => {} + // If we want to skip all, set it + (this, Self::All) => { + *this = Self::All; + } + // If we have some new values to skip, add them + (Self::Values(existing_values), Self::Values(new_values)) => { + existing_values.extend(new_values) + } + } + } + + pub(crate) fn skip(&self, name: &str) -> bool { + match self { + Self::All => true, + Self::Values(values) => values.contains(name), + } + } + + pub(crate) fn skip_all(&mut self) { + *self = Self::All; + } +} + +static RUSTFMT: &str = "rustfmt"; +static SKIP: &str = "skip"; + +/// Say if you're playing with `rustfmt`'s skip attribute +pub(crate) fn is_skip_attr(segments: &[ast::PathSegment]) -> bool { + if segments.len() < 2 || segments[0].ident.to_string() != RUSTFMT { + return false; + } + match segments.len() { + 2 => segments[1].ident.to_string() == SKIP, + 3 => { + segments[1].ident.to_string() == SKIP + && ["macros", "attributes"] + .iter() + .any(|&n| n == pprust::path_segment_to_string(&segments[2])) + } + _ => false, + } +} + +fn get_skip_names(kind: &str, attrs: &[ast::Attribute]) -> Vec { + let mut skip_names = vec![]; + let path = format!("{}::{}::{}", RUSTFMT, SKIP, kind); + for attr in attrs { + // rustc_ast::ast::Path is implemented partialEq + // but it is designed for segments.len() == 1 + if let ast::AttrKind::Normal(normal) = &attr.kind { + if pprust::path_to_string(&normal.item.path) != path { + continue; + } + } + + if let Some(list) = attr.meta_item_list() { + for nested_meta_item in list { + if let Some(name) = nested_meta_item.ident() { + skip_names.push(name.to_string()); + } + } + } + } + skip_names +} diff --git a/src/source_file.rs b/src/source_file.rs new file mode 100644 index 000000000000..56d4ab400383 --- /dev/null +++ b/src/source_file.rs @@ -0,0 +1,105 @@ +use std::fs; +use std::io::{self, Write}; +use std::path::Path; + +use crate::config::FileName; +use crate::emitter::{self, Emitter}; +use crate::parse::session::ParseSess; +use crate::NewlineStyle; + +#[cfg(test)] +use crate::config::Config; +#[cfg(test)] +use crate::create_emitter; +#[cfg(test)] +use crate::formatting::FileRecord; + +use rustc_data_structures::sync::Lrc; + +// Append a newline to the end of each file. +pub(crate) fn append_newline(s: &mut String) { + s.push('\n'); +} + +#[cfg(test)] +pub(crate) fn write_all_files( + source_file: &[FileRecord], + out: &mut T, + config: &Config, +) -> Result<(), io::Error> +where + T: Write, +{ + let mut emitter = create_emitter(config); + + emitter.emit_header(out)?; + for &(ref filename, ref text) in source_file { + write_file( + None, + filename, + text, + out, + &mut *emitter, + config.newline_style(), + )?; + } + emitter.emit_footer(out)?; + + Ok(()) +} + +pub(crate) fn write_file( + parse_sess: Option<&ParseSess>, + filename: &FileName, + formatted_text: &str, + out: &mut T, + emitter: &mut dyn Emitter, + newline_style: NewlineStyle, +) -> Result +where + T: Write, +{ + fn ensure_real_path(filename: &FileName) -> &Path { + match *filename { + FileName::Real(ref path) => path, + _ => panic!("cannot format `{}` and emit to files", filename), + } + } + + impl From<&FileName> for rustc_span::FileName { + fn from(filename: &FileName) -> rustc_span::FileName { + match filename { + FileName::Real(path) => { + rustc_span::FileName::Real(rustc_span::RealFileName::LocalPath(path.to_owned())) + } + FileName::Stdin => rustc_span::FileName::Custom("stdin".to_owned()), + } + } + } + + // SourceFile's in the SourceMap will always have Unix-style line endings + // See: https://github.com/rust-lang/rustfmt/issues/3850 + // So if the user has explicitly overridden the rustfmt `newline_style` + // config and `filename` is FileName::Real, then we must check the file system + // to get the original file value in order to detect newline_style conflicts. + // Otherwise, parse session is around (cfg(not(test))) and newline_style has been + // left as the default value, then try getting source from the parse session + // source map instead of hitting the file system. This also supports getting + // original text for `FileName::Stdin`. + let original_text = if newline_style != NewlineStyle::Auto && *filename != FileName::Stdin { + Lrc::new(fs::read_to_string(ensure_real_path(filename))?) + } else { + match parse_sess.and_then(|sess| sess.get_original_snippet(filename)) { + Some(ori) => ori, + None => Lrc::new(fs::read_to_string(ensure_real_path(filename))?), + } + }; + + let formatted_file = emitter::FormattedFile { + filename, + original_text: original_text.as_str(), + formatted_text, + }; + + emitter.emit_formatted_file(out, formatted_file) +} diff --git a/src/source_map.rs b/src/source_map.rs new file mode 100644 index 000000000000..76e0d24cf1eb --- /dev/null +++ b/src/source_map.rs @@ -0,0 +1,82 @@ +//! This module contains utilities that work with the `SourceMap` from `libsyntax`/`syntex_syntax`. +//! This includes extension traits and methods for looking up spans and line ranges for AST nodes. + +use rustc_span::{BytePos, Span}; + +use crate::comment::FindUncommented; +use crate::config::file_lines::LineRange; +use crate::visitor::SnippetProvider; + +pub(crate) trait SpanUtils { + fn span_after(&self, original: Span, needle: &str) -> BytePos; + fn span_after_last(&self, original: Span, needle: &str) -> BytePos; + fn span_before(&self, original: Span, needle: &str) -> BytePos; + fn span_before_last(&self, original: Span, needle: &str) -> BytePos; + fn opt_span_after(&self, original: Span, needle: &str) -> Option; + fn opt_span_before(&self, original: Span, needle: &str) -> Option; +} + +pub(crate) trait LineRangeUtils { + /// Returns the `LineRange` that corresponds to `span` in `self`. + /// + /// # Panics + /// + /// Panics if `span` crosses a file boundary, which shouldn't happen. + fn lookup_line_range(&self, span: Span) -> LineRange; +} + +impl SpanUtils for SnippetProvider { + fn span_after(&self, original: Span, needle: &str) -> BytePos { + self.opt_span_after(original, needle).unwrap_or_else(|| { + panic!( + "bad span: `{}`: `{}`", + needle, + self.span_to_snippet(original).unwrap() + ) + }) + } + + fn span_after_last(&self, original: Span, needle: &str) -> BytePos { + let snippet = self.span_to_snippet(original).unwrap(); + let mut offset = 0; + + while let Some(additional_offset) = snippet[offset..].find_uncommented(needle) { + offset += additional_offset + needle.len(); + } + + original.lo() + BytePos(offset as u32) + } + + fn span_before(&self, original: Span, needle: &str) -> BytePos { + self.opt_span_before(original, needle).unwrap_or_else(|| { + panic!( + "bad span: `{}`: `{}`", + needle, + self.span_to_snippet(original).unwrap() + ) + }) + } + + fn span_before_last(&self, original: Span, needle: &str) -> BytePos { + let snippet = self.span_to_snippet(original).unwrap(); + let mut offset = 0; + + while let Some(additional_offset) = snippet[offset..].find_uncommented(needle) { + offset += additional_offset + needle.len(); + } + + original.lo() + BytePos(offset as u32 - 1) + } + + fn opt_span_after(&self, original: Span, needle: &str) -> Option { + self.opt_span_before(original, needle) + .map(|bytepos| bytepos + BytePos(needle.len() as u32)) + } + + fn opt_span_before(&self, original: Span, needle: &str) -> Option { + let snippet = self.span_to_snippet(original)?; + let offset = snippet.find_uncommented(needle)?; + + Some(original.lo() + BytePos(offset as u32)) + } +} diff --git a/src/spanned.rs b/src/spanned.rs new file mode 100644 index 000000000000..2136cfeae1af --- /dev/null +++ b/src/spanned.rs @@ -0,0 +1,199 @@ +use std::cmp::max; + +use rustc_ast::{ast, ptr}; +use rustc_span::{source_map, Span}; + +use crate::macros::MacroArg; +use crate::utils::{mk_sp, outer_attributes}; + +/// Spanned returns a span including attributes, if available. +pub(crate) trait Spanned { + fn span(&self) -> Span; +} + +impl Spanned for ptr::P { + fn span(&self) -> Span { + (**self).span() + } +} + +impl Spanned for source_map::Spanned { + fn span(&self) -> Span { + self.span + } +} + +macro_rules! span_with_attrs_lo_hi { + ($this:ident, $lo:expr, $hi:expr) => {{ + let attrs = outer_attributes(&$this.attrs); + if attrs.is_empty() { + mk_sp($lo, $hi) + } else { + mk_sp(attrs[0].span.lo(), $hi) + } + }}; +} + +macro_rules! span_with_attrs { + ($this:ident) => { + span_with_attrs_lo_hi!($this, $this.span.lo(), $this.span.hi()) + }; +} + +macro_rules! implement_spanned { + ($this:ty) => { + impl Spanned for $this { + fn span(&self) -> Span { + span_with_attrs!(self) + } + } + }; +} + +// Implement `Spanned` for structs with `attrs` field. +implement_spanned!(ast::AssocItem); +implement_spanned!(ast::Expr); +implement_spanned!(ast::ExprField); +implement_spanned!(ast::ForeignItem); +implement_spanned!(ast::Item); +implement_spanned!(ast::Local); + +impl Spanned for ast::Stmt { + fn span(&self) -> Span { + match self.kind { + ast::StmtKind::Local(ref local) => mk_sp(local.span().lo(), self.span.hi()), + ast::StmtKind::Item(ref item) => mk_sp(item.span().lo(), self.span.hi()), + ast::StmtKind::Expr(ref expr) | ast::StmtKind::Semi(ref expr) => { + mk_sp(expr.span().lo(), self.span.hi()) + } + ast::StmtKind::MacCall(ref mac_stmt) => { + if mac_stmt.attrs.is_empty() { + self.span + } else { + mk_sp(mac_stmt.attrs[0].span.lo(), self.span.hi()) + } + } + ast::StmtKind::Empty => self.span, + } + } +} + +impl Spanned for ast::Pat { + fn span(&self) -> Span { + self.span + } +} + +impl Spanned for ast::Ty { + fn span(&self) -> Span { + self.span + } +} + +impl Spanned for ast::Arm { + fn span(&self) -> Span { + let lo = if self.attrs.is_empty() { + self.pat.span.lo() + } else { + self.attrs[0].span.lo() + }; + span_with_attrs_lo_hi!(self, lo, self.body.span.hi()) + } +} + +impl Spanned for ast::Param { + fn span(&self) -> Span { + if crate::items::is_named_param(self) { + mk_sp(crate::items::span_lo_for_param(self), self.ty.span.hi()) + } else { + self.ty.span + } + } +} + +impl Spanned for ast::GenericParam { + fn span(&self) -> Span { + 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() + } else { + self.bounds.last().unwrap().span().hi() + }; + let ty_hi = if let ast::GenericParamKind::Type { + default: Some(ref ty), + } + | ast::GenericParamKind::Const { ref ty, .. } = self.kind + { + ty.span().hi() + } else { + hi + }; + mk_sp(lo, max(hi, ty_hi)) + } +} + +impl Spanned for ast::FieldDef { + fn span(&self) -> Span { + span_with_attrs_lo_hi!(self, self.span.lo(), self.ty.span.hi()) + } +} + +impl Spanned for ast::WherePredicate { + fn span(&self) -> Span { + match *self { + ast::WherePredicate::BoundPredicate(ref p) => p.span, + ast::WherePredicate::RegionPredicate(ref p) => p.span, + ast::WherePredicate::EqPredicate(ref p) => p.span, + } + } +} + +impl Spanned for ast::FnRetTy { + fn span(&self) -> Span { + match *self { + ast::FnRetTy::Default(span) => span, + ast::FnRetTy::Ty(ref ty) => ty.span, + } + } +} + +impl Spanned for ast::GenericArg { + fn span(&self) -> Span { + match *self { + ast::GenericArg::Lifetime(ref lt) => lt.ident.span, + ast::GenericArg::Type(ref ty) => ty.span(), + ast::GenericArg::Const(ref _const) => _const.value.span(), + } + } +} + +impl Spanned for ast::GenericBound { + fn span(&self) -> Span { + match *self { + ast::GenericBound::Trait(ref ptr, _) => ptr.span, + ast::GenericBound::Outlives(ref l) => l.ident.span, + } + } +} + +impl Spanned for MacroArg { + fn span(&self) -> Span { + match *self { + MacroArg::Expr(ref expr) => expr.span(), + MacroArg::Ty(ref ty) => ty.span(), + MacroArg::Pat(ref pat) => pat.span(), + MacroArg::Item(ref item) => item.span(), + MacroArg::Keyword(_, span) => span, + } + } +} + +impl Spanned for ast::NestedMetaItem { + fn span(&self) -> Span { + self.span() + } +} diff --git a/src/stmt.rs b/src/stmt.rs new file mode 100644 index 000000000000..0b3854425ea5 --- /dev/null +++ b/src/stmt.rs @@ -0,0 +1,116 @@ +use rustc_ast::ast; +use rustc_span::Span; + +use crate::comment::recover_comment_removed; +use crate::config::Version; +use crate::expr::{format_expr, ExprType}; +use crate::rewrite::{Rewrite, RewriteContext}; +use crate::shape::Shape; +use crate::source_map::LineRangeUtils; +use crate::spanned::Spanned; +use crate::utils::semicolon_for_stmt; + +pub(crate) struct Stmt<'a> { + inner: &'a ast::Stmt, + is_last: bool, +} + +impl<'a> Spanned for Stmt<'a> { + fn span(&self) -> Span { + self.inner.span() + } +} + +impl<'a> Stmt<'a> { + pub(crate) fn as_ast_node(&self) -> &ast::Stmt { + self.inner + } + + pub(crate) fn to_item(&self) -> Option<&ast::Item> { + match self.inner.kind { + ast::StmtKind::Item(ref item) => Some(&**item), + _ => None, + } + } + + pub(crate) fn from_ast_node(inner: &'a ast::Stmt, is_last: bool) -> Self { + Stmt { inner, is_last } + } + + pub(crate) fn from_ast_nodes(iter: I) -> Vec + where + I: Iterator, + { + let mut result = vec![]; + let mut iter = iter.peekable(); + while iter.peek().is_some() { + result.push(Stmt { + inner: iter.next().unwrap(), + is_last: iter.peek().is_none(), + }) + } + result + } + + pub(crate) fn is_empty(&self) -> bool { + matches!(self.inner.kind, ast::StmtKind::Empty) + } + + fn is_last_expr(&self) -> bool { + if !self.is_last { + return false; + } + + match self.as_ast_node().kind { + ast::StmtKind::Expr(ref expr) => match expr.kind { + ast::ExprKind::Ret(..) | ast::ExprKind::Continue(..) | ast::ExprKind::Break(..) => { + false + } + _ => true, + }, + _ => false, + } + } +} + +impl<'a> Rewrite for Stmt<'a> { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + let expr_type = if context.config.version() == Version::Two && self.is_last_expr() { + ExprType::SubExpression + } else { + ExprType::Statement + }; + format_stmt(context, shape, self.as_ast_node(), expr_type) + } +} + +impl Rewrite for ast::Stmt { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + format_stmt(context, shape, self, ExprType::Statement) + } +} + +fn format_stmt( + context: &RewriteContext<'_>, + shape: Shape, + stmt: &ast::Stmt, + expr_type: ExprType, +) -> Option { + skip_out_of_file_lines_range!(context, stmt.span()); + + let result = match stmt.kind { + ast::StmtKind::Local(ref local) => local.rewrite(context, shape), + ast::StmtKind::Expr(ref ex) | ast::StmtKind::Semi(ref ex) => { + let suffix = if semicolon_for_stmt(context, stmt) { + ";" + } else { + "" + }; + + let shape = shape.sub_width(suffix.len())?; + format_expr(ex, expr_type, context, shape).map(|s| s + suffix) + } + ast::StmtKind::MacCall(..) | ast::StmtKind::Item(..) | ast::StmtKind::Empty => None, + }; + result.and_then(|res| recover_comment_removed(res, stmt.span(), context)) +} diff --git a/src/string.rs b/src/string.rs new file mode 100644 index 000000000000..78b72a50cb2f --- /dev/null +++ b/src/string.rs @@ -0,0 +1,725 @@ +// Format string literals. + +use regex::Regex; +use unicode_categories::UnicodeCategories; +use unicode_segmentation::UnicodeSegmentation; + +use crate::config::Config; +use crate::shape::Shape; +use crate::utils::{unicode_str_width, wrap_str}; + +const MIN_STRING: usize = 10; + +/// Describes the layout of a piece of text. +pub(crate) struct StringFormat<'a> { + /// The opening sequence of characters for the piece of text + pub(crate) opener: &'a str, + /// The closing sequence of characters for the piece of text + pub(crate) closer: &'a str, + /// The opening sequence of characters for a line + pub(crate) line_start: &'a str, + /// The closing sequence of characters for a line + pub(crate) line_end: &'a str, + /// The allocated box to fit the text into + pub(crate) shape: Shape, + /// Trim trailing whitespaces + pub(crate) trim_end: bool, + pub(crate) config: &'a Config, +} + +impl<'a> StringFormat<'a> { + pub(crate) fn new(shape: Shape, config: &'a Config) -> StringFormat<'a> { + StringFormat { + opener: "\"", + closer: "\"", + line_start: " ", + line_end: "\\", + shape, + trim_end: false, + config, + } + } + + /// Returns the maximum number of graphemes that is possible on a line while taking the + /// indentation into account. + /// + /// If we cannot put at least a single character per line, the rewrite won't succeed. + fn max_width_with_indent(&self) -> Option { + Some( + self.shape + .width + .checked_sub(self.opener.len() + self.line_end.len() + 1)? + + 1, + ) + } + + /// Like max_width_with_indent but the indentation is not subtracted. + /// This allows to fit more graphemes from the string on a line when + /// SnippetState::EndWithLineFeed. + fn max_width_without_indent(&self) -> Option { + self.config.max_width().checked_sub(self.line_end.len()) + } +} + +pub(crate) fn rewrite_string<'a>( + orig: &str, + fmt: &StringFormat<'a>, + newline_max_chars: usize, +) -> Option { + let max_width_with_indent = fmt.max_width_with_indent()?; + let max_width_without_indent = fmt.max_width_without_indent()?; + let indent_with_newline = fmt.shape.indent.to_string_with_newline(fmt.config); + let indent_without_newline = fmt.shape.indent.to_string(fmt.config); + + // Strip line breaks. + // With this regex applied, all remaining whitespaces are significant + let strip_line_breaks_re = Regex::new(r"([^\\](\\\\)*)\\[\n\r][[:space:]]*").unwrap(); + let stripped_str = strip_line_breaks_re.replace_all(orig, "$1"); + + let graphemes = UnicodeSegmentation::graphemes(&*stripped_str, false).collect::>(); + + // `cur_start` is the position in `orig` of the start of the current line. + let mut cur_start = 0; + let mut result = String::with_capacity( + stripped_str + .len() + .checked_next_power_of_two() + .unwrap_or(usize::max_value()), + ); + result.push_str(fmt.opener); + + // Snip a line at a time from `stripped_str` until it is used up. Push the snippet + // onto result. + let mut cur_max_width = max_width_with_indent; + let is_bareline_ok = fmt.line_start.is_empty() || is_whitespace(fmt.line_start); + loop { + // All the input starting at cur_start fits on the current line + if graphemes_width(&graphemes[cur_start..]) <= cur_max_width { + for (i, grapheme) in graphemes[cur_start..].iter().enumerate() { + if is_new_line(grapheme) { + // take care of blank lines + result = trim_end_but_line_feed(fmt.trim_end, result); + result.push('\n'); + if !is_bareline_ok && cur_start + i + 1 < graphemes.len() { + result.push_str(&indent_without_newline); + result.push_str(fmt.line_start); + } + } else { + result.push_str(grapheme); + } + } + result = trim_end_but_line_feed(fmt.trim_end, result); + break; + } + + // The input starting at cur_start needs to be broken + match break_string( + cur_max_width, + fmt.trim_end, + fmt.line_end, + &graphemes[cur_start..], + ) { + SnippetState::LineEnd(line, len) => { + result.push_str(&line); + result.push_str(fmt.line_end); + result.push_str(&indent_with_newline); + result.push_str(fmt.line_start); + cur_max_width = newline_max_chars; + cur_start += len; + } + SnippetState::EndWithLineFeed(line, len) => { + if line == "\n" && fmt.trim_end { + result = result.trim_end().to_string(); + } + result.push_str(&line); + if is_bareline_ok { + // the next line can benefit from the full width + cur_max_width = max_width_without_indent; + } else { + result.push_str(&indent_without_newline); + result.push_str(fmt.line_start); + cur_max_width = max_width_with_indent; + } + cur_start += len; + } + SnippetState::EndOfInput(line) => { + result.push_str(&line); + break; + } + } + } + + result.push_str(fmt.closer); + wrap_str(result, fmt.config.max_width(), fmt.shape) +} + +/// Returns the index to the end of the URL if the split at index of the given string includes a +/// URL or alike. Otherwise, returns `None`. +fn detect_url(s: &[&str], index: usize) -> Option { + let start = match s[..=index].iter().rposition(|g| is_whitespace(g)) { + Some(pos) => pos + 1, + None => 0, + }; + // 8 = minimum length for a string to contain a URL + if s.len() < start + 8 { + return None; + } + let split = s[start..].concat(); + if split.contains("https://") + || split.contains("http://") + || split.contains("ftp://") + || split.contains("file://") + { + match s[index..].iter().position(|g| is_whitespace(g)) { + Some(pos) => Some(index + pos - 1), + None => Some(s.len() - 1), + } + } else { + None + } +} + +/// Trims whitespaces to the right except for the line feed character. +fn trim_end_but_line_feed(trim_end: bool, result: String) -> String { + let whitespace_except_line_feed = |c: char| c.is_whitespace() && c != '\n'; + if trim_end && result.ends_with(whitespace_except_line_feed) { + result + .trim_end_matches(whitespace_except_line_feed) + .to_string() + } else { + result + } +} + +/// Result of breaking a string so it fits in a line and the state it ended in. +/// The state informs about what to do with the snippet and how to continue the breaking process. +#[derive(Debug, PartialEq)] +enum SnippetState { + /// The input could not be broken and so rewriting the string is finished. + EndOfInput(String), + /// The input could be broken and the returned snippet should be ended with a + /// `[StringFormat::line_end]`. The next snippet needs to be indented. + /// + /// The returned string is the line to print out and the number is the length that got read in + /// the text being rewritten. That length may be greater than the returned string if trailing + /// whitespaces got trimmed. + LineEnd(String, usize), + /// The input could be broken but a newline is present that cannot be trimmed. The next snippet + /// to be rewritten *could* use more width than what is specified by the given shape. For + /// example with a multiline string, the next snippet does not need to be indented, allowing + /// more characters to be fit within a line. + /// + /// The returned string is the line to print out and the number is the length that got read in + /// the text being rewritten. + EndWithLineFeed(String, usize), +} + +fn not_whitespace_except_line_feed(g: &str) -> bool { + is_new_line(g) || !is_whitespace(g) +} + +/// Break the input string at a boundary character around the offset `max_width`. A boundary +/// character is either a punctuation or a whitespace. +/// FIXME(issue#3281): We must follow UAX#14 algorithm instead of this. +fn break_string(max_width: usize, trim_end: bool, line_end: &str, input: &[&str]) -> SnippetState { + let break_at = |index /* grapheme at index is included */| { + // Take in any whitespaces to the left/right of `input[index]` while + // preserving line feeds + let index_minus_ws = input[0..=index] + .iter() + .rposition(|grapheme| not_whitespace_except_line_feed(grapheme)) + .unwrap_or(index); + // Take into account newlines occurring in input[0..=index], i.e., the possible next new + // line. If there is one, then text after it could be rewritten in a way that the available + // space is fully used. + for (i, grapheme) in input[0..=index].iter().enumerate() { + if is_new_line(grapheme) { + if i <= index_minus_ws { + let mut line = &input[0..i].concat()[..]; + if trim_end { + line = line.trim_end(); + } + return SnippetState::EndWithLineFeed(format!("{}\n", line), i + 1); + } + break; + } + } + + let mut index_plus_ws = index; + for (i, grapheme) in input[index + 1..].iter().enumerate() { + if !trim_end && is_new_line(grapheme) { + return SnippetState::EndWithLineFeed( + input[0..=index + 1 + i].concat(), + index + 2 + i, + ); + } else if not_whitespace_except_line_feed(grapheme) { + index_plus_ws = index + i; + break; + } + } + + if trim_end { + SnippetState::LineEnd(input[0..=index_minus_ws].concat(), index_plus_ws + 1) + } else { + SnippetState::LineEnd(input[0..=index_plus_ws].concat(), index_plus_ws + 1) + } + }; + + // find a first index where the unicode width of input[0..x] become > max_width + let max_width_index_in_input = { + let mut cur_width = 0; + let mut cur_index = 0; + for (i, grapheme) in input.iter().enumerate() { + cur_width += unicode_str_width(grapheme); + cur_index = i; + if cur_width > max_width { + break; + } + } + 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() + && trim_end + && !is_whitespace(input[max_width_index_in_input - 1]) + && is_whitespace(input[max_width_index_in_input]) + { + // At a breaking point already + // The line won't invalidate the rewriting because: + // - no extra space needed for the line_end character + // - extra whitespaces to the right can be trimmed + return break_at(max_width_index_in_input - 1); + } + if let Some(url_index_end) = detect_url(input, max_width_index_in_input) { + let index_plus_ws = url_index_end + + input[url_index_end..] + .iter() + .skip(1) + .position(|grapheme| not_whitespace_except_line_feed(grapheme)) + .unwrap_or(0); + return if trim_end { + SnippetState::LineEnd(input[..=url_index_end].concat(), index_plus_ws + 1) + } else { + SnippetState::LineEnd(input[..=index_plus_ws].concat(), index_plus_ws + 1) + }; + } + + match input[0..max_width_index_in_input] + .iter() + .rposition(|grapheme| is_whitespace(grapheme)) + { + // Found a whitespace and what is on its left side is big enough. + Some(index) if index >= MIN_STRING => break_at(index), + // No whitespace found, try looking for a punctuation instead + _ => match (0..max_width_index_in_input) + .rev() + .skip_while(|pos| !is_valid_linebreak(input, *pos)) + .next() + { + // Found a punctuation and what is on its left side is big enough. + Some(index) if index >= MIN_STRING => break_at(index), + // Either no boundary character was found to the left of `input[max_chars]`, or the line + // got too small. We try searching for a boundary character to the right. + _ => match (max_width_index_in_input..input.len()) + .skip_while(|pos| !is_valid_linebreak(input, *pos)) + .next() + { + // A boundary was found after the line limit + Some(index) => break_at(index), + // No boundary to the right, the input cannot be broken + None => SnippetState::EndOfInput(input.concat()), + }, + }, + } +} + +fn is_valid_linebreak(input: &[&str], pos: usize) -> bool { + let is_whitespace = is_whitespace(input[pos]); + if is_whitespace { + return true; + } + let is_punctuation = is_punctuation(input[pos]); + if is_punctuation && !is_part_of_type(input, pos) { + return true; + } + false +} + +fn is_part_of_type(input: &[&str], pos: usize) -> bool { + input.get(pos..=pos + 1) == Some(&[":", ":"]) + || input.get(pos.saturating_sub(1)..=pos) == Some(&[":", ":"]) +} + +fn is_new_line(grapheme: &str) -> bool { + let bytes = grapheme.as_bytes(); + bytes.starts_with(b"\n") || bytes.starts_with(b"\r\n") +} + +fn is_whitespace(grapheme: &str) -> bool { + grapheme.chars().all(char::is_whitespace) +} + +fn is_punctuation(grapheme: &str) -> bool { + grapheme + .chars() + .all(UnicodeCategories::is_punctuation_other) +} + +fn graphemes_width(graphemes: &[&str]) -> usize { + graphemes.iter().map(|s| unicode_str_width(s)).sum() +} + +#[cfg(test)] +mod test { + use super::{break_string, detect_url, rewrite_string, SnippetState, StringFormat}; + use crate::config::Config; + use crate::shape::{Indent, Shape}; + use unicode_segmentation::UnicodeSegmentation; + + #[test] + fn issue343() { + let config = Default::default(); + let fmt = StringFormat::new(Shape::legacy(2, Indent::empty()), &config); + rewrite_string("eq_", &fmt, 2); + } + + #[test] + fn line_break_at_valid_points_test() { + let string = "[TheName](Dont::break::my::type::That::would::be::very::nice) break here"; + let graphemes = UnicodeSegmentation::graphemes(&*string, false).collect::>(); + assert_eq!( + break_string(20, false, "", &graphemes[..]), + SnippetState::LineEnd( + "[TheName](Dont::break::my::type::That::would::be::very::nice) ".to_string(), + 62 + ) + ); + } + + #[test] + fn should_break_on_whitespace() { + let string = "Placerat felis. Mauris porta ante sagittis purus."; + let graphemes = UnicodeSegmentation::graphemes(&*string, false).collect::>(); + assert_eq!( + break_string(20, false, "", &graphemes[..]), + SnippetState::LineEnd("Placerat felis. ".to_string(), 16) + ); + assert_eq!( + break_string(20, true, "", &graphemes[..]), + SnippetState::LineEnd("Placerat felis.".to_string(), 16) + ); + } + + #[test] + fn should_break_on_punctuation() { + let string = "Placerat_felis._Mauris_porta_ante_sagittis_purus."; + let graphemes = UnicodeSegmentation::graphemes(&*string, false).collect::>(); + assert_eq!( + break_string(20, false, "", &graphemes[..]), + SnippetState::LineEnd("Placerat_felis.".to_string(), 15) + ); + } + + #[test] + fn should_break_forward() { + let string = "Venenatis_tellus_vel_tellus. Aliquam aliquam dolor at justo."; + let graphemes = UnicodeSegmentation::graphemes(&*string, false).collect::>(); + assert_eq!( + break_string(20, false, "", &graphemes[..]), + SnippetState::LineEnd("Venenatis_tellus_vel_tellus. ".to_string(), 29) + ); + assert_eq!( + break_string(20, true, "", &graphemes[..]), + SnippetState::LineEnd("Venenatis_tellus_vel_tellus.".to_string(), 29) + ); + } + + #[test] + fn nothing_to_break() { + let string = "Venenatis_tellus_vel_tellus"; + let graphemes = UnicodeSegmentation::graphemes(&*string, false).collect::>(); + assert_eq!( + break_string(20, false, "", &graphemes[..]), + SnippetState::EndOfInput("Venenatis_tellus_vel_tellus".to_string()) + ); + } + + #[test] + fn significant_whitespaces() { + let string = "Neque in sem. \n Pellentesque tellus augue."; + let graphemes = UnicodeSegmentation::graphemes(&*string, false).collect::>(); + assert_eq!( + break_string(15, false, "", &graphemes[..]), + SnippetState::EndWithLineFeed("Neque in sem. \n".to_string(), 20) + ); + assert_eq!( + break_string(25, false, "", &graphemes[..]), + SnippetState::EndWithLineFeed("Neque in sem. \n".to_string(), 20) + ); + + assert_eq!( + break_string(15, true, "", &graphemes[..]), + SnippetState::LineEnd("Neque in sem.".to_string(), 19) + ); + assert_eq!( + break_string(25, true, "", &graphemes[..]), + SnippetState::EndWithLineFeed("Neque in sem.\n".to_string(), 20) + ); + } + + #[test] + fn big_whitespace() { + let string = "Neque in sem. Pellentesque tellus augue."; + let graphemes = UnicodeSegmentation::graphemes(&*string, false).collect::>(); + assert_eq!( + break_string(20, false, "", &graphemes[..]), + SnippetState::LineEnd("Neque in sem. ".to_string(), 25) + ); + assert_eq!( + break_string(20, true, "", &graphemes[..]), + SnippetState::LineEnd("Neque in sem.".to_string(), 25) + ); + } + + #[test] + fn newline_in_candidate_line() { + let string = "Nulla\nconsequat erat at massa. Vivamus id mi."; + + let graphemes = UnicodeSegmentation::graphemes(&*string, false).collect::>(); + assert_eq!( + break_string(25, false, "", &graphemes[..]), + SnippetState::EndWithLineFeed("Nulla\n".to_string(), 6) + ); + assert_eq!( + break_string(25, true, "", &graphemes[..]), + SnippetState::EndWithLineFeed("Nulla\n".to_string(), 6) + ); + + let mut config: Config = Default::default(); + config.set().max_width(27); + let fmt = StringFormat::new(Shape::legacy(25, Indent::empty()), &config); + let rewritten_string = rewrite_string(string, &fmt, 27); + assert_eq!( + rewritten_string, + Some("\"Nulla\nconsequat erat at massa. \\\n Vivamus id mi.\"".to_string()) + ); + } + + #[test] + fn last_line_fit_with_trailing_whitespaces() { + let string = "Vivamus id mi. "; + let config: Config = Default::default(); + let mut fmt = StringFormat::new(Shape::legacy(25, Indent::empty()), &config); + + fmt.trim_end = true; + let rewritten_string = rewrite_string(string, &fmt, 25); + assert_eq!(rewritten_string, Some("\"Vivamus id mi.\"".to_string())); + + fmt.trim_end = false; // default value of trim_end + let rewritten_string = rewrite_string(string, &fmt, 25); + assert_eq!(rewritten_string, Some("\"Vivamus id mi. \"".to_string())); + } + + #[test] + fn last_line_fit_with_newline() { + let string = "Vivamus id mi.\nVivamus id mi."; + let config: Config = Default::default(); + let fmt = StringFormat { + opener: "", + closer: "", + line_start: "// ", + line_end: "", + shape: Shape::legacy(100, Indent::from_width(&config, 4)), + trim_end: true, + config: &config, + }; + + let rewritten_string = rewrite_string(string, &fmt, 100); + assert_eq!( + rewritten_string, + Some("Vivamus id mi.\n // Vivamus id mi.".to_string()) + ); + } + + #[test] + fn overflow_in_non_string_content() { + let comment = "Aenean metus.\nVestibulum ac lacus. Vivamus porttitor"; + let config: Config = Default::default(); + let fmt = StringFormat { + opener: "", + closer: "", + line_start: "// ", + line_end: "", + shape: Shape::legacy(30, Indent::from_width(&config, 8)), + trim_end: true, + config: &config, + }; + + assert_eq!( + rewrite_string(comment, &fmt, 30), + Some( + "Aenean metus.\n // Vestibulum ac lacus. Vivamus\n // porttitor" + .to_string() + ) + ); + } + + #[test] + fn overflow_in_non_string_content_with_line_end() { + let comment = "Aenean metus.\nVestibulum ac lacus. Vivamus porttitor"; + let config: Config = Default::default(); + let fmt = StringFormat { + opener: "", + closer: "", + line_start: "// ", + line_end: "@", + shape: Shape::legacy(30, Indent::from_width(&config, 8)), + trim_end: true, + config: &config, + }; + + assert_eq!( + rewrite_string(comment, &fmt, 30), + Some( + "Aenean metus.\n // Vestibulum ac lacus. Vivamus@\n // porttitor" + .to_string() + ) + ); + } + + #[test] + fn blank_line_with_non_empty_line_start() { + let config: Config = Default::default(); + let mut fmt = StringFormat { + opener: "", + closer: "", + line_start: "// ", + line_end: "", + shape: Shape::legacy(30, Indent::from_width(&config, 4)), + trim_end: true, + config: &config, + }; + + let comment = "Aenean metus. Vestibulum\n\nac lacus. Vivamus porttitor"; + assert_eq!( + rewrite_string(comment, &fmt, 30), + Some( + "Aenean metus. Vestibulum\n //\n // ac lacus. Vivamus porttitor".to_string() + ) + ); + + fmt.shape = Shape::legacy(15, Indent::from_width(&config, 4)); + let comment = "Aenean\n\nmetus. Vestibulum ac lacus. Vivamus porttitor"; + assert_eq!( + rewrite_string(comment, &fmt, 15), + Some( + r#"Aenean + // + // metus. Vestibulum + // ac lacus. Vivamus + // porttitor"# + .to_string() + ) + ); + } + + #[test] + fn retain_blank_lines() { + let config: Config = Default::default(); + let fmt = StringFormat { + opener: "", + closer: "", + line_start: "// ", + line_end: "", + shape: Shape::legacy(20, Indent::from_width(&config, 4)), + trim_end: true, + config: &config, + }; + + let comment = "Aenean\n\nmetus. Vestibulum ac lacus.\n\n"; + assert_eq!( + rewrite_string(comment, &fmt, 20), + Some( + "Aenean\n //\n // metus. Vestibulum ac\n // lacus.\n //\n".to_string() + ) + ); + + let comment = "Aenean\n\nmetus. Vestibulum ac lacus.\n"; + assert_eq!( + rewrite_string(comment, &fmt, 20), + Some("Aenean\n //\n // metus. Vestibulum ac\n // lacus.\n".to_string()) + ); + + let comment = "Aenean\n \nmetus. Vestibulum ac lacus."; + assert_eq!( + rewrite_string(comment, &fmt, 20), + Some("Aenean\n //\n // metus. Vestibulum ac\n // lacus.".to_string()) + ); + } + + #[test] + fn boundary_on_edge() { + let config: Config = Default::default(); + let mut fmt = StringFormat { + opener: "", + closer: "", + line_start: "// ", + line_end: "", + shape: Shape::legacy(13, Indent::from_width(&config, 4)), + trim_end: true, + config: &config, + }; + + let comment = "Aenean metus. Vestibulum ac lacus."; + assert_eq!( + rewrite_string(comment, &fmt, 13), + Some("Aenean metus.\n // Vestibulum ac\n // lacus.".to_string()) + ); + + fmt.trim_end = false; + let comment = "Vestibulum ac lacus."; + assert_eq!( + rewrite_string(comment, &fmt, 13), + Some("Vestibulum \n // ac lacus.".to_string()) + ); + + fmt.trim_end = true; + fmt.line_end = "\\"; + let comment = "Vestibulum ac lacus."; + assert_eq!( + rewrite_string(comment, &fmt, 13), + Some("Vestibulum\\\n // ac lacus.".to_string()) + ); + } + + #[test] + fn detect_urls() { + let string = "aaa http://example.org something"; + let graphemes = UnicodeSegmentation::graphemes(&*string, false).collect::>(); + assert_eq!(detect_url(&graphemes, 8), Some(21)); + + let string = "https://example.org something"; + let graphemes = UnicodeSegmentation::graphemes(&*string, false).collect::>(); + assert_eq!(detect_url(&graphemes, 0), Some(18)); + + let string = "aaa ftp://example.org something"; + let graphemes = UnicodeSegmentation::graphemes(&*string, false).collect::>(); + assert_eq!(detect_url(&graphemes, 8), Some(20)); + + let string = "aaa file://example.org something"; + let graphemes = UnicodeSegmentation::graphemes(&*string, false).collect::>(); + assert_eq!(detect_url(&graphemes, 8), Some(21)); + + let string = "aaa http not an url"; + let graphemes = UnicodeSegmentation::graphemes(&*string, false).collect::>(); + assert_eq!(detect_url(&graphemes, 6), None); + + let string = "aaa file://example.org"; + let graphemes = UnicodeSegmentation::graphemes(&*string, false).collect::>(); + assert_eq!(detect_url(&graphemes, 8), Some(21)); + } +} diff --git a/src/syntux.rs b/src/syntux.rs new file mode 100644 index 000000000000..845576bd8d8b --- /dev/null +++ b/src/syntux.rs @@ -0,0 +1,4 @@ +//! This module defines a thin abstract layer on top of the rustc's parser and syntax libraries. + +pub(crate) mod parser; +pub(crate) mod session; diff --git a/src/test/configuration_snippet.rs b/src/test/configuration_snippet.rs new file mode 100644 index 000000000000..c70b3c5facd5 --- /dev/null +++ b/src/test/configuration_snippet.rs @@ -0,0 +1,327 @@ +use std::collections::{HashMap, HashSet}; +use std::fs; +use std::io::{BufRead, BufReader, Write}; +use std::iter::Enumerate; +use std::path::{Path, PathBuf}; + +use super::{print_mismatches, write_message, DIFF_CONTEXT_SIZE}; +use crate::config::{Config, EmitMode, Verbosity}; +use crate::rustfmt_diff::{make_diff, Mismatch}; +use crate::{Input, Session}; + +const CONFIGURATIONS_FILE_NAME: &str = "Configurations.md"; + +// This enum is used to represent one of three text features in Configurations.md: a block of code +// with its starting line number, the name of a rustfmt configuration option, or the value of a +// rustfmt configuration option. +enum ConfigurationSection { + CodeBlock((String, u32)), // (String: block of code, u32: line number of code block start) + ConfigName(String), + ConfigValue(String), +} + +impl ConfigurationSection { + fn get_section>( + file: &mut Enumerate, + ) -> Option { + lazy_static! { + static ref CONFIG_NAME_REGEX: regex::Regex = + regex::Regex::new(r"^## `([^`]+)`").expect("failed creating configuration pattern"); + // Configuration values, which will be passed to `from_str`: + // + // - must be prefixed with `####` + // - must be wrapped in backticks + // - may by wrapped in double quotes (which will be stripped) + static ref CONFIG_VALUE_REGEX: regex::Regex = + regex::Regex::new(r#"^#### `"?([^`]+?)"?`"#) + .expect("failed creating configuration value pattern"); + } + + loop { + match file.next() { + Some((i, line)) => { + if line.starts_with("```rust") { + // Get the lines of the code block. + let lines: Vec = file + .map(|(_i, l)| l) + .take_while(|l| !l.starts_with("```")) + .collect(); + let block = format!("{}\n", lines.join("\n")); + + // +1 to translate to one-based indexing + // +1 to get to first line of code (line after "```") + let start_line = (i + 2) as u32; + + return Some(ConfigurationSection::CodeBlock((block, start_line))); + } else if let Some(c) = CONFIG_NAME_REGEX.captures(&line) { + return Some(ConfigurationSection::ConfigName(String::from(&c[1]))); + } else if let Some(c) = CONFIG_VALUE_REGEX.captures(&line) { + return Some(ConfigurationSection::ConfigValue(String::from(&c[1]))); + } + } + None => return None, // reached the end of the file + } + } + } +} + +// This struct stores the information about code blocks in the configurations +// file, formats the code blocks, and prints formatting errors. +struct ConfigCodeBlock { + config_name: Option, + config_value: Option, + code_block: Option, + code_block_start: Option, +} + +impl ConfigCodeBlock { + fn new() -> ConfigCodeBlock { + ConfigCodeBlock { + config_name: None, + config_value: None, + code_block: None, + code_block_start: None, + } + } + + fn set_config_name(&mut self, name: Option) { + self.config_name = name; + self.config_value = None; + } + + fn set_config_value(&mut self, value: Option) { + self.config_value = value; + } + + fn set_code_block(&mut self, code_block: String, code_block_start: u32) { + self.code_block = Some(code_block); + self.code_block_start = Some(code_block_start); + } + + fn get_block_config(&self) -> Config { + let mut config = Config::default(); + config.set().verbose(Verbosity::Quiet); + if self.config_name.is_some() && self.config_value.is_some() { + config.override_value( + self.config_name.as_ref().unwrap(), + self.config_value.as_ref().unwrap(), + ); + } + config + } + + fn code_block_valid(&self) -> bool { + // We never expect to not have a code block. + assert!(self.code_block.is_some() && self.code_block_start.is_some()); + + // See if code block begins with #![rustfmt::skip]. + let fmt_skip = self.fmt_skip(); + + if self.config_name.is_none() && !fmt_skip { + write_message(&format!( + "No configuration name for {}:{}", + CONFIGURATIONS_FILE_NAME, + self.code_block_start.unwrap() + )); + return false; + } + if self.config_value.is_none() && !fmt_skip { + write_message(&format!( + "No configuration value for {}:{}", + CONFIGURATIONS_FILE_NAME, + self.code_block_start.unwrap() + )); + return false; + } + true + } + + /// True if the code block starts with #![rustfmt::skip] + fn fmt_skip(&self) -> bool { + self.code_block + .as_ref() + .unwrap() + .lines() + .nth(0) + .unwrap_or("") + == "#![rustfmt::skip]" + } + + fn has_parsing_errors(&self, session: &Session<'_, T>) -> bool { + if session.has_parsing_errors() { + write_message(&format!( + "\u{261d}\u{1f3fd} Cannot format {}:{}", + CONFIGURATIONS_FILE_NAME, + self.code_block_start.unwrap() + )); + return true; + } + + false + } + + fn print_diff(&self, compare: Vec) { + let mut mismatches = HashMap::new(); + mismatches.insert(PathBuf::from(CONFIGURATIONS_FILE_NAME), compare); + print_mismatches(mismatches, |line_num| { + format!( + "\nMismatch at {}:{}:", + CONFIGURATIONS_FILE_NAME, + line_num + self.code_block_start.unwrap() - 1 + ) + }); + } + + fn formatted_has_diff(&self, text: &str) -> bool { + let compare = make_diff(self.code_block.as_ref().unwrap(), text, DIFF_CONTEXT_SIZE); + if !compare.is_empty() { + self.print_diff(compare); + return true; + } + + false + } + + // Return a bool indicating if formatting this code block is an idempotent + // operation. This function also triggers printing any formatting failure + // messages. + fn formatted_is_idempotent(&self) -> bool { + // Verify that we have all of the expected information. + if !self.code_block_valid() { + return false; + } + + let input = Input::Text(self.code_block.as_ref().unwrap().to_owned()); + let mut config = self.get_block_config(); + config.set().emit_mode(EmitMode::Stdout); + let mut buf: Vec = vec![]; + + { + let mut session = Session::new(config, Some(&mut buf)); + session.format(input).unwrap(); + if self.has_parsing_errors(&session) { + return false; + } + } + + !self.formatted_has_diff(&String::from_utf8(buf).unwrap()) + } + + // Extract a code block from the iterator. Behavior: + // - Rust code blocks are identifed by lines beginning with "```rust". + // - One explicit configuration setting is supported per code block. + // - Rust code blocks with no configuration setting are illegal and cause an + // assertion failure, unless the snippet begins with #![rustfmt::skip]. + // - Configuration names in Configurations.md must be in the form of + // "## `NAME`". + // - Configuration values in Configurations.md must be in the form of + // "#### `VALUE`". + fn extract>( + file: &mut Enumerate, + prev: Option<&ConfigCodeBlock>, + hash_set: &mut HashSet, + ) -> Option { + let mut code_block = ConfigCodeBlock::new(); + code_block.config_name = prev.and_then(|cb| cb.config_name.clone()); + + loop { + match ConfigurationSection::get_section(file) { + Some(ConfigurationSection::CodeBlock((block, start_line))) => { + code_block.set_code_block(block, start_line); + break; + } + Some(ConfigurationSection::ConfigName(name)) => { + assert!( + Config::is_valid_name(&name), + "an unknown configuration option was found: {}", + name + ); + assert!( + hash_set.remove(&name), + "multiple configuration guides found for option {}", + name + ); + code_block.set_config_name(Some(name)); + } + Some(ConfigurationSection::ConfigValue(value)) => { + code_block.set_config_value(Some(value)); + } + None => return None, // end of file was reached + } + } + + Some(code_block) + } +} + +#[test] +fn configuration_snippet_tests() { + super::init_log(); + let blocks = get_code_blocks(); + let failures = blocks + .iter() + .filter(|block| !block.fmt_skip()) + .map(ConfigCodeBlock::formatted_is_idempotent) + .fold(0, |acc, r| acc + (!r as u32)); + + // Display results. + println!("Ran {} configurations tests.", blocks.len()); + assert_eq!(failures, 0, "{} configurations tests failed", failures); +} + +// Read Configurations.md and build a `Vec` of `ConfigCodeBlock` structs with one +// entry for each Rust code block found. +fn get_code_blocks() -> Vec { + let mut file_iter = 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(); + let mut code_blocks: Vec = Vec::new(); + let mut hash_set = Config::hash_set(); + + while let Some(cb) = ConfigCodeBlock::extract(&mut file_iter, code_blocks.last(), &mut hash_set) + { + code_blocks.push(cb); + } + + for name in hash_set { + if !Config::is_hidden_option(&name) { + panic!("{} does not have a configuration guide", name); + } + } + + 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/test/mod.rs b/src/test/mod.rs new file mode 100644 index 000000000000..cfad4a8ed0e3 --- /dev/null +++ b/src/test/mod.rs @@ -0,0 +1,1049 @@ +use std::collections::HashMap; +use std::env; +use std::fs; +use std::io::{self, BufRead, BufReader, Read, Write}; +use std::iter::Peekable; +use std::mem; +use std::path::{Path, PathBuf}; +use std::process::{Command, Stdio}; +use std::str::Chars; +use std::thread; + +use crate::config::{Color, Config, EmitMode, FileName, NewlineStyle}; +use crate::formatting::{ReportedErrors, SourceFile}; +use crate::rustfmt_diff::{make_diff, print_diff, DiffLine, Mismatch, ModifiedChunk, OutputWriter}; +use crate::source_file; +use crate::{is_nightly_channel, FormatReport, FormatReportFormatterBuilder, Input, Session}; + +use rustfmt_config_proc_macro::nightly_only_test; + +mod configuration_snippet; +mod mod_resolver; +mod parser; + +const DIFF_CONTEXT_SIZE: usize = 3; + +// A list of files on which we want to skip testing. +const FILE_SKIP_LIST: &[&str] = &[ + // We want to make sure that the `skip_children` is correctly working, + // so we do not want to test this file directly. + "configs/skip_children/foo/mod.rs", + "issue-3434/no_entry.rs", + "issue-3665/sub_mod.rs", + // Testing for issue-3779 + "issue-3779/ice.rs", + // These files and directory are a part of modules defined inside `cfg_if!`. + "cfg_if/mod.rs", + "cfg_if/detect", + "issue-3253/foo.rs", + "issue-3253/bar.rs", + "issue-3253/paths", + // These files and directory are a part of modules defined inside `cfg_attr(..)`. + "cfg_mod/dir", + "cfg_mod/bar.rs", + "cfg_mod/foo.rs", + "cfg_mod/wasm32.rs", + "skip/foo.rs", +]; + +fn init_log() { + let _ = env_logger::builder().is_test(true).try_init(); +} + +struct TestSetting { + /// The size of the stack of the thread that run tests. + stack_size: usize, +} + +impl Default for TestSetting { + fn default() -> Self { + TestSetting { + stack_size: 8_388_608, // 8MB + } + } +} + +fn run_test_with(test_setting: &TestSetting, f: F) +where + F: FnOnce(), + F: Send + 'static, +{ + thread::Builder::new() + .stack_size(test_setting.stack_size) + .spawn(f) + .expect("Failed to create a test thread") + .join() + .expect("Failed to join a test thread") +} + +fn is_subpath

(path: &Path, subpath: &P) -> bool +where + P: AsRef, +{ + (0..path.components().count()) + .map(|i| { + path.components() + .skip(i) + .take(subpath.as_ref().components().count()) + }) + .any(|c| c.zip(subpath.as_ref().components()).all(|(a, b)| a == b)) +} + +fn is_file_skip(path: &Path) -> bool { + FILE_SKIP_LIST + .iter() + .any(|file_path| is_subpath(path, file_path)) +} + +// Returns a `Vec` containing `PathBuf`s of files with an `rs` extension in the +// given path. The `recursive` argument controls if files from subdirectories +// are also returned. +fn get_test_files(path: &Path, recursive: bool) -> Vec { + let mut files = vec![]; + if path.is_dir() { + for entry in fs::read_dir(path).expect(&format!( + "couldn't read directory {}", + path.to_str().unwrap() + )) { + let entry = entry.expect("couldn't get `DirEntry`"); + let path = entry.path(); + if path.is_dir() && recursive { + files.append(&mut get_test_files(&path, recursive)); + } else if path.extension().map_or(false, |f| f == "rs") && !is_file_skip(&path) { + files.push(path); + } + } + } + files +} + +fn verify_config_used(path: &Path, config_name: &str) { + for entry in fs::read_dir(path).expect(&format!( + "couldn't read {} directory", + path.to_str().unwrap() + )) { + let entry = entry.expect("couldn't get directory entry"); + let path = entry.path(); + if path.extension().map_or(false, |f| f == "rs") { + // check if "// rustfmt-:" appears in the file. + let filebuf = BufReader::new( + fs::File::open(&path) + .unwrap_or_else(|_| panic!("couldn't read file {}", path.display())), + ); + assert!( + filebuf + .lines() + .map(Result::unwrap) + .take_while(|l| l.starts_with("//")) + .any(|l| l.starts_with(&format!("// rustfmt-{}", config_name))), + "config option file {} does not contain expected config name", + path.display() + ); + } + } +} + +#[test] +fn verify_config_test_names() { + init_log(); + for path in &[ + Path::new("tests/source/configs"), + Path::new("tests/target/configs"), + ] { + for entry in fs::read_dir(path).expect("couldn't read configs directory") { + let entry = entry.expect("couldn't get directory entry"); + let path = entry.path(); + if path.is_dir() { + let config_name = path.file_name().unwrap().to_str().unwrap(); + + // Make sure that config name is used in the files in the directory. + verify_config_used(&path, config_name); + } + } + } +} + +// This writes to the terminal using the same approach (via `term::stdout` or +// `println!`) that is used by `rustfmt::rustfmt_diff::print_diff`. Writing +// using only one or the other will cause the output order to differ when +// `print_diff` selects the approach not used. +fn write_message(msg: &str) { + let mut writer = OutputWriter::new(Color::Auto); + writer.writeln(msg, None); +} + +// Integration tests. The files in `tests/source` are formatted and compared +// to their equivalent in `tests/target`. The target file and config can be +// overridden by annotations in the source file. The input and output must match +// exactly. +#[test] +fn system_tests() { + init_log(); + run_test_with(&TestSetting::default(), || { + // Get all files in the tests/source directory. + let files = get_test_files(Path::new("tests/source"), true); + let (_reports, count, fails) = check_files(files, &None); + + // Display results. + println!("Ran {} system tests.", count); + assert_eq!(fails, 0, "{} system tests failed", fails); + assert!( + count >= 300, + "Expected a minimum of {} system tests to be executed", + 300 + ) + }); +} + +// Do the same for tests/coverage-source directory. +// The only difference is the coverage mode. +#[test] +fn coverage_tests() { + init_log(); + let files = get_test_files(Path::new("tests/coverage/source"), true); + let (_reports, count, fails) = check_files(files, &None); + + println!("Ran {} tests in coverage mode.", count); + assert_eq!(fails, 0, "{} tests failed", fails); +} + +#[test] +fn checkstyle_test() { + init_log(); + let filename = "tests/writemode/source/fn-single-line.rs"; + let expected_filename = "tests/writemode/target/checkstyle.xml"; + assert_output(Path::new(filename), Path::new(expected_filename)); +} + +#[test] +fn json_test() { + init_log(); + let filename = "tests/writemode/source/json.rs"; + let expected_filename = "tests/writemode/target/output.json"; + assert_output(Path::new(filename), Path::new(expected_filename)); +} + +#[test] +fn modified_test() { + init_log(); + use std::io::BufRead; + + // Test "modified" output + let filename = "tests/writemode/source/modified.rs"; + let mut data = Vec::new(); + let mut config = Config::default(); + config + .set() + .emit_mode(crate::config::EmitMode::ModifiedLines); + + { + let mut session = Session::new(config, Some(&mut data)); + session.format(Input::File(filename.into())).unwrap(); + } + + let mut lines = data.lines(); + let mut chunks = Vec::new(); + while let Some(Ok(header)) = lines.next() { + // Parse the header line + let values: Vec<_> = header + .split(' ') + .map(|s| s.parse::().unwrap()) + .collect(); + assert_eq!(values.len(), 3); + let line_number_orig = values[0]; + let lines_removed = values[1]; + let num_added = values[2]; + let mut added_lines = Vec::new(); + for _ in 0..num_added { + added_lines.push(lines.next().unwrap().unwrap()); + } + chunks.push(ModifiedChunk { + line_number_orig, + lines_removed, + lines: added_lines, + }); + } + + assert_eq!( + chunks, + vec![ + ModifiedChunk { + line_number_orig: 4, + lines_removed: 4, + lines: vec!["fn blah() {}".into()], + }, + ModifiedChunk { + line_number_orig: 9, + lines_removed: 6, + lines: vec!["#[cfg(a, b)]".into(), "fn main() {}".into()], + }, + ], + ); +} + +// Helper function for comparing the results of rustfmt +// to a known output file generated by one of the write modes. +fn assert_output(source: &Path, expected_filename: &Path) { + let config = read_config(source); + let (_, source_file, _) = format_file(source, config.clone()); + + // Populate output by writing to a vec. + let mut out = vec![]; + let _ = source_file::write_all_files(&source_file, &mut out, &config); + let output = String::from_utf8(out).unwrap(); + + 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 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"); + } +} + +// 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] +#[test] +fn idempotence_tests() { + init_log(); + run_test_with(&TestSetting::default(), || { + // Get all files in the tests/target directory. + let files = get_test_files(Path::new("tests/target"), true); + let (_reports, count, fails) = check_files(files, &None); + + // Display results. + println!("Ran {} idempotent tests.", count); + assert_eq!(fails, 0, "{} idempotent tests failed", fails); + assert!( + count >= 400, + "Expected a minimum of {} idempotent tests to be executed", + 400 + ) + }); +} + +// Run rustfmt on itself. This operation must be idempotent. We also check that +// no warnings are emitted. +// Issue-3443: these tests require nightly +#[nightly_only_test] +#[test] +fn self_tests() { + init_log(); + let mut files = get_test_files(Path::new("tests"), false); + let bin_directories = vec!["cargo-fmt", "git-rustfmt", "bin", "format-diff"]; + for dir in bin_directories { + let mut path = PathBuf::from("src"); + path.push(dir); + path.push("main.rs"); + files.push(path); + } + files.push(PathBuf::from("src/lib.rs")); + + let (reports, count, fails) = check_files(files, &Some(PathBuf::from("rustfmt.toml"))); + let mut warnings = 0; + + // Display results. + println!("Ran {} self tests.", count); + assert_eq!(fails, 0, "{} self tests failed", fails); + + for format_report in reports { + println!( + "{}", + FormatReportFormatterBuilder::new(&format_report).build() + ); + warnings += format_report.warning_count(); + } + + assert_eq!( + warnings, 0, + "Rustfmt's code generated {} warnings", + warnings + ); +} + +#[test] +fn format_files_find_new_files_via_cfg_if() { + init_log(); + run_test_with(&TestSetting::default(), || { + // To repro issue-4656, it is necessary that these files are parsed + // as a part of the same session (hence this separate test runner). + let files = vec![ + Path::new("tests/source/issue-4656/lib2.rs"), + Path::new("tests/source/issue-4656/lib.rs"), + ]; + + let config = Config::default(); + let mut session = Session::::new(config, None); + + let mut write_result = HashMap::new(); + for file in files { + assert!(file.exists()); + let result = session.format(Input::File(file.into())).unwrap(); + assert!(!session.has_formatting_errors()); + assert!(!result.has_warnings()); + let mut source_file = SourceFile::new(); + mem::swap(&mut session.source_file, &mut source_file); + + for (filename, text) in source_file { + if let FileName::Real(ref filename) = filename { + write_result.insert(filename.to_owned(), text); + } + } + } + assert_eq!( + 3, + write_result.len(), + "Should have uncovered an extra file (format_me_please.rs) via lib.rs" + ); + assert!(handle_result(write_result, None).is_ok()); + }); +} + +#[test] +fn stdin_formatting_smoke_test() { + init_log(); + let input = Input::Text("fn main () {}".to_owned()); + let mut config = Config::default(); + config.set().emit_mode(EmitMode::Stdout); + let mut buf: Vec = vec![]; + { + let mut session = Session::new(config, Some(&mut buf)); + session.format(input).unwrap(); + assert!(session.has_no_errors()); + } + + #[cfg(not(windows))] + assert_eq!(buf, ":\n\nfn main() {}\n".as_bytes()); + #[cfg(windows)] + assert_eq!(buf, ":\n\nfn main() {}\r\n".as_bytes()); +} + +#[test] +fn stdin_parser_panic_caught() { + init_log(); + // See issue #3239. + for text in ["{", "}"].iter().cloned().map(String::from) { + let mut buf = vec![]; + let mut session = Session::new(Default::default(), Some(&mut buf)); + let _ = session.format(Input::Text(text)); + + assert!(session.has_parsing_errors()); + } +} + +/// Ensures that `EmitMode::ModifiedLines` works with input from `stdin`. Useful +/// when embedding Rustfmt (e.g. inside RLS). +#[test] +fn stdin_works_with_modified_lines() { + init_log(); + let input = "\nfn\n some( )\n{\n}\nfn main () {}\n"; + let output = "1 6 2\nfn some() {}\nfn main() {}\n"; + + let input = Input::Text(input.to_owned()); + let mut config = Config::default(); + config.set().newline_style(NewlineStyle::Unix); + config.set().emit_mode(EmitMode::ModifiedLines); + let mut buf: Vec = vec![]; + { + let mut session = Session::new(config, Some(&mut buf)); + session.format(input).unwrap(); + let errors = ReportedErrors { + has_diff: true, + ..Default::default() + }; + assert_eq!(session.errors, errors); + } + 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(); + let input = String::from("fn main() { println!(\"This should not be formatted.\"); }"); + let mut child = Command::new(rustfmt().to_str().unwrap()) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .arg("--config-path=./tests/config/disable_all_formatting.toml") + .spawn() + .expect("failed to execute child"); + + { + let stdin = child.stdin.as_mut().expect("failed to get stdin"); + stdin + .write_all(input.as_bytes()) + .expect("failed to write stdin"); + } + + let output = child.wait_with_output().expect("failed to wait on child"); + assert!(output.status.success()); + assert!(output.stderr.is_empty()); + assert_eq!(input, String::from_utf8(output.stdout).unwrap()); +} + +#[test] +fn stdin_generated_files_issue_5172() { + init_log(); + let input = Input::Text("//@generated\nfn main() {}".to_owned()); + let mut config = Config::default(); + config.set().emit_mode(EmitMode::Stdout); + config.set().format_generated_files(false); + config.set().newline_style(NewlineStyle::Unix); + let mut buf: Vec = vec![]; + { + let mut session = Session::new(config, Some(&mut buf)); + session.format(input).unwrap(); + assert!(session.has_no_errors()); + } + // N.B. this should be changed once `format_generated_files` is supported with stdin + assert_eq!( + String::from_utf8(buf).unwrap(), + ":\n\n//@generated\nfn main() {}\n", + ); +} + +#[test] +fn stdin_handles_mod_inner_ignore_attr() { + // see https://github.com/rust-lang/rustfmt/issues/5368 + init_log(); + let input = String::from("#![rustfmt::skip]\n\nfn main() { }"); + let mut child = Command::new(rustfmt().to_str().unwrap()) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn() + .expect("failed to execute child"); + + { + let stdin = child.stdin.as_mut().expect("failed to get stdin"); + stdin + .write_all(input.as_bytes()) + .expect("failed to write stdin"); + } + + let output = child.wait_with_output().expect("failed to wait on child"); + assert!(output.status.success()); + assert!(output.stderr.is_empty()); + assert_eq!(input, String::from_utf8(output.stdout).unwrap()); +} + +#[test] +fn format_lines_errors_are_reported() { + init_log(); + let long_identifier = String::from_utf8(vec![b'a'; 239]).unwrap(); + let input = Input::Text(format!("fn {}() {{}}", long_identifier)); + let mut config = Config::default(); + config.set().error_on_line_overflow(true); + let mut session = Session::::new(config, None); + session.format(input).unwrap(); + assert!(session.has_formatting_errors()); +} + +#[test] +fn format_lines_errors_are_reported_with_tabs() { + init_log(); + let long_identifier = String::from_utf8(vec![b'a'; 97]).unwrap(); + let input = Input::Text(format!("fn a() {{\n\t{}\n}}", long_identifier)); + let mut config = Config::default(); + config.set().error_on_line_overflow(true); + config.set().hard_tabs(true); + let mut session = Session::::new(config, None); + session.format(input).unwrap(); + assert!(session.has_formatting_errors()); +} + +// For each file, run rustfmt and collect the output. +// Returns the number of files checked and the number of failures. +fn check_files(files: Vec, opt_config: &Option) -> (Vec, u32, u32) { + let mut count = 0; + let mut fails = 0; + let mut reports = vec![]; + + for file_name in files { + let sig_comments = read_significant_comments(&file_name); + if sig_comments.contains_key("unstable") && !is_nightly_channel!() { + debug!( + "Skipping '{}' because it requires unstable \ + features which are only available on nightly...", + file_name.display() + ); + continue; + } + + debug!("Testing '{}'...", file_name.display()); + + match idempotent_check(&file_name, opt_config) { + Ok(ref report) if report.has_warnings() => { + print!("{}", FormatReportFormatterBuilder::new(report).build()); + fails += 1; + } + Ok(report) => reports.push(report), + Err(err) => { + if let IdempotentCheckError::Mismatch(msg) = err { + print_mismatches_default_message(msg); + } + fails += 1; + } + } + + count += 1; + } + + (reports, count, fails) +} + +fn print_mismatches_default_message(result: HashMap>) { + for (file_name, diff) in result { + let mismatch_msg_formatter = + |line_num| format!("\nMismatch at {}:{}:", file_name.display(), line_num); + print_diff(diff, &mismatch_msg_formatter, &Default::default()); + } + + if let Some(mut t) = term::stdout() { + t.reset().unwrap_or(()); + } +} + +fn print_mismatches String>( + result: HashMap>, + mismatch_msg_formatter: T, +) { + for (_file_name, diff) in result { + print_diff(diff, &mismatch_msg_formatter, &Default::default()); + } + + if let Some(mut t) = term::stdout() { + t.reset().unwrap_or(()); + } +} + +fn read_config(filename: &Path) -> Config { + let sig_comments = read_significant_comments(filename); + // Look for a config file. If there is a 'config' property in the significant comments, use + // that. Otherwise, if there are no significant comments at all, look for a config file with + // the same name as the test file. + let mut config = if !sig_comments.is_empty() { + get_config(sig_comments.get("config").map(Path::new)) + } else { + get_config(filename.with_extension("toml").file_name().map(Path::new)) + }; + + for (key, val) in &sig_comments { + if key != "target" && key != "config" && key != "unstable" { + config.override_value(key, val); + if config.is_default(key) { + warn!("Default value {} used explicitly for {}", val, key); + } + } + } + + config +} + +fn format_file>(filepath: P, config: Config) -> (bool, SourceFile, FormatReport) { + let filepath = filepath.into(); + let input = Input::File(filepath); + let mut session = Session::::new(config, None); + let result = session.format(input).unwrap(); + let parsing_errors = session.has_parsing_errors(); + let mut source_file = SourceFile::new(); + mem::swap(&mut session.source_file, &mut source_file); + (parsing_errors, source_file, result) +} + +enum IdempotentCheckError { + Mismatch(HashMap>), + Parse, +} + +fn idempotent_check( + filename: &PathBuf, + opt_config: &Option, +) -> Result { + let sig_comments = read_significant_comments(filename); + let config = if let Some(ref config_file_path) = opt_config { + Config::from_toml_path(config_file_path).expect("`rustfmt.toml` not found") + } else { + read_config(filename) + }; + let (parsing_errors, source_file, format_report) = format_file(filename, config); + if parsing_errors { + return Err(IdempotentCheckError::Parse); + } + + let mut write_result = HashMap::new(); + for (filename, text) in source_file { + if let FileName::Real(ref filename) = filename { + write_result.insert(filename.to_owned(), text); + } + } + + let target = sig_comments.get("target").map(|x| &(*x)[..]); + + handle_result(write_result, target).map(|_| format_report) +} + +// Reads test config file using the supplied (optional) file name. If there's no file name or the +// file doesn't exist, just return the default config. Otherwise, the file must be read +// successfully. +fn get_config(config_file: Option<&Path>) -> Config { + let config_file_name = match config_file { + None => return Default::default(), + Some(file_name) => { + let mut full_path = PathBuf::from("tests/config/"); + full_path.push(file_name); + if !full_path.exists() { + return Default::default(); + }; + full_path + } + }; + + let mut def_config_file = fs::File::open(config_file_name).expect("couldn't open config"); + let mut def_config = String::new(); + def_config_file + .read_to_string(&mut def_config) + .expect("Couldn't read config"); + + Config::from_toml(&def_config, Path::new("tests/config/")).expect("invalid TOML") +} + +// Reads significant comments of the form: `// rustfmt-key: value` into a hash map. +fn read_significant_comments(file_name: &Path) -> HashMap { + let file = fs::File::open(file_name) + .unwrap_or_else(|_| panic!("couldn't read file {}", file_name.display())); + let reader = BufReader::new(file); + let pattern = r"^\s*//\s*rustfmt-([^:]+):\s*(\S+)"; + let regex = regex::Regex::new(pattern).expect("failed creating pattern 1"); + + // Matches lines containing significant comments or whitespace. + let line_regex = regex::Regex::new(r"(^\s*$)|(^\s*//\s*rustfmt-[^:]+:\s*\S+)") + .expect("failed creating pattern 2"); + + reader + .lines() + .map(|line| line.expect("failed getting line")) + .filter(|line| line_regex.is_match(line)) + .filter_map(|line| { + regex.captures_iter(&line).next().map(|capture| { + ( + capture + .get(1) + .expect("couldn't unwrap capture") + .as_str() + .to_owned(), + capture + .get(2) + .expect("couldn't unwrap capture") + .as_str() + .to_owned(), + ) + }) + }) + .collect() +} + +// Compares output to input. +// TODO: needs a better name, more explanation. +fn handle_result( + result: HashMap, + target: Option<&str>, +) -> Result<(), IdempotentCheckError> { + let mut failures = HashMap::new(); + + for (file_name, fmt_text) in result { + // If file is in tests/source, compare to file with same name in tests/target. + let target = get_target(&file_name, target); + let open_error = format!("couldn't open target {:?}", target); + let mut f = fs::File::open(&target).expect(&open_error); + + let mut text = String::new(); + let read_error = format!("failed reading target {:?}", target); + f.read_to_string(&mut text).expect(&read_error); + + // Ignore LF and CRLF difference for Windows. + if !string_eq_ignore_newline_repr(&fmt_text, &text) { + let diff = make_diff(&text, &fmt_text, DIFF_CONTEXT_SIZE); + assert!( + !diff.is_empty(), + "Empty diff? Maybe due to a missing a newline at the end of a file?" + ); + failures.insert(file_name, diff); + } + } + + if failures.is_empty() { + Ok(()) + } else { + Err(IdempotentCheckError::Mismatch(failures)) + } +} + +// Maps source file paths to their target paths. +fn get_target(file_name: &Path, target: Option<&str>) -> PathBuf { + if let Some(n) = file_name + .components() + .position(|c| c.as_os_str() == "source") + { + let mut target_file_name = PathBuf::new(); + for (i, c) in file_name.components().enumerate() { + if i == n { + target_file_name.push("target"); + } else { + target_file_name.push(c.as_os_str()); + } + } + if let Some(replace_name) = target { + target_file_name.with_file_name(replace_name) + } else { + target_file_name + } + } else { + // This is either and idempotence check or a self check. + file_name.to_owned() + } +} + +#[test] +fn rustfmt_diff_make_diff_tests() { + init_log(); + let diff = make_diff("a\nb\nc\nd", "a\ne\nc\nd", 3); + assert_eq!( + diff, + vec![Mismatch { + line_number: 1, + line_number_orig: 1, + lines: vec![ + DiffLine::Context("a".into()), + DiffLine::Resulting("b".into()), + DiffLine::Expected("e".into()), + DiffLine::Context("c".into()), + DiffLine::Context("d".into()), + ], + }] + ); +} + +#[test] +fn rustfmt_diff_no_diff_test() { + init_log(); + let diff = make_diff("a\nb\nc\nd", "a\nb\nc\nd", 3); + assert_eq!(diff, vec![]); +} + +// Compare strings without distinguishing between CRLF and LF +fn string_eq_ignore_newline_repr(left: &str, right: &str) -> bool { + let left = CharsIgnoreNewlineRepr(left.chars().peekable()); + let right = CharsIgnoreNewlineRepr(right.chars().peekable()); + left.eq(right) +} + +struct CharsIgnoreNewlineRepr<'a>(Peekable>); + +impl<'a> Iterator for CharsIgnoreNewlineRepr<'a> { + type Item = char; + + fn next(&mut self) -> Option { + self.0.next().map(|c| { + if c == '\r' { + if *self.0.peek().unwrap_or(&'\0') == '\n' { + self.0.next(); + '\n' + } else { + '\r' + } + } else { + c + } + }) + } +} + +#[test] +fn string_eq_ignore_newline_repr_test() { + init_log(); + assert!(string_eq_ignore_newline_repr("", "")); + assert!(!string_eq_ignore_newline_repr("", "abc")); + assert!(!string_eq_ignore_newline_repr("abc", "")); + assert!(string_eq_ignore_newline_repr("a\nb\nc\rd", "a\nb\r\nc\rd")); + assert!(string_eq_ignore_newline_repr("a\r\n\r\n\r\nb", "a\n\n\nb")); + assert!(!string_eq_ignore_newline_repr("a\r\nbcd", "a\nbcdefghijk")); +} + +struct TempFile { + path: PathBuf, +} + +fn make_temp_file(file_name: &'static str) -> TempFile { + use std::env::var; + use std::fs::File; + + // Used in the Rust build system. + let target_dir = var("RUSTFMT_TEST_DIR").unwrap_or_else(|_| ".".to_owned()); + let path = Path::new(&target_dir).join(file_name); + + let mut file = File::create(&path).expect("couldn't create temp file"); + let content = "fn main() {}\n"; + file.write_all(content.as_bytes()) + .expect("couldn't write temp file"); + TempFile { path } +} + +impl Drop for TempFile { + fn drop(&mut self) { + use std::fs::remove_file; + remove_file(&self.path).expect("couldn't delete temp file"); + } +} + +fn rustfmt() -> PathBuf { + let mut me = env::current_exe().expect("failed to get current executable"); + // Chop of the test name. + me.pop(); + // Chop off `deps`. + me.pop(); + + me.push("rustfmt"); + assert!( + me.is_file() || me.with_extension("exe").is_file(), + "{}", + "no rustfmt bin, try running `cargo build` or `cargo build --release` before testing" + ); + me +} + +#[test] +fn verify_check_works() { + init_log(); + let temp_file = make_temp_file("temp_check.rs"); + + Command::new(rustfmt().to_str().unwrap()) + .arg("--check") + .arg(temp_file.path.to_str().unwrap()) + .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/test/mod_resolver.rs b/src/test/mod_resolver.rs new file mode 100644 index 000000000000..aacb2acc6849 --- /dev/null +++ b/src/test/mod_resolver.rs @@ -0,0 +1,82 @@ +use std::io; +use std::path::PathBuf; + +use super::read_config; + +use crate::{FileName, Input, Session}; + +fn verify_mod_resolution(input_file_name: &str, exp_misformatted_files: &[&str]) { + let input_file = PathBuf::from(input_file_name); + let config = read_config(&input_file); + let mut session = Session::::new(config, None); + let report = session + .format(Input::File(input_file_name.into())) + .expect("Should not have had any execution errors"); + let errors_by_file = &report.internal.borrow().0; + for exp_file in exp_misformatted_files { + assert!(errors_by_file.contains_key(&FileName::Real(PathBuf::from(exp_file)))); + } +} + +#[test] +fn nested_out_of_line_mods_loaded() { + // See also https://github.com/rust-lang/rustfmt/issues/4874 + verify_mod_resolution( + "tests/mod-resolver/issue-4874/main.rs", + &[ + "tests/mod-resolver/issue-4874/bar/baz.rs", + "tests/mod-resolver/issue-4874/foo/qux.rs", + ], + ); +} + +#[test] +fn out_of_line_nested_inline_within_out_of_line() { + // See also https://github.com/rust-lang/rustfmt/issues/5063 + verify_mod_resolution( + "tests/mod-resolver/issue-5063/main.rs", + &[ + "tests/mod-resolver/issue-5063/foo/bar/baz.rs", + "tests/mod-resolver/issue-5063/foo.rs", + ], + ); +} + +#[test] +fn skip_out_of_line_nested_inline_within_out_of_line() { + // See also https://github.com/rust-lang/rustfmt/issues/5065 + verify_mod_resolution( + "tests/mod-resolver/skip-files-issue-5065/main.rs", + &["tests/mod-resolver/skip-files-issue-5065/one.rs"], + ); +} + +#[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/test/parser.rs b/src/test/parser.rs new file mode 100644 index 000000000000..ae4a4f94d928 --- /dev/null +++ b/src/test/parser.rs @@ -0,0 +1,57 @@ +use std::io; +use std::path::PathBuf; + +use super::read_config; + +use crate::modules::{ModuleResolutionError, ModuleResolutionErrorKind}; +use crate::{ErrorKind, Input, Session}; + +#[test] +fn parser_errors_in_submods_are_surfaced() { + // See also https://github.com/rust-lang/rustfmt/issues/4126 + let filename = "tests/parser/issue-4126/lib.rs"; + let input_file = PathBuf::from(filename); + let exp_mod_name = "invalid"; + let config = read_config(&input_file); + let mut session = Session::::new(config, None); + if let Err(ErrorKind::ModuleResolutionError(ModuleResolutionError { module, kind })) = + session.format(Input::File(filename.into())) + { + assert_eq!(&module, exp_mod_name); + if let ModuleResolutionErrorKind::ParseError { + file: unparseable_file, + } = kind + { + assert_eq!( + unparseable_file, + PathBuf::from("tests/parser/issue-4126/invalid.rs"), + ); + } else { + panic!("Expected parser error"); + } + } else { + panic!("Expected ModuleResolution operation error"); + } +} + +fn assert_parser_error(filename: &str) { + let file = PathBuf::from(filename); + let config = read_config(&file); + let mut session = Session::::new(config, None); + let _ = session.format(Input::File(filename.into())).unwrap(); + assert!(session.has_parsing_errors()); +} + +#[test] +fn parser_creation_errors_on_entry_new_parser_from_file_panic() { + // See also https://github.com/rust-lang/rustfmt/issues/4418 + let filename = "tests/parser/issue_4418.rs"; + assert_parser_error(filename); +} + +#[test] +fn crate_parsing_errors_on_unclosed_delims() { + // See also https://github.com/rust-lang/rustfmt/issues/4466 + let filename = "tests/parser/unclosed-delims/issue_4466.rs"; + assert_parser_error(filename); +} diff --git a/src/types.rs b/src/types.rs new file mode 100644 index 000000000000..18a08f17ba02 --- /dev/null +++ b/src/types.rs @@ -0,0 +1,1142 @@ +use std::iter::ExactSizeIterator; +use std::ops::Deref; + +use rustc_ast::ast::{self, FnRetTy, Mutability, Term}; +use rustc_ast::ptr; +use rustc_span::{symbol::kw, BytePos, Pos, Span}; + +use crate::comment::{combine_strs_with_missing_comments, contains_comment}; +use crate::config::lists::*; +use crate::config::{IndentStyle, TypeDensity, Version}; +use crate::expr::{ + format_expr, rewrite_assign_rhs, rewrite_call, rewrite_tuple, rewrite_unary_prefix, ExprType, + RhsAssignKind, +}; +use crate::lists::{ + definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator, +}; +use crate::macros::{rewrite_macro, MacroPosition}; +use crate::overflow; +use crate::pairs::{rewrite_pair, PairParts}; +use crate::rewrite::{Rewrite, RewriteContext}; +use crate::shape::Shape; +use crate::source_map::SpanUtils; +use crate::spanned::Spanned; +use crate::utils::{ + colon_spaces, extra_offset, first_line_width, format_extern, format_mutability, + last_line_extendable, last_line_width, mk_sp, rewrite_ident, +}; + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub(crate) enum PathContext { + Expr, + Type, + Import, +} + +// Does not wrap on simple segments. +pub(crate) fn rewrite_path( + context: &RewriteContext<'_>, + path_context: PathContext, + qself: &Option>, + path: &ast::Path, + shape: Shape, +) -> Option { + let skip_count = qself.as_ref().map_or(0, |x| x.position); + + let mut result = if path.is_global() && qself.is_none() && path_context != PathContext::Import { + "::".to_owned() + } else { + String::new() + }; + + let mut span_lo = path.span.lo(); + + if let Some(qself) = qself { + result.push('<'); + + let fmt_ty = qself.ty.rewrite(context, shape)?; + result.push_str(&fmt_ty); + + if skip_count > 0 { + result.push_str(" as "); + if path.is_global() && path_context != PathContext::Import { + result.push_str("::"); + } + + // 3 = ">::".len() + let shape = shape.sub_width(3)?; + + result = rewrite_path_segments( + PathContext::Type, + result, + path.segments.iter().take(skip_count), + span_lo, + path.span.hi(), + context, + shape, + )?; + } + + result.push_str(">::"); + span_lo = qself.ty.span.hi() + BytePos(1); + } + + rewrite_path_segments( + path_context, + result, + path.segments.iter().skip(skip_count), + span_lo, + path.span.hi(), + context, + shape, + ) +} + +fn rewrite_path_segments<'a, I>( + path_context: PathContext, + mut buffer: String, + iter: I, + mut span_lo: BytePos, + span_hi: BytePos, + context: &RewriteContext<'_>, + shape: Shape, +) -> Option +where + I: Iterator, +{ + let mut first = true; + let shape = shape.visual_indent(0); + + for segment in iter { + // Indicates a global path, shouldn't be rendered. + if segment.ident.name == kw::PathRoot { + continue; + } + if first { + first = false; + } else { + buffer.push_str("::"); + } + + let extra_offset = extra_offset(&buffer, shape); + let new_shape = shape.shrink_left(extra_offset)?; + let segment_string = rewrite_segment( + path_context, + segment, + &mut span_lo, + span_hi, + context, + new_shape, + )?; + + buffer.push_str(&segment_string); + } + + Some(buffer) +} + +#[derive(Debug)] +pub(crate) enum SegmentParam<'a> { + Const(&'a ast::AnonConst), + LifeTime(&'a ast::Lifetime), + Type(&'a ast::Ty), + Binding(&'a ast::AssocConstraint), +} + +impl<'a> SegmentParam<'a> { + fn from_generic_arg(arg: &ast::GenericArg) -> SegmentParam<'_> { + match arg { + ast::GenericArg::Lifetime(ref lt) => SegmentParam::LifeTime(lt), + ast::GenericArg::Type(ref ty) => SegmentParam::Type(ty), + ast::GenericArg::Const(const_) => SegmentParam::Const(const_), + } + } +} + +impl<'a> Spanned for SegmentParam<'a> { + fn span(&self) -> Span { + match *self { + SegmentParam::Const(const_) => const_.value.span, + SegmentParam::LifeTime(lt) => lt.ident.span, + SegmentParam::Type(ty) => ty.span, + SegmentParam::Binding(binding) => binding.span, + } + } +} + +impl<'a> Rewrite for SegmentParam<'a> { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + match *self { + SegmentParam::Const(const_) => const_.rewrite(context, shape), + SegmentParam::LifeTime(lt) => lt.rewrite(context, shape), + SegmentParam::Type(ty) => ty.rewrite(context, shape), + SegmentParam::Binding(atc) => atc.rewrite(context, shape), + } + } +} + +impl Rewrite for ast::AssocConstraint { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + use ast::AssocConstraintKind::{Bound, Equality}; + + let mut result = String::with_capacity(128); + result.push_str(rewrite_ident(context, self.ident)); + + if let Some(ref gen_args) = self.gen_args { + let budget = shape.width.checked_sub(result.len())?; + let shape = Shape::legacy(budget, shape.indent + result.len()); + let gen_str = rewrite_generic_args(gen_args, context, shape, gen_args.span())?; + result.push_str(&gen_str); + } + + let infix = match (&self.kind, context.config.type_punctuation_density()) { + (Bound { .. }, _) => ": ", + (Equality { .. }, TypeDensity::Wide) => " = ", + (Equality { .. }, TypeDensity::Compressed) => "=", + }; + result.push_str(infix); + + let budget = shape.width.checked_sub(result.len())?; + let shape = Shape::legacy(budget, shape.indent + result.len()); + let rewrite = self.kind.rewrite(context, shape)?; + result.push_str(&rewrite); + + Some(result) + } +} + +impl Rewrite for ast::AssocConstraintKind { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + match self { + ast::AssocConstraintKind::Equality { term } => match term { + Term::Ty(ty) => ty.rewrite(context, shape), + Term::Const(c) => c.rewrite(context, shape), + }, + ast::AssocConstraintKind::Bound { bounds } => bounds.rewrite(context, shape), + } + } +} + +// Formats a path segment. There are some hacks involved to correctly determine +// the segment's associated span since it's not part of the AST. +// +// The span_lo is assumed to be greater than the end of any previous segment's +// parameters and lesser or equal than the start of current segment. +// +// span_hi is assumed equal to the end of the entire path. +// +// When the segment contains a positive number of parameters, we update span_lo +// so that invariants described above will hold for the next segment. +fn rewrite_segment( + path_context: PathContext, + segment: &ast::PathSegment, + span_lo: &mut BytePos, + span_hi: BytePos, + context: &RewriteContext<'_>, + shape: Shape, +) -> Option { + let mut result = String::with_capacity(128); + result.push_str(rewrite_ident(context, segment.ident)); + + let ident_len = result.len(); + let shape = if context.use_block_indent() { + shape.offset_left(ident_len)? + } else { + shape.shrink_left(ident_len)? + }; + + if let Some(ref args) = segment.args { + let generics_str = rewrite_generic_args(args, context, shape, mk_sp(*span_lo, span_hi))?; + match **args { + ast::GenericArgs::AngleBracketed(ref data) if !data.args.is_empty() => { + // HACK: squeeze out the span between the identifier and the parameters. + // The hack is required so that we don't remove the separator inside macro calls. + // This does not work in the presence of comment, hoping that people are + // sane about where to put their comment. + let separator_snippet = context + .snippet(mk_sp(segment.ident.span.hi(), data.span.lo())) + .trim(); + let force_separator = context.inside_macro() && separator_snippet.starts_with("::"); + let separator = if path_context == PathContext::Expr || force_separator { + "::" + } else { + "" + }; + result.push_str(separator); + + // Update position of last bracket. + *span_lo = context + .snippet_provider + .span_after(mk_sp(*span_lo, span_hi), "<"); + } + _ => (), + } + result.push_str(&generics_str) + } + + Some(result) +} + +fn format_function_type<'a, I>( + inputs: I, + output: &FnRetTy, + variadic: bool, + span: Span, + context: &RewriteContext<'_>, + shape: Shape, +) -> Option +where + I: ExactSizeIterator, + ::Item: Deref, + ::Target: Rewrite + Spanned + 'a, +{ + debug!("format_function_type {:#?}", shape); + + let ty_shape = match context.config.indent_style() { + // 4 = " -> " + IndentStyle::Block => shape.offset_left(4)?, + IndentStyle::Visual => shape.block_left(4)?, + }; + let output = match *output { + FnRetTy::Ty(ref ty) => { + let type_str = ty.rewrite(context, ty_shape)?; + format!(" -> {}", type_str) + } + FnRetTy::Default(..) => String::new(), + }; + + let list_shape = if context.use_block_indent() { + Shape::indented( + shape.block().indent.block_indent(context.config), + context.config, + ) + } else { + // 2 for () + let budget = shape.width.checked_sub(2)?; + // 1 for ( + let offset = shape.indent + 1; + Shape::legacy(budget, offset) + }; + + let is_inputs_empty = inputs.len() == 0; + let list_lo = context.snippet_provider.span_after(span, "("); + let (list_str, tactic) = if is_inputs_empty { + let tactic = get_tactics(&[], &output, shape); + let list_hi = context.snippet_provider.span_before(span, ")"); + let comment = context + .snippet_provider + .span_to_snippet(mk_sp(list_lo, list_hi))? + .trim(); + let comment = if comment.starts_with("//") { + format!( + "{}{}{}", + &list_shape.indent.to_string_with_newline(context.config), + comment, + &shape.block().indent.to_string_with_newline(context.config) + ) + } else { + comment.to_string() + }; + (comment, tactic) + } else { + let items = itemize_list( + context.snippet_provider, + inputs, + ")", + ",", + |arg| arg.span().lo(), + |arg| arg.span().hi(), + |arg| arg.rewrite(context, list_shape), + list_lo, + span.hi(), + false, + ); + + let item_vec: Vec<_> = items.collect(); + let tactic = get_tactics(&item_vec, &output, shape); + let trailing_separator = if !context.use_block_indent() || variadic { + SeparatorTactic::Never + } else { + context.config.trailing_comma() + }; + + let fmt = ListFormatting::new(list_shape, context.config) + .tactic(tactic) + .trailing_separator(trailing_separator) + .ends_with_newline(tactic.ends_with_newline(context.config.indent_style())) + .preserve_newline(true); + (write_list(&item_vec, &fmt)?, tactic) + }; + + let args = if tactic == DefinitiveListTactic::Horizontal + || !context.use_block_indent() + || is_inputs_empty + { + format!("({})", list_str) + } else { + format!( + "({}{}{})", + list_shape.indent.to_string_with_newline(context.config), + list_str, + shape.block().indent.to_string_with_newline(context.config), + ) + }; + if output.is_empty() || last_line_width(&args) + first_line_width(&output) <= shape.width { + Some(format!("{}{}", args, output)) + } else { + Some(format!( + "{}\n{}{}", + args, + list_shape.indent.to_string(context.config), + output.trim_start() + )) + } +} + +fn type_bound_colon(context: &RewriteContext<'_>) -> &'static str { + colon_spaces(context.config) +} + +// If the return type is multi-lined, then force to use multiple lines for +// arguments as well. +fn get_tactics(item_vec: &[ListItem], output: &str, shape: Shape) -> DefinitiveListTactic { + if output.contains('\n') { + DefinitiveListTactic::Vertical + } else { + definitive_tactic( + item_vec, + ListTactic::HorizontalVertical, + Separator::Comma, + // 2 is for the case of ',\n' + shape.width.saturating_sub(2 + output.len()), + ) + } +} + +impl Rewrite for ast::WherePredicate { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + // FIXME: dead spans? + let result = match *self { + ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate { + ref bound_generic_params, + ref bounded_ty, + ref bounds, + .. + }) => { + let type_str = bounded_ty.rewrite(context, shape)?; + let colon = type_bound_colon(context).trim_end(); + let lhs = if let Some(lifetime_str) = + rewrite_lifetime_param(context, shape, bound_generic_params) + { + format!("for<{}> {}{}", lifetime_str, type_str, colon) + } else { + format!("{}{}", type_str, colon) + }; + + rewrite_assign_rhs(context, lhs, bounds, &RhsAssignKind::Bounds, shape)? + } + ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate { + ref lifetime, + ref bounds, + .. + }) => rewrite_bounded_lifetime(lifetime, bounds, context, shape)?, + ast::WherePredicate::EqPredicate(ast::WhereEqPredicate { + ref lhs_ty, + ref rhs_ty, + .. + }) => { + let lhs_ty_str = lhs_ty.rewrite(context, shape).map(|lhs| lhs + " =")?; + rewrite_assign_rhs(context, lhs_ty_str, &**rhs_ty, &RhsAssignKind::Ty, shape)? + } + }; + + Some(result) + } +} + +impl Rewrite for ast::GenericArg { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + match *self { + ast::GenericArg::Lifetime(ref lt) => lt.rewrite(context, shape), + ast::GenericArg::Type(ref ty) => ty.rewrite(context, shape), + ast::GenericArg::Const(ref const_) => const_.rewrite(context, shape), + } + } +} + +fn rewrite_generic_args( + gen_args: &ast::GenericArgs, + context: &RewriteContext<'_>, + shape: Shape, + span: Span, +) -> Option { + match gen_args { + ast::GenericArgs::AngleBracketed(ref data) if !data.args.is_empty() => { + let args = data + .args + .iter() + .map(|x| match x { + ast::AngleBracketedArg::Arg(generic_arg) => { + SegmentParam::from_generic_arg(generic_arg) + } + ast::AngleBracketedArg::Constraint(constraint) => { + SegmentParam::Binding(constraint) + } + }) + .collect::>(); + + overflow::rewrite_with_angle_brackets(context, "", args.iter(), shape, span) + } + ast::GenericArgs::Parenthesized(ref data) => format_function_type( + data.inputs.iter().map(|x| &**x), + &data.output, + false, + data.span, + context, + shape, + ), + _ => Some("".to_owned()), + } +} + +fn rewrite_bounded_lifetime( + lt: &ast::Lifetime, + bounds: &[ast::GenericBound], + context: &RewriteContext<'_>, + shape: Shape, +) -> Option { + let result = lt.rewrite(context, shape)?; + + if bounds.is_empty() { + Some(result) + } else { + let colon = type_bound_colon(context); + let overhead = last_line_width(&result) + colon.len(); + let result = format!( + "{}{}{}", + result, + colon, + join_bounds(context, shape.sub_width(overhead)?, bounds, true)? + ); + Some(result) + } +} + +impl Rewrite for ast::AnonConst { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + format_expr(&self.value, ExprType::SubExpression, context, shape) + } +} + +impl Rewrite for ast::Lifetime { + fn rewrite(&self, context: &RewriteContext<'_>, _: Shape) -> Option { + Some(rewrite_ident(context, self.ident).to_owned()) + } +} + +impl Rewrite for ast::GenericBound { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + match *self { + ast::GenericBound::Trait(ref poly_trait_ref, trait_bound_modifier) => { + let snippet = context.snippet(self.span()); + let has_paren = snippet.starts_with('(') && snippet.ends_with(')'); + let rewrite = match trait_bound_modifier { + ast::TraitBoundModifier::None => poly_trait_ref.rewrite(context, shape), + ast::TraitBoundModifier::Maybe => poly_trait_ref + .rewrite(context, shape.offset_left(1)?) + .map(|s| format!("?{}", s)), + ast::TraitBoundModifier::MaybeConst => poly_trait_ref + .rewrite(context, shape.offset_left(7)?) + .map(|s| format!("~const {}", s)), + ast::TraitBoundModifier::MaybeConstMaybe => poly_trait_ref + .rewrite(context, shape.offset_left(8)?) + .map(|s| format!("~const ?{}", s)), + ast::TraitBoundModifier::Negative => poly_trait_ref + .rewrite(context, shape.offset_left(1)?) + .map(|s| format!("!{}", s)), + ast::TraitBoundModifier::MaybeConstNegative => poly_trait_ref + .rewrite(context, shape.offset_left(8)?) + .map(|s| format!("~const !{}", s)), + }; + rewrite.map(|s| if has_paren { format!("({})", s) } else { s }) + } + ast::GenericBound::Outlives(ref lifetime) => lifetime.rewrite(context, shape), + } + } +} + +impl Rewrite for ast::GenericBounds { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + if self.is_empty() { + return Some(String::new()); + } + + join_bounds(context, shape, self, true) + } +} + +impl Rewrite for ast::GenericParam { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + // FIXME: If there are more than one attributes, this will force multiline. + let mut result = self.attrs.rewrite(context, shape).unwrap_or(String::new()); + let has_attrs = !result.is_empty(); + + let mut param = String::with_capacity(128); + + let param_start = if let ast::GenericParamKind::Const { + ref ty, + kw_span, + default, + } = &self.kind + { + param.push_str("const "); + param.push_str(rewrite_ident(context, self.ident)); + param.push_str(": "); + param.push_str(&ty.rewrite(context, shape)?); + if let Some(default) = default { + let eq_str = match context.config.type_punctuation_density() { + TypeDensity::Compressed => "=", + TypeDensity::Wide => " = ", + }; + param.push_str(eq_str); + let budget = shape.width.checked_sub(param.len())?; + let rewrite = default.rewrite(context, Shape::legacy(budget, shape.indent))?; + param.push_str(&rewrite); + } + kw_span.lo() + } else { + param.push_str(rewrite_ident(context, self.ident)); + self.ident.span.lo() + }; + + if !self.bounds.is_empty() { + param.push_str(type_bound_colon(context)); + param.push_str(&self.bounds.rewrite(context, shape)?) + } + if let ast::GenericParamKind::Type { + default: Some(ref def), + } = self.kind + { + let eq_str = match context.config.type_punctuation_density() { + TypeDensity::Compressed => "=", + TypeDensity::Wide => " = ", + }; + param.push_str(eq_str); + let budget = shape.width.checked_sub(param.len())?; + let rewrite = + def.rewrite(context, Shape::legacy(budget, shape.indent + param.len()))?; + param.push_str(&rewrite); + } + + if let Some(last_attr) = self.attrs.last().filter(|last_attr| { + contains_comment(context.snippet(mk_sp(last_attr.span.hi(), param_start))) + }) { + result = combine_strs_with_missing_comments( + context, + &result, + ¶m, + mk_sp(last_attr.span.hi(), param_start), + shape, + !last_attr.is_doc_comment(), + )?; + } else { + // 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 if has_attrs { + result.push(' '); + } + result.push_str(¶m); + } + + Some(result) + } +} + +impl Rewrite for ast::PolyTraitRef { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + if let Some(lifetime_str) = + rewrite_lifetime_param(context, shape, &self.bound_generic_params) + { + // 6 is "for<> ".len() + let extra_offset = lifetime_str.len() + 6; + let path_str = self + .trait_ref + .rewrite(context, shape.offset_left(extra_offset)?)?; + + Some(format!("for<{}> {}", lifetime_str, path_str)) + } else { + self.trait_ref.rewrite(context, shape) + } + } +} + +impl Rewrite for ast::TraitRef { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + rewrite_path(context, PathContext::Type, &None, &self.path, shape) + } +} + +impl Rewrite for ast::Ty { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + match self.kind { + ast::TyKind::TraitObject(ref bounds, tobj_syntax) => { + // we have to consider 'dyn' keyword is used or not!!! + let is_dyn = tobj_syntax == ast::TraitObjectSyntax::Dyn; + // 4 is length of 'dyn ' + let shape = if is_dyn { shape.offset_left(4)? } else { shape }; + let mut res = bounds.rewrite(context, shape)?; + // We may have falsely removed a trailing `+` inside macro call. + if context.inside_macro() && bounds.len() == 1 { + if context.snippet(self.span).ends_with('+') && !res.ends_with('+') { + res.push('+'); + } + } + if is_dyn { + Some(format!("dyn {}", res)) + } else { + Some(res) + } + } + ast::TyKind::Ptr(ref mt) => { + let prefix = match mt.mutbl { + Mutability::Mut => "*mut ", + Mutability::Not => "*const ", + }; + + rewrite_unary_prefix(context, prefix, &*mt.ty, shape) + } + ast::TyKind::Ref(ref lifetime, ref mt) => { + let mut_str = format_mutability(mt.mutbl); + let mut_len = mut_str.len(); + let mut result = String::with_capacity(128); + result.push('&'); + let ref_hi = context.snippet_provider.span_after(self.span(), "&"); + let mut cmnt_lo = ref_hi; + + if let Some(ref lifetime) = *lifetime { + let lt_budget = shape.width.checked_sub(2 + mut_len)?; + let lt_str = lifetime.rewrite( + context, + Shape::legacy(lt_budget, shape.indent + 2 + mut_len), + )?; + let before_lt_span = mk_sp(cmnt_lo, lifetime.ident.span.lo()); + if contains_comment(context.snippet(before_lt_span)) { + result = combine_strs_with_missing_comments( + context, + &result, + <_str, + before_lt_span, + shape, + true, + )?; + } else { + result.push_str(<_str); + } + result.push(' '); + cmnt_lo = lifetime.ident.span.hi(); + } + + if ast::Mutability::Mut == mt.mutbl { + let mut_hi = context.snippet_provider.span_after(self.span(), "mut"); + let before_mut_span = mk_sp(cmnt_lo, mut_hi - BytePos::from_usize(3)); + if contains_comment(context.snippet(before_mut_span)) { + result = combine_strs_with_missing_comments( + context, + result.trim_end(), + mut_str, + before_mut_span, + shape, + true, + )?; + } else { + result.push_str(mut_str); + } + cmnt_lo = mut_hi; + } + + let before_ty_span = mk_sp(cmnt_lo, mt.ty.span.lo()); + if contains_comment(context.snippet(before_ty_span)) { + result = combine_strs_with_missing_comments( + context, + result.trim_end(), + &mt.ty.rewrite(context, shape)?, + before_ty_span, + shape, + true, + )?; + } else { + let used_width = last_line_width(&result); + let budget = shape.width.checked_sub(used_width)?; + let ty_str = mt + .ty + .rewrite(context, Shape::legacy(budget, shape.indent + used_width))?; + result.push_str(&ty_str); + } + + Some(result) + } + // FIXME: we drop any comments here, even though it's a silly place to put + // comments. + ast::TyKind::Paren(ref ty) => { + if context.config.version() == Version::One + || context.config.indent_style() == IndentStyle::Visual + { + let budget = shape.width.checked_sub(2)?; + return ty + .rewrite(context, Shape::legacy(budget, shape.indent + 1)) + .map(|ty_str| format!("({})", ty_str)); + } + + // 2 = () + if let Some(sh) = shape.sub_width(2) { + if let Some(ref s) = ty.rewrite(context, sh) { + if !s.contains('\n') { + return Some(format!("({})", s)); + } + } + } + + let indent_str = shape.indent.to_string_with_newline(context.config); + let shape = shape + .block_indent(context.config.tab_spaces()) + .with_max_width(context.config); + let rw = ty.rewrite(context, shape)?; + Some(format!( + "({}{}{})", + shape.to_string_with_newline(context.config), + rw, + indent_str + )) + } + ast::TyKind::Slice(ref ty) => { + let budget = shape.width.checked_sub(4)?; + ty.rewrite(context, Shape::legacy(budget, shape.indent + 1)) + .map(|ty_str| format!("[{}]", ty_str)) + } + ast::TyKind::Tup(ref items) => { + rewrite_tuple(context, items.iter(), self.span, shape, items.len() == 1) + } + ast::TyKind::Path(ref q_self, ref path) => { + rewrite_path(context, PathContext::Type, q_self, path, shape) + } + ast::TyKind::Array(ref ty, ref repeats) => rewrite_pair( + &**ty, + &*repeats.value, + PairParts::new("[", "; ", "]"), + context, + shape, + SeparatorPlace::Back, + ), + ast::TyKind::Infer => { + if shape.width >= 1 { + Some("_".to_owned()) + } else { + None + } + } + ast::TyKind::BareFn(ref bare_fn) => rewrite_bare_fn(bare_fn, self.span, context, shape), + ast::TyKind::Never => Some(String::from("!")), + ast::TyKind::MacCall(ref mac) => { + rewrite_macro(mac, None, context, shape, MacroPosition::Expression) + } + ast::TyKind::ImplicitSelf => Some(String::from("")), + ast::TyKind::ImplTrait(_, ref it) => { + // Empty trait is not a parser error. + if it.is_empty() { + return Some("impl".to_owned()); + } + let rw = if context.config.version() == Version::One { + it.rewrite(context, shape) + } else { + join_bounds(context, shape, it, false) + }; + rw.map(|it_str| { + let space = if it_str.is_empty() { "" } else { " " }; + format!("impl{}{}", space, it_str) + }) + } + ast::TyKind::CVarArgs => Some("...".to_owned()), + ast::TyKind::Err => Some(context.snippet(self.span).to_owned()), + ast::TyKind::Typeof(ref anon_const) => rewrite_call( + context, + "typeof", + &[anon_const.value.clone()], + self.span, + shape, + ), + } + } +} + +fn rewrite_bare_fn( + bare_fn: &ast::BareFnTy, + span: Span, + context: &RewriteContext<'_>, + shape: Shape, +) -> Option { + debug!("rewrite_bare_fn {:#?}", shape); + + let mut result = String::with_capacity(128); + + if let Some(ref lifetime_str) = rewrite_lifetime_param(context, shape, &bare_fn.generic_params) + { + result.push_str("for<"); + // 6 = "for<> ".len(), 4 = "for<". + // This doesn't work out so nicely for multiline situation with lots of + // rightward drift. If that is a problem, we could use the list stuff. + result.push_str(lifetime_str); + result.push_str("> "); + } + + result.push_str(crate::utils::format_unsafety(bare_fn.unsafety)); + + result.push_str(&format_extern( + bare_fn.ext, + context.config.force_explicit_abi(), + false, + )); + + result.push_str("fn"); + + let func_ty_shape = if context.use_block_indent() { + shape.offset_left(result.len())? + } else { + shape.visual_indent(result.len()).sub_width(result.len())? + }; + + let rewrite = format_function_type( + bare_fn.decl.inputs.iter(), + &bare_fn.decl.output, + bare_fn.decl.c_variadic(), + span, + context, + func_ty_shape, + )?; + + result.push_str(&rewrite); + + Some(result) +} + +fn is_generic_bounds_in_order(generic_bounds: &[ast::GenericBound]) -> bool { + let is_trait = |b: &ast::GenericBound| match b { + ast::GenericBound::Outlives(..) => false, + ast::GenericBound::Trait(..) => true, + }; + let is_lifetime = |b: &ast::GenericBound| !is_trait(b); + let last_trait_index = generic_bounds.iter().rposition(is_trait); + let first_lifetime_index = generic_bounds.iter().position(is_lifetime); + match (last_trait_index, first_lifetime_index) { + (Some(last_trait_index), Some(first_lifetime_index)) => { + last_trait_index < first_lifetime_index + } + _ => true, + } +} + +fn join_bounds( + context: &RewriteContext<'_>, + shape: Shape, + items: &[ast::GenericBound], + need_indent: bool, +) -> Option { + join_bounds_inner(context, shape, items, need_indent, false) +} + +fn join_bounds_inner( + context: &RewriteContext<'_>, + shape: Shape, + items: &[ast::GenericBound], + need_indent: bool, + force_newline: bool, +) -> Option { + debug_assert!(!items.is_empty()); + + let generic_bounds_in_order = is_generic_bounds_in_order(items); + let is_bound_extendable = |s: &str, b: &ast::GenericBound| match b { + ast::GenericBound::Outlives(..) => true, + ast::GenericBound::Trait(..) => last_line_extendable(s), + }; + + // Whether a GenericBound item is a PathSegment segment that includes internal array + // that contains more than one item + let is_item_with_multi_items_array = |item: &ast::GenericBound| match item { + ast::GenericBound::Trait(ref poly_trait_ref, ..) => { + let segments = &poly_trait_ref.trait_ref.path.segments; + if segments.len() > 1 { + true + } else { + if let Some(args_in) = &segments[0].args { + matches!( + args_in.deref(), + ast::GenericArgs::AngleBracketed(bracket_args) + if bracket_args.args.len() > 1 + ) + } else { + false + } + } + } + _ => false, + }; + + let result = items.iter().enumerate().try_fold( + (String::new(), None, false), + |(strs, prev_trailing_span, prev_extendable), (i, item)| { + let trailing_span = if i < items.len() - 1 { + let hi = context + .snippet_provider + .span_before(mk_sp(items[i + 1].span().lo(), item.span().hi()), "+"); + + Some(mk_sp(item.span().hi(), hi)) + } else { + None + }; + let (leading_span, has_leading_comment) = if i > 0 { + let lo = context + .snippet_provider + .span_after(mk_sp(items[i - 1].span().hi(), item.span().lo()), "+"); + + let span = mk_sp(lo, item.span().lo()); + + let has_comments = contains_comment(context.snippet(span)); + + (Some(mk_sp(lo, item.span().lo())), has_comments) + } else { + (None, false) + }; + let prev_has_trailing_comment = match prev_trailing_span { + Some(ts) => contains_comment(context.snippet(ts)), + _ => false, + }; + + let shape = if need_indent && force_newline { + shape + .block_indent(context.config.tab_spaces()) + .with_max_width(context.config) + } else { + shape + }; + let whitespace = if force_newline && (!prev_extendable || !generic_bounds_in_order) { + shape + .indent + .to_string_with_newline(context.config) + .to_string() + } else { + String::from(" ") + }; + + let joiner = match context.config.type_punctuation_density() { + TypeDensity::Compressed => String::from("+"), + TypeDensity::Wide => whitespace + "+ ", + }; + let joiner = if has_leading_comment { + joiner.trim_end() + } else { + &joiner + }; + let joiner = if prev_has_trailing_comment { + joiner.trim_start() + } else { + joiner + }; + + let (extendable, trailing_str) = if i == 0 { + let bound_str = item.rewrite(context, shape)?; + (is_bound_extendable(&bound_str, item), bound_str) + } else { + let bound_str = &item.rewrite(context, shape)?; + match leading_span { + Some(ls) if has_leading_comment => ( + is_bound_extendable(bound_str, item), + combine_strs_with_missing_comments( + context, joiner, bound_str, ls, shape, true, + )?, + ), + _ => ( + is_bound_extendable(bound_str, item), + String::from(joiner) + bound_str, + ), + } + }; + match prev_trailing_span { + Some(ts) if prev_has_trailing_comment => combine_strs_with_missing_comments( + context, + &strs, + &trailing_str, + ts, + shape, + true, + ) + .map(|v| (v, trailing_span, extendable)), + _ => Some((strs + &trailing_str, trailing_span, extendable)), + } + }, + )?; + + // Whether to retry with a forced newline: + // Only if result is not already multiline and did not exceed line width, + // and either there is more than one item; + // or the single item is of type `Trait`, + // and any of the internal arrays contains more than one item; + let retry_with_force_newline = match context.config.version() { + Version::One => { + !force_newline + && items.len() > 1 + && (result.0.contains('\n') || result.0.len() > shape.width) + } + Version::Two if force_newline => false, + Version::Two if (!result.0.contains('\n') && result.0.len() <= shape.width) => false, + Version::Two if items.len() > 1 => true, + Version::Two => is_item_with_multi_items_array(&items[0]), + }; + + if retry_with_force_newline { + join_bounds_inner(context, shape, items, need_indent, true) + } else { + Some(result.0) + } +} + +pub(crate) fn opaque_ty(ty: &Option>) -> Option<&ast::GenericBounds> { + ty.as_ref().and_then(|t| match &t.kind { + ast::TyKind::ImplTrait(_, bounds) => Some(bounds), + _ => None, + }) +} + +pub(crate) fn can_be_overflowed_type( + context: &RewriteContext<'_>, + ty: &ast::Ty, + len: usize, +) -> bool { + match ty.kind { + ast::TyKind::Tup(..) => context.use_block_indent() && len == 1, + ast::TyKind::Ref(_, ref mutty) | ast::TyKind::Ptr(ref mutty) => { + can_be_overflowed_type(context, &*mutty.ty, len) + } + _ => false, + } +} + +/// Returns `None` if there is no `LifetimeDef` in the given generic parameters. +pub(crate) fn rewrite_lifetime_param( + context: &RewriteContext<'_>, + shape: Shape, + generic_params: &[ast::GenericParam], +) -> Option { + let result = generic_params + .iter() + .filter(|p| matches!(p.kind, ast::GenericParamKind::Lifetime)) + .map(|lt| lt.rewrite(context, shape)) + .collect::>>()? + .join(", "); + if result.is_empty() { + None + } else { + Some(result) + } +} diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 000000000000..890a05b8c825 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,712 @@ +use std::borrow::Cow; + +use rustc_ast::ast::{ + self, Attribute, MetaItem, MetaItemKind, NestedMetaItem, NodeId, Path, Visibility, + VisibilityKind, +}; +use rustc_ast::ptr; +use rustc_ast_pretty::pprust; +use rustc_span::{sym, symbol, BytePos, LocalExpnId, Span, Symbol, SyntaxContext}; +use unicode_width::UnicodeWidthStr; + +use crate::comment::{filter_normal_code, CharClasses, FullCodeCharKind, LineClasses}; +use crate::config::{Config, Version}; +use crate::rewrite::RewriteContext; +use crate::shape::{Indent, Shape}; + +#[inline] +pub(crate) fn depr_skip_annotation() -> Symbol { + Symbol::intern("rustfmt_skip") +} + +#[inline] +pub(crate) fn skip_annotation() -> Symbol { + Symbol::intern("rustfmt::skip") +} + +pub(crate) fn rewrite_ident<'a>(context: &'a RewriteContext<'_>, ident: symbol::Ident) -> &'a str { + context.snippet(ident.span) +} + +// Computes the length of a string's last line, minus offset. +pub(crate) fn extra_offset(text: &str, shape: Shape) -> usize { + match text.rfind('\n') { + // 1 for newline character + Some(idx) => text.len().saturating_sub(idx + 1 + shape.used_width()), + None => text.len(), + } +} + +pub(crate) fn is_same_visibility(a: &Visibility, b: &Visibility) -> bool { + match (&a.kind, &b.kind) { + ( + VisibilityKind::Restricted { path: p, .. }, + VisibilityKind::Restricted { path: q, .. }, + ) => pprust::path_to_string(p) == pprust::path_to_string(q), + (VisibilityKind::Public, VisibilityKind::Public) + | (VisibilityKind::Inherited, VisibilityKind::Inherited) => true, + _ => false, + } +} + +// Uses Cow to avoid allocating in the common cases. +pub(crate) fn format_visibility( + context: &RewriteContext<'_>, + vis: &Visibility, +) -> Cow<'static, str> { + match vis.kind { + VisibilityKind::Public => Cow::from("pub "), + VisibilityKind::Inherited => Cow::from(""), + VisibilityKind::Restricted { ref path, .. } => { + let Path { ref segments, .. } = **path; + let mut segments_iter = segments.iter().map(|seg| rewrite_ident(context, seg.ident)); + if path.is_global() { + segments_iter + .next() + .expect("Non-global path in pub(restricted)?"); + } + let is_keyword = |s: &str| s == "crate" || s == "self" || s == "super"; + let path = segments_iter.collect::>().join("::"); + let in_str = if is_keyword(&path) { "" } else { "in " }; + + Cow::from(format!("pub({}{}) ", in_str, path)) + } + } +} + +#[inline] +pub(crate) fn format_async(is_async: &ast::Async) -> &'static str { + match is_async { + ast::Async::Yes { .. } => "async ", + ast::Async::No => "", + } +} + +#[inline] +pub(crate) fn format_constness(constness: ast::Const) -> &'static str { + match constness { + ast::Const::Yes(..) => "const ", + ast::Const::No => "", + } +} + +#[inline] +pub(crate) fn format_constness_right(constness: ast::Const) -> &'static str { + match constness { + ast::Const::Yes(..) => " const", + ast::Const::No => "", + } +} + +#[inline] +pub(crate) fn format_defaultness(defaultness: ast::Defaultness) -> &'static str { + match defaultness { + ast::Defaultness::Default(..) => "default ", + ast::Defaultness::Final => "", + } +} + +#[inline] +pub(crate) fn format_unsafety(unsafety: ast::Unsafe) -> &'static str { + match unsafety { + ast::Unsafe::Yes(..) => "unsafe ", + ast::Unsafe::No => "", + } +} + +#[inline] +pub(crate) fn format_auto(is_auto: ast::IsAuto) -> &'static str { + match is_auto { + ast::IsAuto::Yes => "auto ", + ast::IsAuto::No => "", + } +} + +#[inline] +pub(crate) fn format_mutability(mutability: ast::Mutability) -> &'static str { + match mutability { + ast::Mutability::Mut => "mut ", + ast::Mutability::Not => "", + } +} + +#[inline] +pub(crate) fn format_extern( + ext: ast::Extern, + explicit_abi: bool, + is_mod: bool, +) -> Cow<'static, str> { + let abi = match ext { + ast::Extern::None => "Rust".to_owned(), + ast::Extern::Implicit(_) => "C".to_owned(), + ast::Extern::Explicit(abi, _) => abi.symbol_unescaped.to_string(), + }; + + if abi == "Rust" && !is_mod { + Cow::from("") + } else if abi == "C" && !explicit_abi { + Cow::from("extern ") + } else { + Cow::from(format!(r#"extern "{}" "#, abi)) + } +} + +#[inline] +// Transform `Vec>` into `Vec<&T>` +pub(crate) fn ptr_vec_to_ref_vec(vec: &[ptr::P]) -> Vec<&T> { + vec.iter().map(|x| &**x).collect::>() +} + +#[inline] +pub(crate) fn filter_attributes( + attrs: &[ast::Attribute], + style: ast::AttrStyle, +) -> Vec { + attrs + .iter() + .filter(|a| a.style == style) + .cloned() + .collect::>() +} + +#[inline] +pub(crate) fn inner_attributes(attrs: &[ast::Attribute]) -> Vec { + filter_attributes(attrs, ast::AttrStyle::Inner) +} + +#[inline] +pub(crate) fn outer_attributes(attrs: &[ast::Attribute]) -> Vec { + filter_attributes(attrs, ast::AttrStyle::Outer) +} + +#[inline] +pub(crate) fn is_single_line(s: &str) -> bool { + !s.chars().any(|c| c == '\n') +} + +#[inline] +pub(crate) fn first_line_contains_single_line_comment(s: &str) -> bool { + s.lines().next().map_or(false, |l| l.contains("//")) +} + +#[inline] +pub(crate) fn last_line_contains_single_line_comment(s: &str) -> bool { + s.lines().last().map_or(false, |l| l.contains("//")) +} + +#[inline] +pub(crate) fn is_attributes_extendable(attrs_str: &str) -> bool { + !attrs_str.contains('\n') && !last_line_contains_single_line_comment(attrs_str) +} + +/// The width of the first line in s. +#[inline] +pub(crate) fn first_line_width(s: &str) -> usize { + unicode_str_width(s.splitn(2, '\n').next().unwrap_or("")) +} + +/// The width of the last line in s. +#[inline] +pub(crate) fn last_line_width(s: &str) -> usize { + unicode_str_width(s.rsplitn(2, '\n').next().unwrap_or("")) +} + +/// The total used width of the last line. +#[inline] +pub(crate) fn last_line_used_width(s: &str, offset: usize) -> usize { + if s.contains('\n') { + last_line_width(s) + } else { + offset + unicode_str_width(s) + } +} + +#[inline] +pub(crate) fn trimmed_last_line_width(s: &str) -> usize { + unicode_str_width(match s.rfind('\n') { + Some(n) => s[(n + 1)..].trim(), + None => s.trim(), + }) +} + +#[inline] +pub(crate) fn last_line_extendable(s: &str) -> bool { + if s.ends_with("\"#") { + return true; + } + for c in s.chars().rev() { + match c { + '(' | ')' | ']' | '}' | '?' | '>' => continue, + '\n' => break, + _ if c.is_whitespace() => continue, + _ => return false, + } + } + true +} + +#[inline] +fn is_skip(meta_item: &MetaItem) -> bool { + match meta_item.kind { + MetaItemKind::Word => { + let path_str = pprust::path_to_string(&meta_item.path); + path_str == skip_annotation().as_str() || path_str == depr_skip_annotation().as_str() + } + MetaItemKind::List(ref l) => { + meta_item.has_name(sym::cfg_attr) && l.len() == 2 && is_skip_nested(&l[1]) + } + _ => false, + } +} + +#[inline] +fn is_skip_nested(meta_item: &NestedMetaItem) -> bool { + match meta_item { + NestedMetaItem::MetaItem(ref mi) => is_skip(mi), + NestedMetaItem::Lit(_) => false, + } +} + +#[inline] +pub(crate) fn contains_skip(attrs: &[Attribute]) -> bool { + attrs + .iter() + .any(|a| a.meta().map_or(false, |a| is_skip(&a))) +} + +#[inline] +pub(crate) fn semicolon_for_expr(context: &RewriteContext<'_>, expr: &ast::Expr) -> bool { + // Never try to insert semicolons on expressions when we're inside + // a macro definition - this can prevent the macro from compiling + // when used in expression position + if context.is_macro_def { + return false; + } + + match expr.kind { + ast::ExprKind::Ret(..) | ast::ExprKind::Continue(..) | ast::ExprKind::Break(..) => { + context.config.trailing_semicolon() + } + _ => false, + } +} + +#[inline] +pub(crate) fn semicolon_for_stmt(context: &RewriteContext<'_>, stmt: &ast::Stmt) -> bool { + match stmt.kind { + ast::StmtKind::Semi(ref expr) => match expr.kind { + ast::ExprKind::While(..) | ast::ExprKind::Loop(..) | ast::ExprKind::ForLoop(..) => { + false + } + ast::ExprKind::Break(..) | ast::ExprKind::Continue(..) | ast::ExprKind::Ret(..) => { + context.config.trailing_semicolon() + } + _ => true, + }, + ast::StmtKind::Expr(..) => false, + _ => true, + } +} + +#[inline] +pub(crate) fn stmt_expr(stmt: &ast::Stmt) -> Option<&ast::Expr> { + match stmt.kind { + ast::StmtKind::Expr(ref expr) => Some(expr), + _ => None, + } +} + +/// Returns the number of LF and CRLF respectively. +pub(crate) fn count_lf_crlf(input: &str) -> (usize, usize) { + let mut lf = 0; + let mut crlf = 0; + let mut is_crlf = false; + for c in input.as_bytes() { + match c { + b'\r' => is_crlf = true, + b'\n' if is_crlf => crlf += 1, + b'\n' => lf += 1, + _ => is_crlf = false, + } + } + (lf, crlf) +} + +pub(crate) fn count_newlines(input: &str) -> usize { + // Using bytes to omit UTF-8 decoding + bytecount::count(input.as_bytes(), b'\n') +} + +// For format_missing and last_pos, need to use the source callsite (if applicable). +// Required as generated code spans aren't guaranteed to follow on from the last span. +macro_rules! source { + ($this:ident, $sp:expr) => { + $sp.source_callsite() + }; +} + +pub(crate) fn mk_sp(lo: BytePos, hi: BytePos) -> Span { + Span::new(lo, hi, SyntaxContext::root(), None) +} + +pub(crate) fn mk_sp_lo_plus_one(lo: BytePos) -> Span { + Span::new(lo, lo + BytePos(1), SyntaxContext::root(), None) +} + +// Returns `true` if the given span does not intersect with file lines. +macro_rules! out_of_file_lines_range { + ($self:ident, $span:expr) => { + !$self.config.file_lines().is_all() + && !$self + .config + .file_lines() + .intersects(&$self.parse_sess.lookup_line_range($span)) + }; +} + +macro_rules! skip_out_of_file_lines_range { + ($self:ident, $span:expr) => { + if out_of_file_lines_range!($self, $span) { + return None; + } + }; +} + +macro_rules! skip_out_of_file_lines_range_visitor { + ($self:ident, $span:expr) => { + if out_of_file_lines_range!($self, $span) { + $self.push_rewrite($span, None); + return; + } + }; +} + +// Wraps String in an Option. Returns Some when the string adheres to the +// Rewrite constraints defined for the Rewrite trait and None otherwise. +pub(crate) fn wrap_str(s: String, max_width: usize, shape: Shape) -> Option { + if filtered_str_fits(&s, max_width, shape) { + Some(s) + } else { + None + } +} + +pub(crate) fn filtered_str_fits(snippet: &str, max_width: usize, shape: Shape) -> bool { + let snippet = &filter_normal_code(snippet); + if !snippet.is_empty() { + // First line must fits with `shape.width`. + if first_line_width(snippet) > shape.width { + return false; + } + // If the snippet does not include newline, we are done. + if is_single_line(snippet) { + return true; + } + // The other lines must fit within the maximum width. + if snippet + .lines() + .skip(1) + .any(|line| unicode_str_width(line) > max_width) + { + return false; + } + // A special check for the last line, since the caller may + // place trailing characters on this line. + if last_line_width(snippet) > shape.used_width() + shape.width { + return false; + } + } + true +} + +#[inline] +pub(crate) fn colon_spaces(config: &Config) -> &'static str { + let before = config.space_before_colon(); + let after = config.space_after_colon(); + match (before, after) { + (true, true) => " : ", + (true, false) => " :", + (false, true) => ": ", + (false, false) => ":", + } +} + +#[inline] +pub(crate) fn left_most_sub_expr(e: &ast::Expr) -> &ast::Expr { + match e.kind { + ast::ExprKind::Call(ref e, _) + | ast::ExprKind::Binary(_, ref e, _) + | ast::ExprKind::Cast(ref e, _) + | ast::ExprKind::Type(ref e, _) + | ast::ExprKind::Assign(ref e, _, _) + | ast::ExprKind::AssignOp(_, ref e, _) + | ast::ExprKind::Field(ref e, _) + | ast::ExprKind::Index(ref e, _) + | ast::ExprKind::Range(Some(ref e), _, _) + | ast::ExprKind::Try(ref e) => left_most_sub_expr(e), + _ => e, + } +} + +#[inline] +pub(crate) fn starts_with_newline(s: &str) -> bool { + s.starts_with('\n') || s.starts_with("\r\n") +} + +#[inline] +pub(crate) fn first_line_ends_with(s: &str, c: char) -> bool { + s.lines().next().map_or(false, |l| l.ends_with(c)) +} + +// States whether an expression's last line exclusively consists of closing +// parens, braces, and brackets in its idiomatic formatting. +pub(crate) fn is_block_expr(context: &RewriteContext<'_>, expr: &ast::Expr, repr: &str) -> bool { + match expr.kind { + ast::ExprKind::MacCall(..) + | ast::ExprKind::FormatArgs(..) + | ast::ExprKind::Call(..) + | ast::ExprKind::MethodCall(..) + | ast::ExprKind::Array(..) + | ast::ExprKind::Struct(..) + | ast::ExprKind::While(..) + | ast::ExprKind::If(..) + | ast::ExprKind::Block(..) + | ast::ExprKind::ConstBlock(..) + | ast::ExprKind::Async(..) + | ast::ExprKind::Loop(..) + | ast::ExprKind::ForLoop(..) + | ast::ExprKind::TryBlock(..) + | ast::ExprKind::Match(..) => repr.contains('\n'), + ast::ExprKind::Paren(ref expr) + | ast::ExprKind::Binary(_, _, ref expr) + | ast::ExprKind::Index(_, ref expr) + | ast::ExprKind::Unary(_, ref expr) + | ast::ExprKind::Try(ref expr) + | ast::ExprKind::Yield(Some(ref expr)) => is_block_expr(context, expr, repr), + ast::ExprKind::Closure(ref closure) => is_block_expr(context, &closure.body, repr), + // This can only be a string lit + ast::ExprKind::Lit(_) => { + repr.contains('\n') && trimmed_last_line_width(repr) <= context.config.tab_spaces() + } + ast::ExprKind::AddrOf(..) + | ast::ExprKind::Assign(..) + | ast::ExprKind::AssignOp(..) + | ast::ExprKind::Await(..) + | ast::ExprKind::Break(..) + | ast::ExprKind::Cast(..) + | ast::ExprKind::Continue(..) + | ast::ExprKind::Err + | ast::ExprKind::Field(..) + | ast::ExprKind::IncludedBytes(..) + | ast::ExprKind::InlineAsm(..) + | ast::ExprKind::OffsetOf(..) + | ast::ExprKind::Let(..) + | ast::ExprKind::Path(..) + | ast::ExprKind::Range(..) + | ast::ExprKind::Repeat(..) + | ast::ExprKind::Ret(..) + | ast::ExprKind::Become(..) + | ast::ExprKind::Yeet(..) + | ast::ExprKind::Tup(..) + | ast::ExprKind::Type(..) + | ast::ExprKind::Yield(None) + | ast::ExprKind::Underscore => false, + } +} + +/// Removes trailing spaces from the specified snippet. We do not remove spaces +/// inside strings or comments. +pub(crate) fn remove_trailing_white_spaces(text: &str) -> String { + let mut buffer = String::with_capacity(text.len()); + let mut space_buffer = String::with_capacity(128); + for (char_kind, c) in CharClasses::new(text.chars()) { + match c { + '\n' => { + if char_kind == FullCodeCharKind::InString { + buffer.push_str(&space_buffer); + } + space_buffer.clear(); + buffer.push('\n'); + } + _ if c.is_whitespace() => { + space_buffer.push(c); + } + _ => { + if !space_buffer.is_empty() { + buffer.push_str(&space_buffer); + space_buffer.clear(); + } + buffer.push(c); + } + } + } + buffer +} + +/// Indent each line according to the specified `indent`. +/// e.g. +/// +/// ```rust,compile_fail +/// foo!{ +/// x, +/// y, +/// foo( +/// a, +/// b, +/// c, +/// ), +/// } +/// ``` +/// +/// will become +/// +/// ```rust,compile_fail +/// foo!{ +/// x, +/// y, +/// foo( +/// a, +/// b, +/// c, +/// ), +/// } +/// ``` +pub(crate) fn trim_left_preserve_layout( + orig: &str, + indent: Indent, + config: &Config, +) -> Option { + let mut lines = LineClasses::new(orig); + let first_line = lines.next().map(|(_, s)| s.trim_end().to_owned())?; + let mut trimmed_lines = Vec::with_capacity(16); + + let mut veto_trim = false; + let min_prefix_space_width = lines + .filter_map(|(kind, line)| { + let mut trimmed = true; + let prefix_space_width = if is_empty_line(&line) { + None + } else { + Some(get_prefix_space_width(config, &line)) + }; + + // just InString{Commented} in order to allow the start of a string to be indented + let new_veto_trim_value = (kind == FullCodeCharKind::InString + || (config.version() == Version::Two + && kind == FullCodeCharKind::InStringCommented)) + && !line.ends_with('\\'); + let line = if veto_trim || new_veto_trim_value { + veto_trim = new_veto_trim_value; + trimmed = false; + line + } else { + line.trim().to_owned() + }; + trimmed_lines.push((trimmed, line, prefix_space_width)); + + // Because there is a veto against trimming and indenting lines within a string, + // such lines should not be taken into account when computing the minimum. + match kind { + FullCodeCharKind::InStringCommented | FullCodeCharKind::EndStringCommented + if config.version() == Version::Two => + { + None + } + FullCodeCharKind::InString | FullCodeCharKind::EndString => None, + _ => prefix_space_width, + } + }) + .min()?; + + Some( + first_line + + "\n" + + &trimmed_lines + .iter() + .map( + |&(trimmed, ref line, prefix_space_width)| match prefix_space_width { + _ if !trimmed => line.to_owned(), + Some(original_indent_width) => { + let new_indent_width = indent.width() + + original_indent_width.saturating_sub(min_prefix_space_width); + let new_indent = Indent::from_width(config, new_indent_width); + format!("{}{}", new_indent.to_string(config), line) + } + None => String::new(), + }, + ) + .collect::>() + .join("\n"), + ) +} + +/// 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 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 { + s.is_empty() || s.chars().all(char::is_whitespace) +} + +fn get_prefix_space_width(config: &Config, s: &str) -> usize { + let mut width = 0; + for c in s.chars() { + match c { + ' ' => width += 1, + '\t' => width += config.tab_spaces(), + _ => return width, + } + } + width +} + +pub(crate) trait NodeIdExt { + fn root() -> Self; +} + +impl NodeIdExt for NodeId { + fn root() -> NodeId { + NodeId::placeholder_from_expn_id(LocalExpnId::ROOT) + } +} + +pub(crate) fn unicode_str_width(s: &str) -> usize { + s.width() +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_remove_trailing_white_spaces() { + let s = " r#\"\n test\n \"#"; + assert_eq!(remove_trailing_white_spaces(s), s); + } + + #[test] + fn test_trim_left_preserve_layout() { + let s = "aaa\n\tbbb\n ccc"; + let config = Config::default(); + let indent = Indent::new(4, 0); + assert_eq!( + trim_left_preserve_layout(s, indent, &config), + Some("aaa\n bbb\n ccc".to_string()) + ); + } +} diff --git a/src/vertical.rs b/src/vertical.rs new file mode 100644 index 000000000000..a06bc995aa55 --- /dev/null +++ b/src/vertical.rs @@ -0,0 +1,302 @@ +// Format with vertical alignment. + +use std::cmp; + +use itertools::Itertools; +use rustc_ast::ast; +use rustc_span::{BytePos, Span}; + +use crate::comment::combine_strs_with_missing_comments; +use crate::config::lists::*; +use crate::expr::rewrite_field; +use crate::items::{rewrite_struct_field, rewrite_struct_field_prefix}; +use crate::lists::{ + definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator, +}; +use crate::rewrite::{Rewrite, RewriteContext}; +use crate::shape::{Indent, Shape}; +use crate::source_map::SpanUtils; +use crate::spanned::Spanned; +use crate::utils::{ + contains_skip, is_attributes_extendable, mk_sp, rewrite_ident, trimmed_last_line_width, +}; + +pub(crate) trait AlignedItem { + fn skip(&self) -> bool; + fn get_span(&self) -> Span; + fn rewrite_prefix(&self, context: &RewriteContext<'_>, shape: Shape) -> Option; + fn rewrite_aligned_item( + &self, + context: &RewriteContext<'_>, + shape: Shape, + prefix_max_width: usize, + ) -> Option; +} + +impl AlignedItem for ast::FieldDef { + fn skip(&self) -> bool { + contains_skip(&self.attrs) + } + + fn get_span(&self) -> Span { + self.span() + } + + fn rewrite_prefix(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + let attrs_str = self.attrs.rewrite(context, shape)?; + let missing_span = if self.attrs.is_empty() { + mk_sp(self.span.lo(), self.span.lo()) + } else { + mk_sp(self.attrs.last().unwrap().span.hi(), self.span.lo()) + }; + let attrs_extendable = self.ident.is_none() && is_attributes_extendable(&attrs_str); + rewrite_struct_field_prefix(context, self).and_then(|field_str| { + combine_strs_with_missing_comments( + context, + &attrs_str, + &field_str, + missing_span, + shape, + attrs_extendable, + ) + }) + } + + fn rewrite_aligned_item( + &self, + context: &RewriteContext<'_>, + shape: Shape, + prefix_max_width: usize, + ) -> Option { + rewrite_struct_field(context, self, shape, prefix_max_width) + } +} + +impl AlignedItem for ast::ExprField { + fn skip(&self) -> bool { + contains_skip(&self.attrs) + } + + fn get_span(&self) -> Span { + self.span() + } + + fn rewrite_prefix(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + let attrs_str = self.attrs.rewrite(context, shape)?; + let name = rewrite_ident(context, self.ident); + let missing_span = if self.attrs.is_empty() { + mk_sp(self.span.lo(), self.span.lo()) + } else { + mk_sp(self.attrs.last().unwrap().span.hi(), self.span.lo()) + }; + combine_strs_with_missing_comments( + context, + &attrs_str, + name, + missing_span, + shape, + is_attributes_extendable(&attrs_str), + ) + } + + fn rewrite_aligned_item( + &self, + context: &RewriteContext<'_>, + shape: Shape, + prefix_max_width: usize, + ) -> Option { + rewrite_field(context, self, shape, prefix_max_width) + } +} + +pub(crate) fn rewrite_with_alignment( + fields: &[T], + context: &RewriteContext<'_>, + shape: Shape, + span: Span, + one_line_width: usize, +) -> Option { + let (spaces, group_index) = if context.config.struct_field_align_threshold() > 0 { + group_aligned_items(context, fields) + } else { + ("", fields.len() - 1) + }; + let init = &fields[0..=group_index]; + let rest = &fields[group_index + 1..]; + let init_last_pos = if rest.is_empty() { + span.hi() + } else { + // Decide whether the missing comments should stick to init or rest. + let init_hi = init[init.len() - 1].get_span().hi(); + let rest_lo = rest[0].get_span().lo(); + let missing_span = mk_sp(init_hi, rest_lo); + let missing_span = mk_sp( + context.snippet_provider.span_after(missing_span, ","), + missing_span.hi(), + ); + + let snippet = context.snippet(missing_span); + if snippet.trim_start().starts_with("//") { + let offset = snippet.lines().next().map_or(0, str::len); + // 2 = "," + "\n" + init_hi + BytePos(offset as u32 + 2) + } else if snippet.trim_start().starts_with("/*") { + let comment_lines = snippet + .lines() + .position(|line| line.trim_end().ends_with("*/")) + .unwrap_or(0); + + let offset = snippet + .lines() + .take(comment_lines + 1) + .collect::>() + .join("\n") + .len(); + + init_hi + BytePos(offset as u32 + 2) + } else { + missing_span.lo() + } + }; + let init_span = mk_sp(span.lo(), init_last_pos); + let one_line_width = if rest.is_empty() { one_line_width } else { 0 }; + + // 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 { + let rest_span = mk_sp(init_last_pos, span.hi()); + let rest_str = rewrite_with_alignment(rest, context, shape, rest_span, one_line_width)?; + Some(format!( + "{}{}\n{}{}", + result, + spaces, + &shape.indent.to_string(context.config), + &rest_str + )) + } +} + +fn struct_field_prefix_max_min_width( + context: &RewriteContext<'_>, + fields: &[T], + shape: Shape, +) -> (usize, usize) { + fields + .iter() + .map(|field| { + field + .rewrite_prefix(context, shape) + .map(|field_str| trimmed_last_line_width(&field_str)) + }) + .fold_options((0, ::std::usize::MAX), |(max_len, min_len), len| { + (cmp::max(max_len, len), cmp::min(min_len, len)) + }) + .unwrap_or((0, 0)) +} + +fn rewrite_aligned_items_inner( + context: &RewriteContext<'_>, + fields: &[T], + 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)?; + let (mut field_prefix_max_width, field_prefix_min_width) = + struct_field_prefix_max_min_width(context, fields, item_shape); + let max_diff = field_prefix_max_width.saturating_sub(field_prefix_min_width); + if max_diff > context.config.struct_field_align_threshold() { + field_prefix_max_width = 0; + } + + let mut items = itemize_list( + context.snippet_provider, + fields.iter(), + "}", + ",", + |field| field.get_span().lo(), + |field| field.get_span().hi(), + |field| field.rewrite_aligned_item(context, item_shape, field_prefix_max_width), + span.lo(), + span.hi(), + false, + ) + .collect::>(); + + let tactic = definitive_tactic( + &items, + ListTactic::HorizontalVertical, + Separator::Comma, + one_line_width, + ); + + if tactic == DefinitiveListTactic::Horizontal { + // since the items fits on a line, there is no need to align them + let do_rewrite = + |field: &T| -> Option { field.rewrite_aligned_item(context, item_shape, 0) }; + fields + .iter() + .zip(items.iter_mut()) + .for_each(|(field, list_item): (&T, &mut ListItem)| { + if list_item.item.is_some() { + list_item.item = do_rewrite(field); + } + }); + } + + 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(separator_tactic) + .preserve_newline(true); + write_list(&items, &fmt) +} + +/// Returns the index in `fields` up to which a field belongs to the current group. +/// The returned string is the group separator to use when rewriting the fields. +/// Groups are defined by blank lines. +fn group_aligned_items( + context: &RewriteContext<'_>, + fields: &[T], +) -> (&'static str, usize) { + let mut index = 0; + for i in 0..fields.len() - 1 { + if fields[i].skip() { + return ("", index); + } + let span = mk_sp(fields[i].get_span().hi(), fields[i + 1].get_span().lo()); + let snippet = context + .snippet(span) + .lines() + .skip(1) + .collect::>() + .join("\n"); + let has_blank_line = snippet + .lines() + .dropping_back(1) + .any(|l| l.trim().is_empty()); + if has_blank_line { + return ("\n", index); + } + index += 1; + } + ("", index) +} diff --git a/src/visitor.rs b/src/visitor.rs new file mode 100644 index 000000000000..f4d84d1381fc --- /dev/null +++ b/src/visitor.rs @@ -0,0 +1,1019 @@ +use std::cell::{Cell, RefCell}; +use std::rc::Rc; + +use rustc_ast::{ast, token::Delimiter, visit}; +use rustc_data_structures::sync::Lrc; +use rustc_span::{symbol, BytePos, Pos, Span}; + +use crate::attr::*; +use crate::comment::{contains_comment, rewrite_comment, CodeCharKind, CommentCodeSlices}; +use crate::config::Version; +use crate::config::{BraceStyle, Config, MacroSelector}; +use crate::coverage::transform_missing_snippet; +use crate::items::{ + format_impl, format_trait, format_trait_alias, is_mod_decl, is_use_item, rewrite_extern_crate, + rewrite_type_alias, FnBraceStyle, FnSig, ItemVisitorKind, StaticParts, StructParts, +}; +use crate::macros::{macro_style, rewrite_macro, rewrite_macro_def, MacroPosition}; +use crate::modules::Module; +use crate::parse::session::ParseSess; +use crate::rewrite::{Rewrite, RewriteContext}; +use crate::shape::{Indent, Shape}; +use crate::skip::{is_skip_attr, SkipContext}; +use crate::source_map::{LineRangeUtils, SpanUtils}; +use crate::spanned::Spanned; +use crate::stmt::Stmt; +use crate::utils::{ + self, contains_skip, count_newlines, depr_skip_annotation, format_unsafety, inner_attributes, + last_line_width, mk_sp, ptr_vec_to_ref_vec, rewrite_ident, starts_with_newline, stmt_expr, +}; +use crate::{ErrorKind, FormatReport, FormattingError}; + +/// Creates a string slice corresponding to the specified span. +pub(crate) struct SnippetProvider { + /// A pointer to the content of the file we are formatting. + big_snippet: Lrc, + /// A position of the start of `big_snippet`, used as an offset. + start_pos: usize, + /// An end position of the file that this snippet lives. + end_pos: usize, +} + +impl SnippetProvider { + pub(crate) fn span_to_snippet(&self, span: Span) -> Option<&str> { + let start_index = span.lo().to_usize().checked_sub(self.start_pos)?; + let end_index = span.hi().to_usize().checked_sub(self.start_pos)?; + Some(&self.big_snippet[start_index..end_index]) + } + + pub(crate) fn new(start_pos: BytePos, end_pos: BytePos, big_snippet: Lrc) -> Self { + let start_pos = start_pos.to_usize(); + let end_pos = end_pos.to_usize(); + SnippetProvider { + big_snippet, + start_pos, + end_pos, + } + } + + pub(crate) fn entire_snippet(&self) -> &str { + self.big_snippet.as_str() + } + + pub(crate) fn start_pos(&self) -> BytePos { + BytePos::from_usize(self.start_pos) + } + + pub(crate) fn end_pos(&self) -> BytePos { + BytePos::from_usize(self.end_pos) + } +} + +pub(crate) struct FmtVisitor<'a> { + parent_context: Option<&'a RewriteContext<'a>>, + pub(crate) parse_sess: &'a ParseSess, + pub(crate) buffer: String, + pub(crate) last_pos: BytePos, + // FIXME: use an RAII util or closure for indenting + pub(crate) block_indent: Indent, + pub(crate) config: &'a Config, + pub(crate) is_if_else_block: bool, + pub(crate) snippet_provider: &'a SnippetProvider, + pub(crate) line_number: usize, + /// List of 1-based line ranges which were annotated with skip + /// Both bounds are inclusifs. + pub(crate) skipped_range: Rc>>, + pub(crate) macro_rewrite_failure: bool, + pub(crate) report: FormatReport, + pub(crate) skip_context: SkipContext, + pub(crate) is_macro_def: bool, +} + +impl<'a> Drop for FmtVisitor<'a> { + fn drop(&mut self) { + if let Some(ctx) = self.parent_context { + if self.macro_rewrite_failure { + ctx.macro_rewrite_failure.replace(true); + } + } + } +} + +impl<'b, 'a: 'b> FmtVisitor<'a> { + fn set_parent_context(&mut self, context: &'a RewriteContext<'_>) { + self.parent_context = Some(context); + } + + pub(crate) fn shape(&self) -> Shape { + Shape::indented(self.block_indent, self.config) + } + + fn next_span(&self, hi: BytePos) -> Span { + mk_sp(self.last_pos, hi) + } + + fn visit_stmt(&mut self, stmt: &Stmt<'_>, include_empty_semi: bool) { + debug!( + "visit_stmt: {}", + self.parse_sess.span_to_debug_info(stmt.span()) + ); + + if stmt.is_empty() { + // If the statement is empty, just skip over it. Before that, make sure any comment + // snippet preceding the semicolon is picked up. + let snippet = self.snippet(mk_sp(self.last_pos, stmt.span().lo())); + let original_starts_with_newline = snippet + .find(|c| c != ' ') + .map_or(false, |i| starts_with_newline(&snippet[i..])); + let snippet = snippet.trim(); + if !snippet.is_empty() { + // FIXME(calebcartwright 2021-01-03) - This exists strictly to maintain legacy + // formatting where rustfmt would preserve redundant semicolons on Items in a + // statement position. + // See comment within `walk_stmts` for more info + if include_empty_semi { + self.format_missing(stmt.span().hi()); + } else { + if original_starts_with_newline { + self.push_str("\n"); + } + + self.push_str(&self.block_indent.to_string(self.config)); + self.push_str(snippet); + } + } else if include_empty_semi { + self.push_str(";"); + } + self.last_pos = stmt.span().hi(); + return; + } + + match stmt.as_ast_node().kind { + ast::StmtKind::Item(ref item) => { + self.visit_item(item); + self.last_pos = stmt.span().hi(); + } + ast::StmtKind::Local(..) | ast::StmtKind::Expr(..) | ast::StmtKind::Semi(..) => { + let attrs = get_attrs_from_stmt(stmt.as_ast_node()); + if contains_skip(attrs) { + self.push_skipped_with_span( + attrs, + stmt.span(), + get_span_without_attrs(stmt.as_ast_node()), + ); + } else { + let shape = self.shape(); + let rewrite = self.with_context(|ctx| stmt.rewrite(ctx, shape)); + self.push_rewrite(stmt.span(), rewrite) + } + } + ast::StmtKind::MacCall(ref mac_stmt) => { + if self.visit_attrs(&mac_stmt.attrs, ast::AttrStyle::Outer) { + self.push_skipped_with_span( + &mac_stmt.attrs, + stmt.span(), + get_span_without_attrs(stmt.as_ast_node()), + ); + } else { + self.visit_mac(&mac_stmt.mac, None, MacroPosition::Statement); + } + self.format_missing(stmt.span().hi()); + } + ast::StmtKind::Empty => (), + } + } + + /// Remove spaces between the opening brace and the first statement or the inner attribute + /// of the block. + fn trim_spaces_after_opening_brace( + &mut self, + b: &ast::Block, + inner_attrs: Option<&[ast::Attribute]>, + ) { + if let Some(first_stmt) = b.stmts.first() { + let hi = inner_attrs + .and_then(|attrs| inner_attributes(attrs).first().map(|attr| attr.span.lo())) + .unwrap_or_else(|| first_stmt.span().lo()); + let missing_span = self.next_span(hi); + let snippet = self.snippet(missing_span); + let len = CommentCodeSlices::new(snippet) + .next() + .and_then(|(kind, _, s)| { + if kind == CodeCharKind::Normal { + s.rfind('\n') + } else { + None + } + }); + if let Some(len) = len { + self.last_pos = self.last_pos + BytePos::from_usize(len); + } + } + } + + pub(crate) fn visit_block( + &mut self, + b: &ast::Block, + inner_attrs: Option<&[ast::Attribute]>, + has_braces: bool, + ) { + debug!( + "visit_block: {}", + self.parse_sess.span_to_debug_info(b.span), + ); + + // Check if this block has braces. + let brace_compensation = BytePos(if has_braces { 1 } else { 0 }); + + self.last_pos = self.last_pos + brace_compensation; + self.block_indent = self.block_indent.block_indent(self.config); + self.push_str("{"); + self.trim_spaces_after_opening_brace(b, inner_attrs); + + // Format inner attributes if available. + if let Some(attrs) = inner_attrs { + self.visit_attrs(attrs, ast::AttrStyle::Inner); + } + + self.walk_block_stmts(b); + + if !b.stmts.is_empty() { + if let Some(expr) = stmt_expr(&b.stmts[b.stmts.len() - 1]) { + if utils::semicolon_for_expr(&self.get_context(), expr) { + self.push_str(";"); + } + } + } + + let rest_span = self.next_span(b.span.hi()); + if out_of_file_lines_range!(self, rest_span) { + self.push_str(self.snippet(rest_span)); + self.block_indent = self.block_indent.block_unindent(self.config); + } else { + // Ignore the closing brace. + let missing_span = self.next_span(b.span.hi() - brace_compensation); + self.close_block(missing_span, self.unindent_comment_on_closing_brace(b)); + } + self.last_pos = source!(self, b.span).hi(); + } + + fn close_block(&mut self, span: Span, unindent_comment: bool) { + let config = self.config; + + let mut last_hi = span.lo(); + let mut unindented = false; + let mut prev_ends_with_newline = false; + let mut extra_newline = false; + + let skip_normal = |s: &str| { + let trimmed = s.trim(); + trimmed.is_empty() || trimmed.chars().all(|c| c == ';') + }; + + let comment_snippet = self.snippet(span); + + let align_to_right = if unindent_comment && contains_comment(comment_snippet) { + let first_lines = comment_snippet.splitn(2, '/').next().unwrap_or(""); + last_line_width(first_lines) > last_line_width(comment_snippet) + } else { + false + }; + + for (kind, offset, sub_slice) in CommentCodeSlices::new(comment_snippet) { + let sub_slice = transform_missing_snippet(config, sub_slice); + + debug!("close_block: {:?} {:?} {:?}", kind, offset, sub_slice); + + match kind { + CodeCharKind::Comment => { + if !unindented && unindent_comment && !align_to_right { + unindented = true; + self.block_indent = self.block_indent.block_unindent(config); + } + let span_in_between = mk_sp(last_hi, span.lo() + BytePos::from_usize(offset)); + let snippet_in_between = self.snippet(span_in_between); + let mut comment_on_same_line = !snippet_in_between.contains('\n'); + + let mut comment_shape = + Shape::indented(self.block_indent, config).comment(config); + if self.config.version() == Version::Two && comment_on_same_line { + self.push_str(" "); + // put the first line of the comment on the same line as the + // block's last line + match sub_slice.find('\n') { + None => { + self.push_str(&sub_slice); + } + Some(offset) if offset + 1 == sub_slice.len() => { + self.push_str(&sub_slice[..offset]); + } + Some(offset) => { + let first_line = &sub_slice[..offset]; + self.push_str(first_line); + self.push_str(&self.block_indent.to_string_with_newline(config)); + + // put the other lines below it, shaping it as needed + let other_lines = &sub_slice[offset + 1..]; + let comment_str = + rewrite_comment(other_lines, false, comment_shape, config); + match comment_str { + Some(ref s) => self.push_str(s), + None => self.push_str(other_lines), + } + } + } + } else { + if comment_on_same_line { + // 1 = a space before `//` + let offset_len = 1 + last_line_width(&self.buffer) + .saturating_sub(self.block_indent.width()); + match comment_shape + .visual_indent(offset_len) + .sub_width(offset_len) + { + Some(shp) => comment_shape = shp, + None => comment_on_same_line = false, + } + }; + + if comment_on_same_line { + self.push_str(" "); + } else { + if count_newlines(snippet_in_between) >= 2 || extra_newline { + self.push_str("\n"); + } + self.push_str(&self.block_indent.to_string_with_newline(config)); + } + + let comment_str = rewrite_comment(&sub_slice, false, comment_shape, config); + match comment_str { + Some(ref s) => self.push_str(s), + None => self.push_str(&sub_slice), + } + } + } + CodeCharKind::Normal if skip_normal(&sub_slice) => { + extra_newline = prev_ends_with_newline && sub_slice.contains('\n'); + continue; + } + CodeCharKind::Normal => { + self.push_str(&self.block_indent.to_string_with_newline(config)); + self.push_str(sub_slice.trim()); + } + } + prev_ends_with_newline = sub_slice.ends_with('\n'); + extra_newline = false; + last_hi = span.lo() + BytePos::from_usize(offset + sub_slice.len()); + } + if unindented { + self.block_indent = self.block_indent.block_indent(self.config); + } + self.block_indent = self.block_indent.block_unindent(self.config); + self.push_str(&self.block_indent.to_string_with_newline(config)); + self.push_str("}"); + } + + fn unindent_comment_on_closing_brace(&self, b: &ast::Block) -> bool { + self.is_if_else_block && !b.stmts.is_empty() + } + + // Note that this only gets called for function definitions. Required methods + // on traits do not get handled here. + pub(crate) fn visit_fn( + &mut self, + fk: visit::FnKind<'_>, + fd: &ast::FnDecl, + s: Span, + defaultness: ast::Defaultness, + inner_attrs: Option<&[ast::Attribute]>, + ) { + let indent = self.block_indent; + let block; + let rewrite = match fk { + visit::FnKind::Fn(_, ident, _, _, _, Some(ref b)) => { + block = b; + self.rewrite_fn_before_block( + indent, + ident, + &FnSig::from_fn_kind(&fk, fd, defaultness), + mk_sp(s.lo(), b.span.lo()), + ) + } + _ => unreachable!(), + }; + + if let Some((fn_str, fn_brace_style)) = rewrite { + self.format_missing_with_indent(source!(self, s).lo()); + + if let Some(rw) = self.single_line_fn(&fn_str, block, inner_attrs) { + self.push_str(&rw); + self.last_pos = s.hi(); + return; + } + + self.push_str(&fn_str); + match fn_brace_style { + FnBraceStyle::SameLine => self.push_str(" "), + FnBraceStyle::NextLine => { + self.push_str(&self.block_indent.to_string_with_newline(self.config)) + } + _ => unreachable!(), + } + self.last_pos = source!(self, block.span).lo(); + } else { + self.format_missing(source!(self, block.span).lo()); + } + + self.visit_block(block, inner_attrs, true) + } + + pub(crate) fn visit_item(&mut self, item: &ast::Item) { + skip_out_of_file_lines_range_visitor!(self, item.span); + + // This is where we bail out if there is a skip attribute. This is only + // complex in the module case. It is complex because the module could be + // in a separate file and there might be attributes in both files, but + // the AST lumps them all together. + let filtered_attrs; + let mut attrs = &item.attrs; + let skip_context_saved = self.skip_context.clone(); + self.skip_context.update_with_attrs(attrs); + + let should_visit_node_again = match item.kind { + // For use/extern crate items, skip rewriting attributes but check for a skip attribute. + ast::ItemKind::Use(..) | ast::ItemKind::ExternCrate(_) => { + if contains_skip(attrs) { + self.push_skipped_with_span(attrs.as_slice(), item.span(), item.span()); + false + } else { + true + } + } + // Module is inline, in this case we treat it like any other item. + _ if !is_mod_decl(item) => { + if self.visit_attrs(&item.attrs, ast::AttrStyle::Outer) { + self.push_skipped_with_span(item.attrs.as_slice(), item.span(), item.span()); + false + } else { + true + } + } + // Module is not inline, but should be skipped. + ast::ItemKind::Mod(..) if contains_skip(&item.attrs) => false, + // Module is not inline and should not be skipped. We want + // to process only the attributes in the current file. + ast::ItemKind::Mod(..) => { + filtered_attrs = filter_inline_attrs(&item.attrs, item.span()); + // Assert because if we should skip it should be caught by + // the above case. + assert!(!self.visit_attrs(&filtered_attrs, ast::AttrStyle::Outer)); + attrs = &filtered_attrs; + true + } + _ => { + if self.visit_attrs(&item.attrs, ast::AttrStyle::Outer) { + self.push_skipped_with_span(item.attrs.as_slice(), item.span(), item.span()); + false + } else { + true + } + } + }; + + // TODO(calebcartwright): consider enabling box_patterns feature gate + if should_visit_node_again { + match item.kind { + ast::ItemKind::Use(ref tree) => self.format_import(item, tree), + ast::ItemKind::Impl(ref iimpl) => { + let block_indent = self.block_indent; + let rw = self.with_context(|ctx| format_impl(ctx, item, iimpl, block_indent)); + self.push_rewrite(item.span, rw); + } + ast::ItemKind::Trait(..) => { + let block_indent = self.block_indent; + let rw = self.with_context(|ctx| format_trait(ctx, item, block_indent)); + self.push_rewrite(item.span, rw); + } + ast::ItemKind::TraitAlias(ref generics, ref generic_bounds) => { + let shape = Shape::indented(self.block_indent, self.config); + let rw = format_trait_alias( + &self.get_context(), + item.ident, + &item.vis, + generics, + generic_bounds, + shape, + ); + self.push_rewrite(item.span, rw); + } + ast::ItemKind::ExternCrate(_) => { + let rw = rewrite_extern_crate(&self.get_context(), item, self.shape()); + let span = if attrs.is_empty() { + item.span + } else { + mk_sp(attrs[0].span.lo(), item.span.hi()) + }; + self.push_rewrite(span, rw); + } + ast::ItemKind::Struct(..) | ast::ItemKind::Union(..) => { + self.visit_struct(&StructParts::from_item(item)); + } + ast::ItemKind::Enum(ref def, ref generics) => { + self.format_missing_with_indent(source!(self, item.span).lo()); + self.visit_enum(item.ident, &item.vis, def, generics, item.span); + self.last_pos = source!(self, item.span).hi(); + } + ast::ItemKind::Mod(unsafety, ref mod_kind) => { + self.format_missing_with_indent(source!(self, item.span).lo()); + self.format_mod(mod_kind, unsafety, &item.vis, item.span, item.ident, attrs); + } + ast::ItemKind::MacCall(ref mac) => { + self.visit_mac(mac, Some(item.ident), MacroPosition::Item); + } + ast::ItemKind::ForeignMod(ref foreign_mod) => { + self.format_missing_with_indent(source!(self, item.span).lo()); + self.format_foreign_mod(foreign_mod, item.span); + } + ast::ItemKind::Static(..) | ast::ItemKind::Const(..) => { + self.visit_static(&StaticParts::from_item(item)); + } + ast::ItemKind::Fn(ref fn_kind) => { + let ast::Fn { + defaultness, + ref sig, + ref generics, + ref body, + } = **fn_kind; + if let Some(ref body) = body { + let inner_attrs = inner_attributes(&item.attrs); + let fn_ctxt = match sig.header.ext { + ast::Extern::None => visit::FnCtxt::Free, + _ => visit::FnCtxt::Foreign, + }; + self.visit_fn( + visit::FnKind::Fn( + fn_ctxt, + item.ident, + sig, + &item.vis, + generics, + Some(body), + ), + &sig.decl, + item.span, + defaultness, + Some(&inner_attrs), + ) + } else { + let indent = self.block_indent; + let rewrite = self.rewrite_required_fn( + indent, item.ident, sig, &item.vis, generics, item.span, + ); + self.push_rewrite(item.span, rewrite); + } + } + ast::ItemKind::TyAlias(ref ty_alias) => { + use ItemVisitorKind::Item; + self.visit_ty_alias_kind(ty_alias, &Item(item), item.span); + } + ast::ItemKind::GlobalAsm(..) => { + let snippet = Some(self.snippet(item.span).to_owned()); + self.push_rewrite(item.span, snippet); + } + ast::ItemKind::MacroDef(ref def) => { + let rewrite = rewrite_macro_def( + &self.get_context(), + self.shape(), + self.block_indent, + def, + item.ident, + &item.vis, + item.span, + ); + self.push_rewrite(item.span, rewrite); + } + }; + } + self.skip_context = skip_context_saved; + } + + fn visit_ty_alias_kind( + &mut self, + ty_kind: &ast::TyAlias, + visitor_kind: &ItemVisitorKind<'_>, + span: Span, + ) { + let rewrite = rewrite_type_alias( + ty_kind, + &self.get_context(), + self.block_indent, + visitor_kind, + span, + ); + self.push_rewrite(span, rewrite); + } + + fn visit_assoc_item(&mut self, visitor_kind: &ItemVisitorKind<'_>) { + use ItemVisitorKind::*; + // TODO(calebcartwright): Not sure the skip spans are correct + let (ai, skip_span, assoc_ctxt) = match visitor_kind { + AssocTraitItem(ai) => (*ai, ai.span(), visit::AssocCtxt::Trait), + AssocImplItem(ai) => (*ai, ai.span, visit::AssocCtxt::Impl), + _ => unreachable!(), + }; + skip_out_of_file_lines_range_visitor!(self, ai.span); + + if self.visit_attrs(&ai.attrs, ast::AttrStyle::Outer) { + self.push_skipped_with_span(ai.attrs.as_slice(), skip_span, skip_span); + return; + } + + // TODO(calebcartwright): consider enabling box_patterns feature gate + match (&ai.kind, visitor_kind) { + (ast::AssocItemKind::Const(..), AssocTraitItem(_)) => { + self.visit_static(&StaticParts::from_trait_item(ai)) + } + (ast::AssocItemKind::Const(..), AssocImplItem(_)) => { + self.visit_static(&StaticParts::from_impl_item(ai)) + } + (ast::AssocItemKind::Fn(ref fn_kind), _) => { + let ast::Fn { + defaultness, + ref sig, + ref generics, + ref body, + } = **fn_kind; + if let Some(ref body) = body { + let inner_attrs = inner_attributes(&ai.attrs); + let fn_ctxt = visit::FnCtxt::Assoc(assoc_ctxt); + self.visit_fn( + visit::FnKind::Fn(fn_ctxt, ai.ident, sig, &ai.vis, generics, Some(body)), + &sig.decl, + ai.span, + defaultness, + Some(&inner_attrs), + ); + } else { + let indent = self.block_indent; + let rewrite = + self.rewrite_required_fn(indent, ai.ident, sig, &ai.vis, generics, ai.span); + self.push_rewrite(ai.span, rewrite); + } + } + (ast::AssocItemKind::Type(ref ty_alias), _) => { + self.visit_ty_alias_kind(ty_alias, visitor_kind, ai.span); + } + (ast::AssocItemKind::MacCall(ref mac), _) => { + self.visit_mac(mac, Some(ai.ident), MacroPosition::Item); + } + _ => unreachable!(), + } + } + + pub(crate) fn visit_trait_item(&mut self, ti: &ast::AssocItem) { + self.visit_assoc_item(&ItemVisitorKind::AssocTraitItem(ti)); + } + + pub(crate) fn visit_impl_item(&mut self, ii: &ast::AssocItem) { + self.visit_assoc_item(&ItemVisitorKind::AssocImplItem(ii)); + } + + fn visit_mac(&mut self, mac: &ast::MacCall, ident: Option, pos: MacroPosition) { + skip_out_of_file_lines_range_visitor!(self, mac.span()); + + // 1 = ; + let shape = self.shape().saturating_sub_width(1); + let rewrite = self.with_context(|ctx| rewrite_macro(mac, ident, ctx, shape, pos)); + // As of v638 of the rustc-ap-* crates, the associated span no longer includes + // the trailing semicolon. This determines the correct span to ensure scenarios + // with whitespace between the delimiters and trailing semi (i.e. `foo!(abc) ;`) + // are formatted correctly. + let (span, rewrite) = match macro_style(mac, &self.get_context()) { + Delimiter::Bracket | Delimiter::Parenthesis if MacroPosition::Item == pos => { + let search_span = mk_sp(mac.span().hi(), self.snippet_provider.end_pos()); + let hi = self.snippet_provider.span_before(search_span, ";"); + let target_span = mk_sp(mac.span().lo(), hi + BytePos(1)); + let rewrite = rewrite.map(|rw| { + if !rw.ends_with(';') { + format!("{};", rw) + } else { + rw + } + }); + (target_span, rewrite) + } + _ => (mac.span(), rewrite), + }; + + self.push_rewrite(span, rewrite); + } + + pub(crate) fn push_str(&mut self, s: &str) { + self.line_number += count_newlines(s); + self.buffer.push_str(s); + } + + #[allow(clippy::needless_pass_by_value)] + fn push_rewrite_inner(&mut self, span: Span, rewrite: Option) { + if let Some(ref s) = rewrite { + self.push_str(s); + } else { + let snippet = self.snippet(span); + self.push_str(snippet.trim()); + } + self.last_pos = source!(self, span).hi(); + } + + pub(crate) fn push_rewrite(&mut self, span: Span, rewrite: Option) { + self.format_missing_with_indent(source!(self, span).lo()); + self.push_rewrite_inner(span, rewrite); + } + + pub(crate) fn push_skipped_with_span( + &mut self, + attrs: &[ast::Attribute], + item_span: Span, + main_span: Span, + ) { + self.format_missing_with_indent(source!(self, item_span).lo()); + // do not take into account the lines with attributes as part of the skipped range + let attrs_end = attrs + .iter() + .map(|attr| self.parse_sess.line_of_byte_pos(attr.span.hi())) + .max() + .unwrap_or(1); + let first_line = self.parse_sess.line_of_byte_pos(main_span.lo()); + // Statement can start after some newlines and/or spaces + // or it can be on the same line as the last attribute. + // So here we need to take a minimum between the two. + let lo = std::cmp::min(attrs_end + 1, first_line); + self.push_rewrite_inner(item_span, None); + let hi = self.line_number + 1; + self.skipped_range.borrow_mut().push((lo, hi)); + } + + pub(crate) fn from_context(ctx: &'a RewriteContext<'_>) -> FmtVisitor<'a> { + let mut visitor = FmtVisitor::from_parse_sess( + ctx.parse_sess, + ctx.config, + ctx.snippet_provider, + ctx.report.clone(), + ); + visitor.skip_context.update(ctx.skip_context.clone()); + visitor.set_parent_context(ctx); + visitor + } + + pub(crate) fn from_parse_sess( + parse_session: &'a ParseSess, + config: &'a Config, + snippet_provider: &'a SnippetProvider, + report: FormatReport, + ) -> FmtVisitor<'a> { + let mut skip_context = SkipContext::default(); + let mut macro_names = Vec::new(); + for macro_selector in config.skip_macro_invocations().0 { + match macro_selector { + MacroSelector::Name(name) => macro_names.push(name.to_string()), + MacroSelector::All => skip_context.macros.skip_all(), + } + } + skip_context.macros.extend(macro_names); + FmtVisitor { + parent_context: None, + parse_sess: parse_session, + buffer: String::with_capacity(snippet_provider.big_snippet.len() * 2), + last_pos: BytePos(0), + block_indent: Indent::empty(), + config, + is_if_else_block: false, + snippet_provider, + line_number: 0, + skipped_range: Rc::new(RefCell::new(vec![])), + is_macro_def: false, + macro_rewrite_failure: false, + report, + skip_context, + } + } + + pub(crate) fn opt_snippet(&'b self, span: Span) -> Option<&'a str> { + self.snippet_provider.span_to_snippet(span) + } + + pub(crate) fn snippet(&'b self, span: Span) -> &'a str { + self.opt_snippet(span).unwrap() + } + + // Returns true if we should skip the following item. + pub(crate) fn visit_attrs(&mut self, attrs: &[ast::Attribute], style: ast::AttrStyle) -> bool { + for attr in attrs { + if attr.has_name(depr_skip_annotation()) { + let file_name = self.parse_sess.span_to_filename(attr.span); + self.report.append( + file_name, + vec![FormattingError::from_span( + attr.span, + self.parse_sess, + ErrorKind::DeprecatedAttr, + )], + ); + } else { + match &attr.kind { + ast::AttrKind::Normal(ref normal) + if self.is_unknown_rustfmt_attr(&normal.item.path.segments) => + { + let file_name = self.parse_sess.span_to_filename(attr.span); + self.report.append( + file_name, + vec![FormattingError::from_span( + attr.span, + self.parse_sess, + ErrorKind::BadAttr, + )], + ); + } + _ => (), + } + } + } + if contains_skip(attrs) { + return true; + } + + let attrs: Vec<_> = attrs.iter().filter(|a| a.style == style).cloned().collect(); + if attrs.is_empty() { + return false; + } + + let rewrite = attrs.rewrite(&self.get_context(), self.shape()); + let span = mk_sp(attrs[0].span.lo(), attrs[attrs.len() - 1].span.hi()); + self.push_rewrite(span, rewrite); + + false + } + + fn is_unknown_rustfmt_attr(&self, segments: &[ast::PathSegment]) -> bool { + if segments[0].ident.to_string() != "rustfmt" { + return false; + } + !is_skip_attr(segments) + } + + fn walk_mod_items(&mut self, items: &[rustc_ast::ptr::P]) { + self.visit_items_with_reordering(&ptr_vec_to_ref_vec(items)); + } + + fn walk_stmts(&mut self, stmts: &[Stmt<'_>], include_current_empty_semi: bool) { + if stmts.is_empty() { + return; + } + + // Extract leading `use ...;`. + let items: Vec<_> = stmts + .iter() + .take_while(|stmt| stmt.to_item().map_or(false, is_use_item)) + .filter_map(|stmt| stmt.to_item()) + .collect(); + + if items.is_empty() { + self.visit_stmt(&stmts[0], include_current_empty_semi); + + // FIXME(calebcartwright 2021-01-03) - This exists strictly to maintain legacy + // formatting where rustfmt would preserve redundant semicolons on Items in a + // statement position. + // + // Starting in rustc-ap-* v692 (~2020-12-01) the rustc parser now parses this as + // two separate statements (Item and Empty kinds), whereas before it was parsed as + // a single statement with the statement's span including the redundant semicolon. + // + // rustfmt typically tosses unnecessary/redundant semicolons, and eventually we + // should toss these as well, but doing so at this time would + // break the Stability Guarantee + // N.B. This could be updated to utilize the version gates. + let include_next_empty = if stmts.len() > 1 { + matches!( + (&stmts[0].as_ast_node().kind, &stmts[1].as_ast_node().kind), + (ast::StmtKind::Item(_), ast::StmtKind::Empty) + ) + } else { + false + }; + + self.walk_stmts(&stmts[1..], include_next_empty); + } else { + self.visit_items_with_reordering(&items); + self.walk_stmts(&stmts[items.len()..], false); + } + } + + fn walk_block_stmts(&mut self, b: &ast::Block) { + self.walk_stmts(&Stmt::from_ast_nodes(b.stmts.iter()), false) + } + + fn format_mod( + &mut self, + mod_kind: &ast::ModKind, + unsafety: ast::Unsafe, + vis: &ast::Visibility, + s: Span, + ident: symbol::Ident, + attrs: &[ast::Attribute], + ) { + let vis_str = utils::format_visibility(&self.get_context(), vis); + self.push_str(&*vis_str); + self.push_str(format_unsafety(unsafety)); + self.push_str("mod "); + // Calling `to_owned()` to work around borrow checker. + let ident_str = rewrite_ident(&self.get_context(), ident).to_owned(); + self.push_str(&ident_str); + + if let ast::ModKind::Loaded(ref items, ast::Inline::Yes, ref spans) = mod_kind { + let ast::ModSpans { + inner_span, + inject_use_span: _, + } = *spans; + match self.config.brace_style() { + BraceStyle::AlwaysNextLine => { + let indent_str = self.block_indent.to_string_with_newline(self.config); + self.push_str(&indent_str); + self.push_str("{"); + } + _ => self.push_str(" {"), + } + // Hackery to account for the closing }. + let mod_lo = self.snippet_provider.span_after(source!(self, s), "{"); + let body_snippet = + self.snippet(mk_sp(mod_lo, source!(self, inner_span).hi() - BytePos(1))); + let body_snippet = body_snippet.trim(); + if body_snippet.is_empty() { + self.push_str("}"); + } else { + self.last_pos = mod_lo; + self.block_indent = self.block_indent.block_indent(self.config); + self.visit_attrs(attrs, ast::AttrStyle::Inner); + self.walk_mod_items(items); + let missing_span = self.next_span(inner_span.hi() - BytePos(1)); + self.close_block(missing_span, false); + } + self.last_pos = source!(self, inner_span).hi(); + } else { + self.push_str(";"); + self.last_pos = source!(self, s).hi(); + } + } + + pub(crate) fn format_separate_mod(&mut self, m: &Module<'_>, end_pos: BytePos) { + self.block_indent = Indent::empty(); + let skipped = self.visit_attrs(m.attrs(), ast::AttrStyle::Inner); + assert!( + !skipped, + "Skipping module must be handled before reaching this line." + ); + self.walk_mod_items(&m.items); + self.format_missing_with_indent(end_pos); + } + + pub(crate) fn skip_empty_lines(&mut self, end_pos: BytePos) { + while let Some(pos) = self + .snippet_provider + .opt_span_after(self.next_span(end_pos), "\n") + { + if let Some(snippet) = self.opt_snippet(self.next_span(pos)) { + if snippet.trim().is_empty() { + self.last_pos = pos; + } else { + return; + } + } + } + } + + pub(crate) fn with_context(&mut self, f: F) -> Option + where + F: Fn(&RewriteContext<'_>) -> Option, + { + let context = self.get_context(); + let result = f(&context); + + self.macro_rewrite_failure |= context.macro_rewrite_failure.get(); + result + } + + pub(crate) fn get_context(&self) -> RewriteContext<'_> { + RewriteContext { + parse_sess: self.parse_sess, + config: self.config, + inside_macro: Rc::new(Cell::new(false)), + use_block: Cell::new(false), + is_if_else_block: Cell::new(false), + force_one_line_chain: Cell::new(false), + snippet_provider: self.snippet_provider, + macro_rewrite_failure: Cell::new(false), + is_macro_def: self.is_macro_def, + report: self.report.clone(), + skip_context: self.skip_context.clone(), + skipped_range: self.skipped_range.clone(), + } + } +} diff --git a/tests/cargo-fmt/main.rs b/tests/cargo-fmt/main.rs new file mode 100644 index 000000000000..701c36fadeaf --- /dev/null +++ b/tests/cargo-fmt/main.rs @@ -0,0 +1,119 @@ +// Integration tests for cargo-fmt. + +use std::env; +use std::path::Path; +use std::process::Command; + +use rustfmt_config_proc_macro::rustfmt_only_ci_test; + +/// Run the cargo-fmt executable and return its output. +fn cargo_fmt(args: &[&str]) -> (String, String) { + let mut bin_dir = env::current_exe().unwrap(); + bin_dir.pop(); // chop off test exe name + if bin_dir.ends_with("deps") { + bin_dir.pop(); + } + let cmd = bin_dir.join(format!("cargo-fmt{}", env::consts::EXE_SUFFIX)); + + // Ensure cargo-fmt runs the rustfmt binary from the local target dir. + let path = env::var_os("PATH").unwrap_or_default(); + let mut paths = env::split_paths(&path).collect::>(); + paths.insert(0, bin_dir); + let new_path = env::join_paths(paths).unwrap(); + + match Command::new(&cmd).args(args).env("PATH", new_path).output() { + Ok(output) => ( + String::from_utf8(output.stdout).expect("utf-8"), + String::from_utf8(output.stderr).expect("utf-8"), + ), + Err(e) => panic!("failed to run `{:?} {:?}`: {}", cmd, args, e), + } +} + +macro_rules! assert_that { + ($args:expr, $check:ident $check_args:tt) => { + let (stdout, stderr) = cargo_fmt($args); + if !stdout.$check$check_args { + panic!( + "Output not expected for cargo-fmt {:?}\n\ + expected: {}{}\n\ + actual stdout:\n{}\n\ + actual stderr:\n{}", + $args, + stringify!($check), + stringify!($check_args), + stdout, + stderr + ); + } + }; +} + +#[rustfmt_only_ci_test] +#[test] +fn version() { + assert_that!(&["--version"], starts_with("rustfmt ")); + assert_that!(&["--version"], starts_with("rustfmt ")); + assert_that!(&["--", "-V"], starts_with("rustfmt ")); + assert_that!(&["--", "--version"], starts_with("rustfmt ")); +} + +#[rustfmt_only_ci_test] +#[test] +fn print_config() { + assert_that!( + &["--", "--print-config", "current", "."], + contains("max_width = ") + ); +} + +#[rustfmt_only_ci_test] +#[test] +fn rustfmt_help() { + assert_that!(&["--", "--help"], contains("Format Rust code")); + assert_that!(&["--", "-h"], contains("Format Rust code")); + assert_that!(&["--", "--help=config"], contains("Configuration Options:")); +} + +#[rustfmt_only_ci_test] +#[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()))) + } +} + +#[rustfmt_only_ci_test] +#[test] +fn cargo_fmt_emits_error_on_line_overflow_true() { + // See also https://github.com/rust-lang/rustfmt/issues/3164 + let args = [ + "--check", + "--manifest-path", + "tests/cargo-fmt/source/issue_3164/Cargo.toml", + "--", + "--config", + "error_on_line_overflow=true", + ]; + + let (_stdout, stderr) = cargo_fmt(&args); + assert!(stderr.contains( + "line formatted, but exceeded maximum width (maximum: 100 (see `max_width` option)" + )) +} diff --git a/tests/cargo-fmt/source/divergent-crate-dir-names/Cargo.toml b/tests/cargo-fmt/source/divergent-crate-dir-names/Cargo.toml new file mode 100644 index 000000000000..315364a64573 --- /dev/null +++ b/tests/cargo-fmt/source/divergent-crate-dir-names/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "cargo-fmt-test" +version = "0.1.0" +authors = ["calebcartwright"] +edition = "2018" + +[dependencies] +indexmap = "1.0.2" + +[workspace] +members = [ + "dependency-dir-name", +] \ No newline at end of file diff --git a/tests/cargo-fmt/source/divergent-crate-dir-names/dependency-dir-name/Cargo.toml b/tests/cargo-fmt/source/divergent-crate-dir-names/dependency-dir-name/Cargo.toml new file mode 100644 index 000000000000..4493882bf40a --- /dev/null +++ b/tests/cargo-fmt/source/divergent-crate-dir-names/dependency-dir-name/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "dependency-crate-name" +version = "0.1.0" +authors = ["calebcartwright"] +edition = "2018" + +[dependencies] +subdep-crate-name = { path = "subdep-dir-name" } +indexmap = "1.0.2" +rusty-hook = "0.8.4" \ No newline at end of file diff --git a/tests/cargo-fmt/source/divergent-crate-dir-names/dependency-dir-name/src/lib.rs b/tests/cargo-fmt/source/divergent-crate-dir-names/dependency-dir-name/src/lib.rs new file mode 100644 index 000000000000..e93b18d725b9 --- /dev/null +++ b/tests/cargo-fmt/source/divergent-crate-dir-names/dependency-dir-name/src/lib.rs @@ -0,0 +1,7 @@ +#[cfg(test)] +mod tests { +#[test] +fn it_works() { + assert_eq!(2 + 2, 4); +} +} diff --git a/tests/cargo-fmt/source/divergent-crate-dir-names/dependency-dir-name/subdep-dir-name/Cargo.toml b/tests/cargo-fmt/source/divergent-crate-dir-names/dependency-dir-name/subdep-dir-name/Cargo.toml new file mode 100644 index 000000000000..7dad09f4077b --- /dev/null +++ b/tests/cargo-fmt/source/divergent-crate-dir-names/dependency-dir-name/subdep-dir-name/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "subdep-crate-name" +version = "0.1.0" +authors = ["calebcartwright"] +edition = "2018" + +[dependencies] diff --git a/tests/cargo-fmt/source/divergent-crate-dir-names/dependency-dir-name/subdep-dir-name/src/lib.rs b/tests/cargo-fmt/source/divergent-crate-dir-names/dependency-dir-name/subdep-dir-name/src/lib.rs new file mode 100644 index 000000000000..1c08c1c4fd38 --- /dev/null +++ b/tests/cargo-fmt/source/divergent-crate-dir-names/dependency-dir-name/subdep-dir-name/src/lib.rs @@ -0,0 +1,7 @@ +#[cfg(test)] +mod tests { +#[test] +fn sub_test_that_works() { + assert_eq!(3 + 3, 6); +} + } diff --git a/tests/cargo-fmt/source/divergent-crate-dir-names/src/main.rs b/tests/cargo-fmt/source/divergent-crate-dir-names/src/main.rs new file mode 100644 index 000000000000..f5c339a8dd14 --- /dev/null +++ b/tests/cargo-fmt/source/divergent-crate-dir-names/src/main.rs @@ -0,0 +1,3 @@ +fn main() { +println!("Hello, world!"); +} diff --git a/tests/cargo-fmt/source/issue_3164/Cargo.toml b/tests/cargo-fmt/source/issue_3164/Cargo.toml new file mode 100644 index 000000000000..580ef7e6e24f --- /dev/null +++ b/tests/cargo-fmt/source/issue_3164/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "issue_3164" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/cargo-fmt/source/issue_3164/src/main.rs b/tests/cargo-fmt/source/issue_3164/src/main.rs new file mode 100644 index 000000000000..9330107ac8dc --- /dev/null +++ b/tests/cargo-fmt/source/issue_3164/src/main.rs @@ -0,0 +1,13 @@ +#[allow(unused_macros)] +macro_rules! foo { + ($id:ident) => { + macro_rules! bar { + ($id2:tt) => { + #[cfg(any(target_feature = $id2, target_feature = $id2, target_feature = $id2, target_feature = $id2, target_feature = $id2))] + fn $id() {} + }; + } + }; +} + +fn main() {} diff --git a/tests/cargo-fmt/source/workspaces/path-dep-above/e/Cargo.toml b/tests/cargo-fmt/source/workspaces/path-dep-above/e/Cargo.toml new file mode 100644 index 000000000000..eaf1d76f999d --- /dev/null +++ b/tests/cargo-fmt/source/workspaces/path-dep-above/e/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "e" +version = "0.1.0" +edition = "2018" +[dependencies] +c = { path = "../ws/c" } + +[workspace] diff --git a/tests/cargo-fmt/source/workspaces/path-dep-above/e/src/main.rs b/tests/cargo-fmt/source/workspaces/path-dep-above/e/src/main.rs new file mode 100644 index 000000000000..1c26a3895f37 --- /dev/null +++ b/tests/cargo-fmt/source/workspaces/path-dep-above/e/src/main.rs @@ -0,0 +1 @@ +struct E{ } diff --git a/tests/cargo-fmt/source/workspaces/path-dep-above/ws/Cargo.toml b/tests/cargo-fmt/source/workspaces/path-dep-above/ws/Cargo.toml new file mode 100644 index 000000000000..202739b613b8 --- /dev/null +++ b/tests/cargo-fmt/source/workspaces/path-dep-above/ws/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = [ + "a", + "b" +] \ No newline at end of file diff --git a/tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/Cargo.toml b/tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/Cargo.toml new file mode 100644 index 000000000000..712a113448fb --- /dev/null +++ b/tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "a" +version = "0.1.0" +edition = "2018" +[dependencies] +d = { path = "./d" } diff --git a/tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/d/Cargo.toml b/tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/d/Cargo.toml new file mode 100644 index 000000000000..fb0f06fe5fce --- /dev/null +++ b/tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/d/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "d" +version = "0.1.0" +edition = "2018" +[dependencies] +e = { path = "../../../e" } +f = { path = "f" } diff --git a/tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/d/f/Cargo.toml b/tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/d/f/Cargo.toml new file mode 100644 index 000000000000..5c4fa5617886 --- /dev/null +++ b/tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/d/f/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "f" +version = "0.1.0" +edition = "2018" diff --git a/tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/d/f/src/lib.rs b/tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/d/f/src/lib.rs new file mode 100644 index 000000000000..c655c4d5e1a8 --- /dev/null +++ b/tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/d/f/src/lib.rs @@ -0,0 +1 @@ +struct F{ } \ No newline at end of file diff --git a/tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/d/src/lib.rs b/tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/d/src/lib.rs new file mode 100644 index 000000000000..04e6e4cb9402 --- /dev/null +++ b/tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/d/src/lib.rs @@ -0,0 +1 @@ +struct D{ } \ No newline at end of file diff --git a/tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/src/main.rs b/tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/src/main.rs new file mode 100644 index 000000000000..04e6e4cb9402 --- /dev/null +++ b/tests/cargo-fmt/source/workspaces/path-dep-above/ws/a/src/main.rs @@ -0,0 +1 @@ +struct D{ } \ No newline at end of file diff --git a/tests/cargo-fmt/source/workspaces/path-dep-above/ws/b/Cargo.toml b/tests/cargo-fmt/source/workspaces/path-dep-above/ws/b/Cargo.toml new file mode 100644 index 000000000000..47a24ff4f275 --- /dev/null +++ b/tests/cargo-fmt/source/workspaces/path-dep-above/ws/b/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "b" +version = "0.1.0" +edition = "2018" +[dependencies] +c = { path = "../c" } diff --git a/tests/cargo-fmt/source/workspaces/path-dep-above/ws/b/src/main.rs b/tests/cargo-fmt/source/workspaces/path-dep-above/ws/b/src/main.rs new file mode 100644 index 000000000000..4833bbc69b48 --- /dev/null +++ b/tests/cargo-fmt/source/workspaces/path-dep-above/ws/b/src/main.rs @@ -0,0 +1 @@ +struct B{ } \ No newline at end of file diff --git a/tests/cargo-fmt/source/workspaces/path-dep-above/ws/c/Cargo.toml b/tests/cargo-fmt/source/workspaces/path-dep-above/ws/c/Cargo.toml new file mode 100644 index 000000000000..49fa6c395eb6 --- /dev/null +++ b/tests/cargo-fmt/source/workspaces/path-dep-above/ws/c/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "c" +version = "0.1.0" +edition = "2018" diff --git a/tests/cargo-fmt/source/workspaces/path-dep-above/ws/c/src/lib.rs b/tests/cargo-fmt/source/workspaces/path-dep-above/ws/c/src/lib.rs new file mode 100644 index 000000000000..1245ac91d60a --- /dev/null +++ b/tests/cargo-fmt/source/workspaces/path-dep-above/ws/c/src/lib.rs @@ -0,0 +1 @@ +struct C{ } \ No newline at end of file diff --git a/tests/config/disable_all_formatting.toml b/tests/config/disable_all_formatting.toml new file mode 100644 index 000000000000..c7ad93bafe36 --- /dev/null +++ b/tests/config/disable_all_formatting.toml @@ -0,0 +1 @@ +disable_all_formatting = true diff --git a/tests/config/issue-1111.toml b/tests/config/issue-1111.toml new file mode 100644 index 000000000000..44148a2d3c3e --- /dev/null +++ b/tests/config/issue-1111.toml @@ -0,0 +1 @@ +reorder_imports = true diff --git a/tests/config/issue-2641.toml b/tests/config/issue-2641.toml new file mode 100644 index 000000000000..11c9dca8a06c --- /dev/null +++ b/tests/config/issue-2641.toml @@ -0,0 +1 @@ +newline_style = "Windows" diff --git a/tests/config/issue-3779.toml b/tests/config/issue-3779.toml new file mode 100644 index 000000000000..6ca52aee3226 --- /dev/null +++ b/tests/config/issue-3779.toml @@ -0,0 +1,3 @@ +ignore = [ + "tests/**/issue-3779/ice.rs" +] diff --git a/tests/config/skip_children.toml b/tests/config/skip_children.toml new file mode 100644 index 000000000000..f52930d50b61 --- /dev/null +++ b/tests/config/skip_children.toml @@ -0,0 +1 @@ +skip_children = true diff --git a/tests/config/small_tabs.toml b/tests/config/small_tabs.toml new file mode 100644 index 000000000000..4c37100894f8 --- /dev/null +++ b/tests/config/small_tabs.toml @@ -0,0 +1,10 @@ +max_width = 100 +comment_width = 80 +tab_spaces = 2 +newline_style = "Unix" +brace_style = "SameLineWhere" +fn_params_layout = "Tall" +trailing_comma = "Vertical" +indent_style = "Block" +reorder_imports = false +format_strings = true diff --git a/tests/coverage/source/comments.rs b/tests/coverage/source/comments.rs new file mode 100644 index 000000000000..10940039e8ea --- /dev/null +++ b/tests/coverage/source/comments.rs @@ -0,0 +1,7 @@ +// rustfmt-emit_mode: coverage +/// Here's a doc comment! +fn main() { + // foo is bar + let foo = "bar"; + // loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong comment!!!!! +} diff --git a/tests/coverage/target/comments.rs b/tests/coverage/target/comments.rs new file mode 100644 index 000000000000..95e7b4705e3a --- /dev/null +++ b/tests/coverage/target/comments.rs @@ -0,0 +1,7 @@ +XX XXXXXXXXXXXXXXXXXX XXXXXXXX +/// Here's a doc comment! +fn main() { + XX XXX XX XXX + let foo = "bar"; + XX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXX +} diff --git a/tests/mod-resolver/issue-4874/bar/baz.rs b/tests/mod-resolver/issue-4874/bar/baz.rs new file mode 100644 index 000000000000..d31b675ea260 --- /dev/null +++ b/tests/mod-resolver/issue-4874/bar/baz.rs @@ -0,0 +1,5 @@ +fn + fail_fmt_check + ( + + ) {} \ No newline at end of file diff --git a/tests/mod-resolver/issue-4874/foo.rs b/tests/mod-resolver/issue-4874/foo.rs new file mode 100644 index 000000000000..246d847869a1 --- /dev/null +++ b/tests/mod-resolver/issue-4874/foo.rs @@ -0,0 +1 @@ +mod qux; \ No newline at end of file diff --git a/tests/mod-resolver/issue-4874/foo/qux.rs b/tests/mod-resolver/issue-4874/foo/qux.rs new file mode 100644 index 000000000000..d8bb610a64db --- /dev/null +++ b/tests/mod-resolver/issue-4874/foo/qux.rs @@ -0,0 +1,5 @@ + fn + badly_formatted + ( + + ) {} \ No newline at end of file diff --git a/tests/mod-resolver/issue-4874/main.rs b/tests/mod-resolver/issue-4874/main.rs new file mode 100644 index 000000000000..3609415b1468 --- /dev/null +++ b/tests/mod-resolver/issue-4874/main.rs @@ -0,0 +1,8 @@ +fn main() { + println!("Hello, world!"); +} + +mod foo; +mod bar { + mod baz; +} \ No newline at end of file diff --git a/tests/mod-resolver/issue-5063/foo.rs b/tests/mod-resolver/issue-5063/foo.rs new file mode 100644 index 000000000000..d56974773fb9 --- /dev/null +++ b/tests/mod-resolver/issue-5063/foo.rs @@ -0,0 +1,2 @@ +mod bar { + mod baz;} \ No newline at end of file diff --git a/tests/mod-resolver/issue-5063/foo/bar/baz.rs b/tests/mod-resolver/issue-5063/foo/bar/baz.rs new file mode 100644 index 000000000000..3519b0ee59c8 --- /dev/null +++ b/tests/mod-resolver/issue-5063/foo/bar/baz.rs @@ -0,0 +1 @@ +fn baz() { } \ No newline at end of file diff --git a/tests/mod-resolver/issue-5063/main.rs b/tests/mod-resolver/issue-5063/main.rs new file mode 100644 index 000000000000..41c81c7bb433 --- /dev/null +++ b/tests/mod-resolver/issue-5063/main.rs @@ -0,0 +1,5 @@ +fn main() { + println!("Hello, world!"); +} + +mod foo; \ No newline at end of file diff --git a/tests/mod-resolver/issue-5167/src/a.rs b/tests/mod-resolver/issue-5167/src/a.rs new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/mod-resolver/issue-5167/src/a/mod.rs b/tests/mod-resolver/issue-5167/src/a/mod.rs new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/mod-resolver/issue-5167/src/lib.rs b/tests/mod-resolver/issue-5167/src/lib.rs new file mode 100644 index 000000000000..f21af614da05 --- /dev/null +++ b/tests/mod-resolver/issue-5167/src/lib.rs @@ -0,0 +1 @@ +mod a; diff --git a/tests/mod-resolver/issue-5198/a.rs b/tests/mod-resolver/issue-5198/a.rs new file mode 100644 index 000000000000..cd686f561169 --- /dev/null +++ b/tests/mod-resolver/issue-5198/a.rs @@ -0,0 +1 @@ +fn main( ) { println!("Hello World!") } diff --git a/tests/mod-resolver/issue-5198/lib.rs b/tests/mod-resolver/issue-5198/lib.rs new file mode 100644 index 000000000000..696832913c87 --- /dev/null +++ b/tests/mod-resolver/issue-5198/lib.rs @@ -0,0 +1,3 @@ +mod a; +mod b; +mod c; diff --git a/tests/mod-resolver/issue-5198/lib/b.rs b/tests/mod-resolver/issue-5198/lib/b.rs new file mode 100644 index 000000000000..cd686f561169 --- /dev/null +++ b/tests/mod-resolver/issue-5198/lib/b.rs @@ -0,0 +1 @@ +fn main( ) { println!("Hello World!") } diff --git a/tests/mod-resolver/issue-5198/lib/c/d.rs b/tests/mod-resolver/issue-5198/lib/c/d.rs new file mode 100644 index 000000000000..d1604aa23a3c --- /dev/null +++ b/tests/mod-resolver/issue-5198/lib/c/d.rs @@ -0,0 +1,3 @@ +mod e; +mod f; +mod g; diff --git a/tests/mod-resolver/issue-5198/lib/c/d/explanation.txt b/tests/mod-resolver/issue-5198/lib/c/d/explanation.txt new file mode 100644 index 000000000000..254102ebabdc --- /dev/null +++ b/tests/mod-resolver/issue-5198/lib/c/d/explanation.txt @@ -0,0 +1,16 @@ +This file is contained in the './lib/c/d/' directory. + +The directory name './lib/c/d/' conflicts with the './lib/c/d.rs' file name. + +'./lib/c/d.rs' defines 3 external modules: + + * mod e; + * mod f; + * mod g; + +Module resolution will fail if we look for './lib/c/d/e.rs' or './lib/c/d/e/mod.rs', +so we should fall back to looking for './lib/c/e.rs', which correctly finds the module, that +rustfmt should format. + +'./lib/c/d/f.rs' and './lib/c/d/g/mod.rs' exist at the default submodule paths so we should be able +to resolve these modules with no problems. \ No newline at end of file diff --git a/tests/mod-resolver/issue-5198/lib/c/d/f.rs b/tests/mod-resolver/issue-5198/lib/c/d/f.rs new file mode 100644 index 000000000000..cd686f561169 --- /dev/null +++ b/tests/mod-resolver/issue-5198/lib/c/d/f.rs @@ -0,0 +1 @@ +fn main( ) { println!("Hello World!") } diff --git a/tests/mod-resolver/issue-5198/lib/c/d/g/mod.rs b/tests/mod-resolver/issue-5198/lib/c/d/g/mod.rs new file mode 100644 index 000000000000..cd686f561169 --- /dev/null +++ b/tests/mod-resolver/issue-5198/lib/c/d/g/mod.rs @@ -0,0 +1 @@ +fn main( ) { println!("Hello World!") } diff --git a/tests/mod-resolver/issue-5198/lib/c/e.rs b/tests/mod-resolver/issue-5198/lib/c/e.rs new file mode 100644 index 000000000000..cd686f561169 --- /dev/null +++ b/tests/mod-resolver/issue-5198/lib/c/e.rs @@ -0,0 +1 @@ +fn main( ) { println!("Hello World!") } diff --git a/tests/mod-resolver/issue-5198/lib/c/mod.rs b/tests/mod-resolver/issue-5198/lib/c/mod.rs new file mode 100644 index 000000000000..81904619650f --- /dev/null +++ b/tests/mod-resolver/issue-5198/lib/c/mod.rs @@ -0,0 +1,3 @@ +mod d; + +fn main( ) { println!("Hello World!") } diff --git a/tests/mod-resolver/issue-5198/lib/explanation.txt b/tests/mod-resolver/issue-5198/lib/explanation.txt new file mode 100644 index 000000000000..90464def8ebc --- /dev/null +++ b/tests/mod-resolver/issue-5198/lib/explanation.txt @@ -0,0 +1,16 @@ +This file is contained in the './lib' directory. + +The directory name './lib' conflicts with the './lib.rs' file name. + +'lib.rs' defines 3 external modules: + + * mod a; + * mod b; + * mod c; + +Module resolution will fail if we look for './lib/a.rs' or './lib/a/mod.rs', +so we should fall back to looking for './a.rs', which correctly finds the module 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/tests/mod-resolver/module-not-found/bad_path_attribute/lib.rs b/tests/mod-resolver/module-not-found/bad_path_attribute/lib.rs new file mode 100644 index 000000000000..2a63c961be8f --- /dev/null +++ b/tests/mod-resolver/module-not-found/bad_path_attribute/lib.rs @@ -0,0 +1,3 @@ +// module resolution fails because the path does not exist. +#[path = "path/to/does_not_exist.rs"] +mod a; diff --git a/tests/mod-resolver/module-not-found/relative_module/a.rs b/tests/mod-resolver/module-not-found/relative_module/a.rs new file mode 100644 index 000000000000..4a1eac8965de --- /dev/null +++ b/tests/mod-resolver/module-not-found/relative_module/a.rs @@ -0,0 +1,2 @@ +// module resolution fails because `./a/b.rs` does not exist +mod b; diff --git a/tests/mod-resolver/module-not-found/relative_module/lib.rs b/tests/mod-resolver/module-not-found/relative_module/lib.rs new file mode 100644 index 000000000000..f21af614da05 --- /dev/null +++ b/tests/mod-resolver/module-not-found/relative_module/lib.rs @@ -0,0 +1 @@ +mod a; diff --git a/tests/mod-resolver/module-not-found/sibling_module/lib.rs b/tests/mod-resolver/module-not-found/sibling_module/lib.rs new file mode 100644 index 000000000000..d9d9e1e3c908 --- /dev/null +++ b/tests/mod-resolver/module-not-found/sibling_module/lib.rs @@ -0,0 +1,2 @@ +// module resolution fails because `./a.rs` does not exist +mod a; diff --git a/tests/mod-resolver/skip-files-issue-5065/foo.rs b/tests/mod-resolver/skip-files-issue-5065/foo.rs new file mode 100644 index 000000000000..74889acf0c38 --- /dev/null +++ b/tests/mod-resolver/skip-files-issue-5065/foo.rs @@ -0,0 +1,5 @@ +#![rustfmt::skip] + +mod bar { + + mod baz;} \ No newline at end of file diff --git a/tests/mod-resolver/skip-files-issue-5065/foo/bar/baz.rs b/tests/mod-resolver/skip-files-issue-5065/foo/bar/baz.rs new file mode 100644 index 000000000000..3519b0ee59c8 --- /dev/null +++ b/tests/mod-resolver/skip-files-issue-5065/foo/bar/baz.rs @@ -0,0 +1 @@ +fn baz() { } \ No newline at end of file diff --git a/tests/mod-resolver/skip-files-issue-5065/main.rs b/tests/mod-resolver/skip-files-issue-5065/main.rs new file mode 100644 index 000000000000..3122e4f220f6 --- /dev/null +++ b/tests/mod-resolver/skip-files-issue-5065/main.rs @@ -0,0 +1,9 @@ +#![rustfmt::skip] + +mod foo; +mod one; + +fn main() {println!("Hello, world!"); +} + +// trailing commet diff --git a/tests/mod-resolver/skip-files-issue-5065/one.rs b/tests/mod-resolver/skip-files-issue-5065/one.rs new file mode 100644 index 000000000000..e7eb2c2d64dd --- /dev/null +++ b/tests/mod-resolver/skip-files-issue-5065/one.rs @@ -0,0 +1 @@ +struct One { value: String } \ No newline at end of file diff --git a/tests/mod-resolver/test-submodule-issue-5119/Cargo.toml b/tests/mod-resolver/test-submodule-issue-5119/Cargo.toml new file mode 100644 index 000000000000..0993f1279599 --- /dev/null +++ b/tests/mod-resolver/test-submodule-issue-5119/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "rustfmt-test-submodule-issue" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/mod-resolver/test-submodule-issue-5119/src/lib.rs b/tests/mod-resolver/test-submodule-issue-5119/src/lib.rs new file mode 100644 index 000000000000..3f7ddba8a288 --- /dev/null +++ b/tests/mod-resolver/test-submodule-issue-5119/src/lib.rs @@ -0,0 +1,7 @@ +pub fn foo() -> i32 { +3 +} + +pub fn bar() -> i32 { +4 +} diff --git a/tests/mod-resolver/test-submodule-issue-5119/tests/test1.rs b/tests/mod-resolver/test-submodule-issue-5119/tests/test1.rs new file mode 100644 index 000000000000..da4e86169ad9 --- /dev/null +++ b/tests/mod-resolver/test-submodule-issue-5119/tests/test1.rs @@ -0,0 +1,8 @@ +mod test1 { +#[cfg(unix)] +mod sub1; +#[cfg(not(unix))] +mod sub2; + +mod sub3; +} diff --git a/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub1.rs b/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub1.rs new file mode 100644 index 000000000000..b760ba23cd27 --- /dev/null +++ b/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub1.rs @@ -0,0 +1,6 @@ +use rustfmt_test_submodule_issue::foo; + +#[test] +fn test_foo() { +assert_eq!(3, foo()); +} diff --git a/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub2.rs b/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub2.rs new file mode 100644 index 000000000000..4fd8286eac40 --- /dev/null +++ b/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub2.rs @@ -0,0 +1,6 @@ +use rustfmt_test_submodule_issue::bar; + +#[test] +fn test_bar() { +assert_eq!(4, bar()); +} diff --git a/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/mod.rs b/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/mod.rs new file mode 100644 index 000000000000..e029785bc245 --- /dev/null +++ b/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/mod.rs @@ -0,0 +1 @@ +mod sub4; diff --git a/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/sub4.rs b/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/sub4.rs new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/parser/issue-4126/invalid.rs b/tests/parser/issue-4126/invalid.rs new file mode 100644 index 000000000000..7709c8484642 --- /dev/null +++ b/tests/parser/issue-4126/invalid.rs @@ -0,0 +1,6 @@ +fn foo() { + if bar && if !baz { + next_is_none = Some(true); + } + println!("foo"); +} diff --git a/tests/parser/issue-4126/lib.rs b/tests/parser/issue-4126/lib.rs new file mode 100644 index 000000000000..aac63e3557fe --- /dev/null +++ b/tests/parser/issue-4126/lib.rs @@ -0,0 +1 @@ +mod invalid; diff --git a/tests/parser/issue_4418.rs b/tests/parser/issue_4418.rs new file mode 100644 index 000000000000..ff30235f076b --- /dev/null +++ b/tests/parser/issue_4418.rs @@ -0,0 +1 @@ +} \ No newline at end of file diff --git a/tests/parser/unclosed-delims/issue_4466.rs b/tests/parser/unclosed-delims/issue_4466.rs new file mode 100644 index 000000000000..2c2c81c91d19 --- /dev/null +++ b/tests/parser/unclosed-delims/issue_4466.rs @@ -0,0 +1,11 @@ +fn main() { + if true { + println!("answer: {}", a_func(); + } else { + println!("don't think so."); + } +} + +fn a_func() -> i32 { + 42 +} \ No newline at end of file diff --git a/tests/rustfmt/main.rs b/tests/rustfmt/main.rs new file mode 100644 index 000000000000..4936a7174636 --- /dev/null +++ b/tests/rustfmt/main.rs @@ -0,0 +1,188 @@ +//! Integration tests for rustfmt. + +use std::env; +use std::fs::remove_file; +use std::path::Path; +use std::process::Command; + +use rustfmt_config_proc_macro::rustfmt_only_ci_test; + +/// Run the rustfmt executable and return its output. +fn rustfmt(args: &[&str]) -> (String, String) { + let mut bin_dir = env::current_exe().unwrap(); + bin_dir.pop(); // chop off test exe name + if bin_dir.ends_with("deps") { + bin_dir.pop(); + } + let cmd = bin_dir.join(format!("rustfmt{}", env::consts::EXE_SUFFIX)); + + // Ensure the rustfmt binary runs from the local target dir. + let path = env::var_os("PATH").unwrap_or_default(); + let mut paths = env::split_paths(&path).collect::>(); + paths.insert(0, bin_dir); + let new_path = env::join_paths(paths).unwrap(); + + match Command::new(&cmd).args(args).env("PATH", new_path).output() { + Ok(output) => ( + String::from_utf8(output.stdout).expect("utf-8"), + String::from_utf8(output.stderr).expect("utf-8"), + ), + Err(e) => panic!("failed to run `{:?} {:?}`: {}", cmd, args, e), + } +} + +macro_rules! assert_that { + ($args:expr, $($check:ident $check_args:tt)&&+) => { + let (stdout, stderr) = rustfmt($args); + if $(!stdout.$check$check_args && !stderr.$check$check_args)||* { + panic!( + "Output not expected for rustfmt {:?}\n\ + expected: {}\n\ + actual stdout:\n{}\n\ + actual stderr:\n{}", + $args, + stringify!($( $check$check_args )&&*), + stdout, + stderr + ); + } + }; +} + +#[rustfmt_only_ci_test] +#[test] +fn print_config() { + assert_that!( + &["--print-config", "unknown"], + starts_with("Unknown print-config option") + ); + assert_that!(&["--print-config", "default"], contains("max_width = 100")); + assert_that!(&["--print-config", "minimal"], contains("PATH required")); + assert_that!( + &["--print-config", "minimal", "minimal-config"], + contains("doesn't work with standard input.") + ); + + let (stdout, stderr) = rustfmt(&[ + "--print-config", + "minimal", + "minimal-config", + "src/shape.rs", + ]); + assert!( + Path::new("minimal-config").exists(), + "stdout:\n{}\nstderr:\n{}", + stdout, + stderr + ); + remove_file("minimal-config").unwrap(); +} + +#[rustfmt_only_ci_test] +#[test] +fn inline_config() { + // single invocation + assert_that!( + &[ + "--print-config", + "current", + ".", + "--config=color=Never,edition=2018" + ], + contains("color = \"Never\"") && contains("edition = \"2018\"") + ); + + // multiple overriding invocations + assert_that!( + &[ + "--print-config", + "current", + ".", + "--config", + "color=never,edition=2018", + "--config", + "color=always,format_strings=true" + ], + contains("color = \"Always\"") + && contains("edition = \"2018\"") + && 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 because 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")); +} + +#[test] +fn rustfmt_emits_error_on_line_overflow_true() { + // See also https://github.com/rust-lang/rustfmt/issues/3164 + let args = [ + "--config", + "error_on_line_overflow=true", + "tests/cargo-fmt/source/issue_3164/src/main.rs", + ]; + + let (_stdout, stderr) = rustfmt(&args); + assert!(stderr.contains( + "line formatted, but exceeded maximum width (maximum: 100 (see `max_width` option)" + )) +} + +#[test] +#[allow(non_snake_case)] +fn dont_emit_ICE() { + let files = ["tests/target/issue_5728.rs", "tests/target/issue_5729.rs"]; + + for file in files { + let args = [file]; + let (_stdout, stderr) = rustfmt(&args); + assert!(!stderr.contains("thread 'main' panicked")); + } +} diff --git a/tests/source/5131_crate.rs b/tests/source/5131_crate.rs new file mode 100644 index 000000000000..96a31659022a --- /dev/null +++ b/tests/source/5131_crate.rs @@ -0,0 +1,14 @@ +// rustfmt-imports_granularity: Crate + +use foo::a; +use foo::a; +use foo::b; +use foo::b as b2; +use foo::b::f; +use foo::b::g; +use foo::b::g as g2; +use foo::c; +use foo::d::e; +use qux::h; +use qux::h as h2; +use qux::i; diff --git a/tests/source/5131_module.rs b/tests/source/5131_module.rs new file mode 100644 index 000000000000..3e9139177c56 --- /dev/null +++ b/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/tests/source/5131_one.rs b/tests/source/5131_one.rs new file mode 100644 index 000000000000..61ddf13410d4 --- /dev/null +++ b/tests/source/5131_one.rs @@ -0,0 +1,15 @@ +// rustfmt-imports_granularity: One + +pub use foo::x; +pub use foo::x as x2; +pub use foo::y; +use bar::a; +use bar::b; +use bar::b::f; +use bar::b::f as f2; +use bar::b::g; +use bar::c; +use bar::d::e; +use bar::d::e as e2; +use qux::h; +use qux::i; diff --git a/tests/source/alignment_2633/block_style.rs b/tests/source/alignment_2633/block_style.rs new file mode 100644 index 000000000000..77fb2919edbd --- /dev/null +++ b/tests/source/alignment_2633/block_style.rs @@ -0,0 +1,8 @@ +// rustfmt-struct_field_align_threshold: 50 + +fn func() { + Ok(ServerInformation { name: unwrap_message_string(items.get(0)), + vendor: unwrap_message_string(items.get(1)), + version: unwrap_message_string(items.get(2)), + spec_version: unwrap_message_string(items.get(3)), }); +} diff --git a/tests/source/alignment_2633/visual_style.rs b/tests/source/alignment_2633/visual_style.rs new file mode 100644 index 000000000000..f34cc621e64c --- /dev/null +++ b/tests/source/alignment_2633/visual_style.rs @@ -0,0 +1,9 @@ +// rustfmt-struct_field_align_threshold: 50 +// rustfmt-indent_style: Visual + +fn func() { + Ok(ServerInformation { name: unwrap_message_string(items.get(0)), + vendor: unwrap_message_string(items.get(1)), + version: unwrap_message_string(items.get(2)), + spec_version: unwrap_message_string(items.get(3)), }); +} diff --git a/tests/source/array_comment.rs b/tests/source/array_comment.rs new file mode 100644 index 000000000000..87372b2793fc --- /dev/null +++ b/tests/source/array_comment.rs @@ -0,0 +1,19 @@ +// Issue 2842 +// The comment should not make the last line shorter + +static XXX: [i8; 64] = [ + 1, // Comment + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +]; + +static XXX: [i8; 64] = [ + 1, + // Comment + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +]; + +static XXX: [i8; 64] = [ + 1, + // Comment + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +]; diff --git a/tests/source/assignment.rs b/tests/source/assignment.rs new file mode 100644 index 000000000000..71de325566dd --- /dev/null +++ b/tests/source/assignment.rs @@ -0,0 +1,34 @@ +// Test assignment + +fn main() { + let some_var : Type ; + + let mut mutable; + + let variable = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA::BBBBBBBBBBBBBBBBBBBBBB::CCCCCCCCCCCCCCCCCCCCCC::EEEEEE; + + variable = LOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONG; + + let single_line_fit = + DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD; + + single_line_fit = 5;single_lit_fit >>= 10; + + + // #2791 + let x = 2;;;; +} + +fn break_meee() { + { + (block_start, block_size, margin_block_start, margin_block_end) = match (block_start, + block_end, + block_size) { + x => 1, + _ => 2, + }; + } +} + +// #2018 +pub const EXPLAIN_UNSIZED_TUPLE_COERCION: &'static str = "Unsized tuple coercion is not stable enough for use and is subject to change"; diff --git a/tests/source/associated-types-bounds-wrapping.rs b/tests/source/associated-types-bounds-wrapping.rs new file mode 100644 index 000000000000..464f428c7f9a --- /dev/null +++ b/tests/source/associated-types-bounds-wrapping.rs @@ -0,0 +1,5 @@ +// Test proper wrapping of long associated type bounds + +pub trait HttpService { + type WsService: 'static + Service; +} diff --git a/tests/source/associated_type_bounds.rs b/tests/source/associated_type_bounds.rs new file mode 100644 index 000000000000..8572778a5a53 --- /dev/null +++ b/tests/source/associated_type_bounds.rs @@ -0,0 +1,13 @@ +// See #3657 - https://github.com/rust-lang/rustfmt/issues/3657 + +#![feature(associated_type_bounds)] + +fn f>() {} + +fn g>() {} + +fn h>() {} + +fn i>() {} + +fn j>() {} diff --git a/tests/source/async_block.rs b/tests/source/async_block.rs new file mode 100644 index 000000000000..18cb4fb5f5cc --- /dev/null +++ b/tests/source/async_block.rs @@ -0,0 +1,51 @@ +// rustfmt-edition: 2018 + +fn main() { + let x = async { + Ok(()) + }; +} + +fn baz() { + // test + let x = async { + // async blocks are great + Ok(()) + }; + + let y = async { + Ok(()) + }; // comment + + spawn( + a, + async move { + action(); + Ok(()) + }, + ); + + spawn( + a, + async move || { + action(); + Ok(()) + }, + ); + + spawn( + a, + static async || { + action(); + Ok(()) + }, + ); + + spawn( + a, + static async move || { + action(); + Ok(()) + }, + ); +} diff --git a/tests/source/async_fn.rs b/tests/source/async_fn.rs new file mode 100644 index 000000000000..c63cf5b0f51e --- /dev/null +++ b/tests/source/async_fn.rs @@ -0,0 +1,28 @@ +// rustfmt-edition: 2018 + +async fn bar() -> Result<(), ()> { + Ok(()) +} + +pub async fn baz() -> Result<(), ()> { + Ok(()) +} + +async unsafe fn foo() { + async move { + Ok(()) + } +} + +async unsafe fn rust() { + async move { // comment + Ok(()) + } +} + +async fn await_try() { + something + .await + ? + ; +} diff --git a/tests/source/attrib.rs b/tests/source/attrib.rs new file mode 100644 index 000000000000..d45fba552243 --- /dev/null +++ b/tests/source/attrib.rs @@ -0,0 +1,234 @@ +// rustfmt-wrap_comments: true +// Test attributes and doc comments are preserved. +#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", + html_favicon_url = "https://doc.rust-lang.org/favicon.ico", + html_root_url = "https://doc.rust-lang.org/nightly/", + html_playground_url = "https://play.rust-lang.org/", test(attr(deny(warnings))))] + +//! Doc comment + +#![attribute] + +//! Crate doc comment + +// Comment + +// Comment on attribute +#![the(attribute)] + +// Another comment + +/// Blah blah blah. +/// Blah blah blah. +/// Blah blah blah. +/// Blah blah blah. + +/// Blah blah blah. +impl Bar { + /// Blah blah blooo. + /// Blah blah blooo. + /// Blah blah blooo. + /// Blah blah blooo. + #[an_attribute] + #[doc = "an attribute that shouldn't be normalized to a doc comment"] + fn foo(&mut self) -> isize { + } + + /// Blah blah bing. + /// Blah blah bing. + /// Blah blah bing. + + + /// Blah blah bing. + /// Blah blah bing. + /// Blah blah bing. + pub fn f2(self) { + (foo, bar) + } + + #[another_attribute] + fn f3(self) -> Dog { + } + + /// Blah blah bing. + + #[attrib1] + /// Blah blah bing. + #[attrib2] + // Another comment that needs rewrite because it's tooooooooooooooooooooooooooooooo loooooooooooong. + /// Blah blah bing. + fn f4(self) -> Cat { + } + + // We want spaces around `=` + #[cfg(feature="nightly")] + fn f5(self) -> Monkey {} +} + +// #984 +struct Foo { + # [ derive ( Clone , PartialEq , Debug , Deserialize , Serialize ) ] + foo: usize, +} + +// #1668 + +/// Default path (*nix) +#[cfg(all(unix, not(target_os = "macos"), not(target_os = "ios"), not(target_os = "android")))] +fn foo() { + #[cfg(target_os = "freertos")] + match port_id { + 'a' | 'A' => GpioPort { port_address: GPIO_A }, + 'b' | 'B' => GpioPort { port_address: GPIO_B }, + _ => panic!(), + } + + #[cfg_attr(not(target_os = "freertos"), allow(unused_variables))] + let x = 3; +} + +// #1777 +#[test] +#[should_panic(expected = "(")] +#[should_panic(expected = /* ( */ "(")] +#[should_panic(/* ((((( */expected /* ((((( */= /* ((((( */ "("/* ((((( */)] +#[should_panic( + /* (((((((( *//* + (((((((((()(((((((( */ + expected = "(" + // (((((((( +)] +fn foo() {} + +// #1799 +fn issue_1799() { + #[allow(unreachable_code)] // https://github.com/rust-lang/rust/issues/43336 + Some( Err(error) ) ; + + #[allow(unreachable_code)] + // https://github.com/rust-lang/rust/issues/43336 + Some( Err(error) ) ; +} + +// Formatting inner attributes +fn inner_attributes() { + #![ this_is_an_inner_attribute ( foo ) ] + + foo(); +} + +impl InnerAttributes() { + #![ this_is_an_inner_attribute ( foo ) ] + + fn foo() {} +} + +mod InnerAttributes { + #![ this_is_an_inner_attribute ( foo ) ] +} + +fn attributes_on_statements() { + // Local + # [ attr ( on ( local ) ) ] + let x = 3; + + // Item + # [ attr ( on ( item ) ) ] + use foo; + + // Expr + # [ attr ( on ( expr ) ) ] + {} + + // Semi + # [ attr ( on ( semi ) ) ] + foo(); + + // Mac + # [ attr ( on ( mac ) ) ] + foo!(); +} + +// Large derives +#[derive(Add, Sub, Mul, Div, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Debug, Hash, Serialize, Mul)] + + +/// Foo bar baz + + +#[derive(Add, Sub, Mul, Div, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Debug, Hash, Serialize, Deserialize)] +pub struct HP(pub u8); + +// Long `#[doc = "..."]` +struct A { #[doc = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"] b: i32 } + +// #2647 +#[cfg(feature = "this_line_is_101_characters_long_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")] +pub fn foo() {} + +// path attrs +#[clippy::bar] +#[clippy::bar(a, b, c)] +pub fn foo() {} + +mod issue_2620 { + #[derive(Debug, StructOpt)] +#[structopt(about = "Display information about the character on FF Logs")] +pub struct Params { + #[structopt(help = "The server the character is on")] + server: String, + #[structopt(help = "The character's first name")] + first_name: String, + #[structopt(help = "The character's last name")] + last_name: String, + #[structopt( + short = "j", + long = "job", + help = "The job to look at", + parse(try_from_str) + )] + job: Option +} +} + +// #2969 +#[cfg(not(all(feature="std", + any(target_os = "linux", target_os = "android", + target_os = "netbsd", + target_os = "dragonfly", + target_os = "haiku", + target_os = "emscripten", + target_os = "solaris", + target_os = "cloudabi", + target_os = "macos", target_os = "ios", + target_os = "freebsd", + target_os = "openbsd", + target_os = "redox", + target_os = "fuchsia", + windows, + all(target_arch = "wasm32", feature = "stdweb"), + all(target_arch = "wasm32", feature = "wasm-bindgen"), + ))))] +type Os = NoSource; + +// #3313 +fn stmt_expr_attributes() { + let foo ; + #[must_use] + foo = false ; +} + +// #3509 +fn issue3509() { + match MyEnum { + MyEnum::Option1 if cfg!(target_os = "windows") => + #[cfg(target_os = "windows")]{ + 1 + } + } + match MyEnum { + MyEnum::Option1 if cfg!(target_os = "windows") => + #[cfg(target_os = "windows")] + 1, + } +} diff --git a/tests/source/big-impl-block.rs b/tests/source/big-impl-block.rs new file mode 100644 index 000000000000..f71e6515c429 --- /dev/null +++ b/tests/source/big-impl-block.rs @@ -0,0 +1,123 @@ +// #1357 +impl< + 'a, + Select, + From, + Distinct, + Where, + Order, + Limit, + Offset, + Groupby, + DB, +> InternalBoxedDsl<'a, DB> + for SelectStatement< + Select, + From, + Distinct, + Where, + Order, + Limit, + Offset, + GroupBy, + > where + DB: Backend, + Select: QueryFragment + SelectableExpression + 'a, + Distinct: QueryFragment + 'a, + Where: Into + 'a>>>, + Order: QueryFragment + 'a, + Limit: QueryFragment + 'a, + Offset: QueryFragment + 'a, +{ + type Output = BoxedSelectStatement<'a, Select::SqlTypeForSelect, From, DB>; + + fn internal_into_boxed(self) -> Self::Output { + BoxedSelectStatement::new( + Box::new(self.select), + self.from, + Box::new(self.distinct), + self.where_clause.into(), + Box::new(self.order), + Box::new(self.limit), + Box::new(self.offset), + ) + } +} + +// #1369 +impl< + ExcessivelyLongGenericName, + ExcessivelyLongGenericName, + AnotherExcessivelyLongGenericName, +> Foo for Bar { + fn foo() {} +} +impl Foo< + ExcessivelyLongGenericName, + ExcessivelyLongGenericName, + AnotherExcessivelyLongGenericName, +> for Bar { + fn foo() {} +} +impl< + ExcessivelyLongGenericName, + ExcessivelyLongGenericName, + AnotherExcessivelyLongGenericName, +> Foo< + ExcessivelyLongGenericName, + ExcessivelyLongGenericName, + AnotherExcessivelyLongGenericName, +> for Bar { + fn foo() {} +} +impl< + ExcessivelyLongGenericName, + ExcessivelyLongGenericName, + AnotherExcessivelyLongGenericName, +> Foo for Bar< + ExcessivelyLongGenericName, + ExcessivelyLongGenericName, + AnotherExcessivelyLongGenericName, +> { + fn foo() {} +} +impl Foo< + ExcessivelyLongGenericName, + ExcessivelyLongGenericName, + AnotherExcessivelyLongGenericName, +> for Bar< + ExcessivelyLongGenericName, + ExcessivelyLongGenericName, + AnotherExcessivelyLongGenericName, +> { + fn foo() {} +} +impl< + ExcessivelyLongGenericName, + ExcessivelyLongGenericName, + AnotherExcessivelyLongGenericName, +> Foo< + ExcessivelyLongGenericName, + ExcessivelyLongGenericName, + AnotherExcessivelyLongGenericName, +> for Bar< + ExcessivelyLongGenericName, + ExcessivelyLongGenericName, + AnotherExcessivelyLongGenericName, +> { + fn foo() {} +} + +// #1689 +impl SubSelectDirect + where + M: select::Selector, + S: event::Stream, + F: for<'t> FnMut(transform::Api< + 't, + Stream>, + >) + -> transform::Api<'t, X>, + X: event::Stream, +{ +} diff --git a/tests/source/big-impl-visual.rs b/tests/source/big-impl-visual.rs new file mode 100644 index 000000000000..7d906ac37f99 --- /dev/null +++ b/tests/source/big-impl-visual.rs @@ -0,0 +1,106 @@ +// rustfmt-indent_style: Visual + +// #1357 +impl< + 'a, + Select, + From, + Distinct, + Where, + Order, + Limit, + Offset, + Groupby, + DB, +> InternalBoxedDsl<'a, DB> + for SelectStatement< + Select, + From, + Distinct, + Where, + Order, + Limit, + Offset, + GroupBy, + > where + DB: Backend, + Select: QueryFragment + SelectableExpression + 'a, + Distinct: QueryFragment + 'a, + Where: Into + 'a>>>, + Order: QueryFragment + 'a, + Limit: QueryFragment + 'a, + Offset: QueryFragment + 'a, +{ + type Output = BoxedSelectStatement<'a, Select::SqlTypeForSelect, From, DB>; + + fn internal_into_boxed(self) -> Self::Output { + BoxedSelectStatement::new( + Box::new(self.select), + self.from, + Box::new(self.distinct), + self.where_clause.into(), + Box::new(self.order), + Box::new(self.limit), + Box::new(self.offset), + ) + } +} + +// #1369 +impl< + ExcessivelyLongGenericName, + ExcessivelyLongGenericName, + AnotherExcessivelyLongGenericName, +> Foo for Bar { + fn foo() {} +} +impl Foo< + ExcessivelyLongGenericName, + ExcessivelyLongGenericName, + AnotherExcessivelyLongGenericName, +> for Bar { + fn foo() {} +} +impl< + ExcessivelyLongGenericName, + ExcessivelyLongGenericName, + AnotherExcessivelyLongGenericName, +> Foo< + ExcessivelyLongGenericName, + ExcessivelyLongGenericName, + AnotherExcessivelyLongGenericName, +> for Bar { + fn foo() {} +} +impl< + ExcessivelyLongGenericName, + ExcessivelyLongGenericName, + AnotherExcessivelyLongGenericName, +> Foo for Bar< + ExcessivelyLongGenericName, + ExcessivelyLongGenericName, + AnotherExcessivelyLongGenericName, +> { + fn foo() {} +} +impl Foo< + ExcessivelyLongGenericName, + ExcessivelyLongGenericName, + AnotherExcessivelyLongGenericName, +> for Bar< + ExcessivelyLongGenericName, + ExcessivelyLongGenericName, + AnotherExcessivelyLongGenericName, +> { + fn foo() {} +} +impl Foo + for Bar { + fn foo() {} +} diff --git a/tests/source/binary-expr.rs b/tests/source/binary-expr.rs new file mode 100644 index 000000000000..f7502931d969 --- /dev/null +++ b/tests/source/binary-expr.rs @@ -0,0 +1,10 @@ +// Binary expressions + +fn foo() { + // 100 + let x = aaaaaaaaaa || bbbbbbbbbb || cccccccccc || dddddddddd && eeeeeeeeee || ffffffffff || ggg; + // 101 + let x = aaaaaaaaaa || bbbbbbbbbb || cccccccccc || dddddddddd && eeeeeeeeee || ffffffffff || gggg; + // 104 + let x = aaaaaaaaaa || bbbbbbbbbb || cccccccccc || dddddddddd && eeeeeeeeee || ffffffffff || gggggggg; +} diff --git a/tests/source/binop-separator-back/bitwise.rs b/tests/source/binop-separator-back/bitwise.rs new file mode 100644 index 000000000000..3804bf3215b1 --- /dev/null +++ b/tests/source/binop-separator-back/bitwise.rs @@ -0,0 +1,14 @@ +// rustfmt-binop_separator: Back + +fn main() { + let value = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ^ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ; + + let value = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ & abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ; + + let value = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ | abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ; + + let value = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ << abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ; + + let value = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ >> abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ; + +} diff --git a/tests/source/binop-separator-back/comp.rs b/tests/source/binop-separator-back/comp.rs new file mode 100644 index 000000000000..50a27127445d --- /dev/null +++ b/tests/source/binop-separator-back/comp.rs @@ -0,0 +1,23 @@ +// rustfmt-binop_separator: Back + +fn main() { + if abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ < abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ { + // + } + + if abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ <= abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ { + // + } + + if abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ > abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ { + // + } + + if abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ >= abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ { + // + } + + if abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ == abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ { + // + } +} diff --git a/tests/source/binop-separator-back/logic.rs b/tests/source/binop-separator-back/logic.rs new file mode 100644 index 000000000000..8c297e5a6750 --- /dev/null +++ b/tests/source/binop-separator-back/logic.rs @@ -0,0 +1,7 @@ +// rustfmt-binop_separator: Back + +fn main() { + if abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ && abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ || abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ { + // + } +} diff --git a/tests/source/binop-separator-back/math.rs b/tests/source/binop-separator-back/math.rs new file mode 100644 index 000000000000..3af4aad16051 --- /dev/null +++ b/tests/source/binop-separator-back/math.rs @@ -0,0 +1,7 @@ +// rustfmt-binop_separator: Back + +fn main() { + let value = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ; + + let value = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ * abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ - abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ / abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ * abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ * abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ / abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ / abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ; +} diff --git a/tests/source/binop-separator-back/patterns.rs b/tests/source/binop-separator-back/patterns.rs new file mode 100644 index 000000000000..a8c3b5cdd9b1 --- /dev/null +++ b/tests/source/binop-separator-back/patterns.rs @@ -0,0 +1,9 @@ +// rustfmt-binop_separator: Back + +fn main() { + match val { + ThisIsA::ReallyLongPatternNameToHelpOverflowTheNextValueOntoTheNextLine | ThisIsA::SecondValueSeparatedByAPipe | ThisIsA::ThirdValueSeparatedByAPipe => { + // + } + } +} diff --git a/tests/source/binop-separator-back/range.rs b/tests/source/binop-separator-back/range.rs new file mode 100644 index 000000000000..bdd3de9922b0 --- /dev/null +++ b/tests/source/binop-separator-back/range.rs @@ -0,0 +1,7 @@ +// rustfmt-binop_separator: Back + +fn main() { + let value = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ..abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ; + + let value = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ..=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ; +} diff --git a/tests/source/break-and-continue.rs b/tests/source/break-and-continue.rs new file mode 100644 index 000000000000..c01d8a078455 --- /dev/null +++ b/tests/source/break-and-continue.rs @@ -0,0 +1,23 @@ +// break and continue formatting + +#![feature(loop_break_value)] + +fn main() { + 'a: loop { + break 'a; + } + + let mut done = false; + 'b: while !done { + done = true; + continue 'b; + } + + let x = loop { + break 5; + }; + + let x = 'c: loop { + break 'c 5; + }; +} diff --git a/tests/source/catch.rs b/tests/source/catch.rs new file mode 100644 index 000000000000..541db1dc90c2 --- /dev/null +++ b/tests/source/catch.rs @@ -0,0 +1,28 @@ +// rustfmt-edition: 2018 +#![feature(try_blocks)] + +fn main() { + let x = try { + foo()? + }; + + let x = try /* Invisible comment */ { foo()? }; + + let x = try { + unsafe { foo()? } + }; + + let y = match (try { + foo()? + }) { + _ => (), + }; + + try { + foo()?; + }; + + try { + // Regular try block + }; +} diff --git a/tests/source/cfg_if/detect/arch/aarch64.rs b/tests/source/cfg_if/detect/arch/aarch64.rs new file mode 100644 index 000000000000..ebae2bd28540 --- /dev/null +++ b/tests/source/cfg_if/detect/arch/aarch64.rs @@ -0,0 +1,106 @@ +//! Aarch64 run-time features. + +/// Checks if `aarch64` feature is enabled. +#[macro_export] +#[unstable(feature = "stdsimd", issue = "27731")] +#[allow_internal_unstable(stdsimd_internal,stdsimd)] +macro_rules! is_aarch64_feature_detected { + ("neon") => { + // FIXME: this should be removed once we rename Aarch64 neon to asimd + cfg!(target_feature = "neon") || + $crate::detect::check_for($crate::detect::Feature::asimd) + }; + ("asimd") => { + cfg!(target_feature = "neon") || + $crate::detect::check_for($crate::detect::Feature::asimd) + }; + ("pmull") => { + cfg!(target_feature = "pmull") || + $crate::detect::check_for($crate::detect::Feature::pmull) + }; + ("fp") => { + cfg!(target_feature = "fp") || + $crate::detect::check_for($crate::detect::Feature::fp) + }; + ("fp16") => { + cfg!(target_feature = "fp16") || + $crate::detect::check_for($crate::detect::Feature::fp16) + }; + ("sve") => { + cfg!(target_feature = "sve") || + $crate::detect::check_for($crate::detect::Feature::sve) + }; + ("crc") => { + cfg!(target_feature = "crc") || + $crate::detect::check_for($crate::detect::Feature::crc) + }; + ("crypto") => { + cfg!(target_feature = "crypto") || + $crate::detect::check_for($crate::detect::Feature::crypto) + }; + ("lse") => { + cfg!(target_feature = "lse") || + $crate::detect::check_for($crate::detect::Feature::lse) + }; + ("rdm") => { + cfg!(target_feature = "rdm") || + $crate::detect::check_for($crate::detect::Feature::rdm) + }; + ("rcpc") => { + cfg!(target_feature = "rcpc") || + $crate::detect::check_for($crate::detect::Feature::rcpc) + }; + ("dotprod") => { + cfg!(target_feature = "dotprod") || + $crate::detect::check_for($crate::detect::Feature::dotprod) + }; + ("ras") => { + compile_error!("\"ras\" feature cannot be detected at run-time") + }; + ("v8.1a") => { + compile_error!("\"v8.1a\" feature cannot be detected at run-time") + }; + ("v8.2a") => { + compile_error!("\"v8.2a\" feature cannot be detected at run-time") + }; + ("v8.3a") => { + compile_error!("\"v8.3a\" feature cannot be detected at run-time") + }; + ($t:tt,) => { + is_aarch64_feature_detected!($t); + }; + ($t:tt) => { compile_error!(concat!("unknown aarch64 target feature: ", $t)) }; +} + +/// ARM Aarch64 CPU Feature enum. Each variant denotes a position in a bitset +/// for a particular feature. +/// +/// PLEASE: do not use this, it is an implementation detail subject to change. +#[doc(hidden)] +#[allow(non_camel_case_types)] +#[repr(u8)] +#[unstable(feature = "stdsimd_internal", issue = "0")] +pub enum Feature { + /// ARM Advanced SIMD (ASIMD) + asimd, + /// Polynomial Multiply + pmull, + /// Floating point support + fp, + /// Half-float support. + fp16, + /// Scalable Vector Extension (SVE) + sve, + /// CRC32 (Cyclic Redundancy Check) + crc, + /// Crypto: AES + PMULL + SHA1 + SHA2 + crypto, + /// Atomics (Large System Extension) + lse, + /// Rounding Double Multiply (ASIMDRDM) + rdm, + /// Release consistent Processor consistent (RcPc) + rcpc, + /// Vector Dot-Product (ASIMDDP) + dotprod, +} diff --git a/tests/source/cfg_if/detect/arch/arm.rs b/tests/source/cfg_if/detect/arch/arm.rs new file mode 100644 index 000000000000..b2626bf2923b --- /dev/null +++ b/tests/source/cfg_if/detect/arch/arm.rs @@ -0,0 +1,39 @@ +//! Run-time feature detection on ARM Aarch32. + +/// Checks if `arm` feature is enabled. +#[macro_export] +#[unstable(feature = "stdsimd", issue = "27731")] +#[allow_internal_unstable(stdsimd_internal,stdsimd)] +macro_rules! is_arm_feature_detected { + ("neon") => { + cfg!(target_feature = "neon") || + $crate::detect::check_for($crate::detect::Feature::neon) + }; + ("pmull") => { + cfg!(target_feature = "pmull") || + $crate::detect::check_for($crate::detect::Feature::pmull) + }; + ("v7") => { compile_error!("\"v7\" feature cannot be detected at run-time") }; + ("vfp2") => { compile_error!("\"vfp2\" feature cannot be detected at run-time") }; + ("vfp3") => { compile_error!("\"vfp3\" feature cannot be detected at run-time") }; + ("vfp4") => { compile_error!("\"vfp4\" feature cannot be detected at run-time") }; + ($t:tt,) => { + is_arm_feature_detected!($t); + }; + ($t:tt) => { compile_error!(concat!("unknown arm target feature: ", $t)) }; +} + +/// ARM CPU Feature enum. Each variant denotes a position in a bitset for a +/// particular feature. +/// +/// PLEASE: do not use this, it is an implementation detail subject to change. +#[doc(hidden)] +#[allow(non_camel_case_types)] +#[repr(u8)] +#[unstable(feature = "stdsimd_internal", issue = "0")] +pub enum Feature { + /// ARM Advanced SIMD (NEON) - Aarch32 + neon, + /// Polynomial Multiply + pmull, +} diff --git a/tests/source/cfg_if/detect/arch/mips.rs b/tests/source/cfg_if/detect/arch/mips.rs new file mode 100644 index 000000000000..f4381b811cdf --- /dev/null +++ b/tests/source/cfg_if/detect/arch/mips.rs @@ -0,0 +1,29 @@ +//! Run-time feature detection on MIPS. + +/// Checks if `mips` feature is enabled. +#[macro_export] +#[unstable(feature = "stdsimd", issue = "27731")] +#[allow_internal_unstable(stdsimd_internal,stdsimd)] +macro_rules! is_mips_feature_detected { + ("msa") => { + cfg!(target_feature = "msa") || + $crate::detect::check_for($crate::detect::Feature::msa) + }; + ($t:tt,) => { + is_mips_feature_detected!($t); + }; + ($t:tt) => { compile_error!(concat!("unknown mips target feature: ", $t)) }; +} + +/// MIPS CPU Feature enum. Each variant denotes a position in a bitset for a +/// particular feature. +/// +/// PLEASE: do not use this, it is an implementation detail subject to change. +#[doc(hidden)] +#[allow(non_camel_case_types)] +#[repr(u8)] +#[unstable(feature = "stdsimd_internal", issue = "0")] +pub enum Feature { + /// MIPS SIMD Architecture (MSA) + msa, +} diff --git a/tests/source/cfg_if/detect/arch/mips64.rs b/tests/source/cfg_if/detect/arch/mips64.rs new file mode 100644 index 000000000000..2663bc68ba98 --- /dev/null +++ b/tests/source/cfg_if/detect/arch/mips64.rs @@ -0,0 +1,29 @@ +//! Run-time feature detection on MIPS64. + +/// Checks if `mips64` feature is enabled. +#[macro_export] +#[unstable(feature = "stdsimd", issue = "27731")] +#[allow_internal_unstable(stdsimd_internal,stdsimd)] +macro_rules! is_mips64_feature_detected { + ("msa") => { + cfg!(target_feature = "msa") || + $crate::detect::check_for($crate::detect::Feature::msa) + }; + ($t:tt,) => { + is_mips64_feature_detected!($t); + }; + ($t:tt) => { compile_error!(concat!("unknown mips64 target feature: ", $t)) }; +} + +/// MIPS64 CPU Feature enum. Each variant denotes a position in a bitset +/// for a particular feature. +/// +/// PLEASE: do not use this, it is an implementation detail subject to change. +#[doc(hidden)] +#[allow(non_camel_case_types)] +#[repr(u8)] +#[unstable(feature = "stdsimd_internal", issue = "0")] +pub enum Feature { + /// MIPS SIMD Architecture (MSA) + msa, +} diff --git a/tests/source/cfg_if/detect/arch/powerpc.rs b/tests/source/cfg_if/detect/arch/powerpc.rs new file mode 100644 index 000000000000..a342dc1aacc0 --- /dev/null +++ b/tests/source/cfg_if/detect/arch/powerpc.rs @@ -0,0 +1,42 @@ +//! Run-time feature detection on PowerPC. + +/// Checks if `powerpc` feature is enabled. +#[macro_export] +#[unstable(feature = "stdsimd", issue = "27731")] +#[allow_internal_unstable(stdsimd_internal,stdsimd)] +macro_rules! is_powerpc_feature_detected { + ("altivec") => { + cfg!(target_feature = "altivec") || + $crate::detect::check_for($crate::detect::Feature::altivec) + }; + ("vsx") => { + cfg!(target_feature = "vsx") || + $crate::detect::check_for($crate::detect::Feature::vsx) + }; + ("power8") => { + cfg!(target_feature = "power8") || + $crate::detect::check_for($crate::detect::Feature::power8) + }; + ($t:tt,) => { + is_powerpc_feature_detected!($t); + }; + ($t:tt) => { compile_error!(concat!("unknown powerpc target feature: ", $t)) }; +} + + +/// PowerPC CPU Feature enum. Each variant denotes a position in a bitset +/// for a particular feature. +/// +/// PLEASE: do not use this, it is an implementation detail subject to change. +#[doc(hidden)] +#[allow(non_camel_case_types)] +#[repr(u8)] +#[unstable(feature = "stdsimd_internal", issue = "0")] +pub enum Feature { + /// Altivec + altivec, + /// VSX + vsx, + /// Power8 + power8, +} diff --git a/tests/source/cfg_if/detect/arch/powerpc64.rs b/tests/source/cfg_if/detect/arch/powerpc64.rs new file mode 100644 index 000000000000..2e82c569252a --- /dev/null +++ b/tests/source/cfg_if/detect/arch/powerpc64.rs @@ -0,0 +1,42 @@ +//! Run-time feature detection on PowerPC64. + +/// Checks if `powerpc64` feature is enabled. +#[macro_export] +#[unstable(feature = "stdsimd", issue = "27731")] +#[allow_internal_unstable(stdsimd_internal,stdsimd)] +macro_rules! is_powerpc64_feature_detected { + ("altivec") => { + cfg!(target_feature = "altivec") || + $crate::detect::check_for($crate::detect::Feature::altivec) + }; + ("vsx") => { + cfg!(target_feature = "vsx") || + $crate::detect::check_for($crate::detect::Feature::vsx) + }; + ("power8") => { + cfg!(target_feature = "power8") || + $crate::detect::check_for($crate::detect::Feature::power8) + }; + ($t:tt,) => { + is_powerpc64_feature_detected!($t); + }; + ($t:tt) => { compile_error!(concat!("unknown powerpc64 target feature: ", $t)) }; +} + + +/// PowerPC64 CPU Feature enum. Each variant denotes a position in a bitset +/// for a particular feature. +/// +/// PLEASE: do not use this, it is an implementation detail subject to change. +#[doc(hidden)] +#[allow(non_camel_case_types)] +#[repr(u8)] +#[unstable(feature = "stdsimd_internal", issue = "0")] +pub enum Feature { + /// Altivec + altivec, + /// VSX + vsx, + /// Power8 + power8, +} diff --git a/tests/source/cfg_if/detect/arch/x86.rs b/tests/source/cfg_if/detect/arch/x86.rs new file mode 100644 index 000000000000..131cbb855f16 --- /dev/null +++ b/tests/source/cfg_if/detect/arch/x86.rs @@ -0,0 +1,348 @@ +//! This module implements minimal run-time feature detection for x86. +//! +//! The features are detected using the `detect_features` function below. +//! This function uses the CPUID instruction to read the feature flags from the +//! CPU and encodes them in a `usize` where each bit position represents +//! whether a feature is available (bit is set) or unavailable (bit is cleared). +//! +//! The enum `Feature` is used to map bit positions to feature names, and the +//! the `__crate::detect::check_for!` macro is used to map string literals (e.g., +//! "avx") to these bit positions (e.g., `Feature::avx`). +//! +//! The run-time feature detection is performed by the +//! `__crate::detect::check_for(Feature) -> bool` function. On its first call, +//! this functions queries the CPU for the available features and stores them +//! in a global `AtomicUsize` variable. The query is performed by just checking +//! whether the feature bit in this global variable is set or cleared. + +/// A macro to test at *runtime* whether a CPU feature is available on +/// x86/x86-64 platforms. +/// +/// This macro is provided in the standard library and will detect at runtime +/// whether the specified CPU feature is detected. This does **not** resolve at +/// compile time unless the specified feature is already enabled for the entire +/// crate. Runtime detection currently relies mostly on the `cpuid` instruction. +/// +/// This macro only takes one argument which is a string literal of the feature +/// being tested for. The feature names supported are the lowercase versions of +/// the ones defined by Intel in [their documentation][docs]. +/// +/// ## Supported arguments +/// +/// This macro supports the same names that `#[target_feature]` supports. Unlike +/// `#[target_feature]`, however, this macro does not support names separated +/// with a comma. Instead testing for multiple features must be done through +/// separate macro invocations for now. +/// +/// Supported arguments are: +/// +/// * `"aes"` +/// * `"pclmulqdq"` +/// * `"rdrand"` +/// * `"rdseed"` +/// * `"tsc"` +/// * `"mmx"` +/// * `"sse"` +/// * `"sse2"` +/// * `"sse3"` +/// * `"ssse3"` +/// * `"sse4.1"` +/// * `"sse4.2"` +/// * `"sse4a"` +/// * `"sha"` +/// * `"avx"` +/// * `"avx2"` +/// * `"avx512f"` +/// * `"avx512cd"` +/// * `"avx512er"` +/// * `"avx512pf"` +/// * `"avx512bw"` +/// * `"avx512dq"` +/// * `"avx512vl"` +/// * `"avx512ifma"` +/// * `"avx512vbmi"` +/// * `"avx512vpopcntdq"` +/// * `"f16c"` +/// * `"fma"` +/// * `"bmi1"` +/// * `"bmi2"` +/// * `"abm"` +/// * `"lzcnt"` +/// * `"tbm"` +/// * `"popcnt"` +/// * `"fxsr"` +/// * `"xsave"` +/// * `"xsaveopt"` +/// * `"xsaves"` +/// * `"xsavec"` +/// * `"adx"` +/// * `"rtm"` +/// +/// [docs]: https://software.intel.com/sites/landingpage/IntrinsicsGuide +#[macro_export] +#[stable(feature = "simd_x86", since = "1.27.0")] +#[allow_internal_unstable(stdsimd_internal,stdsimd)] +macro_rules! is_x86_feature_detected { + ("aes") => { + cfg!(target_feature = "aes") || $crate::detect::check_for( + $crate::detect::Feature::aes) }; + ("pclmulqdq") => { + cfg!(target_feature = "pclmulqdq") || $crate::detect::check_for( + $crate::detect::Feature::pclmulqdq) }; + ("rdrand") => { + cfg!(target_feature = "rdrand") || $crate::detect::check_for( + $crate::detect::Feature::rdrand) }; + ("rdseed") => { + cfg!(target_feature = "rdseed") || $crate::detect::check_for( + $crate::detect::Feature::rdseed) }; + ("tsc") => { + cfg!(target_feature = "tsc") || $crate::detect::check_for( + $crate::detect::Feature::tsc) }; + ("mmx") => { + cfg!(target_feature = "mmx") || $crate::detect::check_for( + $crate::detect::Feature::mmx) }; + ("sse") => { + cfg!(target_feature = "sse") || $crate::detect::check_for( + $crate::detect::Feature::sse) }; + ("sse2") => { + cfg!(target_feature = "sse2") || $crate::detect::check_for( + $crate::detect::Feature::sse2) + }; + ("sse3") => { + cfg!(target_feature = "sse3") || $crate::detect::check_for( + $crate::detect::Feature::sse3) + }; + ("ssse3") => { + cfg!(target_feature = "ssse3") || $crate::detect::check_for( + $crate::detect::Feature::ssse3) + }; + ("sse4.1") => { + cfg!(target_feature = "sse4.1") || $crate::detect::check_for( + $crate::detect::Feature::sse4_1) + }; + ("sse4.2") => { + cfg!(target_feature = "sse4.2") || $crate::detect::check_for( + $crate::detect::Feature::sse4_2) + }; + ("sse4a") => { + cfg!(target_feature = "sse4a") || $crate::detect::check_for( + $crate::detect::Feature::sse4a) + }; + ("sha") => { + cfg!(target_feature = "sha") || $crate::detect::check_for( + $crate::detect::Feature::sha) + }; + ("avx") => { + cfg!(target_feature = "avx") || $crate::detect::check_for( + $crate::detect::Feature::avx) + }; + ("avx2") => { + cfg!(target_feature = "avx2") || $crate::detect::check_for( + $crate::detect::Feature::avx2) + }; + ("avx512f") => { + cfg!(target_feature = "avx512f") || $crate::detect::check_for( + $crate::detect::Feature::avx512f) + }; + ("avx512cd") => { + cfg!(target_feature = "avx512cd") || $crate::detect::check_for( + $crate::detect::Feature::avx512cd) + }; + ("avx512er") => { + cfg!(target_feature = "avx512er") || $crate::detect::check_for( + $crate::detect::Feature::avx512er) + }; + ("avx512pf") => { + cfg!(target_feature = "avx512pf") || $crate::detect::check_for( + $crate::detect::Feature::avx512pf) + }; + ("avx512bw") => { + cfg!(target_feature = "avx512bw") || $crate::detect::check_for( + $crate::detect::Feature::avx512bw) + }; + ("avx512dq") => { + cfg!(target_feature = "avx512dq") || $crate::detect::check_for( + $crate::detect::Feature::avx512dq) + }; + ("avx512vl") => { + cfg!(target_Feature = "avx512vl") || $crate::detect::check_for( + $crate::detect::Feature::avx512vl) + }; + ("avx512ifma") => { + cfg!(target_feature = "avx512ifma") || $crate::detect::check_for( + $crate::detect::Feature::avx512_ifma) + }; + ("avx512vbmi") => { + cfg!(target_feature = "avx512vbmi") || $crate::detect::check_for( + $crate::detect::Feature::avx512_vbmi) + }; + ("avx512vpopcntdq") => { + cfg!(target_feature = "avx512vpopcntdq") || $crate::detect::check_for( + $crate::detect::Feature::avx512_vpopcntdq) + }; + ("f16c") => { + cfg!(target_feature = "f16c") || $crate::detect::check_for( + $crate::detect::Feature::f16c) + }; + ("fma") => { + cfg!(target_feature = "fma") || $crate::detect::check_for( + $crate::detect::Feature::fma) + }; + ("bmi1") => { + cfg!(target_feature = "bmi1") || $crate::detect::check_for( + $crate::detect::Feature::bmi) + }; + ("bmi2") => { + cfg!(target_feature = "bmi2") || $crate::detect::check_for( + $crate::detect::Feature::bmi2) + }; + ("abm") => { + cfg!(target_feature = "abm") || $crate::detect::check_for( + $crate::detect::Feature::abm) + }; + ("lzcnt") => { + cfg!(target_feature = "lzcnt") || $crate::detect::check_for( + $crate::detect::Feature::abm) + }; + ("tbm") => { + cfg!(target_feature = "tbm") || $crate::detect::check_for( + $crate::detect::Feature::tbm) + }; + ("popcnt") => { + cfg!(target_feature = "popcnt") || $crate::detect::check_for( + $crate::detect::Feature::popcnt) + }; + ("fxsr") => { + cfg!(target_feature = "fxsr") || $crate::detect::check_for( + $crate::detect::Feature::fxsr) + }; + ("xsave") => { + cfg!(target_feature = "xsave") || $crate::detect::check_for( + $crate::detect::Feature::xsave) + }; + ("xsaveopt") => { + cfg!(target_feature = "xsaveopt") || $crate::detect::check_for( + $crate::detect::Feature::xsaveopt) + }; + ("xsaves") => { + cfg!(target_feature = "xsaves") || $crate::detect::check_for( + $crate::detect::Feature::xsaves) + }; + ("xsavec") => { + cfg!(target_feature = "xsavec") || $crate::detect::check_for( + $crate::detect::Feature::xsavec) + }; + ("cmpxchg16b") => { + cfg!(target_feature = "cmpxchg16b") || $crate::detect::check_for( + $crate::detect::Feature::cmpxchg16b) + }; + ("adx") => { + cfg!(target_feature = "adx") || $crate::detect::check_for( + $crate::detect::Feature::adx) + }; + ("rtm") => { + cfg!(target_feature = "rtm") || $crate::detect::check_for( + $crate::detect::Feature::rtm) + }; + ($t:tt,) => { + is_x86_feature_detected!($t); + }; + ($t:tt) => { + compile_error!(concat!("unknown target feature: ", $t)) + }; +} + +/// X86 CPU Feature enum. Each variant denotes a position in a bitset for a +/// particular feature. +/// +/// This is an unstable implementation detail subject to change. +#[allow(non_camel_case_types)] +#[repr(u8)] +#[doc(hidden)] +#[unstable(feature = "stdsimd_internal", issue = "0")] +pub enum Feature { + /// AES (Advanced Encryption Standard New Instructions AES-NI) + aes, + /// CLMUL (Carry-less Multiplication) + pclmulqdq, + /// RDRAND + rdrand, + /// RDSEED + rdseed, + /// TSC (Time Stamp Counter) + tsc, + /// MMX + mmx, + /// SSE (Streaming SIMD Extensions) + sse, + /// SSE2 (Streaming SIMD Extensions 2) + sse2, + /// SSE3 (Streaming SIMD Extensions 3) + sse3, + /// SSSE3 (Supplemental Streaming SIMD Extensions 3) + ssse3, + /// SSE4.1 (Streaming SIMD Extensions 4.1) + sse4_1, + /// SSE4.2 (Streaming SIMD Extensions 4.2) + sse4_2, + /// SSE4a (Streaming SIMD Extensions 4a) + sse4a, + /// SHA + sha, + /// AVX (Advanced Vector Extensions) + avx, + /// AVX2 (Advanced Vector Extensions 2) + avx2, + /// AVX-512 F (Foundation) + avx512f, + /// AVX-512 CD (Conflict Detection Instructions) + avx512cd, + /// AVX-512 ER (Exponential and Reciprocal Instructions) + avx512er, + /// AVX-512 PF (Prefetch Instructions) + avx512pf, + /// AVX-512 BW (Byte and Word Instructions) + avx512bw, + /// AVX-512 DQ (Doubleword and Quadword) + avx512dq, + /// AVX-512 VL (Vector Length Extensions) + avx512vl, + /// AVX-512 IFMA (Integer Fused Multiply Add) + avx512_ifma, + /// AVX-512 VBMI (Vector Byte Manipulation Instructions) + avx512_vbmi, + /// AVX-512 VPOPCNTDQ (Vector Population Count Doubleword and + /// Quadword) + avx512_vpopcntdq, + /// F16C (Conversions between IEEE-754 `binary16` and `binary32` formats) + f16c, + /// FMA (Fused Multiply Add) + fma, + /// BMI1 (Bit Manipulation Instructions 1) + bmi, + /// BMI1 (Bit Manipulation Instructions 2) + bmi2, + /// ABM (Advanced Bit Manipulation) on AMD / LZCNT (Leading Zero + /// Count) on Intel + abm, + /// TBM (Trailing Bit Manipulation) + tbm, + /// POPCNT (Population Count) + popcnt, + /// FXSR (Floating-point context fast save and restore) + fxsr, + /// XSAVE (Save Processor Extended States) + xsave, + /// XSAVEOPT (Save Processor Extended States Optimized) + xsaveopt, + /// XSAVES (Save Processor Extended States Supervisor) + xsaves, + /// XSAVEC (Save Processor Extended States Compacted) + xsavec, + /// CMPXCH16B, a 16-byte compare-and-swap instruction + cmpxchg16b, + /// ADX, Intel ADX (Multi-Precision Add-Carry Instruction Extensions) + adx, + /// RTM, Intel (Restricted Transactional Memory) + rtm, +} diff --git a/tests/source/cfg_if/detect/bit.rs b/tests/source/cfg_if/detect/bit.rs new file mode 100644 index 000000000000..578f0b16b742 --- /dev/null +++ b/tests/source/cfg_if/detect/bit.rs @@ -0,0 +1,9 @@ +//! Bit manipulation utilities. + +/// Tests the `bit` of `x`. +#[allow(dead_code)] +#[inline] +pub(crate) fn test(x: usize, bit: u32) -> bool { + debug_assert!(bit < 32, "bit index out-of-bounds"); + x & (1 << bit) != 0 +} diff --git a/tests/source/cfg_if/detect/cache.rs b/tests/source/cfg_if/detect/cache.rs new file mode 100644 index 000000000000..92bc4b58d167 --- /dev/null +++ b/tests/source/cfg_if/detect/cache.rs @@ -0,0 +1,164 @@ +//! Caches run-time feature detection so that it only needs to be computed +//! once. + +#![allow(dead_code)] // not used on all platforms + +use crate::sync::atomic::Ordering; + +#[cfg(target_pointer_width = "64")] +use crate::sync::atomic::AtomicU64; + +#[cfg(target_pointer_width = "32")] +use crate::sync::atomic::AtomicU32; + +/// Sets the `bit` of `x`. +#[inline] +const fn set_bit(x: u64, bit: u32) -> u64 { + x | 1 << bit +} + +/// Tests the `bit` of `x`. +#[inline] +const fn test_bit(x: u64, bit: u32) -> bool { + x & (1 << bit) != 0 +} + +/// Maximum number of features that can be cached. +const CACHE_CAPACITY: u32 = 63; + +/// This type is used to initialize the cache +#[derive(Copy, Clone)] +pub(crate) struct Initializer(u64); + +#[allow(clippy::use_self)] +impl Default for Initializer { + fn default() -> Self { + Initializer(0) + } +} + +impl Initializer { + /// Tests the `bit` of the cache. + #[allow(dead_code)] + #[inline] + pub(crate) fn test(self, bit: u32) -> bool { + // FIXME: this way of making sure that the cache is large enough is + // brittle. + debug_assert!( + bit < CACHE_CAPACITY, + "too many features, time to increase the cache size!" + ); + test_bit(self.0, bit) + } + + /// Sets the `bit` of the cache. + #[inline] + pub(crate) fn set(&mut self, bit: u32) { + // FIXME: this way of making sure that the cache is large enough is + // brittle. + debug_assert!( + bit < CACHE_CAPACITY, + "too many features, time to increase the cache size!" + ); + let v = self.0; + self.0 = set_bit(v, bit); + } +} + +/// This global variable is a cache of the features supported by the CPU. +static CACHE: Cache = Cache::uninitialized(); + +/// Feature cache with capacity for `CACHE_CAPACITY` features. +/// +/// Note: the last feature bit is used to represent an +/// uninitialized cache. +#[cfg(target_pointer_width = "64")] +struct Cache(AtomicU64); + +#[cfg(target_pointer_width = "64")] +#[allow(clippy::use_self)] +impl Cache { + /// Creates an uninitialized cache. + #[allow(clippy::declare_interior_mutable_const)] + const fn uninitialized() -> Self { + Cache(AtomicU64::new(u64::max_value())) + } + /// Is the cache uninitialized? + #[inline] + pub(crate) fn is_uninitialized(&self) -> bool { + self.0.load(Ordering::Relaxed) == u64::max_value() + } + + /// Is the `bit` in the cache set? + #[inline] + pub(crate) fn test(&self, bit: u32) -> bool { + test_bit(CACHE.0.load(Ordering::Relaxed), bit) + } + + /// Initializes the cache. + #[inline] + pub(crate) fn initialize(&self, value: Initializer) { + self.0.store(value.0, Ordering::Relaxed); + } +} + +/// Feature cache with capacity for `CACHE_CAPACITY` features. +/// +/// Note: the last feature bit is used to represent an +/// uninitialized cache. +#[cfg(target_pointer_width = "32")] +struct Cache(AtomicU32, AtomicU32); + +#[cfg(target_pointer_width = "32")] +impl Cache { + /// Creates an uninitialized cache. + const fn uninitialized() -> Self { + Cache( + AtomicU32::new(u32::max_value()), + AtomicU32::new(u32::max_value()), + ) + } + /// Is the cache uninitialized? + #[inline] + pub(crate) fn is_uninitialized(&self) -> bool { + self.1.load(Ordering::Relaxed) == u32::max_value() + } + + /// Is the `bit` in the cache set? + #[inline] + pub(crate) fn test(&self, bit: u32) -> bool { + if bit < 32 { + test_bit(CACHE.0.load(Ordering::Relaxed) as u64, bit) + } else { + test_bit(CACHE.1.load(Ordering::Relaxed) as u64, bit - 32) + } + } + + /// Initializes the cache. + #[inline] + pub(crate) fn initialize(&self, value: Initializer) { + let lo: u32 = value.0 as u32; + let hi: u32 = (value.0 >> 32) as u32; + self.0.store(lo, Ordering::Relaxed); + self.1.store(hi, Ordering::Relaxed); + } +} + +/// Tests the `bit` of the storage. If the storage has not been initialized, +/// initializes it with the result of `f()`. +/// +/// On its first invocation, it detects the CPU features and caches them in the +/// `CACHE` global variable as an `AtomicU64`. +/// +/// It uses the `Feature` variant to index into this variable as a bitset. If +/// the bit is set, the feature is enabled, and otherwise it is disabled. +#[inline] +pub(crate) fn test(bit: u32, f: F) -> bool +where + F: FnOnce() -> Initializer, +{ + if CACHE.is_uninitialized() { + CACHE.initialize(f()); + } + CACHE.test(bit) +} diff --git a/tests/source/cfg_if/detect/error_macros.rs b/tests/source/cfg_if/detect/error_macros.rs new file mode 100644 index 000000000000..6769757ed933 --- /dev/null +++ b/tests/source/cfg_if/detect/error_macros.rs @@ -0,0 +1,150 @@ +//! The `is_{target_arch}_feature_detected!` macro are only available on their +//! architecture. These macros provide a better error messages when the user +//! attempts to call them in a different architecture. + +/// Prevents compilation if `is_x86_feature_detected` is used somewhere +/// else than `x86` and `x86_64` targets. +#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] +#[macro_export] +#[unstable(feature = "stdsimd", issue = "27731")] +macro_rules! is_x86_feature_detected { + ($t: tt) => { + compile_error!( + r#" + is_x86_feature_detected can only be used on x86 and x86_64 targets. + You can prevent it from being used in other architectures by + guarding it behind a cfg(target_arch) as follows: + + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { + if is_x86_feature_detected(...) { ... } + } + "# + ) + }; +} + +/// Prevents compilation if `is_arm_feature_detected` is used somewhere else +/// than `ARM` targets. +#[cfg(not(target_arch = "arm"))] +#[macro_export] +#[unstable(feature = "stdsimd", issue = "27731")] +macro_rules! is_arm_feature_detected { + ($t:tt) => { + compile_error!( + r#" + is_arm_feature_detected can only be used on ARM targets. + You can prevent it from being used in other architectures by + guarding it behind a cfg(target_arch) as follows: + + #[cfg(target_arch = "arm")] { + if is_arm_feature_detected(...) { ... } + } + "# + ) + }; +} + +/// Prevents compilation if `is_aarch64_feature_detected` is used somewhere else +/// than `aarch64` targets. +#[cfg(not(target_arch = "aarch64"))] +#[macro_export] +#[unstable(feature = "stdsimd", issue = "27731")] +macro_rules! is_aarch64_feature_detected { + ($t: tt) => { + compile_error!( + r#" + is_aarch64_feature_detected can only be used on AArch64 targets. + You can prevent it from being used in other architectures by + guarding it behind a cfg(target_arch) as follows: + + #[cfg(target_arch = "aarch64")] { + if is_aarch64_feature_detected(...) { ... } + } + "# + ) + }; +} + +/// Prevents compilation if `is_powerpc_feature_detected` is used somewhere else +/// than `PowerPC` targets. +#[cfg(not(target_arch = "powerpc"))] +#[macro_export] +#[unstable(feature = "stdsimd", issue = "27731")] +macro_rules! is_powerpc_feature_detected { + ($t:tt) => { + compile_error!( + r#" +is_powerpc_feature_detected can only be used on PowerPC targets. +You can prevent it from being used in other architectures by +guarding it behind a cfg(target_arch) as follows: + + #[cfg(target_arch = "powerpc")] { + if is_powerpc_feature_detected(...) { ... } + } +"# + ) + }; +} + +/// Prevents compilation if `is_powerpc64_feature_detected` is used somewhere +/// else than `PowerPC64` targets. +#[cfg(not(target_arch = "powerpc64"))] +#[macro_export] +#[unstable(feature = "stdsimd", issue = "27731")] +macro_rules! is_powerpc64_feature_detected { + ($t:tt) => { + compile_error!( + r#" +is_powerpc64_feature_detected can only be used on PowerPC64 targets. +You can prevent it from being used in other architectures by +guarding it behind a cfg(target_arch) as follows: + + #[cfg(target_arch = "powerpc64")] { + if is_powerpc64_feature_detected(...) { ... } + } +"# + ) + }; +} + +/// Prevents compilation if `is_mips_feature_detected` is used somewhere else +/// than `MIPS` targets. +#[cfg(not(target_arch = "mips"))] +#[macro_export] +#[unstable(feature = "stdsimd", issue = "27731")] +macro_rules! is_mips_feature_detected { + ($t:tt) => { + compile_error!( + r#" + is_mips_feature_detected can only be used on MIPS targets. + You can prevent it from being used in other architectures by + guarding it behind a cfg(target_arch) as follows: + + #[cfg(target_arch = "mips")] { + if is_mips_feature_detected(...) { ... } + } + "# + ) + }; +} + +/// Prevents compilation if `is_mips64_feature_detected` is used somewhere else +/// than `MIPS64` targets. +#[cfg(not(target_arch = "mips64"))] +#[macro_export] +#[unstable(feature = "stdsimd", issue = "27731")] +macro_rules! is_mips64_feature_detected { + ($t:tt) => { + compile_error!( + r#" + is_mips64_feature_detected can only be used on MIPS64 targets. + You can prevent it from being used in other architectures by + guarding it behind a cfg(target_arch) as follows: + + #[cfg(target_arch = "mips64")] { + if is_mips64_feature_detected(...) { ... } + } + "# + ) + }; +} diff --git a/tests/source/cfg_if/detect/mod.rs b/tests/source/cfg_if/detect/mod.rs new file mode 100644 index 000000000000..f446e88eedc8 --- /dev/null +++ b/tests/source/cfg_if/detect/mod.rs @@ -0,0 +1,85 @@ +//! This module implements run-time feature detection. +//! +//! The `is_{arch}_feature_detected!("feature-name")` macros take the name of a +//! feature as a string-literal, and return a boolean indicating whether the +//! feature is enabled at run-time or not. +//! +//! These macros do two things: +//! * map the string-literal into an integer stored as a `Feature` enum, +//! * call a `os::check_for(x: Feature)` function that returns `true` if the +//! feature is enabled. +//! +//! The `Feature` enums are also implemented in the `arch/{target_arch}.rs` +//! modules. +//! +//! The `check_for` functions are, in general, Operating System dependent. Most +//! architectures do not allow user-space programs to query the feature bits +//! due to security concerns (x86 is the big exception). These functions are +//! implemented in the `os/{target_os}.rs` modules. + +#[macro_use] +mod error_macros; + +cfg_if! { + if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { + #[path = "arch/x86.rs"] + #[macro_use] + mod arch; + } else if #[cfg(target_arch = "arm")] { + #[path = "arch/arm.rs"] + #[macro_use] + mod arch; + } else if #[cfg(target_arch = "aarch64")] { + #[path = "arch/aarch64.rs"] + #[macro_use] + mod arch; + } else if #[cfg(target_arch = "powerpc")] { + #[path = "arch/powerpc.rs"] + #[macro_use] + mod arch; + } else if #[cfg(target_arch = "powerpc64")] { + #[path = "arch/powerpc64.rs"] + #[macro_use] + mod arch; + } else if #[cfg(target_arch = "mips")] { + #[path = "arch/mips.rs"] + #[macro_use] + mod arch; + } else if #[cfg(target_arch = "mips64")] { + #[path = "arch/mips64.rs"] + #[macro_use] + mod arch; + } else { + // Unimplemented architecture: + mod arch { + pub enum Feature { + Null + } + } + } +} +pub use self::arch::Feature; + +mod bit; +mod cache; + +cfg_if! { + if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { + // On x86/x86_64 no OS specific functionality is required. + #[path = "os/x86.rs"] + mod os; + } else if #[cfg(all(target_os = "linux", feature = "use_std"))] { + #[path = "os/linux/mod.rs"] + mod os; + } else if #[cfg(target_os = "freebsd")] { + #[cfg(target_arch = "aarch64")] + #[path = "os/aarch64.rs"] + mod aarch64; + #[path = "os/freebsd/mod.rs"] + mod os; + } else { + #[path = "os/other.rs"] + mod os; + } +} +pub use self::os::check_for; diff --git a/tests/source/cfg_if/detect/os/aarch64.rs b/tests/source/cfg_if/detect/os/aarch64.rs new file mode 100644 index 000000000000..dfb8c87707f0 --- /dev/null +++ b/tests/source/cfg_if/detect/os/aarch64.rs @@ -0,0 +1,79 @@ +//! Run-time feature detection for Aarch64 on any OS that emulates the mrs instruction. +//! +//! On FreeBSD >= 12.0, Linux >= 4.11 and other operating systems, it is possible to use +//! privileged system registers from userspace to check CPU feature support. +//! +//! AArch64 system registers ID_AA64ISAR0_EL1, ID_AA64PFR0_EL1, ID_AA64ISAR1_EL1 +//! have bits dedicated to features like AdvSIMD, CRC32, AES, atomics (LSE), etc. +//! Each part of the register indicates the level of support for a certain feature, e.g. +//! when ID_AA64ISAR0_EL1\[7:4\] is >= 1, AES is supported; when it's >= 2, PMULL is supported. +//! +//! For proper support of [SoCs where different cores have different capabilities](https://medium.com/@jadr2ddude/a-big-little-problem-a-tale-of-big-little-gone-wrong-e7778ce744bb), +//! the OS has to always report only the features supported by all cores, like [FreeBSD does](https://reviews.freebsd.org/D17137#393947). +//! +//! References: +//! +//! - [Zircon implementation](https://fuchsia.googlesource.com/zircon/+/master/kernel/arch/arm64/feature.cpp) +//! - [Linux documentation](https://www.kernel.org/doc/Documentation/arm64/cpu-feature-registers.txt) + +use crate::detect::{Feature, cache}; + +/// Try to read the features from the system registers. +/// +/// This will cause SIGILL if the current OS is not trapping the mrs instruction. +pub(crate) fn detect_features() -> cache::Initializer { + let mut value = cache::Initializer::default(); + + { + let mut enable_feature = |f, enable| { + if enable { + value.set(f as u32); + } + }; + + // ID_AA64ISAR0_EL1 - Instruction Set Attribute Register 0 + let aa64isar0: u64; + unsafe { asm!("mrs $0, ID_AA64ISAR0_EL1" : "=r"(aa64isar0)); } + + let aes = bits_shift(aa64isar0, 7, 4) >= 1; + let pmull = bits_shift(aa64isar0, 7, 4) >= 2; + let sha1 = bits_shift(aa64isar0, 11, 8) >= 1; + let sha2 = bits_shift(aa64isar0, 15, 12) >= 1; + enable_feature(Feature::pmull, pmull); + // Crypto is specified as AES + PMULL + SHA1 + SHA2 per LLVM/hosts.cpp + enable_feature(Feature::crypto, aes && pmull && sha1 && sha2); + enable_feature(Feature::lse, bits_shift(aa64isar0, 23, 20) >= 1); + enable_feature(Feature::crc, bits_shift(aa64isar0, 19, 16) >= 1); + + // ID_AA64PFR0_EL1 - Processor Feature Register 0 + let aa64pfr0: u64; + unsafe { asm!("mrs $0, ID_AA64PFR0_EL1" : "=r"(aa64pfr0)); } + + let fp = bits_shift(aa64pfr0, 19, 16) < 0xF; + let fphp = bits_shift(aa64pfr0, 19, 16) >= 1; + let asimd = bits_shift(aa64pfr0, 23, 20) < 0xF; + let asimdhp = bits_shift(aa64pfr0, 23, 20) >= 1; + enable_feature(Feature::fp, fp); + enable_feature(Feature::fp16, fphp); + // SIMD support requires float support - if half-floats are + // supported, it also requires half-float support: + enable_feature(Feature::asimd, fp && asimd && (!fphp | asimdhp)); + // SIMD extensions require SIMD support: + enable_feature(Feature::rdm, asimd && bits_shift(aa64isar0, 31, 28) >= 1); + enable_feature(Feature::dotprod, asimd && bits_shift(aa64isar0, 47, 44) >= 1); + enable_feature(Feature::sve, asimd && bits_shift(aa64pfr0, 35, 32) >= 1); + + // ID_AA64ISAR1_EL1 - Instruction Set Attribute Register 1 + let aa64isar1: u64; + unsafe { asm!("mrs $0, ID_AA64ISAR1_EL1" : "=r"(aa64isar1)); } + + enable_feature(Feature::rcpc, bits_shift(aa64isar1, 23, 20) >= 1); + } + + value +} + +#[inline] +fn bits_shift(x: u64, high: usize, low: usize) -> u64 { + (x >> low) & ((1 << (high - low + 1)) - 1) +} diff --git a/tests/source/cfg_if/detect/os/freebsd/aarch64.rs b/tests/source/cfg_if/detect/os/freebsd/aarch64.rs new file mode 100644 index 000000000000..910d2f33b390 --- /dev/null +++ b/tests/source/cfg_if/detect/os/freebsd/aarch64.rs @@ -0,0 +1,28 @@ +//! Run-time feature detection for Aarch64 on FreeBSD. + +use crate::detect::{Feature, cache}; +use super::super::aarch64::detect_features; + +/// Performs run-time feature detection. +#[inline] +pub fn check_for(x: Feature) -> bool { + cache::test(x as u32, detect_features) +} + +#[cfg(test)] +mod tests { + #[test] + fn dump() { + println!("asimd: {:?}", is_aarch64_feature_detected!("asimd")); + println!("pmull: {:?}", is_aarch64_feature_detected!("pmull")); + println!("fp: {:?}", is_aarch64_feature_detected!("fp")); + println!("fp16: {:?}", is_aarch64_feature_detected!("fp16")); + println!("sve: {:?}", is_aarch64_feature_detected!("sve")); + println!("crc: {:?}", is_aarch64_feature_detected!("crc")); + println!("crypto: {:?}", is_aarch64_feature_detected!("crypto")); + println!("lse: {:?}", is_aarch64_feature_detected!("lse")); + println!("rdm: {:?}", is_aarch64_feature_detected!("rdm")); + println!("rcpc: {:?}", is_aarch64_feature_detected!("rcpc")); + println!("dotprod: {:?}", is_aarch64_feature_detected!("dotprod")); + } +} diff --git a/tests/source/cfg_if/detect/os/freebsd/arm.rs b/tests/source/cfg_if/detect/os/freebsd/arm.rs new file mode 100644 index 000000000000..e13847dcbd89 --- /dev/null +++ b/tests/source/cfg_if/detect/os/freebsd/arm.rs @@ -0,0 +1,27 @@ +//! Run-time feature detection for ARM on FreeBSD + +use crate::detect::{Feature, cache}; +use super::{auxvec}; + +/// Performs run-time feature detection. +#[inline] +pub fn check_for(x: Feature) -> bool { + cache::test(x as u32, detect_features) +} + +/// Try to read the features from the auxiliary vector +fn detect_features() -> cache::Initializer { + let mut value = cache::Initializer::default(); + let enable_feature = |value: &mut cache::Initializer, f, enable| { + if enable { + value.set(f as u32); + } + }; + + if let Ok(auxv) = auxvec::auxv() { + enable_feature(&mut value, Feature::neon, auxv.hwcap & 0x00001000 != 0); + enable_feature(&mut value, Feature::pmull, auxv.hwcap2 & 0x00000002 != 0); + return value; + } + value +} diff --git a/tests/source/cfg_if/detect/os/freebsd/auxvec.rs b/tests/source/cfg_if/detect/os/freebsd/auxvec.rs new file mode 100644 index 000000000000..a2bac7676014 --- /dev/null +++ b/tests/source/cfg_if/detect/os/freebsd/auxvec.rs @@ -0,0 +1,86 @@ +//! Parses ELF auxiliary vectors. +#![cfg_attr(any(target_arch = "arm", target_arch = "powerpc64"), allow(dead_code))] + +/// Key to access the CPU Hardware capabilities bitfield. +pub(crate) const AT_HWCAP: usize = 25; +/// Key to access the CPU Hardware capabilities 2 bitfield. +pub(crate) const AT_HWCAP2: usize = 26; + +/// Cache HWCAP bitfields of the ELF Auxiliary Vector. +/// +/// If an entry cannot be read all the bits in the bitfield are set to zero. +/// This should be interpreted as all the features being disabled. +#[derive(Debug, Copy, Clone)] +pub(crate) struct AuxVec { + pub hwcap: usize, + pub hwcap2: usize, +} + +/// ELF Auxiliary Vector +/// +/// The auxiliary vector is a memory region in a running ELF program's stack +/// composed of (key: usize, value: usize) pairs. +/// +/// The keys used in the aux vector are platform dependent. For FreeBSD, they are +/// defined in [sys/elf_common.h][elf_common_h]. The hardware capabilities of a given +/// CPU can be queried with the `AT_HWCAP` and `AT_HWCAP2` keys. +/// +/// Note that run-time feature detection is not invoked for features that can +/// be detected at compile-time. +/// +/// [elf_common.h]: https://svnweb.freebsd.org/base/release/12.0.0/sys/sys/elf_common.h?revision=341707 +pub(crate) fn auxv() -> Result { + if let Ok(hwcap) = archauxv(AT_HWCAP) { + if let Ok(hwcap2) = archauxv(AT_HWCAP2) { + if hwcap != 0 && hwcap2 != 0 { + return Ok(AuxVec { hwcap, hwcap2 }); + } + } + } + Err(()) +} + +/// Tries to read the `key` from the auxiliary vector. +fn archauxv(key: usize) -> Result { + use crate::mem; + + #[derive (Copy, Clone)] + #[repr(C)] + pub struct Elf_Auxinfo { + pub a_type: usize, + pub a_un: unnamed, + } + #[derive (Copy, Clone)] + #[repr(C)] + pub union unnamed { + pub a_val: libc::c_long, + pub a_ptr: *mut libc::c_void, + pub a_fcn: Option ()>, + } + + let mut auxv: [Elf_Auxinfo; 27] = + [Elf_Auxinfo{a_type: 0, a_un: unnamed{a_val: 0,},}; 27]; + + let mut len: libc::c_uint = mem::size_of_val(&auxv) as libc::c_uint; + + unsafe { + let mut mib = [libc::CTL_KERN, libc::KERN_PROC, libc::KERN_PROC_AUXV, libc::getpid()]; + + let ret = libc::sysctl(mib.as_mut_ptr(), + mib.len() as u32, + &mut auxv as *mut _ as *mut _, + &mut len as *mut _ as *mut _, + 0 as *mut libc::c_void, + 0, + ); + + if ret != -1 { + for i in 0..auxv.len() { + if auxv[i].a_type == key { + return Ok(auxv[i].a_un.a_val as usize); + } + } + } + } + return Ok(0); +} diff --git a/tests/source/cfg_if/detect/os/freebsd/mod.rs b/tests/source/cfg_if/detect/os/freebsd/mod.rs new file mode 100644 index 000000000000..1a5338a35559 --- /dev/null +++ b/tests/source/cfg_if/detect/os/freebsd/mod.rs @@ -0,0 +1,22 @@ +//! Run-time feature detection on FreeBSD + +mod auxvec; + +cfg_if! { + if #[cfg(target_arch = "aarch64")] { + mod aarch64; + pub use self::aarch64::check_for; + } else if #[cfg(target_arch = "arm")] { + mod arm; + pub use self::arm::check_for; + } else if #[cfg(target_arch = "powerpc64")] { + mod powerpc; + pub use self::powerpc::check_for; + } else { + use crate::arch::detect::Feature; + /// Performs run-time feature detection. + pub fn check_for(_x: Feature) -> bool { + false + } + } +} diff --git a/tests/source/cfg_if/detect/os/freebsd/powerpc.rs b/tests/source/cfg_if/detect/os/freebsd/powerpc.rs new file mode 100644 index 000000000000..c7f761d4d605 --- /dev/null +++ b/tests/source/cfg_if/detect/os/freebsd/powerpc.rs @@ -0,0 +1,27 @@ +//! Run-time feature detection for PowerPC on FreeBSD. + +use crate::detect::{Feature, cache}; +use super::{auxvec}; + +/// Performs run-time feature detection. +#[inline] +pub fn check_for(x: Feature) -> bool { + cache::test(x as u32, detect_features) +} + +fn detect_features() -> cache::Initializer { + let mut value = cache::Initializer::default(); + let enable_feature = |value: &mut cache::Initializer, f, enable| { + if enable { + value.set(f as u32); + } + }; + + if let Ok(auxv) = auxvec::auxv() { + enable_feature(&mut value, Feature::altivec, auxv.hwcap & 0x10000000 != 0); + enable_feature(&mut value, Feature::vsx, auxv.hwcap & 0x00000080 != 0); + enable_feature(&mut value, Feature::power8, auxv.hwcap2 & 0x80000000 != 0); + return value; + } + value +} diff --git a/tests/source/cfg_if/detect/os/linux/aarch64.rs b/tests/source/cfg_if/detect/os/linux/aarch64.rs new file mode 100644 index 000000000000..f7dc0f0222e5 --- /dev/null +++ b/tests/source/cfg_if/detect/os/linux/aarch64.rs @@ -0,0 +1,157 @@ +//! Run-time feature detection for Aarch64 on Linux. + +use crate::detect::{Feature, cache, bit}; +use super::{auxvec, cpuinfo}; + +/// Performs run-time feature detection. +#[inline] +pub fn check_for(x: Feature) -> bool { + cache::test(x as u32, detect_features) +} + +/// Try to read the features from the auxiliary vector, and if that fails, try +/// to read them from /proc/cpuinfo. +fn detect_features() -> cache::Initializer { + if let Ok(auxv) = auxvec::auxv() { + let hwcap: AtHwcap = auxv.into(); + return hwcap.cache(); + } + if let Ok(c) = cpuinfo::CpuInfo::new() { + let hwcap: AtHwcap = c.into(); + return hwcap.cache(); + } + cache::Initializer::default() +} + +/// These values are part of the platform-specific [asm/hwcap.h][hwcap] . +/// +/// [hwcap]: https://github.com/torvalds/linux/blob/master/arch/arm64/include/uapi/asm/hwcap.h +struct AtHwcap { + fp: bool, // 0 + asimd: bool, // 1 + // evtstrm: bool, // 2 + aes: bool, // 3 + pmull: bool, // 4 + sha1: bool, // 5 + sha2: bool, // 6 + crc32: bool, // 7 + atomics: bool, // 8 + fphp: bool, // 9 + asimdhp: bool, // 10 + // cpuid: bool, // 11 + asimdrdm: bool, // 12 + // jscvt: bool, // 13 + // fcma: bool, // 14 + lrcpc: bool, // 15 + // dcpop: bool, // 16 + // sha3: bool, // 17 + // sm3: bool, // 18 + // sm4: bool, // 19 + asimddp: bool, // 20 + // sha512: bool, // 21 + sve: bool, // 22 +} + +impl From for AtHwcap { + /// Reads AtHwcap from the auxiliary vector. + fn from(auxv: auxvec::AuxVec) -> Self { + AtHwcap { + fp: bit::test(auxv.hwcap, 0), + asimd: bit::test(auxv.hwcap, 1), + // evtstrm: bit::test(auxv.hwcap, 2), + aes: bit::test(auxv.hwcap, 3), + pmull: bit::test(auxv.hwcap, 4), + sha1: bit::test(auxv.hwcap, 5), + sha2: bit::test(auxv.hwcap, 6), + crc32: bit::test(auxv.hwcap, 7), + atomics: bit::test(auxv.hwcap, 8), + fphp: bit::test(auxv.hwcap, 9), + asimdhp: bit::test(auxv.hwcap, 10), + // cpuid: bit::test(auxv.hwcap, 11), + asimdrdm: bit::test(auxv.hwcap, 12), + // jscvt: bit::test(auxv.hwcap, 13), + // fcma: bit::test(auxv.hwcap, 14), + lrcpc: bit::test(auxv.hwcap, 15), + // dcpop: bit::test(auxv.hwcap, 16), + // sha3: bit::test(auxv.hwcap, 17), + // sm3: bit::test(auxv.hwcap, 18), + // sm4: bit::test(auxv.hwcap, 19), + asimddp: bit::test(auxv.hwcap, 20), + // sha512: bit::test(auxv.hwcap, 21), + sve: bit::test(auxv.hwcap, 22), + } + } +} + +impl From for AtHwcap { + /// Reads AtHwcap from /proc/cpuinfo . + fn from(c: cpuinfo::CpuInfo) -> Self { + let f = &c.field("Features"); + AtHwcap { + // 64-bit names. FIXME: In 32-bit compatibility mode /proc/cpuinfo will + // map some of the 64-bit names to some 32-bit feature names. This does not + // cover that yet. + fp: f.has("fp"), + asimd: f.has("asimd"), + // evtstrm: f.has("evtstrm"), + aes: f.has("aes"), + pmull: f.has("pmull"), + sha1: f.has("sha1"), + sha2: f.has("sha2"), + crc32: f.has("crc32"), + atomics: f.has("atomics"), + fphp: f.has("fphp"), + asimdhp: f.has("asimdhp"), + // cpuid: f.has("cpuid"), + asimdrdm: f.has("asimdrdm"), + // jscvt: f.has("jscvt"), + // fcma: f.has("fcma"), + lrcpc: f.has("lrcpc"), + // dcpop: f.has("dcpop"), + // sha3: f.has("sha3"), + // sm3: f.has("sm3"), + // sm4: f.has("sm4"), + asimddp: f.has("asimddp"), + // sha512: f.has("sha512"), + sve: f.has("sve"), + } + } +} + +impl AtHwcap { + /// Initializes the cache from the feature -bits. + /// + /// The features are enabled approximately like in LLVM host feature detection: + /// https://github.com/llvm-mirror/llvm/blob/master/lib/Support/Host.cpp#L1273 + fn cache(self) -> cache::Initializer { + let mut value = cache::Initializer::default(); + { + let mut enable_feature = |f, enable| { + if enable { + value.set(f as u32); + } + }; + + enable_feature(Feature::fp, self.fp); + // Half-float support requires float support + enable_feature(Feature::fp16, self.fp && self.fphp); + enable_feature(Feature::pmull, self.pmull); + enable_feature(Feature::crc, self.crc32); + enable_feature(Feature::lse, self.atomics); + enable_feature(Feature::rcpc, self.lrcpc); + + // SIMD support requires float support - if half-floats are + // supported, it also requires half-float support: + let asimd = self.fp && self.asimd && (!self.fphp | self.asimdhp); + enable_feature(Feature::asimd, asimd); + // SIMD extensions require SIMD support: + enable_feature(Feature::rdm, self.asimdrdm && asimd); + enable_feature(Feature::dotprod, self.asimddp && asimd); + enable_feature(Feature::sve, self.sve && asimd); + + // Crypto is specified as AES + PMULL + SHA1 + SHA2 per LLVM/hosts.cpp + enable_feature(Feature::crypto, self.aes && self.pmull && self.sha1 && self.sha2); + } + value + } +} diff --git a/tests/source/cfg_if/detect/os/linux/arm.rs b/tests/source/cfg_if/detect/os/linux/arm.rs new file mode 100644 index 000000000000..0d58a847cd62 --- /dev/null +++ b/tests/source/cfg_if/detect/os/linux/arm.rs @@ -0,0 +1,49 @@ +//! Run-time feature detection for ARM on Linux. + +use crate::detect::{Feature, cache, bit}; +use super::{auxvec, cpuinfo}; + +/// Performs run-time feature detection. +#[inline] +pub fn check_for(x: Feature) -> bool { + cache::test(x as u32, detect_features) +} + +/// Try to read the features from the auxiliary vector, and if that fails, try +/// to read them from /proc/cpuinfo. +fn detect_features() -> cache::Initializer { + let mut value = cache::Initializer::default(); + let enable_feature = |value: &mut cache::Initializer, f, enable| { + if enable { + value.set(f as u32); + } + }; + + // The values are part of the platform-specific [asm/hwcap.h][hwcap] + // + // [hwcap]: https://github.com/torvalds/linux/blob/master/arch/arm64/include/uapi/asm/hwcap.h + if let Ok(auxv) = auxvec::auxv() { + enable_feature(&mut value, Feature::neon, bit::test(auxv.hwcap, 12)); + enable_feature(&mut value, Feature::pmull, bit::test(auxv.hwcap2, 1)); + return value; + } + + if let Ok(c) = cpuinfo::CpuInfo::new() { + enable_feature(&mut value, Feature::neon, c.field("Features").has("neon") && + !has_broken_neon(&c)); + enable_feature(&mut value, Feature::pmull, c.field("Features").has("pmull")); + return value; + } + value +} + +/// Is the CPU known to have a broken NEON unit? +/// +/// See https://crbug.com/341598. +fn has_broken_neon(cpuinfo: &cpuinfo::CpuInfo) -> bool { + cpuinfo.field("CPU implementer") == "0x51" + && cpuinfo.field("CPU architecture") == "7" + && cpuinfo.field("CPU variant") == "0x1" + && cpuinfo.field("CPU part") == "0x04d" + && cpuinfo.field("CPU revision") == "0" +} diff --git a/tests/source/cfg_if/detect/os/linux/auxvec.rs b/tests/source/cfg_if/detect/os/linux/auxvec.rs new file mode 100644 index 000000000000..07b6432eafd9 --- /dev/null +++ b/tests/source/cfg_if/detect/os/linux/auxvec.rs @@ -0,0 +1,307 @@ +//! Parses ELF auxiliary vectors. +#![cfg_attr(not(target_arch = "aarch64"), allow(dead_code))] + +#[cfg(feature = "std_detect_file_io")] +use crate::{fs::File, io::Read}; + +/// Key to access the CPU Hardware capabilities bitfield. +pub(crate) const AT_HWCAP: usize = 16; +/// Key to access the CPU Hardware capabilities 2 bitfield. +#[cfg(any(target_arch = "arm", target_arch = "powerpc64"))] +pub(crate) const AT_HWCAP2: usize = 26; + +/// Cache HWCAP bitfields of the ELF Auxiliary Vector. +/// +/// If an entry cannot be read all the bits in the bitfield are set to zero. +/// This should be interpreted as all the features being disabled. +#[derive(Debug, Copy, Clone)] +pub(crate) struct AuxVec { + pub hwcap: usize, + #[cfg(any(target_arch = "arm", target_arch = "powerpc64"))] + pub hwcap2: usize, +} + +/// ELF Auxiliary Vector +/// +/// The auxiliary vector is a memory region in a running ELF program's stack +/// composed of (key: usize, value: usize) pairs. +/// +/// The keys used in the aux vector are platform dependent. For Linux, they are +/// defined in [linux/auxvec.h][auxvec_h]. The hardware capabilities of a given +/// CPU can be queried with the `AT_HWCAP` and `AT_HWCAP2` keys. +/// +/// There is no perfect way of reading the auxiliary vector. +/// +/// - If the `std_detect_dlsym_getauxval` cargo feature is enabled, this will use +/// `getauxval` if its linked to the binary, and otherwise proceed to a fallback implementation. +/// When `std_detect_dlsym_getauxval` is disabled, this will assume that `getauxval` is +/// linked to the binary - if that is not the case the behavior is undefined. +/// - Otherwise, if the `std_detect_file_io` cargo feature is enabled, it will +/// try to read `/proc/self/auxv`. +/// - If that fails, this function returns an error. +/// +/// Note that run-time feature detection is not invoked for features that can +/// be detected at compile-time. Also note that if this function returns an +/// error, cpuinfo still can (and will) be used to try to perform run-time +/// feature detecton on some platforms. +/// +/// For more information about when `getauxval` is available check the great +/// [`auxv` crate documentation][auxv_docs]. +/// +/// [auxvec_h]: https://github.com/torvalds/linux/blob/master/include/uapi/linux/auxvec.h +/// [auxv_docs]: https://docs.rs/auxv/0.3.3/auxv/ +pub(crate) fn auxv() -> Result { + #[cfg(feature = "std_detect_dlsym_getauxval")] { + // Try to call a dynamically-linked getauxval function. + if let Ok(hwcap) = getauxval(AT_HWCAP) { + // Targets with only AT_HWCAP: + #[cfg(any(target_arch = "aarch64", target_arch = "mips", + target_arch = "mips64"))] + { + if hwcap != 0 { + return Ok(AuxVec { hwcap }); + } + } + + // Targets with AT_HWCAP and AT_HWCAP2: + #[cfg(any(target_arch = "arm", target_arch = "powerpc64"))] + { + if let Ok(hwcap2) = getauxval(AT_HWCAP2) { + if hwcap != 0 && hwcap2 != 0 { + return Ok(AuxVec { hwcap, hwcap2 }); + } + } + } + drop(hwcap); + } + #[cfg(feature = "std_detect_file_io")] { + // If calling getauxval fails, try to read the auxiliary vector from + // its file: + auxv_from_file("/proc/self/auxv") + } + #[cfg(not(feature = "std_detect_file_io"))] { + Err(()) + } + } + + #[cfg(not(feature = "std_detect_dlsym_getauxval"))] { + let hwcap = unsafe { ffi_getauxval(AT_HWCAP) }; + + // Targets with only AT_HWCAP: + #[cfg(any(target_arch = "aarch64", target_arch = "mips", + target_arch = "mips64"))] + { + if hwcap != 0 { + return Ok(AuxVec { hwcap }); + } + } + + // Targets with AT_HWCAP and AT_HWCAP2: + #[cfg(any(target_arch = "arm", target_arch = "powerpc64"))] + { + let hwcap2 = unsafe { ffi_getauxval(AT_HWCAP2) }; + if hwcap != 0 && hwcap2 != 0 { + return Ok(AuxVec { hwcap, hwcap2 }); + } + } + } +} + +/// Tries to read the `key` from the auxiliary vector by calling the +/// dynamically-linked `getauxval` function. If the function is not linked, +/// this function return `Err`. +#[cfg(feature = "std_detect_dlsym_getauxval")] +fn getauxval(key: usize) -> Result { + use libc; + pub type F = unsafe extern "C" fn(usize) -> usize; + unsafe { + let ptr = libc::dlsym( + libc::RTLD_DEFAULT, + "getauxval\0".as_ptr() as *const _, + ); + if ptr.is_null() { + return Err(()); + } + + let ffi_getauxval: F = mem::transmute(ptr); + Ok(ffi_getauxval(key)) + } +} + +/// Tries to read the auxiliary vector from the `file`. If this fails, this +/// function returns `Err`. +#[cfg(feature = "std_detect_file_io")] +fn auxv_from_file(file: &str) -> Result { + let mut file = File::open(file).map_err(|_| ())?; + + // See . + // + // The auxiliary vector contains at most 32 (key,value) fields: from + // `AT_EXECFN = 31` to `AT_NULL = 0`. That is, a buffer of + // 2*32 `usize` elements is enough to read the whole vector. + let mut buf = [0_usize; 64]; + { + let raw: &mut [u8; 64 * mem::size_of::()] = + unsafe { mem::transmute(&mut buf) }; + file.read(raw).map_err(|_| ())?; + } + auxv_from_buf(&buf) +} + +/// Tries to interpret the `buffer` as an auxiliary vector. If that fails, this +/// function returns `Err`. +#[cfg(feature = "std_detect_file_io")] +fn auxv_from_buf(buf: &[usize; 64]) -> Result { + // Targets with only AT_HWCAP: + #[cfg(any(target_arch = "aarch64", target_arch = "mips", + target_arch = "mips64"))] + { + for el in buf.chunks(2) { + match el[0] { + AT_HWCAP => return Ok(AuxVec { hwcap: el[1] }), + _ => (), + } + } + } + // Targets with AT_HWCAP and AT_HWCAP2: + #[cfg(any(target_arch = "arm", target_arch = "powerpc64"))] + { + let mut hwcap = None; + let mut hwcap2 = None; + for el in buf.chunks(2) { + match el[0] { + AT_HWCAP => hwcap = Some(el[1]), + AT_HWCAP2 => hwcap2 = Some(el[1]), + _ => (), + } + } + + if let (Some(hwcap), Some(hwcap2)) = (hwcap, hwcap2) { + return Ok(AuxVec { hwcap, hwcap2 }); + } + } + drop(buf); + Err(()) +} + +#[cfg(test)] +mod tests { + extern crate auxv as auxv_crate; + use super::*; + + // Reads the Auxiliary Vector key from /proc/self/auxv + // using the auxv crate. + #[cfg(feature = "std_detect_file_io")] + fn auxv_crate_getprocfs(key: usize) -> Option { + use self::auxv_crate::AuxvType; + use self::auxv_crate::procfs::search_procfs_auxv; + let k = key as AuxvType; + match search_procfs_auxv(&[k]) { + Ok(v) => Some(v[&k] as usize), + Err(_) => None, + } + } + + // Reads the Auxiliary Vector key from getauxval() + // using the auxv crate. + #[cfg(not(any(target_arch = "mips", target_arch = "mips64")))] + fn auxv_crate_getauxval(key: usize) -> Option { + use self::auxv_crate::AuxvType; + use self::auxv_crate::getauxval::Getauxval; + let q = auxv_crate::getauxval::NativeGetauxval {}; + match q.getauxval(key as AuxvType) { + Ok(v) => Some(v as usize), + Err(_) => None, + } + } + + // FIXME: on mips/mips64 getauxval returns 0, and /proc/self/auxv + // does not always contain the AT_HWCAP key under qemu. + #[cfg(not(any(target_arch = "mips", target_arch = "mips64", target_arch = "powerpc")))] + #[test] + fn auxv_crate() { + let v = auxv(); + if let Some(hwcap) = auxv_crate_getauxval(AT_HWCAP) { + let rt_hwcap = v.expect("failed to find hwcap key").hwcap; + assert_eq!(rt_hwcap, hwcap); + } + + // Targets with AT_HWCAP and AT_HWCAP2: + #[cfg(any(target_arch = "arm", target_arch = "powerpc64"))] + { + if let Some(hwcap2) = auxv_crate_getauxval(AT_HWCAP2) { + let rt_hwcap2 = v.expect("failed to find hwcap2 key").hwcap2; + assert_eq!(rt_hwcap2, hwcap2); + } + } + } + + #[test] + fn auxv_dump() { + if let Ok(auxvec) = auxv() { + println!("{:?}", auxvec); + } else { + println!("both getauxval() and reading /proc/self/auxv failed!"); + } + } + + #[cfg(feature = "std_detect_file_io")] + cfg_if! { + if #[cfg(target_arch = "arm")] { + #[test] + fn linux_rpi3() { + let file = concat!(env!("CARGO_MANIFEST_DIR"), "/src/detect/test_data/linux-rpi3.auxv"); + println!("file: {}", file); + let v = auxv_from_file(file).unwrap(); + assert_eq!(v.hwcap, 4174038); + assert_eq!(v.hwcap2, 16); + } + + #[test] + #[should_panic] + fn linux_macos_vb() { + let file = concat!(env!("CARGO_MANIFEST_DIR"), "/src/detect/test_data/macos-virtualbox-linux-x86-4850HQ.auxv"); + println!("file: {}", file); + let v = auxv_from_file(file).unwrap(); + // this file is incomplete (contains hwcap but not hwcap2), we + // want to fall back to /proc/cpuinfo in this case, so + // reading should fail. assert_eq!(v.hwcap, 126614527); + // assert_eq!(v.hwcap2, 0); + } + } else if #[cfg(target_arch = "aarch64")] { + #[test] + fn linux_x64() { + let file = concat!(env!("CARGO_MANIFEST_DIR"), "/src/detect/test_data/linux-x64-i7-6850k.auxv"); + println!("file: {}", file); + let v = auxv_from_file(file).unwrap(); + assert_eq!(v.hwcap, 3219913727); + } + } + } + + #[test] + #[cfg(feature = "std_detect_file_io")] + fn auxv_dump_procfs() { + if let Ok(auxvec) = auxv_from_file("/proc/self/auxv") { + println!("{:?}", auxvec); + } else { + println!("reading /proc/self/auxv failed!"); + } + } + + #[test] + fn auxv_crate_procfs() { + let v = auxv(); + if let Some(hwcap) = auxv_crate_getprocfs(AT_HWCAP) { + assert_eq!(v.unwrap().hwcap, hwcap); + } + + // Targets with AT_HWCAP and AT_HWCAP2: + #[cfg(any(target_arch = "arm", target_arch = "powerpc64"))] + { + if let Some(hwcap2) = auxv_crate_getprocfs(AT_HWCAP2) { + assert_eq!(v.unwrap().hwcap2, hwcap2); + } + } + } +} diff --git a/tests/source/cfg_if/detect/os/linux/cpuinfo.rs b/tests/source/cfg_if/detect/os/linux/cpuinfo.rs new file mode 100644 index 000000000000..b3168578537f --- /dev/null +++ b/tests/source/cfg_if/detect/os/linux/cpuinfo.rs @@ -0,0 +1,301 @@ +//! Parses /proc/cpuinfo +#![cfg_attr(not(target_arch = "arm"), allow(dead_code))] + +extern crate std; +use self::std::{prelude::v1::*, fs::File, io, io::Read}; + +/// cpuinfo +pub(crate) struct CpuInfo { + raw: String, +} + +impl CpuInfo { + /// Reads /proc/cpuinfo into CpuInfo. + pub(crate) fn new() -> Result { + let mut file = File::open("/proc/cpuinfo")?; + let mut cpui = Self { raw: String::new() }; + file.read_to_string(&mut cpui.raw)?; + Ok(cpui) + } + /// Returns the value of the cpuinfo `field`. + pub(crate) fn field(&self, field: &str) -> CpuInfoField { + for l in self.raw.lines() { + if l.trim().starts_with(field) { + return CpuInfoField::new(l.split(": ").nth(1)); + } + } + CpuInfoField(None) + } + + /// Returns the `raw` contents of `/proc/cpuinfo` + #[cfg(test)] + fn raw(&self) -> &String { + &self.raw + } + + #[cfg(test)] + fn from_str(other: &str) -> Result { + Ok(Self { + raw: String::from(other), + }) + } +} + +/// Field of cpuinfo +#[derive(Debug)] +pub(crate) struct CpuInfoField<'a>(Option<&'a str>); + +impl<'a> PartialEq<&'a str> for CpuInfoField<'a> { + fn eq(&self, other: &&'a str) -> bool { + match self.0 { + None => other.is_empty(), + Some(f) => f == other.trim(), + } + } +} + +impl<'a> CpuInfoField<'a> { + pub(crate) fn new<'b>(v: Option<&'b str>) -> CpuInfoField<'b> { + match v { + None => CpuInfoField::<'b>(None), + Some(f) => CpuInfoField::<'b>(Some(f.trim())), + } + } + /// Does the field exist? + #[cfg(test)] + pub(crate) fn exists(&self) -> bool { + self.0.is_some() + } + /// Does the field contain `other`? + pub(crate) fn has(&self, other: &str) -> bool { + match self.0 { + None => other.is_empty(), + Some(f) => { + let other = other.trim(); + for v in f.split(' ') { + if v == other { + return true; + } + } + false + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn raw_dump() { + let cpuinfo = CpuInfo::new().unwrap(); + if cpuinfo.field("vendor_id") == "GenuineIntel" { + assert!(cpuinfo.field("flags").exists()); + assert!(!cpuinfo.field("vendor33_id").exists()); + assert!(cpuinfo.field("flags").has("sse")); + assert!(!cpuinfo.field("flags").has("avx314")); + } + println!("{}", cpuinfo.raw()); + } + + const CORE_DUO_T6500: &str = r"processor : 0 +vendor_id : GenuineIntel +cpu family : 6 +model : 23 +model name : Intel(R) Core(TM)2 Duo CPU T6500 @ 2.10GHz +stepping : 10 +microcode : 0xa0b +cpu MHz : 1600.000 +cache size : 2048 KB +physical id : 0 +siblings : 2 +core id : 0 +cpu cores : 2 +apicid : 0 +initial apicid : 0 +fdiv_bug : no +hlt_bug : no +f00f_bug : no +coma_bug : no +fpu : yes +fpu_exception : yes +cpuid level : 13 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe nx lm constant_tsc arch_perfmon pebs bts aperfmperf pni dtes64 monitor ds_cpl est tm2 ssse3 cx16 xtpr pdcm sse4_1 xsave lahf_lm dtherm +bogomips : 4190.43 +clflush size : 64 +cache_alignment : 64 +address sizes : 36 bits physical, 48 bits virtual +power management: +"; + + #[test] + fn core_duo_t6500() { + let cpuinfo = CpuInfo::from_str(CORE_DUO_T6500).unwrap(); + assert_eq!(cpuinfo.field("vendor_id"), "GenuineIntel"); + assert_eq!(cpuinfo.field("cpu family"), "6"); + assert_eq!(cpuinfo.field("model"), "23"); + assert_eq!( + cpuinfo.field("model name"), + "Intel(R) Core(TM)2 Duo CPU T6500 @ 2.10GHz" + ); + assert_eq!( + cpuinfo.field("flags"), + "fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe nx lm constant_tsc arch_perfmon pebs bts aperfmperf pni dtes64 monitor ds_cpl est tm2 ssse3 cx16 xtpr pdcm sse4_1 xsave lahf_lm dtherm" + ); + assert!(cpuinfo.field("flags").has("fpu")); + assert!(cpuinfo.field("flags").has("dtherm")); + assert!(cpuinfo.field("flags").has("sse2")); + assert!(!cpuinfo.field("flags").has("avx")); + } + + const ARM_CORTEX_A53: &str = + r"Processor : AArch64 Processor rev 3 (aarch64) + processor : 0 + processor : 1 + processor : 2 + processor : 3 + processor : 4 + processor : 5 + processor : 6 + processor : 7 + Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 + CPU implementer : 0x41 + CPU architecture: AArch64 + CPU variant : 0x0 + CPU part : 0xd03 + CPU revision : 3 + + Hardware : HiKey Development Board + "; + + #[test] + fn arm_cortex_a53() { + let cpuinfo = CpuInfo::from_str(ARM_CORTEX_A53).unwrap(); + assert_eq!( + cpuinfo.field("Processor"), + "AArch64 Processor rev 3 (aarch64)" + ); + assert_eq!( + cpuinfo.field("Features"), + "fp asimd evtstrm aes pmull sha1 sha2 crc32" + ); + assert!(cpuinfo.field("Features").has("pmull")); + assert!(!cpuinfo.field("Features").has("neon")); + assert!(cpuinfo.field("Features").has("asimd")); + } + + const ARM_CORTEX_A57: &str = r"Processor : Cortex A57 Processor rev 1 (aarch64) +processor : 0 +processor : 1 +processor : 2 +processor : 3 +Features : fp asimd aes pmull sha1 sha2 crc32 wp half thumb fastmult vfp edsp neon vfpv3 tlsi vfpv4 idiva idivt +CPU implementer : 0x41 +CPU architecture: 8 +CPU variant : 0x1 +CPU part : 0xd07 +CPU revision : 1"; + + #[test] + fn arm_cortex_a57() { + let cpuinfo = CpuInfo::from_str(ARM_CORTEX_A57).unwrap(); + assert_eq!( + cpuinfo.field("Processor"), + "Cortex A57 Processor rev 1 (aarch64)" + ); + assert_eq!( + cpuinfo.field("Features"), + "fp asimd aes pmull sha1 sha2 crc32 wp half thumb fastmult vfp edsp neon vfpv3 tlsi vfpv4 idiva idivt" + ); + assert!(cpuinfo.field("Features").has("pmull")); + assert!(cpuinfo.field("Features").has("neon")); + assert!(cpuinfo.field("Features").has("asimd")); + } + + const POWER8E_POWERKVM: &str = r"processor : 0 +cpu : POWER8E (raw), altivec supported +clock : 3425.000000MHz +revision : 2.1 (pvr 004b 0201) + +processor : 1 +cpu : POWER8E (raw), altivec supported +clock : 3425.000000MHz +revision : 2.1 (pvr 004b 0201) + +processor : 2 +cpu : POWER8E (raw), altivec supported +clock : 3425.000000MHz +revision : 2.1 (pvr 004b 0201) + +processor : 3 +cpu : POWER8E (raw), altivec supported +clock : 3425.000000MHz +revision : 2.1 (pvr 004b 0201) + +timebase : 512000000 +platform : pSeries +model : IBM pSeries (emulated by qemu) +machine : CHRP IBM pSeries (emulated by qemu)"; + + #[test] + fn power8_powerkvm() { + let cpuinfo = CpuInfo::from_str(POWER8E_POWERKVM).unwrap(); + assert_eq!(cpuinfo.field("cpu"), "POWER8E (raw), altivec supported"); + + assert!(cpuinfo.field("cpu").has("altivec")); + } + + const POWER5P: &str = r"processor : 0 +cpu : POWER5+ (gs) +clock : 1900.098000MHz +revision : 2.1 (pvr 003b 0201) + +processor : 1 +cpu : POWER5+ (gs) +clock : 1900.098000MHz +revision : 2.1 (pvr 003b 0201) + +processor : 2 +cpu : POWER5+ (gs) +clock : 1900.098000MHz +revision : 2.1 (pvr 003b 0201) + +processor : 3 +cpu : POWER5+ (gs) +clock : 1900.098000MHz +revision : 2.1 (pvr 003b 0201) + +processor : 4 +cpu : POWER5+ (gs) +clock : 1900.098000MHz +revision : 2.1 (pvr 003b 0201) + +processor : 5 +cpu : POWER5+ (gs) +clock : 1900.098000MHz +revision : 2.1 (pvr 003b 0201) + +processor : 6 +cpu : POWER5+ (gs) +clock : 1900.098000MHz +revision : 2.1 (pvr 003b 0201) + +processor : 7 +cpu : POWER5+ (gs) +clock : 1900.098000MHz +revision : 2.1 (pvr 003b 0201) + +timebase : 237331000 +platform : pSeries +machine : CHRP IBM,9133-55A"; + + #[test] + fn power5p() { + let cpuinfo = CpuInfo::from_str(POWER5P).unwrap(); + assert_eq!(cpuinfo.field("cpu"), "POWER5+ (gs)"); + + assert!(!cpuinfo.field("cpu").has("altivec")); + } +} diff --git a/tests/source/cfg_if/detect/os/linux/mips.rs b/tests/source/cfg_if/detect/os/linux/mips.rs new file mode 100644 index 000000000000..c0a5fb2e5d88 --- /dev/null +++ b/tests/source/cfg_if/detect/os/linux/mips.rs @@ -0,0 +1,31 @@ +//! Run-time feature detection for MIPS on Linux. + +use crate::detect::{Feature, cache, bit}; +use super::auxvec; + +/// Performs run-time feature detection. +#[inline] +pub fn check_for(x: Feature) -> bool { + cache::test(x as u32, detect_features) +} + +/// Try to read the features from the auxiliary vector, and if that fails, try +/// to read them from `/proc/cpuinfo`. +fn detect_features() -> cache::Initializer { + let mut value = cache::Initializer::default(); + let enable_feature = |value: &mut cache::Initializer, f, enable| { + if enable { + value.set(f as u32); + } + }; + + // The values are part of the platform-specific [asm/hwcap.h][hwcap] + // + // [hwcap]: https://github.com/torvalds/linux/blob/master/arch/arm64/include/uapi/asm/hwcap.h + if let Ok(auxv) = auxvec::auxv() { + enable_feature(&mut value, Feature::msa, bit::test(auxv.hwcap, 1)); + return value; + } + // TODO: fall back via `cpuinfo`. + value +} diff --git a/tests/source/cfg_if/detect/os/linux/mod.rs b/tests/source/cfg_if/detect/os/linux/mod.rs new file mode 100644 index 000000000000..e02d5e6dcda7 --- /dev/null +++ b/tests/source/cfg_if/detect/os/linux/mod.rs @@ -0,0 +1,28 @@ +//! Run-time feature detection on Linux + +mod auxvec; + +#[cfg(feature = "std_detect_file_io")] +mod cpuinfo; + +cfg_if! { + if #[cfg(target_arch = "aarch64")] { + mod aarch64; + pub use self::aarch64::check_for; + } else if #[cfg(target_arch = "arm")] { + mod arm; + pub use self::arm::check_for; + } else if #[cfg(any(target_arch = "mips", target_arch = "mips64"))] { + mod mips; + pub use self::mips::check_for; + } else if #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] { + mod powerpc; + pub use self::powerpc::check_for; + } else { + use crate::detect::Feature; + /// Performs run-time feature detection. + pub fn check_for(_x: Feature) -> bool { + false + } + } +} diff --git a/tests/source/cfg_if/detect/os/linux/powerpc.rs b/tests/source/cfg_if/detect/os/linux/powerpc.rs new file mode 100644 index 000000000000..1c08a58443db --- /dev/null +++ b/tests/source/cfg_if/detect/os/linux/powerpc.rs @@ -0,0 +1,41 @@ +//! Run-time feature detection for PowerPC on Linux. + +use crate::detect::{Feature, cache}; +use super::{auxvec, cpuinfo}; + +/// Performs run-time feature detection. +#[inline] +pub fn check_for(x: Feature) -> bool { + cache::test(x as u32, detect_features) +} + +/// Try to read the features from the auxiliary vector, and if that fails, try +/// to read them from /proc/cpuinfo. +fn detect_features() -> cache::Initializer { + let mut value = cache::Initializer::default(); + let enable_feature = |value: &mut cache::Initializer, f, enable| { + if enable { + value.set(f as u32); + } + }; + + // The values are part of the platform-specific [asm/cputable.h][cputable] + // + // [cputable]: https://github.com/torvalds/linux/blob/master/arch/powerpc/include/uapi/asm/cputable.h + if let Ok(auxv) = auxvec::auxv() { + // note: the PowerPC values are the mask to do the test (instead of the + // index of the bit to test like in ARM and Aarch64) + enable_feature(&mut value, Feature::altivec, auxv.hwcap & 0x10000000 != 0); + enable_feature(&mut value, Feature::vsx, auxv.hwcap & 0x00000080 != 0); + enable_feature(&mut value, Feature::power8, auxv.hwcap2 & 0x80000000 != 0); + return value; + } + + // PowerPC's /proc/cpuinfo lacks a proper Feature field, + // but `altivec` support is indicated in the `cpu` field. + if let Ok(c) = cpuinfo::CpuInfo::new() { + enable_feature(&mut value, Feature::altivec, c.field("cpu").has("altivec")); + return value; + } + value +} diff --git a/tests/source/cfg_if/detect/os/other.rs b/tests/source/cfg_if/detect/os/other.rs new file mode 100644 index 000000000000..23e399ea7907 --- /dev/null +++ b/tests/source/cfg_if/detect/os/other.rs @@ -0,0 +1,9 @@ +//! Other operating systems + +use crate::detect::Feature; + +/// Performs run-time feature detection. +#[inline] +pub fn check_for(_x: Feature) -> bool { + false +} diff --git a/tests/source/cfg_if/detect/os/x86.rs b/tests/source/cfg_if/detect/os/x86.rs new file mode 100644 index 000000000000..9257b8a4be63 --- /dev/null +++ b/tests/source/cfg_if/detect/os/x86.rs @@ -0,0 +1,375 @@ +//! x86 run-time feature detection is OS independent. + +#[cfg(target_arch = "x86")] +use crate::arch::x86::*; +#[cfg(target_arch = "x86_64")] +use crate::arch::x86_64::*; + +use crate::mem; + +use crate::detect::{Feature, cache, bit}; + +/// Performs run-time feature detection. +#[inline] +pub fn check_for(x: Feature) -> bool { + cache::test(x as u32, detect_features) +} + +/// Run-time feature detection on x86 works by using the CPUID instruction. +/// +/// The [CPUID Wikipedia page][wiki_cpuid] contains +/// all the information about which flags to set to query which values, and in +/// which registers these are reported. +/// +/// The definitive references are: +/// - [Intel 64 and IA-32 Architectures Software Developer's Manual Volume 2: +/// Instruction Set Reference, A-Z][intel64_ref]. +/// - [AMD64 Architecture Programmer's Manual, Volume 3: General-Purpose and +/// System Instructions][amd64_ref]. +/// +/// [wiki_cpuid]: https://en.wikipedia.org/wiki/CPUID +/// [intel64_ref]: http://www.intel.de/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf +/// [amd64_ref]: http://support.amd.com/TechDocs/24594.pdf +#[allow(clippy::similar_names)] +fn detect_features() -> cache::Initializer { + let mut value = cache::Initializer::default(); + + // If the x86 CPU does not support the CPUID instruction then it is too + // old to support any of the currently-detectable features. + if !has_cpuid() { + return value; + } + + // Calling `__cpuid`/`__cpuid_count` from here on is safe because the CPU + // has `cpuid` support. + + // 0. EAX = 0: Basic Information: + // - EAX returns the "Highest Function Parameter", that is, the maximum + // leaf value for subsequent calls of `cpuinfo` in range [0, + // 0x8000_0000]. - The vendor ID is stored in 12 u8 ascii chars, + // returned in EBX, EDX, and ECX (in that order): + let (max_basic_leaf, vendor_id) = unsafe { + let CpuidResult { + eax: max_basic_leaf, + ebx, + ecx, + edx, + } = __cpuid(0); + let vendor_id: [[u8; 4]; 3] = [ + mem::transmute(ebx), + mem::transmute(edx), + mem::transmute(ecx), + ]; + let vendor_id: [u8; 12] = mem::transmute(vendor_id); + (max_basic_leaf, vendor_id) + }; + + if max_basic_leaf < 1 { + // Earlier Intel 486, CPUID not implemented + return value; + } + + // EAX = 1, ECX = 0: Queries "Processor Info and Feature Bits"; + // Contains information about most x86 features. + let CpuidResult { + ecx: proc_info_ecx, + edx: proc_info_edx, + .. + } = unsafe { __cpuid(0x0000_0001_u32) }; + + // EAX = 7, ECX = 0: Queries "Extended Features"; + // Contains information about bmi,bmi2, and avx2 support. + let (extended_features_ebx, extended_features_ecx) = if max_basic_leaf >= 7 + { + let CpuidResult { ebx, ecx, .. } = unsafe { __cpuid(0x0000_0007_u32) }; + (ebx, ecx) + } else { + (0, 0) // CPUID does not support "Extended Features" + }; + + // EAX = 0x8000_0000, ECX = 0: Get Highest Extended Function Supported + // - EAX returns the max leaf value for extended information, that is, + // `cpuid` calls in range [0x8000_0000; u32::MAX]: + let CpuidResult { + eax: extended_max_basic_leaf, + .. + } = unsafe { __cpuid(0x8000_0000_u32) }; + + // EAX = 0x8000_0001, ECX=0: Queries "Extended Processor Info and Feature + // Bits" + let extended_proc_info_ecx = if extended_max_basic_leaf >= 1 { + let CpuidResult { ecx, .. } = unsafe { __cpuid(0x8000_0001_u32) }; + ecx + } else { + 0 + }; + + { + // borrows value till the end of this scope: + let mut enable = |r, rb, f| { + if bit::test(r as usize, rb) { + value.set(f as u32); + } + }; + + enable(proc_info_ecx, 0, Feature::sse3); + enable(proc_info_ecx, 1, Feature::pclmulqdq); + enable(proc_info_ecx, 9, Feature::ssse3); + enable(proc_info_ecx, 13, Feature::cmpxchg16b); + enable(proc_info_ecx, 19, Feature::sse4_1); + enable(proc_info_ecx, 20, Feature::sse4_2); + enable(proc_info_ecx, 23, Feature::popcnt); + enable(proc_info_ecx, 25, Feature::aes); + enable(proc_info_ecx, 29, Feature::f16c); + enable(proc_info_ecx, 30, Feature::rdrand); + enable(extended_features_ebx, 18, Feature::rdseed); + enable(extended_features_ebx, 19, Feature::adx); + enable(extended_features_ebx, 11, Feature::rtm); + enable(proc_info_edx, 4, Feature::tsc); + enable(proc_info_edx, 23, Feature::mmx); + enable(proc_info_edx, 24, Feature::fxsr); + enable(proc_info_edx, 25, Feature::sse); + enable(proc_info_edx, 26, Feature::sse2); + enable(extended_features_ebx, 29, Feature::sha); + + enable(extended_features_ebx, 3, Feature::bmi); + enable(extended_features_ebx, 8, Feature::bmi2); + + // `XSAVE` and `AVX` support: + let cpu_xsave = bit::test(proc_info_ecx as usize, 26); + if cpu_xsave { + // 0. Here the CPU supports `XSAVE`. + + // 1. Detect `OSXSAVE`, that is, whether the OS is AVX enabled and + // supports saving the state of the AVX/AVX2 vector registers on + // context-switches, see: + // + // - [intel: is avx enabled?][is_avx_enabled], + // - [mozilla: sse.cpp][mozilla_sse_cpp]. + // + // [is_avx_enabled]: https://software.intel.com/en-us/blogs/2011/04/14/is-avx-enabled + // [mozilla_sse_cpp]: https://hg.mozilla.org/mozilla-central/file/64bab5cbb9b6/mozglue/build/SSE.cpp#l190 + let cpu_osxsave = bit::test(proc_info_ecx as usize, 27); + + if cpu_osxsave { + // 2. The OS must have signaled the CPU that it supports saving and + // restoring the: + // + // * SSE -> `XCR0.SSE[1]` + // * AVX -> `XCR0.AVX[2]` + // * AVX-512 -> `XCR0.AVX-512[7:5]`. + // + // by setting the corresponding bits of `XCR0` to `1`. + // + // This is safe because the CPU supports `xsave` + // and the OS has set `osxsave`. + let xcr0 = unsafe { _xgetbv(0) }; + // Test `XCR0.SSE[1]` and `XCR0.AVX[2]` with the mask `0b110 == 6`: + let os_avx_support = xcr0 & 6 == 6; + // Test `XCR0.AVX-512[7:5]` with the mask `0b1110_0000 == 224`: + let os_avx512_support = xcr0 & 224 == 224; + + // Only if the OS and the CPU support saving/restoring the AVX + // registers we enable `xsave` support: + if os_avx_support { + // See "13.3 ENABLING THE XSAVE FEATURE SET AND XSAVE-ENABLED + // FEATURES" in the "Intel® 64 and IA-32 Architectures Software + // Developer’s Manual, Volume 1: Basic Architecture": + // + // "Software enables the XSAVE feature set by setting + // CR4.OSXSAVE[bit 18] to 1 (e.g., with the MOV to CR4 + // instruction). If this bit is 0, execution of any of XGETBV, + // XRSTOR, XRSTORS, XSAVE, XSAVEC, XSAVEOPT, XSAVES, and XSETBV + // causes an invalid-opcode exception (#UD)" + // + enable(proc_info_ecx, 26, Feature::xsave); + + // For `xsaveopt`, `xsavec`, and `xsaves` we need to query: + // Processor Extended State Enumeration Sub-leaf (EAX = 0DH, + // ECX = 1): + if max_basic_leaf >= 0xd { + let CpuidResult { + eax: proc_extended_state1_eax, + .. + } = unsafe { __cpuid_count(0xd_u32, 1) }; + enable(proc_extended_state1_eax, 0, Feature::xsaveopt); + enable(proc_extended_state1_eax, 1, Feature::xsavec); + enable(proc_extended_state1_eax, 3, Feature::xsaves); + } + + // FMA (uses 256-bit wide registers): + enable(proc_info_ecx, 12, Feature::fma); + + // And AVX/AVX2: + enable(proc_info_ecx, 28, Feature::avx); + enable(extended_features_ebx, 5, Feature::avx2); + + // For AVX-512 the OS also needs to support saving/restoring + // the extended state, only then we enable AVX-512 support: + if os_avx512_support { + enable(extended_features_ebx, 16, Feature::avx512f); + enable(extended_features_ebx, 17, Feature::avx512dq); + enable(extended_features_ebx, 21, Feature::avx512_ifma); + enable(extended_features_ebx, 26, Feature::avx512pf); + enable(extended_features_ebx, 27, Feature::avx512er); + enable(extended_features_ebx, 28, Feature::avx512cd); + enable(extended_features_ebx, 30, Feature::avx512bw); + enable(extended_features_ebx, 31, Feature::avx512vl); + enable(extended_features_ecx, 1, Feature::avx512_vbmi); + enable( + extended_features_ecx, + 14, + Feature::avx512_vpopcntdq, + ); + } + } + } + } + + // This detects ABM on AMD CPUs and LZCNT on Intel CPUs. + // On intel CPUs with popcnt, lzcnt implements the + // "missing part" of ABM, so we map both to the same + // internal feature. + // + // The `is_x86_feature_detected!("lzcnt")` macro then + // internally maps to Feature::abm. + enable(extended_proc_info_ecx, 5, Feature::abm); + // As Hygon Dhyana originates from AMD technology and shares most of the architecture with + // AMD's family 17h, but with different CPU Vendor ID("HygonGenuine")/Family series + // number(Family 18h). + // + // For CPUID feature bits, Hygon Dhyana(family 18h) share the same definition with AMD + // family 17h. + // + // Related AMD CPUID specification is https://www.amd.com/system/files/TechDocs/25481.pdf. + // Related Hygon kernel patch can be found on + // http://lkml.kernel.org/r/5ce86123a7b9dad925ac583d88d2f921040e859b.1538583282.git.puwen@hygon.cn + if vendor_id == *b"AuthenticAMD" || vendor_id == *b"HygonGenuine" { + // These features are available on AMD arch CPUs: + enable(extended_proc_info_ecx, 6, Feature::sse4a); + enable(extended_proc_info_ecx, 21, Feature::tbm); + } + } + + value +} + +#[cfg(test)] +mod tests { + extern crate cupid; + + #[test] + fn dump() { + println!("aes: {:?}", is_x86_feature_detected!("aes")); + println!("pclmulqdq: {:?}", is_x86_feature_detected!("pclmulqdq")); + println!("rdrand: {:?}", is_x86_feature_detected!("rdrand")); + println!("rdseed: {:?}", is_x86_feature_detected!("rdseed")); + println!("tsc: {:?}", is_x86_feature_detected!("tsc")); + println!("sse: {:?}", is_x86_feature_detected!("sse")); + println!("sse2: {:?}", is_x86_feature_detected!("sse2")); + println!("sse3: {:?}", is_x86_feature_detected!("sse3")); + println!("ssse3: {:?}", is_x86_feature_detected!("ssse3")); + println!("sse4.1: {:?}", is_x86_feature_detected!("sse4.1")); + println!("sse4.2: {:?}", is_x86_feature_detected!("sse4.2")); + println!("sse4a: {:?}", is_x86_feature_detected!("sse4a")); + println!("sha: {:?}", is_x86_feature_detected!("sha")); + println!("avx: {:?}", is_x86_feature_detected!("avx")); + println!("avx2: {:?}", is_x86_feature_detected!("avx2")); + println!("avx512f {:?}", is_x86_feature_detected!("avx512f")); + println!("avx512cd {:?}", is_x86_feature_detected!("avx512cd")); + println!("avx512er {:?}", is_x86_feature_detected!("avx512er")); + println!("avx512pf {:?}", is_x86_feature_detected!("avx512pf")); + println!("avx512bw {:?}", is_x86_feature_detected!("avx512bw")); + println!("avx512dq {:?}", is_x86_feature_detected!("avx512dq")); + println!("avx512vl {:?}", is_x86_feature_detected!("avx512vl")); + println!("avx512_ifma {:?}", is_x86_feature_detected!("avx512ifma")); + println!("avx512_vbmi {:?}", is_x86_feature_detected!("avx512vbmi")); + println!( + "avx512_vpopcntdq {:?}", + is_x86_feature_detected!("avx512vpopcntdq") + ); + println!("fma: {:?}", is_x86_feature_detected!("fma")); + println!("abm: {:?}", is_x86_feature_detected!("abm")); + println!("bmi: {:?}", is_x86_feature_detected!("bmi1")); + println!("bmi2: {:?}", is_x86_feature_detected!("bmi2")); + println!("tbm: {:?}", is_x86_feature_detected!("tbm")); + println!("popcnt: {:?}", is_x86_feature_detected!("popcnt")); + println!("lzcnt: {:?}", is_x86_feature_detected!("lzcnt")); + println!("fxsr: {:?}", is_x86_feature_detected!("fxsr")); + println!("xsave: {:?}", is_x86_feature_detected!("xsave")); + println!("xsaveopt: {:?}", is_x86_feature_detected!("xsaveopt")); + println!("xsaves: {:?}", is_x86_feature_detected!("xsaves")); + println!("xsavec: {:?}", is_x86_feature_detected!("xsavec")); + println!("cmpxchg16b: {:?}", is_x86_feature_detected!("cmpxchg16b")); + println!("adx: {:?}", is_x86_feature_detected!("adx")); + println!("rtm: {:?}", is_x86_feature_detected!("rtm")); + } + + #[test] + fn compare_with_cupid() { + let information = cupid::master().unwrap(); + assert_eq!(is_x86_feature_detected!("aes"), information.aesni()); + assert_eq!(is_x86_feature_detected!("pclmulqdq"), information.pclmulqdq()); + assert_eq!(is_x86_feature_detected!("rdrand"), information.rdrand()); + assert_eq!(is_x86_feature_detected!("rdseed"), information.rdseed()); + assert_eq!(is_x86_feature_detected!("tsc"), information.tsc()); + assert_eq!(is_x86_feature_detected!("sse"), information.sse()); + assert_eq!(is_x86_feature_detected!("sse2"), information.sse2()); + assert_eq!(is_x86_feature_detected!("sse3"), information.sse3()); + assert_eq!(is_x86_feature_detected!("ssse3"), information.ssse3()); + assert_eq!(is_x86_feature_detected!("sse4.1"), information.sse4_1()); + assert_eq!(is_x86_feature_detected!("sse4.2"), information.sse4_2()); + assert_eq!(is_x86_feature_detected!("sse4a"), information.sse4a()); + assert_eq!(is_x86_feature_detected!("sha"), information.sha()); + assert_eq!(is_x86_feature_detected!("avx"), information.avx()); + assert_eq!(is_x86_feature_detected!("avx2"), information.avx2()); + assert_eq!(is_x86_feature_detected!("avx512f"), information.avx512f()); + assert_eq!(is_x86_feature_detected!("avx512cd"), information.avx512cd()); + assert_eq!(is_x86_feature_detected!("avx512er"), information.avx512er()); + assert_eq!(is_x86_feature_detected!("avx512pf"), information.avx512pf()); + assert_eq!(is_x86_feature_detected!("avx512bw"), information.avx512bw()); + assert_eq!(is_x86_feature_detected!("avx512dq"), information.avx512dq()); + assert_eq!(is_x86_feature_detected!("avx512vl"), information.avx512vl()); + assert_eq!( + is_x86_feature_detected!("avx512ifma"), + information.avx512_ifma() + ); + assert_eq!( + is_x86_feature_detected!("avx512vbmi"), + information.avx512_vbmi() + ); + assert_eq!( + is_x86_feature_detected!("avx512vpopcntdq"), + information.avx512_vpopcntdq() + ); + assert_eq!(is_x86_feature_detected!("fma"), information.fma()); + assert_eq!(is_x86_feature_detected!("bmi1"), information.bmi1()); + assert_eq!(is_x86_feature_detected!("bmi2"), information.bmi2()); + assert_eq!(is_x86_feature_detected!("popcnt"), information.popcnt()); + assert_eq!(is_x86_feature_detected!("abm"), information.lzcnt()); + assert_eq!(is_x86_feature_detected!("tbm"), information.tbm()); + assert_eq!(is_x86_feature_detected!("lzcnt"), information.lzcnt()); + assert_eq!(is_x86_feature_detected!("xsave"), information.xsave()); + assert_eq!(is_x86_feature_detected!("xsaveopt"), information.xsaveopt()); + assert_eq!( + is_x86_feature_detected!("xsavec"), + information.xsavec_and_xrstor() + ); + assert_eq!( + is_x86_feature_detected!("xsaves"), + information.xsaves_xrstors_and_ia32_xss() + ); + assert_eq!( + is_x86_feature_detected!("cmpxchg16b"), + information.cmpxchg16b(), + ); + assert_eq!( + is_x86_feature_detected!("adx"), + information.adx(), + ); + assert_eq!( + is_x86_feature_detected!("rtm"), + information.rtm(), + ); + } +} diff --git a/tests/source/cfg_if/lib.rs b/tests/source/cfg_if/lib.rs new file mode 100644 index 000000000000..8b3bb304f1c8 --- /dev/null +++ b/tests/source/cfg_if/lib.rs @@ -0,0 +1,49 @@ +//! Run-time feature detection for the Rust standard library. +//! +//! To detect whether a feature is enabled in the system running the binary +//! use one of the appropriate macro for the target: +//! +//! * `x86` and `x86_64`: [`is_x86_feature_detected`] +//! * `arm`: [`is_arm_feature_detected`] +//! * `aarch64`: [`is_aarch64_feature_detected`] +//! * `mips`: [`is_mips_feature_detected`] +//! * `mips64`: [`is_mips64_feature_detected`] +//! * `powerpc`: [`is_powerpc_feature_detected`] +//! * `powerpc64`: [`is_powerpc64_feature_detected`] + +#![unstable(feature = "stdsimd", issue = "27731")] +#![feature(const_fn, staged_api, stdsimd, doc_cfg, allow_internal_unstable)] +#![allow(clippy::shadow_reuse)] +#![deny(clippy::missing_inline_in_public_items)] +#![cfg_attr(target_os = "linux", feature(linkage))] +#![cfg_attr(all(target_os = "freebsd", target_arch = "aarch64"), feature(asm))] +#![cfg_attr(stdsimd_strict, deny(warnings))] +#![cfg_attr(test, allow(unused_imports))] +#![no_std] + +#[macro_use] +extern crate cfg_if; + +cfg_if! { + if #[cfg(feature = "std_detect_file_io")] { + #[cfg_attr(test, macro_use(println))] + extern crate std; + + #[allow(unused_imports)] + use std::{arch, fs, io, mem, sync}; + } else { + #[cfg(test)] + #[macro_use(println)] + extern crate std; + + #[allow(unused_imports)] + use core::{arch, mem, sync}; + } +} + +#[cfg(feature = "std_detect_dlsym_getauxval")] +extern crate libc; + +#[doc(hidden)] +#[unstable(feature = "stdsimd", issue = "27731")] +pub mod detect; diff --git a/tests/source/cfg_if/mod.rs b/tests/source/cfg_if/mod.rs new file mode 100644 index 000000000000..b630e7ff383a --- /dev/null +++ b/tests/source/cfg_if/mod.rs @@ -0,0 +1,5 @@ +//! `std_detect` + +#[doc(hidden)] // unstable implementation detail +#[unstable(feature = "stdsimd", issue = "27731")] +pub mod detect; diff --git a/tests/source/cfg_mod/bar.rs b/tests/source/cfg_mod/bar.rs new file mode 100644 index 000000000000..5b6b5f438370 --- /dev/null +++ b/tests/source/cfg_mod/bar.rs @@ -0,0 +1,3 @@ +fn bar( ) -> &str { +"bar" +} diff --git a/tests/source/cfg_mod/dir/dir1/dir2/wasm32.rs b/tests/source/cfg_mod/dir/dir1/dir2/wasm32.rs new file mode 100644 index 000000000000..0f8c0a3a7ac0 --- /dev/null +++ b/tests/source/cfg_mod/dir/dir1/dir2/wasm32.rs @@ -0,0 +1,6 @@ +fn + wasm32 + () -> &str +{ + "wasm32" +} diff --git a/tests/source/cfg_mod/dir/dir1/dir3/wasm32.rs b/tests/source/cfg_mod/dir/dir1/dir3/wasm32.rs new file mode 100644 index 000000000000..0f8c0a3a7ac0 --- /dev/null +++ b/tests/source/cfg_mod/dir/dir1/dir3/wasm32.rs @@ -0,0 +1,6 @@ +fn + wasm32 + () -> &str +{ + "wasm32" +} diff --git a/tests/source/cfg_mod/foo.rs b/tests/source/cfg_mod/foo.rs new file mode 100644 index 000000000000..de4ce55ef6ff --- /dev/null +++ b/tests/source/cfg_mod/foo.rs @@ -0,0 +1,4 @@ +fn foo( ) + -> &str { + "foo" +} diff --git a/tests/source/cfg_mod/mod.rs b/tests/source/cfg_mod/mod.rs new file mode 100644 index 000000000000..45ba86f11b3f --- /dev/null +++ b/tests/source/cfg_mod/mod.rs @@ -0,0 +1,10 @@ +#[cfg_attr(feature = "foo", path = "foo.rs")] +#[cfg_attr(not(feture = "foo"), path = "bar.rs")] +mod sub_mod; + +#[cfg_attr(target_arch = "wasm32", path = "dir/dir1/dir2/wasm32.rs")] +#[cfg_attr(not(target_arch = "wasm32"), path = "dir/dir1/dir3/wasm32.rs")] +mod wasm32; + +#[some_attr(path = "somewhere.rs")] +mod other; diff --git a/tests/source/cfg_mod/other.rs b/tests/source/cfg_mod/other.rs new file mode 100644 index 000000000000..0b5c04d21920 --- /dev/null +++ b/tests/source/cfg_mod/other.rs @@ -0,0 +1 @@ +fn other() -> &str { "other"} diff --git a/tests/source/cfg_mod/wasm32.rs b/tests/source/cfg_mod/wasm32.rs new file mode 100644 index 000000000000..3741e53fd4e8 --- /dev/null +++ b/tests/source/cfg_mod/wasm32.rs @@ -0,0 +1,4 @@ +fn + wasm32() -> &str { + "wasm32" + } diff --git a/tests/source/chains-visual.rs b/tests/source/chains-visual.rs new file mode 100644 index 000000000000..20a96311e341 --- /dev/null +++ b/tests/source/chains-visual.rs @@ -0,0 +1,158 @@ +// rustfmt-indent_style: Visual +// Test chain formatting. + +fn main() { + // Don't put chains on a single line if it wasn't so in source. + let a = b .c + .d.1 + .foo(|x| x + 1); + + bbbbbbbbbbbbbbbbbbb.ccccccccccccccccccccccccccccccccccccc + .ddddddddddddddddddddddddddd(); + + bbbbbbbbbbbbbbbbbbb.ccccccccccccccccccccccccccccccccccccc.ddddddddddddddddddddddddddd.eeeeeeee(); + + // Test case where first chain element isn't a path, but is shorter than + // the size of a tab. + x() + .y(|| match cond() { true => (), false => () }); + + loong_func() + .quux(move || if true { + 1 + } else { + 2 + }); + + some_fuuuuuuuuunction() + .method_call_a(aaaaa, bbbbb, |c| { + let x = c; + x + }); + + some_fuuuuuuuuunction().method_call_a(aaaaa, bbbbb, |c| { + let x = c; + x + }).method_call_b(aaaaa, bbbbb, |c| { + let x = c; + x + }); + + fffffffffffffffffffffffffffffffffff(a, + { + SCRIPT_TASK_ROOT + .with(|root| { + *root.borrow_mut() = Some(&script_task); + }); + }); + + let suuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuum = xxxxxxx + .map(|x| x + 5) + .map(|x| x / 2) + .fold(0, |acc, x| acc + x); + + aaaaaaaaaaaaaaaa.map(|x| { + x += 1; + x + }).filter(some_mod::some_filter) +} + +fn floaters() { + let z = Foo { + field1: val1, + field2: val2, + }; + + let x = Foo { + field1: val1, + field2: val2, + }.method_call().method_call(); + + let y = if cond { + val1 + } else { + val2 + } + .method_call(); + + { + match x { + PushParam => { + // params are 1-indexed + stack.push(mparams[match cur.to_digit(10) { + Some(d) => d as usize - 1, + None => return Err("bad param number".to_owned()), + }] + .clone()); + } + } + } + + if cond { some(); } else { none(); } + .bar() + .baz(); + + Foo { x: val } .baz(|| { force(); multiline(); }) .quux(); + + Foo { y: i_am_multi_line, z: ok } + .baz(|| { + force(); multiline(); + }) + .quux(); + + a + match x { true => "yay!", false => "boo!" }.bar() +} + +fn is_replaced_content() -> bool { + constellat.send(ConstellationMsg::ViewportConstrained( + self.id, constraints)).unwrap(); +} + +fn issue587() { + a.b::<()>(c); + + std::mem::transmute(dl.symbol::<()>("init").unwrap()) +} + +fn issue_1389() { + let names = String::from_utf8(names)?.split('|').map(str::to_owned).collect(); +} + +fn issue1217() -> Result { +let random_chars: String = OsRng::new()? + .gen_ascii_chars() + .take(self.bit_length) + .collect(); + + Ok(Mnemonic::new(&random_chars)) +} + +fn issue1236(options: Vec) -> Result> { +let process = Command::new("dmenu").stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn() + .chain_err(|| "failed to spawn dmenu")?; +} + +fn issue1434() { + for _ in 0..100 { + let prototype_id = PrototypeIdData::from_reader::<_, B>(&mut self.file_cursor).chain_err(|| { + format!("could not read prototype ID at offset {:#010x}", + current_offset) + })?; + } +} + +fn issue2264() { + { + something.function() + .map(|| { + if let a_very_very_very_very_very_very_very_very_long_variable = + compute_this_variable() + { + println!("Hello"); + } + }) + .collect(); + } +} diff --git a/tests/source/chains.rs b/tests/source/chains.rs new file mode 100644 index 000000000000..c77f5bac4cb2 --- /dev/null +++ b/tests/source/chains.rs @@ -0,0 +1,266 @@ +// rustfmt-use_small_heuristics: Off +// Test chain formatting. + +fn main() { + let a = b .c + .d.1 + .foo(|x| x + 1); + + bbbbbbbbbbbbbbbbbbb.ccccccccccccccccccccccccccccccccccccc + .ddddddddddddddddddddddddddd(); + + bbbbbbbbbbbbbbbbbbb.ccccccccccccccccccccccccccccccccccccc.ddddddddddddddddddddddddddd.eeeeeeee(); + + let f = fooooooooooooooooooooooooooooooooooooooooooooooooooo.baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaar; + + // Test case where first chain element isn't a path, but is shorter than + // the size of a tab. + x() + .y(|| match cond() { true => (), false => () }); + + loong_func() + .quux(move || if true { + 1 + } else { + 2 + }); + + some_fuuuuuuuuunction() + .method_call_a(aaaaa, bbbbb, |c| { + let x = c; + x + }); + + some_fuuuuuuuuunction().method_call_a(aaaaa, bbbbb, |c| { + let x = c; + x + }).method_call_b(aaaaa, bbbbb, |c| { + let x = c; + x + }); + + fffffffffffffffffffffffffffffffffff(a, + { + SCRIPT_TASK_ROOT + .with(|root| { + *root.borrow_mut() = Some(&script_task); + }); + }); + + let suuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuum = xxxxxxx + .map(|x| x + 5) + .map(|x| x / 2) + .fold(0, |acc, x| acc + x); + + body.fold(Body::new(), |mut body, chunk| { + body.extend(chunk); + Ok(body) + }).and_then(move |body| { + let req = Request::from_parts(parts, body); + f(req).map_err(|_| io::Error::new(io::ErrorKind::Other, "")) + }); + + aaaaaaaaaaaaaaaa.map(|x| { + x += 1; + x + }).filter(some_mod::some_filter) +} + +fn floaters() { + let z = Foo { + field1: val1, + field2: val2, + }; + + let x = Foo { + field1: val1, + field2: val2, + }.method_call().method_call(); + + let y = if cond { + val1 + } else { + val2 + } + .method_call(); + + { + match x { + PushParam => { + // params are 1-indexed + stack.push(mparams[match cur.to_digit(10) { + Some(d) => d as usize - 1, + None => return Err("bad param number".to_owned()), + }] + .clone()); + } + } + } + + if cond { some(); } else { none(); } + .bar() + .baz(); + + Foo { x: val } .baz(|| { force(); multiline(); }) .quux(); + + Foo { y: i_am_multi_line, z: ok } + .baz(|| { + force(); multiline(); + }) + .quux(); + + a + match x { true => "yay!", false => "boo!" }.bar() +} + +fn is_replaced_content() -> bool { + constellat.send(ConstellationMsg::ViewportConstrained( + self.id, constraints)).unwrap(); +} + +fn issue587() { + a.b::<()>(c); + + std::mem::transmute(dl.symbol::<()>("init").unwrap()) +} + +fn try_shorthand() { + let x = expr?; + let y = expr.kaas()?.test(); + let loooooooooooooooooooooooooooooooooooooooooong = does_this?.look?.good?.should_we_break?.after_the_first_question_mark?; + let yyyy = expr?.another?.another?.another?.another?.another?.another?.another?.another?.test(); + let zzzz = expr?.another?.another?.another?.another?; + let aaa = x ???????????? ?????????????? ???? ????? ?????????????? ????????? ?????????????? ??; + + let y = a.very .loooooooooooooooooooooooooooooooooooooong() .chain() + .inside() .weeeeeeeeeeeeeee()? .test() .0 + .x; + + parameterized(f, + substs, + def_id, + Ns::Value, + &[], + |tcx| tcx.lookup_item_type(def_id).generics)?; + fooooooooooooooooooooooooooo()?.bar()?.baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaz()?; +} + +fn issue_1004() { + match *self { + ty::ImplOrTraitItem::MethodTraitItem(ref i) => write!(f, "{:?}", i), + ty::ImplOrTraitItem::ConstTraitItem(ref i) => write!(f, "{:?}", i), + ty::ImplOrTraitItem::TypeTraitItem(ref i) => write!(f, "{:?}", i), + } + ?; + + ty::tls::with(|tcx| { + let tap = ty::Binder(TraitAndProjections(principal, projections)); + in_binder(f, tcx, &ty::Binder(""), Some(tap)) + }) + ?; +} + +fn issue1392() { + test_method(r#" + if foo { + a(); + } + else { + b(); + } + "#.trim()); +} + +// #2067 +impl Settings { + fn save(&self) -> Result<()> { + let mut file = File::create(&settings_path).chain_err(|| ErrorKind::WriteError(settings_path.clone()))?; + } +} + +fn issue2126() { + { + { + { + { + { + let x = self.span_from(sub_span.expect("No span found for struct arant variant")); + self.sspanpan_from_span(sub_span.expect("No span found for struct variant")); + let x = self.spanpan_from_span(sub_span.expect("No span found for struct variant"))?; + } + } + } + } + } +} + +// #2200 +impl Foo { + pub fn from_ast(diagnostic: &::errors::Handler, + attrs: &[ast::Attribute]) -> Attributes { + let other_attrs = attrs.iter().filter_map(|attr| { + attr.with_desugared_doc(|attr| { + if attr.check_name("doc") { + if let Some(mi) = attr.meta() { + if let Some(value) = mi.value_str() { + doc_strings.push(DocFragment::Include(line, + attr.span, + filename, + contents)); + } + } + } + }) + }).collect(); + } +} + +// #2415 +// Avoid orphan in chain +fn issue2415() { + let base_url = (|| { + // stuff + + Ok((|| { + // stuff + Some(value.to_string()) + })() + .ok_or("")?) + })() + .unwrap_or_else(|_: Box<::std::error::Error>| String::from("")); +} + +impl issue_2786 { + fn thing(&self) { + foo(|a| { + println!("a"); + println!("b"); + }).bar(|c| { + println!("a"); + println!("b"); + }) + .baz(|c| { + println!("a"); + println!("b"); + }) + } +} + +fn issue_2773() { + let bar = Some(0); + bar.or_else(|| { + // do stuff + None + }).or_else(|| { + // do other stuff + None + }) + .and_then(|val| { + // do this stuff + None + }); +} + +fn issue_3034() { + disallowed_headers.iter().any(|header| *header == name) || + disallowed_header_prefixes.iter().any(|prefix| name.starts_with(prefix)) +} diff --git a/tests/source/chains_with_comment.rs b/tests/source/chains_with_comment.rs new file mode 100644 index 000000000000..91160711b890 --- /dev/null +++ b/tests/source/chains_with_comment.rs @@ -0,0 +1,121 @@ +// Chains with comment. + +fn main() { + let x = y // comment + .z; + + foo // foo + // comment after parent + .x + .y + // comment 1 + .bar() // comment after bar() + // comment 2 + .foobar + // comment after + // comment 3 + .baz(x, y, z); + + self.rev_dep_graph + .iter() + // Remove nodes that are not dirty + .filter(|&(unit, _)| dirties.contains(&unit)) + // Retain only dirty dependencies of the ones that are dirty + .map(|(k, deps)| { + ( + k.clone(), + deps.iter() + .cloned() + .filter(|d| dirties.contains(&d)) + .collect(), + ) + }); + + let y = expr /* comment */.kaas()? +// comment + .test(); + let loooooooooooooooooooooooooooooooooooooooooong = does_this?.look?.good?.should_we_break?.after_the_first_question_mark?; + let zzzz = expr? // comment after parent +// comment 0 +.another??? // comment 1 +.another???? // comment 2 +.another? // comment 3 +.another?; + + let y = a.very .loooooooooooooooooooooooooooooooooooooong() /* comment */ .chain() + .inside() /* comment */ .weeeeeeeeeeeeeee()? .test() .0 + .x; + + parameterized(f, + substs, + def_id, + Ns::Value, + &[], + |tcx| tcx.lookup_item_type(def_id).generics)?; + fooooooooooooooooooooooooooo()?.bar()?.baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaz()?; + + // #2559 + App::new("cargo-cache") +.version(crate_version!()) +.bin_name("cargo") +.about("Manage cargo cache") +.author("matthiaskrgr") +.subcommand( +SubCommand::with_name("cache") +.version(crate_version!()) +.bin_name("cargo-cache") +.about("Manage cargo cache") +.author("matthiaskrgr") +.arg(&list_dirs) +.arg(&remove_dir) +.arg(&gc_repos) +.arg(&info) +.arg(&keep_duplicate_crates) .arg(&dry_run) +.arg(&auto_clean) +.arg(&auto_clean_expensive), + ) // subcommand + .arg(&list_dirs); +} + +// #2177 +impl Foo { + fn dirty_rev_dep_graph( + &self, + dirties: &HashSet, + ) -> HashMap> { + let dirties = self.transitive_dirty_units(dirties); + trace!("transitive_dirty_units: {:?}", dirties); + + self.rev_dep_graph.iter() + // Remove nodes that are not dirty + .filter(|&(unit, _)| dirties.contains(&unit)) + // Retain only dirty dependencies of the ones that are dirty + .map(|(k, deps)| (k.clone(), deps.iter().cloned().filter(|d| dirties.contains(&d)).collect())) + } +} + +// #2907 +fn foo() { + let x = foo + .bar?? ? // comment + .baz; + let x = foo + .bar? ?? + // comment + .baz; + let x = foo + .bar? ? ? // comment + // comment + .baz; + let x = foo + .bar? ?? // comment + // comment + ? ?? + // comment + ? ?? + // comment + ??? + // comment + ? ? ? + .baz; +} diff --git a/tests/source/closure-block-inside-macro.rs b/tests/source/closure-block-inside-macro.rs new file mode 100644 index 000000000000..b3ddfb51263b --- /dev/null +++ b/tests/source/closure-block-inside-macro.rs @@ -0,0 +1,9 @@ +// #1547 +fuzz_target!(|data: &[u8]| if let Some(first) = data.first() { + let index = *first as usize; + if index >= ENCODINGS.len() { + return; + } + let encoding = ENCODINGS[index]; + dispatch_test(encoding, &data[1..]); +}); diff --git a/tests/source/closure.rs b/tests/source/closure.rs new file mode 100644 index 000000000000..b2d28b305d0a --- /dev/null +++ b/tests/source/closure.rs @@ -0,0 +1,223 @@ +// rustfmt-normalize_comments: true +// Closures + +fn main() { + let square = ( |i: i32 | i * i ); + + let commented = |/* first */ a /*argument*/, /* second*/ b: WithType /* argument*/, /* ignored */ _ | + (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb); + + let block_body = move |xxxxxxxxxxxxxxxxxxxxxxxxxxxxx, ref yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy| { + xxxxxxxxxxxxxxxxxxxxxxxxxxxxx + yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy + }; + + let loooooooooooooong_name = |field| { + // format comments. + if field.node.attrs.len() > 0 { field.node.attrs[0].span.lo() + } else { + field.span.lo() + }}; + + let unblock_me = |trivial| { + closure() + }; + + let empty = |arg| {}; + + let simple = |arg| { /* comment formatting */ foo(arg) }; + + let test = | | { do_something(); do_something_else(); }; + + let arg_test = |big_argument_name, test123| looooooooooooooooooong_function_naaaaaaaaaaaaaaaaame(); + + let arg_test = |big_argument_name, test123| {looooooooooooooooooong_function_naaaaaaaaaaaaaaaaame()}; + + let simple_closure = move || -> () {}; + + let closure = |input: Ty| -> Option { + foo() + }; + + let closure_with_return_type = |aaaaaaaaaaaaaaaaaaaaaaarg1, aaaaaaaaaaaaaaaaaaaaaaarg2| -> Strong { "sup".to_owned() }; + + |arg1, arg2, _, _, arg3, arg4| { let temp = arg4 + arg3; + arg2 * arg1 - temp }; + + let block_body_with_comment = args.iter() + .map(|a| { + // Emitting only dep-info is possible only for final crate type, as + // as others may emit required metadata for dependent crate types + if a.starts_with("--emit") && is_final_crate_type && !self.workspace_mode { + "--emit=dep-info" + } else { a } + }); + + for<> || -> () {}; + for< >|| -> () {}; + for< +> || -> () {}; + +for< 'a + ,'b, +'c > |_: &'a (), _: &'b (), _: &'c ()| -> () {}; + +} + +fn issue311() { + let func = |x| println!("{}", x); + + (func)(0.0); +} + +fn issue863() { + let closure = |x| match x { + 0 => true, + _ => false, + } == true; +} + +fn issue934() { + let hash: &Fn(&&Block) -> u64 = &|block| -> u64 { + let mut h = SpanlessHash::new(cx); + h.hash_block(block); + h.finish() + }; + + let hash: &Fn(&&Block) -> u64 = &|block| -> u64 { + let mut h = SpanlessHash::new(cx); + h.hash_block(block); + h.finish(); + }; +} + +impl<'a, 'tcx: 'a> SpanlessEq<'a, 'tcx> { + pub fn eq_expr(&self, left: &Expr, right: &Expr) -> bool { + match (&left.node, &right.node) { + (&ExprBinary(l_op, ref ll, ref lr), &ExprBinary(r_op, ref rl, ref rr)) => { + l_op.node == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) || + swap_binop(l_op.node, ll, lr).map_or(false, |(l_op, ll, lr)| l_op == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)) + } + } + } +} + +fn foo() { + lifetimes_iter___map(|lasdfasfd| { + let hi = if l.bounds.is_empty() { + l.lifetime.span.hi() + }; + }); +} + +fn issue1405() { + open_raw_fd(fd, b'r') + .and_then(|file| Capture::new_raw(None, |_, err| unsafe { + raw::pcap_fopen_offline(file, err) + })); +} + +fn issue1466() { + let vertex_buffer = frame.scope(|ctx| { + let buffer = + ctx.create_host_visible_buffer::>(&vertices); + ctx.create_device_local_buffer(buffer) + }); +} + +fn issue470() { + {{{ + let explicit_arg_decls = + explicit_arguments.into_iter() + .enumerate() + .map(|(index, (ty, pattern))| { + let lvalue = Lvalue::Arg(index as u32); + block = this.pattern(block, + argument_extent, + hair::PatternRef::Hair(pattern), + &lvalue); + ArgDecl { ty: ty } + }); + }}} +} + +// #1509 +impl Foo { + pub fn bar(&self) { + Some(SomeType { + push_closure_out_to_100_chars: iter(otherwise_it_works_ok.into_iter().map(|f| { + Ok(f) + })), + }) + } +} + +fn issue1329() { + aaaaaaaaaaaaaaaa.map(|x| { + x += 1; + x + }) + .filter +} + +fn issue325() { + let f = || unsafe { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }; +} + +fn issue1697() { + Test.func_a(A_VERY_LONG_CONST_VARIABLE_NAME, move |arg1, arg2, arg3, arg4| arg1 + arg2 + arg3 + arg4) +} + +fn issue1694() { + foooooo(|_referencefffffffff: _, _target_reference: _, _oid: _, _target_oid: _| format!("refs/pull/{}/merge", pr_id)) +} + +fn issue1713() { + rayon::join( + || recurse(left, is_less, pred, limit), + || recurse(right, is_less, Some(pivot), limit), + ); + + rayon::join( + 1, + || recurse(left, is_less, pred, limit), + 2, + || recurse(right, is_less, Some(pivot), limit), + ); +} + +fn issue2063() { + |ctx: Ctx<(String, String)>| -> io::Result { + Ok(Response::new().with_body(ctx.params.0)) + } +} + +fn issue1524() { + let f = |x| {{{{x}}}}; + let f = |x| {{{x}}}; + let f = |x| {{x}}; + let f = |x| {x}; + let f = |x| x; +} + +fn issue2171() { + foo(|| unsafe { + if PERIPHERALS { + loop {} + } else { + PERIPHERALS = true; + } + }) +} + +fn issue2207() { + a.map(|_| unsafe { + a_very_very_very_very_very_very_very_long_function_name_or_anything_else() + }.to_string()) +} + +fn issue2262() { + result.init(&mut result.slave.borrow_mut(), &mut (result.strategy)()).map_err(|factory| Error { + factory, + slave: None, + })?; +} diff --git a/tests/source/comment.rs b/tests/source/comment.rs new file mode 100644 index 000000000000..b6ce5267fcb1 --- /dev/null +++ b/tests/source/comment.rs @@ -0,0 +1,90 @@ +// rustfmt-normalize_comments: true +// rustfmt-wrap_comments: true + +//! Doc comment +fn test() { + /*! + * Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam */ + +// comment + // comment2 + + code(); /* leave this comment alone! + * ok? */ + + /* Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a + * diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam + * viverra nec consectetur ante hendrerit. Donec et mollis dolor. + * Praesent et diam eget libero egestas mattis sit amet vitae augue. Nam + * tincidunt congue enim, ut porta lorem lacinia consectetur. Donec ut + * libero sed arcu vehicula ultricies a non tortor. Lorem ipsum dolor sit + * amet, consectetur adipiscing elit. Aenean ut gravida lorem. Ut turpis + * felis, pulvinar a semper sed, adipiscing id dolor. */ + + // Very looooooooooooooooooooooooooooooooooooooooooooooooooooooooong comment that should be split + + // println!("{:?}", rewrite_comment(subslice, + // false, + // comment_width, + // self.block_indent, + // self.config) + // .unwrap()); + + funk(); //dontchangeme + // or me + + // #1388 + const EXCEPTION_PATHS: &'static [&'static str] = + &[// std crates + "src/libstd/sys/", // Platform-specific code for std lives here. + "src/bootstrap"]; +} + + /// test123 +fn doc_comment() { +} + +fn chains() { + foo.bar(|| { + let x = 10; + /* comment */ x }) +} + +fn issue_1086() { + /**/ +} + +/* + * random comment */ + +fn main() {/* Test */} + +// #1643 +fn some_fn() /* some comment */ +{ +} + +fn some_fn1() +// some comment +{ +} + +fn some_fn2() // some comment +{ +} + +fn some_fn3() /* some comment some comment some comment some comment some comment some comment so */ +{ +} + +fn some_fn4() +/* some comment some comment some comment some comment some comment some comment some comment */ +{ +} + +// #1603 +pub enum Foo { + A, // `/** **/` + B, // `/*!` + C, +} diff --git a/tests/source/comment2.rs b/tests/source/comment2.rs new file mode 100644 index 000000000000..d68bb5483dc8 --- /dev/null +++ b/tests/source/comment2.rs @@ -0,0 +1,4 @@ +// rustfmt-wrap_comments: true + +/// This is a long line that angers rustfmt. Rustfmt shall deal with it swiftly and justly. +pub mod foo {} diff --git a/tests/source/comment3.rs b/tests/source/comment3.rs new file mode 100644 index 000000000000..f19a85863344 --- /dev/null +++ b/tests/source/comment3.rs @@ -0,0 +1,5 @@ +// rustfmt-wrap_comments: true + +//! This is a long line that angers rustfmt. Rustfmt shall deal with it swiftly and justly. + +pub mod foo {} diff --git a/tests/source/comment4.rs b/tests/source/comment4.rs new file mode 100644 index 000000000000..f53a8a4a1fe0 --- /dev/null +++ b/tests/source/comment4.rs @@ -0,0 +1,52 @@ +#![allow(dead_code)] // bar + +//! Doc comment +fn test() { +// comment + // comment2 + + code(); /* leave this comment alone! + * ok? */ + + /* Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a + * diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam + * viverra nec consectetur ante hendrerit. Donec et mollis dolor. + * Praesent et diam eget libero egestas mattis sit amet vitae augue. Nam + * tincidunt congue enim, ut porta lorem lacinia consectetur. Donec ut + * libero sed arcu vehicula ultricies a non tortor. Lorem ipsum dolor sit + * amet, consectetur adipiscing elit. Aenean ut gravida lorem. Ut turpis + * felis, pulvinar a semper sed, adipiscing id dolor. */ + + // Very loooooooooooooooooooooooooooooooooooooooooooooooooooooooong comment that should be split + + // println!("{:?}", rewrite_comment(subslice, + // false, + // comment_width, + // self.block_indent, + // self.config) + // .unwrap()); + + funk(); //dontchangeme + // or me +} + + /// test123 +fn doc_comment() { +} + +/* +Regression test for issue #956 + +(some very important text) +*/ + +/* +fn debug_function() { + println!("hello"); +} +// */ + +#[link_section=".vectors"] +#[no_mangle] // Test this attribute is preserved. +#[cfg_attr(rustfmt, rustfmt::skip)] +pub static ISSUE_1284: [i32; 16] = []; diff --git a/tests/source/comment5.rs b/tests/source/comment5.rs new file mode 100644 index 000000000000..2835d8b25785 --- /dev/null +++ b/tests/source/comment5.rs @@ -0,0 +1,14 @@ +// rustfmt-normalize_comments: true +// rustfmt-wrap_comments: true + +//@ special comment +//@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec adiam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam +//@ +//@foo +fn test() {} + +//@@@ another special comment +//@@@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec adiam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam +//@@@ +//@@@foo +fn bar() {} diff --git a/tests/source/comment6.rs b/tests/source/comment6.rs new file mode 100644 index 000000000000..e5d72113ce61 --- /dev/null +++ b/tests/source/comment6.rs @@ -0,0 +1,10 @@ +// rustfmt-wrap_comments: true + +// Pendant la nuit du 9 mars 1860, les nuages, se confondant avec la mer, limitaient à quelques brasses la portée de la vue. +// Sur cette mer démontée, dont les lames déferlaient en projetant des lueurs livides, un léger bâtiment fuyait presque à sec de toile. + +pub mod foo {} + +// ゆく河の流れは絶えずして、しかももとの水にあらず。淀みに浮かぶうたかたは、かつ消えかつ結びて、久しくとどまりたるためしなし。世の中にある人とすみかと、またかくのごとし。 + +pub mod bar {} diff --git a/tests/source/comment_crlf_newline.rs b/tests/source/comment_crlf_newline.rs new file mode 100644 index 000000000000..7a65f762f6c9 --- /dev/null +++ b/tests/source/comment_crlf_newline.rs @@ -0,0 +1,4 @@ +// rustfmt-normalize_comments: true +/* Block comments followed by CRLF newlines should not an extra newline at the end */ + +/* Something else */ diff --git a/tests/source/comments-in-lists/wrap-comments-not-normalized.rs b/tests/source/comments-in-lists/wrap-comments-not-normalized.rs new file mode 100644 index 000000000000..b96c02802d69 --- /dev/null +++ b/tests/source/comments-in-lists/wrap-comments-not-normalized.rs @@ -0,0 +1,129 @@ +// rustfmt-wrap_comments: true + +// https://github.com/rust-lang/rustfmt/issues/4909 +pub enum E { + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + Variant1, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + Variant2, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +} + +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 +} + +pub enum E3 { + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + Variant1, + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + Variant2, + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + +} + +pub struct S { + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + some_field: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + last_field: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +} + +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 +} + +pub struct S3 { + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + some_field: usize, + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + last_field: usize, + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions +} + +fn foo( + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + a: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + b: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +) -> usize { + 5 +} + +fn foo2(// Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +) -> usize { + 5 +} + +fn foo3( + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + a: usize, + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + b: usize, + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + +) -> usize { + 5 +} + +fn main() { + let v = vec![ + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + 1, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + 2, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + ]; + + let v2: Vec = vec![ + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + ]; + + let v3 = vec![ + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + 1, + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + 2, + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + ]; + + // https://github.com/rust-lang/rustfmt/issues/4430 + match a { + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + b => c, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + d => e, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + } + + match a { + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + b => c, + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + d => e, + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + } +} diff --git a/tests/source/comments-in-lists/wrap-comments-true.rs b/tests/source/comments-in-lists/wrap-comments-true.rs new file mode 100644 index 000000000000..360b838520ed --- /dev/null +++ b/tests/source/comments-in-lists/wrap-comments-true.rs @@ -0,0 +1,130 @@ +// rustfmt-normalize_comments: true +// rustfmt-wrap_comments: true + +// https://github.com/rust-lang/rustfmt/issues/4909 +pub enum E { + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + Variant1, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + Variant2, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +} + +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 +} + +pub enum E3 { + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + Variant1, + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + Variant2, + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + +} + +pub struct S { + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + some_field: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + last_field: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +} + +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 +} + +pub struct S3 { + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + some_field: usize, + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + last_field: usize, + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions +} + +fn foo( + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + a: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + b: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +) -> usize { + 5 +} + +fn foo2(// Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +) -> usize { + 5 +} + +fn foo3( + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + a: usize, + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + b: usize, + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + +) -> usize { + 5 +} + +fn main() { + let v = vec![ + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + 1, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + 2, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + ]; + + let v2: Vec = vec![ + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + ]; + + let v3 = vec![ + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + 1, + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + 2, + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + ]; + + // https://github.com/rust-lang/rustfmt/issues/4430 + match a { + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + b => c, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + d => e, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + } + + match a { + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + b => c, + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + d => e, + // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions + } +} diff --git a/tests/source/comments_unicode.rs b/tests/source/comments_unicode.rs new file mode 100644 index 000000000000..e65a245ba934 --- /dev/null +++ b/tests/source/comments_unicode.rs @@ -0,0 +1,140 @@ +impl Default for WhitespaceCharacters { + fn default() -> Self { + Self { + space: '·', // U+00B7 + nbsp: '⍽', // U+237D + tab: '→', // U+2192 + newline: '⏎', // U+23CE + } + } +} + +const RAINBOWS: &[&str] = &[ + "rаinЬοѡ", // hue: 0 + "raіnЬοw", // hue: 2 + "rаіɴЬow", // hue: 2 + "raіɴЬoѡ", // hue: 8 + "ʀainЬow", // hue: 8 + "ʀaіɴboѡ", // hue: 8 + "ʀаіnbοw", // hue: 11 + "rainЬoѡ", // hue: 14 + "raіɴbow", // hue: 14 + "rаiɴЬow", // hue: 20 + "raіnЬow", // hue: 26 + "ʀaiɴbοw", // hue: 32 + "raіɴboѡ", // hue: 35 + "rаiɴbow", // hue: 35 + "rаіnbοw", // hue: 38 + "rаinЬow", // hue: 47 + "ʀaіnboѡ", // hue: 47 + "ʀaіnЬoѡ", // hue: 47 + "ʀаіɴbοw", // hue: 53 + "ʀaіnЬοѡ", // hue: 57 + "raiɴЬoѡ", // hue: 68 + "ʀainbοѡ", // hue: 68 + "ʀаinboѡ", // hue: 68 + "ʀаiɴbοw", // hue: 68 + "ʀаіnbow", // hue: 68 + "rаіnЬοѡ", // hue: 69 + "ʀainЬοw", // hue: 71 + "raiɴbow", // hue: 73 + "raіnЬoѡ", // hue: 74 + "rаіɴbοw", // hue: 77 + "raіnЬοѡ", // hue: 81 + "raiɴЬow", // hue: 83 + "ʀainbοw", // hue: 83 + "ʀаinbow", // hue: 83 + "ʀаiɴbοѡ", // hue: 83 + "ʀаіnboѡ", // hue: 83 + "ʀаіɴЬοѡ", // hue: 84 + "rainЬow", // hue: 85 + "ʀаiɴЬοw", // hue: 86 + "ʀаіnbοѡ", // hue: 89 + "ʀаіnЬοw", // hue: 92 + "rаiɴbοw", // hue: 95 + "ʀаіɴbοѡ", // hue: 98 + "ʀаiɴЬοѡ", // hue: 99 + "raіnbοw", // hue: 101 + "ʀаіɴЬοw", // hue: 101 + "ʀaiɴboѡ", // hue: 104 + "ʀаinbοѡ", // hue: 104 + "rаiɴbοѡ", // hue: 107 + "ʀаinЬοw", // hue: 107 + "rаiɴЬοw", // hue: 110 + "rаіnboѡ", // hue: 110 + "rаіnbοѡ", // hue: 113 + "ʀainЬοѡ", // hue: 114 + "rаіnЬοw", // hue: 116 + "ʀaіɴЬow", // hue: 116 + "rаinbοw", // hue: 122 + "ʀаіɴboѡ", // hue: 125 + "rаinbοѡ", // hue: 131 + "rainbow", // hue: 134 + "rаinЬοw", // hue: 134 + "ʀаiɴboѡ", // hue: 140 + "rainЬοѡ", // hue: 141 + "raіɴЬow", // hue: 143 + "ʀainЬoѡ", // hue: 143 + "ʀaіɴbow", // hue: 143 + "ʀainbow", // hue: 148 + "rаіɴboѡ", // hue: 149 + "ʀainboѡ", // hue: 155 + "ʀaіnbow", // hue: 155 + "ʀaіnЬow", // hue: 155 + "raiɴbοw", // hue: 158 + "ʀаiɴЬoѡ", // hue: 158 + "rainbοw", // hue: 160 + "rаinbow", // hue: 160 + "ʀaіɴbοѡ", // hue: 164 + "ʀаiɴbow", // hue: 164 + "ʀаіnЬoѡ", // hue: 164 + "ʀaiɴЬοѡ", // hue: 165 + "rаiɴboѡ", // hue: 167 + "ʀaіɴЬοw", // hue: 167 + "ʀaіɴЬοѡ", // hue: 171 + "raіnboѡ", // hue: 173 + "ʀаіɴЬoѡ", // hue: 173 + "rаіɴbοѡ", // hue: 176 + "ʀаinЬow", // hue: 176 + "rаiɴЬοѡ", // hue: 177 + "rаіɴЬοw", // hue: 179 + "ʀаinЬoѡ", // hue: 179 + "ʀаіɴbow", // hue: 179 + "rаiɴЬoѡ", // hue: 182 + "raіɴbοѡ", // hue: 188 + "rаіnЬoѡ", // hue: 188 + "raiɴЬοѡ", // hue: 189 + "raіɴЬοw", // hue: 191 + "ʀaіɴbοw", // hue: 191 + "ʀаіnЬow", // hue: 191 + "rainbοѡ", // hue: 194 + "rаinboѡ", // hue: 194 + "rаіnbow", // hue: 194 + "rainЬοw", // hue: 197 + "rаinЬoѡ", // hue: 206 + "rаіɴbow", // hue: 206 + "rаіɴЬοѡ", // hue: 210 + "ʀaiɴЬow", // hue: 212 + "raіɴbοw", // hue: 218 + "rаіnЬow", // hue: 218 + "ʀaiɴbοѡ", // hue: 221 + "ʀaiɴЬοw", // hue: 224 + "ʀaіnbοѡ", // hue: 227 + "raiɴboѡ", // hue: 230 + "ʀaіnbοw", // hue: 230 + "ʀaіnЬοw", // hue: 230 + "ʀаinЬοѡ", // hue: 231 + "rainboѡ", // hue: 232 + "raіnbow", // hue: 232 + "ʀаіɴЬow", // hue: 233 + "ʀaіɴЬoѡ", // hue: 239 + "ʀаіnЬοѡ", // hue: 246 + "raiɴbοѡ", // hue: 248 + "ʀаiɴЬow", // hue: 248 + "raіɴЬοѡ", // hue: 249 + "raiɴЬοw", // hue: 251 + "rаіɴЬoѡ", // hue: 251 + "ʀaiɴbow", // hue: 251 + "ʀаinbοw", // hue: 251 + "raіnbοѡ", // hue: 254 +]; diff --git a/tests/source/configs/blank_lines_lower_bound/1.rs b/tests/source/configs/blank_lines_lower_bound/1.rs new file mode 100644 index 000000000000..c6058a55b0ae --- /dev/null +++ b/tests/source/configs/blank_lines_lower_bound/1.rs @@ -0,0 +1,13 @@ +// rustfmt-blank_lines_lower_bound: 1 + +fn foo() {} +fn bar() {} +// comment +fn foobar() {} + +fn foo1() {} +fn bar1() {} + +// comment + +fn foobar1() {} diff --git a/tests/source/configs/brace_style/fn_always_next_line.rs b/tests/source/configs/brace_style/fn_always_next_line.rs new file mode 100644 index 000000000000..d3bd9ac09aa6 --- /dev/null +++ b/tests/source/configs/brace_style/fn_always_next_line.rs @@ -0,0 +1,14 @@ +// rustfmt-brace_style: AlwaysNextLine +// Function brace style + +fn lorem() { + // body +} + +fn lorem(ipsum: usize) { + // body +} + +fn lorem(ipsum: T) where T: Add + Sub + Mul + Div { + // body +} diff --git a/tests/source/configs/brace_style/fn_prefer_same_line.rs b/tests/source/configs/brace_style/fn_prefer_same_line.rs new file mode 100644 index 000000000000..78a4495243d8 --- /dev/null +++ b/tests/source/configs/brace_style/fn_prefer_same_line.rs @@ -0,0 +1,14 @@ +// rustfmt-brace_style: PreferSameLine +// Function brace style + +fn lorem() { + // body +} + +fn lorem(ipsum: usize) { + // body +} + +fn lorem(ipsum: T) where T: Add + Sub + Mul + Div { + // body +} diff --git a/tests/source/configs/brace_style/fn_same_line_where.rs b/tests/source/configs/brace_style/fn_same_line_where.rs new file mode 100644 index 000000000000..3b78932e1776 --- /dev/null +++ b/tests/source/configs/brace_style/fn_same_line_where.rs @@ -0,0 +1,14 @@ +// rustfmt-brace_style: SameLineWhere +// Function brace style + +fn lorem() { + // body +} + +fn lorem(ipsum: usize) { + // body +} + +fn lorem(ipsum: T) where T: Add + Sub + Mul + Div { + // body +} diff --git a/tests/source/configs/brace_style/item_always_next_line.rs b/tests/source/configs/brace_style/item_always_next_line.rs new file mode 100644 index 000000000000..0cc19b34da7e --- /dev/null +++ b/tests/source/configs/brace_style/item_always_next_line.rs @@ -0,0 +1,20 @@ +// rustfmt-brace_style: AlwaysNextLine +// Item brace style + +enum Foo {} + +struct Bar {} + +struct Lorem { + ipsum: bool, +} + +struct Dolor where T: Eq { + sit: T, +} + +#[cfg(test)] +mod tests { + #[test] + fn it_works() {} +} diff --git a/tests/source/configs/brace_style/item_prefer_same_line.rs b/tests/source/configs/brace_style/item_prefer_same_line.rs new file mode 100644 index 000000000000..4412bc869a26 --- /dev/null +++ b/tests/source/configs/brace_style/item_prefer_same_line.rs @@ -0,0 +1,16 @@ +// rustfmt-brace_style: PreferSameLine +// Item brace style + +struct Lorem { + ipsum: bool, +} + +struct Dolor where T: Eq { + sit: T, +} + +#[cfg(test)] +mod tests { + #[test] + fn it_works() {} +} diff --git a/tests/source/configs/brace_style/item_same_line_where.rs b/tests/source/configs/brace_style/item_same_line_where.rs new file mode 100644 index 000000000000..b8e69147dc5a --- /dev/null +++ b/tests/source/configs/brace_style/item_same_line_where.rs @@ -0,0 +1,16 @@ +// rustfmt-brace_style: SameLineWhere +// Item brace style + +struct Lorem { + ipsum: bool, +} + +struct Dolor where T: Eq { + sit: T, +} + +#[cfg(test)] +mod tests { + #[test] + fn it_works() {} +} diff --git a/tests/source/configs/chain_width/always.rs b/tests/source/configs/chain_width/always.rs new file mode 100644 index 000000000000..2d16d66aec12 --- /dev/null +++ b/tests/source/configs/chain_width/always.rs @@ -0,0 +1,23 @@ +// rustfmt-chain_width: 1 +// setting an unachievable chain_width to always get chains +// on separate lines + +struct Fluent {} + +impl Fluent { + fn blorp(&self) -> &Self { + self + } +} + +fn main() { + let test = Fluent {}; + + // should be left alone + test.blorp(); + + // should be wrapped + test.blorp().blorp(); + test.blorp().blorp().blorp(); + test.blorp().blorp().blorp().blorp(); +} diff --git a/tests/source/configs/chain_width/small.rs b/tests/source/configs/chain_width/small.rs new file mode 100644 index 000000000000..26f9354537a3 --- /dev/null +++ b/tests/source/configs/chain_width/small.rs @@ -0,0 +1,23 @@ +// rustfmt-chain_width: 40 + +struct Fluent {} + +impl Fluent { + fn blorp(&self) -> &Self { + self + } +} + +fn main() { + let test = Fluent {}; + + // should not be wrapped + test.blorp(); + test.blorp().blorp(); + test.blorp().blorp().blorp(); + test.blorp().blorp().blorp().blorp(); + + // should be wrapped + test.blorp().blorp().blorp().blorp().blorp(); + test.blorp().blorp().blorp().blorp().blorp().blorp(); +} diff --git a/tests/source/configs/chain_width/tiny.rs b/tests/source/configs/chain_width/tiny.rs new file mode 100644 index 000000000000..fffc81dd5d6d --- /dev/null +++ b/tests/source/configs/chain_width/tiny.rs @@ -0,0 +1,21 @@ +// rustfmt-chain_width: 20 + +struct Fluent {} + +impl Fluent { + fn blorp(&self) -> &Self { + self + } +} + +fn main() { + let test = Fluent {}; + + // should not be wrapped + test.blorp(); + test.blorp().blorp(); + + // should be wrapped + test.blorp().blorp().blorp(); + test.blorp().blorp().blorp().blorp(); +} diff --git a/tests/source/configs/comment_width/above.rs b/tests/source/configs/comment_width/above.rs new file mode 100644 index 000000000000..36187ce0af4c --- /dev/null +++ b/tests/source/configs/comment_width/above.rs @@ -0,0 +1,7 @@ +// rustfmt-comment_width: 40 +// rustfmt-wrap_comments: true +// Comment width + +fn main() { + // Lorem ipsum dolor sit amet, consectetur adipiscing elit. +} diff --git a/tests/source/configs/comment_width/below.rs b/tests/source/configs/comment_width/below.rs new file mode 100644 index 000000000000..abbc5930c4ce --- /dev/null +++ b/tests/source/configs/comment_width/below.rs @@ -0,0 +1,7 @@ +// rustfmt-comment_width: 80 +// rustfmt-wrap_comments: true +// Comment width + +fn main() { + // Lorem ipsum dolor sit amet, consectetur adipiscing elit. +} diff --git a/tests/source/configs/comment_width/ignore.rs b/tests/source/configs/comment_width/ignore.rs new file mode 100644 index 000000000000..c86e71c28981 --- /dev/null +++ b/tests/source/configs/comment_width/ignore.rs @@ -0,0 +1,7 @@ +// rustfmt-comment_width: 40 +// rustfmt-wrap_comments: false +// Comment width + +fn main() { + // Lorem ipsum dolor sit amet, consectetur adipiscing elit. +} diff --git a/tests/source/configs/condense_wildcard_suffixes/false.rs b/tests/source/configs/condense_wildcard_suffixes/false.rs new file mode 100644 index 000000000000..3b967f35a8e8 --- /dev/null +++ b/tests/source/configs/condense_wildcard_suffixes/false.rs @@ -0,0 +1,6 @@ +// rustfmt-condense_wildcard_suffixes: false +// Condense wildcard suffixes + +fn main() { + let (lorem, ipsum, _, _) = (1, 2, 3, 4); +} diff --git a/tests/source/configs/condense_wildcard_suffixes/true.rs b/tests/source/configs/condense_wildcard_suffixes/true.rs new file mode 100644 index 000000000000..3798a6b99020 --- /dev/null +++ b/tests/source/configs/condense_wildcard_suffixes/true.rs @@ -0,0 +1,6 @@ +// rustfmt-condense_wildcard_suffixes: true +// Condense wildcard suffixes + +fn main() { + let (lorem, ipsum, _, _) = (1, 2, 3, 4); +} diff --git a/tests/source/configs/control_brace_style/always_next_line.rs b/tests/source/configs/control_brace_style/always_next_line.rs new file mode 100644 index 000000000000..c4ddad9ce273 --- /dev/null +++ b/tests/source/configs/control_brace_style/always_next_line.rs @@ -0,0 +1,10 @@ +// rustfmt-control_brace_style: AlwaysNextLine +// Control brace style + +fn main() { + if lorem { println!("ipsum!"); } else { println!("dolor!"); } + match magi { + Homura => "Akemi", + Madoka => "Kaname", + } +} diff --git a/tests/source/configs/control_brace_style/always_same_line.rs b/tests/source/configs/control_brace_style/always_same_line.rs new file mode 100644 index 000000000000..a9c699d27e3e --- /dev/null +++ b/tests/source/configs/control_brace_style/always_same_line.rs @@ -0,0 +1,10 @@ +// rustfmt-control_brace_style: AlwaysSameLine +// Control brace style + +fn main() { + if lorem { println!("ipsum!"); } else { println!("dolor!"); } + match magi { + Homura => "Akemi", + Madoka => "Kaname", + } +} diff --git a/tests/source/configs/control_brace_style/closing_next_line.rs b/tests/source/configs/control_brace_style/closing_next_line.rs new file mode 100644 index 000000000000..1a74a28f26a9 --- /dev/null +++ b/tests/source/configs/control_brace_style/closing_next_line.rs @@ -0,0 +1,10 @@ +// rustfmt-control_brace_style: ClosingNextLine +// Control brace style + +fn main() { + if lorem { println!("ipsum!"); } else { println!("dolor!"); } + match magi { + Homura => "Akemi", + Madoka => "Kaname", + } +} diff --git a/tests/source/configs/disable_all_formatting/false.rs b/tests/source/configs/disable_all_formatting/false.rs new file mode 100644 index 000000000000..834ca7a3c89e --- /dev/null +++ b/tests/source/configs/disable_all_formatting/false.rs @@ -0,0 +1,6 @@ +// rustfmt-disable_all_formatting: false +// Disable all formatting + +fn main() { + if lorem{println!("ipsum!");}else{println!("dolor!");} +} diff --git a/tests/source/configs/disable_all_formatting/true.rs b/tests/source/configs/disable_all_formatting/true.rs new file mode 100644 index 000000000000..56955bf384d6 --- /dev/null +++ b/tests/source/configs/disable_all_formatting/true.rs @@ -0,0 +1,6 @@ +// rustfmt-disable_all_formatting: true +// Disable all formatting + +fn main() { + iflorem{println!("ipsum!");}else{println!("dolor!");} +} diff --git a/tests/source/configs/doc_comment_code_block_width/100.rs b/tests/source/configs/doc_comment_code_block_width/100.rs new file mode 100644 index 000000000000..515780761670 --- /dev/null +++ b/tests/source/configs/doc_comment_code_block_width/100.rs @@ -0,0 +1,16 @@ +// rustfmt-format_code_in_doc_comments: true +// rustfmt-doc_comment_code_block_width: 100 + +/// ```rust +/// impl Test { +/// pub const fn from_bytes(v: &[u8]) -> Result { +/// Self::from_bytes_manual_slice(v, 0, v.len() ) +/// } +/// } +/// ``` + +impl Test { + pub const fn from_bytes(v: &[u8]) -> Result { + Self::from_bytes_manual_slice(v, 0, v.len() ) + } +} diff --git a/tests/source/configs/doc_comment_code_block_width/100_greater_max_width.rs b/tests/source/configs/doc_comment_code_block_width/100_greater_max_width.rs new file mode 100644 index 000000000000..96505c69714e --- /dev/null +++ b/tests/source/configs/doc_comment_code_block_width/100_greater_max_width.rs @@ -0,0 +1,17 @@ +// rustfmt-max_width: 50 +// rustfmt-format_code_in_doc_comments: true +// rustfmt-doc_comment_code_block_width: 100 + +/// ```rust +/// impl Test { +/// pub const fn from_bytes(v: &[u8]) -> Result { +/// Self::from_bytes_manual_slice(v, 0, v.len() ) +/// } +/// } +/// ``` + +impl Test { + pub const fn from_bytes(v: &[u8]) -> Result { + Self::from_bytes_manual_slice(v, 0, v.len() ) + } +} diff --git a/tests/source/configs/doc_comment_code_block_width/50.rs b/tests/source/configs/doc_comment_code_block_width/50.rs new file mode 100644 index 000000000000..2c6307951c84 --- /dev/null +++ b/tests/source/configs/doc_comment_code_block_width/50.rs @@ -0,0 +1,16 @@ +// rustfmt-format_code_in_doc_comments: true +// rustfmt-doc_comment_code_block_width: 50 + +/// ```rust +/// impl Test { +/// pub const fn from_bytes(v: &[u8]) -> Result { +/// Self::from_bytes_manual_slice(v, 0, v.len() ) +/// } +/// } +/// ``` + +impl Test { + pub const fn from_bytes(v: &[u8]) -> Result { + Self::from_bytes_manual_slice(v, 0, v.len() ) + } +} diff --git a/tests/source/configs/empty_item_single_line/false.rs b/tests/source/configs/empty_item_single_line/false.rs new file mode 100644 index 000000000000..9bfb2b964eaa --- /dev/null +++ b/tests/source/configs/empty_item_single_line/false.rs @@ -0,0 +1,16 @@ +// rustfmt-empty_item_single_line: false +// Empty impl on single line + +impl Lorem { + +} + +impl Ipsum { + +} + +fn lorem() { +} + +fn lorem() { +} diff --git a/tests/source/configs/empty_item_single_line/true.rs b/tests/source/configs/empty_item_single_line/true.rs new file mode 100644 index 000000000000..8af8b88ffff8 --- /dev/null +++ b/tests/source/configs/empty_item_single_line/true.rs @@ -0,0 +1,16 @@ +// rustfmt-empty_item_single_line: true +// Empty impl on single line + +impl Lorem { + +} + +impl Ipsum { + +} + +fn lorem() { +} + +fn lorem() { +} diff --git a/tests/source/configs/enum_discrim_align_threshold/40.rs b/tests/source/configs/enum_discrim_align_threshold/40.rs new file mode 100644 index 000000000000..796e47c384ba --- /dev/null +++ b/tests/source/configs/enum_discrim_align_threshold/40.rs @@ -0,0 +1,34 @@ +// rustfmt-enum_discrim_align_threshold: 40 + +enum Standard { + A = 1, + Bcdef = 2, +} + +enum NoDiscrims { + ThisIsAFairlyLongEnumVariantWithoutDiscrimLongerThan40, + A = 1, + ThisIsAnotherFairlyLongEnumVariantWithoutDiscrimLongerThan40, + Bcdef = 2, +} + +enum TooLong { + ThisOneHasDiscrimAaaaaaaaaaaaaaaaaaaaaaChar40 = 10, + A = 1, + Bcdef = 2, +} + +enum Borderline { + ThisOneHasDiscrimAaaaaaaaaaaaaaaaaaaaaa = 10, + A = 1, + Bcdef = 2, +} + +// Live specimen from #1686 +enum LongWithSmallDiff { + SceneColorimetryEstimates = 0x73636F65, + SceneAppearanceEstimates = 0x73617065, + FocalPlaneColorimetryEstimates = 0x66706365, + ReflectionHardcopyOriginalColorimetry = 0x72686F63, + ReflectionPrintOutputColorimetry = 0x72706F63, +} \ No newline at end of file diff --git a/tests/source/configs/error_on_line_overflow/false.rs b/tests/source/configs/error_on_line_overflow/false.rs new file mode 100644 index 000000000000..fa70ae78352f --- /dev/null +++ b/tests/source/configs/error_on_line_overflow/false.rs @@ -0,0 +1,6 @@ +// rustfmt-error_on_line_overflow: false +// Error on line overflow + +fn main() { + let lorem_ipsum_dolor_sit_amet_consectetur_adipiscing_elit_lorem_ipsum_dolor_sit_amet_consectetur_adipiscing_elit; +} diff --git a/tests/source/configs/fn_params_layout/compressed.rs b/tests/source/configs/fn_params_layout/compressed.rs new file mode 100644 index 000000000000..eb573d3121f5 --- /dev/null +++ b/tests/source/configs/fn_params_layout/compressed.rs @@ -0,0 +1,16 @@ +// rustfmt-fn_params_layout: Compressed +// Function arguments density + +trait Lorem { + fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet); + + fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet) { + // body + } + + fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: onsectetur, adipiscing: Adipiscing, elit: Elit); + + fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: onsectetur, adipiscing: Adipiscing, elit: Elit) { + // body + } +} diff --git a/tests/source/configs/fn_params_layout/tall.rs b/tests/source/configs/fn_params_layout/tall.rs new file mode 100644 index 000000000000..4be34f0fe4ac --- /dev/null +++ b/tests/source/configs/fn_params_layout/tall.rs @@ -0,0 +1,16 @@ +// rustfmt-fn_params_layout: Tall +// Function arguments density + +trait Lorem { + fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet); + + fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet) { + // body + } + + fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: onsectetur, adipiscing: Adipiscing, elit: Elit); + + fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: onsectetur, adipiscing: Adipiscing, elit: Elit) { + // body + } +} diff --git a/tests/source/configs/fn_params_layout/vertical.rs b/tests/source/configs/fn_params_layout/vertical.rs new file mode 100644 index 000000000000..674968023f99 --- /dev/null +++ b/tests/source/configs/fn_params_layout/vertical.rs @@ -0,0 +1,16 @@ +// rustfmt-fn_params_layout: Vertical +// Function arguments density + +trait Lorem { + fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet); + + fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet) { + // body + } + + fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: onsectetur, adipiscing: Adipiscing, elit: Elit); + + fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: onsectetur, adipiscing: Adipiscing, elit: Elit) { + // body + } +} diff --git a/tests/source/configs/fn_single_line/false.rs b/tests/source/configs/fn_single_line/false.rs new file mode 100644 index 000000000000..3d092f0c0bf5 --- /dev/null +++ b/tests/source/configs/fn_single_line/false.rs @@ -0,0 +1,11 @@ +// rustfmt-fn_single_line: false +// Single-expression function on single line + +fn lorem() -> usize { + 42 +} + +fn lorem() -> usize { + let ipsum = 42; + ipsum +} diff --git a/tests/source/configs/fn_single_line/true.rs b/tests/source/configs/fn_single_line/true.rs new file mode 100644 index 000000000000..3cb0fdedf0b6 --- /dev/null +++ b/tests/source/configs/fn_single_line/true.rs @@ -0,0 +1,11 @@ +// rustfmt-fn_single_line: true +// Single-expression function on single line + +fn lorem() -> usize { + 42 +} + +fn lorem() -> usize { + let ipsum = 42; + ipsum +} diff --git a/tests/source/configs/force_explicit_abi/false.rs b/tests/source/configs/force_explicit_abi/false.rs new file mode 100644 index 000000000000..3c48f8e0c78d --- /dev/null +++ b/tests/source/configs/force_explicit_abi/false.rs @@ -0,0 +1,6 @@ +// rustfmt-force_explicit_abi: false +// Force explicit abi + +extern { + pub static lorem: c_int; +} diff --git a/tests/source/configs/force_explicit_abi/true.rs b/tests/source/configs/force_explicit_abi/true.rs new file mode 100644 index 000000000000..e5ff6cf7dd7e --- /dev/null +++ b/tests/source/configs/force_explicit_abi/true.rs @@ -0,0 +1,6 @@ +// rustfmt-force_explicit_abi: true +// Force explicit abi + +extern { + pub static lorem: c_int; +} diff --git a/tests/source/configs/force_multiline_block/false.rs b/tests/source/configs/force_multiline_block/false.rs new file mode 100644 index 000000000000..b97e348e5da1 --- /dev/null +++ b/tests/source/configs/force_multiline_block/false.rs @@ -0,0 +1,22 @@ +// rustfmt-force_multiline_blocks: false +// Option forces multiline match arm and closure bodies to be wrapped in a block + +fn main() { + match lorem { + Lorem::Ipsum => { + if ipsum { + println!("dolor"); + } + } + Lorem::Dolor => println!("amet"), + } +} + +fn main() { + result.and_then(|maybe_value| { + match maybe_value { + None => Err("oops"), + Some(value) => Ok(1), + } + }); +} diff --git a/tests/source/configs/force_multiline_block/true.rs b/tests/source/configs/force_multiline_block/true.rs new file mode 100644 index 000000000000..db9d3de46141 --- /dev/null +++ b/tests/source/configs/force_multiline_block/true.rs @@ -0,0 +1,18 @@ +// rustfmt-force_multiline_blocks: true +// Option forces multiline match arm and closure bodies to be wrapped in a block + +fn main() { + match lorem { + Lorem::Ipsum => if ipsum { + println!("dolor"); + }, + Lorem::Dolor => println!("amet"), + } +} + +fn main() { + result.and_then(|maybe_value| match maybe_value { + None => Err("oops"), + Some(value) => Ok(1), + }); +} diff --git a/tests/source/configs/format_generated_files/false.rs b/tests/source/configs/format_generated_files/false.rs new file mode 100644 index 000000000000..dec1e00d117b --- /dev/null +++ b/tests/source/configs/format_generated_files/false.rs @@ -0,0 +1,8 @@ +// @generated +// rustfmt-format_generated_files: false + +fn main() +{ + println!("hello, world") + ; +} diff --git a/tests/source/configs/format_generated_files/true.rs b/tests/source/configs/format_generated_files/true.rs new file mode 100644 index 000000000000..a25ddc25a6a4 --- /dev/null +++ b/tests/source/configs/format_generated_files/true.rs @@ -0,0 +1,8 @@ +// @generated +// rustfmt-format_generated_files: true + +fn main() +{ + println!("hello, world") + ; +} diff --git a/tests/source/configs/format_macro_bodies/false.rs b/tests/source/configs/format_macro_bodies/false.rs new file mode 100644 index 000000000000..d618a1ac3f9b --- /dev/null +++ b/tests/source/configs/format_macro_bodies/false.rs @@ -0,0 +1,7 @@ +// rustfmt-format_macro_bodies: false + +macro_rules! foo { + ($a: ident : $b: ty) => { $a(42): $b; }; + ($a: ident $b: ident $c: ident) => { $a=$b+$c; }; +} + diff --git a/tests/source/configs/format_macro_bodies/true.rs b/tests/source/configs/format_macro_bodies/true.rs new file mode 100644 index 000000000000..b254b82d7193 --- /dev/null +++ b/tests/source/configs/format_macro_bodies/true.rs @@ -0,0 +1,7 @@ +// rustfmt-format_macro_bodies: true + +macro_rules! foo { + ($a: ident : $b: ty) => { $a(42): $b; }; + ($a: ident $b: ident $c: ident) => { $a=$b+$c; }; +} + diff --git a/tests/source/configs/format_macro_matchers/false.rs b/tests/source/configs/format_macro_matchers/false.rs new file mode 100644 index 000000000000..a721bb55c23a --- /dev/null +++ b/tests/source/configs/format_macro_matchers/false.rs @@ -0,0 +1,7 @@ +// rustfmt-format_macro_matchers: false + +macro_rules! foo { + ($a: ident : $b: ty) => { $a(42): $b; }; + ($a: ident $b: ident $c: ident) => { $a=$b+$c; }; +} + diff --git a/tests/source/configs/format_macro_matchers/true.rs b/tests/source/configs/format_macro_matchers/true.rs new file mode 100644 index 000000000000..fa0442e228ac --- /dev/null +++ b/tests/source/configs/format_macro_matchers/true.rs @@ -0,0 +1,6 @@ +// rustfmt-format_macro_matchers: true + +macro_rules! foo { + ($a: ident : $b: ty) => { $a(42): $b; }; + ($a: ident $b: ident $c: ident) => { $a=$b+$c; }; +} diff --git a/tests/source/configs/format_strings/false.rs b/tests/source/configs/format_strings/false.rs new file mode 100644 index 000000000000..ecca0d7d1fca --- /dev/null +++ b/tests/source/configs/format_strings/false.rs @@ -0,0 +1,8 @@ +// rustfmt-format_strings: false +// rustfmt-max_width: 50 +// rustfmt-error_on_line_overflow: false +// Force format strings + +fn main() { + let lorem = "ipsum dolor sit amet consectetur adipiscing elit lorem ipsum dolor sit"; +} diff --git a/tests/source/configs/format_strings/true.rs b/tests/source/configs/format_strings/true.rs new file mode 100644 index 000000000000..337314478212 --- /dev/null +++ b/tests/source/configs/format_strings/true.rs @@ -0,0 +1,7 @@ +// rustfmt-format_strings: true +// rustfmt-max_width: 50 +// Force format strings + +fn main() { + let lorem = "ipsum dolor sit amet consectetur adipiscing elit lorem ipsum dolor sit"; +} diff --git a/tests/source/configs/group_imports/One-merge_imports.rs b/tests/source/configs/group_imports/One-merge_imports.rs new file mode 100644 index 000000000000..157d3857908a --- /dev/null +++ b/tests/source/configs/group_imports/One-merge_imports.rs @@ -0,0 +1,17 @@ +// rustfmt-group_imports: One +// rustfmt-imports_granularity: Crate +use chrono::Utc; +use super::update::convert_publish_payload; + +use juniper::{FieldError, FieldResult}; +use uuid::Uuid; +use alloc::alloc::Layout; + +use std::sync::Arc; +use alloc::vec::Vec; + +use broker::database::PooledConnection; + +use super::schema::{Context, Payload}; +use core::f32; +use crate::models::Event; diff --git a/tests/source/configs/group_imports/One-nested.rs b/tests/source/configs/group_imports/One-nested.rs new file mode 100644 index 000000000000..109bd07e1ee2 --- /dev/null +++ b/tests/source/configs/group_imports/One-nested.rs @@ -0,0 +1,7 @@ +// rustfmt-group_imports: One +mod test { + use crate::foo::bar; + + use std::path; + use crate::foo::bar2; +} diff --git a/tests/source/configs/group_imports/One-no_reorder.rs b/tests/source/configs/group_imports/One-no_reorder.rs new file mode 100644 index 000000000000..f82f62c7f5b2 --- /dev/null +++ b/tests/source/configs/group_imports/One-no_reorder.rs @@ -0,0 +1,16 @@ +// rustfmt-group_imports: One +// rustfmt-reorder_imports: false +use chrono::Utc; +use super::update::convert_publish_payload; + +use juniper::{FieldError, FieldResult}; +use uuid::Uuid; +use alloc::alloc::Layout; + +use std::sync::Arc; + +use broker::database::PooledConnection; + +use super::schema::{Context, Payload}; +use core::f32; +use crate::models::Event; diff --git a/tests/source/configs/group_imports/One.rs b/tests/source/configs/group_imports/One.rs new file mode 100644 index 000000000000..5ab7a950805b --- /dev/null +++ b/tests/source/configs/group_imports/One.rs @@ -0,0 +1,15 @@ +// rustfmt-group_imports: One +use chrono::Utc; +use super::update::convert_publish_payload; + +use juniper::{FieldError, FieldResult}; +use uuid::Uuid; +use alloc::alloc::Layout; + +use std::sync::Arc; + +use broker::database::PooledConnection; + +use super::schema::{Context, Payload}; +use core::f32; +use crate::models::Event; diff --git a/tests/source/configs/group_imports/StdExternalCrate-merge_imports.rs b/tests/source/configs/group_imports/StdExternalCrate-merge_imports.rs new file mode 100644 index 000000000000..ea7f6280a64a --- /dev/null +++ b/tests/source/configs/group_imports/StdExternalCrate-merge_imports.rs @@ -0,0 +1,17 @@ +// rustfmt-group_imports: StdExternalCrate +// rustfmt-imports_granularity: Crate +use chrono::Utc; +use super::update::convert_publish_payload; + +use juniper::{FieldError, FieldResult}; +use uuid::Uuid; +use alloc::alloc::Layout; + +use std::sync::Arc; +use alloc::vec::Vec; + +use broker::database::PooledConnection; + +use super::schema::{Context, Payload}; +use core::f32; +use crate::models::Event; diff --git a/tests/source/configs/group_imports/StdExternalCrate-nested.rs b/tests/source/configs/group_imports/StdExternalCrate-nested.rs new file mode 100644 index 000000000000..08f4e07b704e --- /dev/null +++ b/tests/source/configs/group_imports/StdExternalCrate-nested.rs @@ -0,0 +1,6 @@ +// rustfmt-group_imports: StdExternalCrate +mod test { + use crate::foo::bar; + use std::path; + use crate::foo::bar2; +} diff --git a/tests/source/configs/group_imports/StdExternalCrate-no_reorder.rs b/tests/source/configs/group_imports/StdExternalCrate-no_reorder.rs new file mode 100644 index 000000000000..08c9a72ae61e --- /dev/null +++ b/tests/source/configs/group_imports/StdExternalCrate-no_reorder.rs @@ -0,0 +1,17 @@ +// rustfmt-group_imports: StdExternalCrate +// rustfmt-reorder_imports: false + +use chrono::Utc; +use super::update::convert_publish_payload; + +use juniper::{FieldError, FieldResult}; +use uuid::Uuid; +use alloc::alloc::Layout; + +use std::sync::Arc; + +use broker::database::PooledConnection; + +use super::schema::{Context, Payload}; +use core::f32; +use crate::models::Event; diff --git a/tests/source/configs/group_imports/StdExternalCrate-non_consecutive.rs b/tests/source/configs/group_imports/StdExternalCrate-non_consecutive.rs new file mode 100644 index 000000000000..f239a0efa085 --- /dev/null +++ b/tests/source/configs/group_imports/StdExternalCrate-non_consecutive.rs @@ -0,0 +1,27 @@ +// rustfmt-group_imports: StdExternalCrate +use chrono::Utc; +use super::update::convert_publish_payload; + + + + + +use juniper::{FieldError, FieldResult}; + +use uuid::Uuid; +use alloc::alloc::Layout; + +extern crate uuid; + + + + + +use std::sync::Arc; + + +use broker::database::PooledConnection; + +use super::schema::{Context, Payload}; +use core::f32; +use crate::models::Event; diff --git a/tests/source/configs/group_imports/StdExternalCrate.rs b/tests/source/configs/group_imports/StdExternalCrate.rs new file mode 100644 index 000000000000..d49c8941e6d5 --- /dev/null +++ b/tests/source/configs/group_imports/StdExternalCrate.rs @@ -0,0 +1,15 @@ +// rustfmt-group_imports: StdExternalCrate +use chrono::Utc; +use super::update::convert_publish_payload; + +use juniper::{FieldError, FieldResult}; +use uuid::Uuid; +use alloc::alloc::Layout; + +use std::sync::Arc; + +use broker::database::PooledConnection; + +use super::schema::{Context, Payload}; +use core::f32; +use crate::models::Event; diff --git a/tests/source/configs/hard_tabs/false.rs b/tests/source/configs/hard_tabs/false.rs new file mode 100644 index 000000000000..bf92162b4240 --- /dev/null +++ b/tests/source/configs/hard_tabs/false.rs @@ -0,0 +1,6 @@ +// rustfmt-hard_tabs: false +// Hard tabs + +fn lorem() -> usize { +42 // spaces before 42 +} diff --git a/tests/source/configs/hard_tabs/true.rs b/tests/source/configs/hard_tabs/true.rs new file mode 100644 index 000000000000..738922a4dfb0 --- /dev/null +++ b/tests/source/configs/hard_tabs/true.rs @@ -0,0 +1,6 @@ +// rustfmt-hard_tabs: true +// Hard tabs + +fn lorem() -> usize { +42 // spaces before 42 +} diff --git a/tests/source/configs/imports_layout/merge_mixed.rs b/tests/source/configs/imports_layout/merge_mixed.rs new file mode 100644 index 000000000000..477c4aa1684b --- /dev/null +++ b/tests/source/configs/imports_layout/merge_mixed.rs @@ -0,0 +1,6 @@ +// rustfmt-imports_indent: Block +// rustfmt-imports_granularity: Crate +// rustfmt-imports_layout: Mixed + +use std::{fmt, io, str}; +use std::str::FromStr; diff --git a/tests/source/configs/indent_style/block_args.rs b/tests/source/configs/indent_style/block_args.rs new file mode 100644 index 000000000000..4d2d280a16bc --- /dev/null +++ b/tests/source/configs/indent_style/block_args.rs @@ -0,0 +1,26 @@ +// rustfmt-indent_style: Block +// Function arguments layout + +fn lorem() {} + +fn lorem(ipsum: usize) {} + +fn lorem(ipsum: usize, dolor: usize, sit: usize, amet: usize, consectetur: usize, adipiscing: usize, elit: usize) { + // body +} + +// #1441 +extern "system" { + pub fn GetConsoleHistoryInfo(console_history_info: *mut ConsoleHistoryInfo) -> Boooooooooooooool; +} + +// rustfmt should not add trailing comma for variadic function. See #1623. +extern "C" { + pub fn variadic_fn(first_parameter: FirstParameterType, + second_parameter: SecondParameterType, + ...); +} + +// #1652 +fn deconstruct(foo: Bar) -> (SocketAddr, Header, Method, RequestUri, HttpVersion, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) { +} diff --git a/tests/source/configs/indent_style/block_array.rs b/tests/source/configs/indent_style/block_array.rs new file mode 100644 index 000000000000..8404f65f471c --- /dev/null +++ b/tests/source/configs/indent_style/block_array.rs @@ -0,0 +1,6 @@ +// rustfmt-indent_style: Block +// Array layout + +fn main() { + let lorem = vec!["ipsum","dolor","sit","amet","consectetur","adipiscing","elit"]; +} diff --git a/tests/source/configs/indent_style/block_call.rs b/tests/source/configs/indent_style/block_call.rs new file mode 100644 index 000000000000..c82b6b8e38f8 --- /dev/null +++ b/tests/source/configs/indent_style/block_call.rs @@ -0,0 +1,133 @@ +// rustfmt-indent_style: Block +// Function call style + +fn main() { + lorem("lorem", "ipsum", "dolor", "sit", "amet", "consectetur", "adipiscing", "elit"); + // #1501 + let hyper = Arc::new(Client::with_connector(HttpsConnector::new(TlsClient::new()))); + + // chain + let x = yooooooooooooo.fooooooooooooooo.baaaaaaaaaaaaar(hello, world); + + // #1380 + { + { + let creds = self.client + .client_credentials(&self.config.auth.oauth2.id, &self.config.auth.oauth2.secret)?; + } + } + + // nesting macro and function call + try!(foo(xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx)); + try!(foo(try!(xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx))); +} + +// #1521 +impl Foo { + fn map_pixel_to_coords(&self, point: &Vector2i, view: &View) -> Vector2f { + unsafe { + Vector2f::from_raw(ffi::sfRenderTexture_mapPixelToCoords(self.render_texture, point.raw(), view.raw())) + } + } +} + +fn issue1420() { + given( + r#" + # Getting started + ... + "#, + ) + .running(waltz) +} + +// #1563 +fn query(conn: &Connection) -> Result<()> { + conn.query_row( + r#" + SELECT title, date + FROM posts, + WHERE DATE(date) = $1 + "#, + &[], + |row| { + Post { + title: row.get(0), + date: row.get(1), + } + }, + )?; + + Ok(()) +} + +// #1449 +fn future_rayon_wait_1_thread() { + // run with only 1 worker thread; this would deadlock if we couldn't make progress + let mut result = None; + ThreadPool::new(Configuration::new().num_threads(1)) + .unwrap() + .install( + || { + scope( + |s| { + use std::sync::mpsc::channel; + let (tx, rx) = channel(); + let a = s.spawn_future(lazy(move || Ok::(rx.recv().unwrap()))); + // ^^^^ FIXME: why is this needed? + let b = s.spawn_future(a.map(|v| v + 1)); + let c = s.spawn_future(b.map(|v| v + 1)); + s.spawn(move |_| tx.send(20).unwrap()); + result = Some(c.rayon_wait().unwrap()); + }, + ); + }, + ); + assert_eq!(result, Some(22)); +} + +// #1494 +impl Cursor { + fn foo() { + self.cur_type() + .num_template_args() + .or_else(|| { + let n: c_int = unsafe { clang_Cursor_getNumTemplateArguments(self.x) }; + + if n >= 0 { + Some(n as u32) + } else { + debug_assert_eq!(n, -1); + None + } + }) + .or_else(|| { + let canonical = self.canonical(); + if canonical != *self { + canonical.num_template_args() + } else { + None + } + }); + } +} + +fn issue1581() { + bootstrap.checks.register( + "PERSISTED_LOCATIONS", + move || if locations2.0.inner_mut.lock().poisoned { + Check::new( + State::Error, + "Persisted location storage is poisoned due to a write failure", + ) + } else { + Check::new(State::Healthy, "Persisted location storage is healthy") + }, + ); +} + +fn issue1651() { + { + let type_list: Vec<_> = try_opt!(types.iter().map(|ty| ty.rewrite(context, shape)).collect()); + } +} diff --git a/tests/source/configs/indent_style/block_chain.rs b/tests/source/configs/indent_style/block_chain.rs new file mode 100644 index 000000000000..41d91469114c --- /dev/null +++ b/tests/source/configs/indent_style/block_chain.rs @@ -0,0 +1,6 @@ +// rustfmt-indent_style: Block +// Chain indent + +fn main() { + let lorem = ipsum.dolor().sit().amet().consectetur().adipiscing().elite(); +} diff --git a/tests/source/configs/indent_style/block_generic.rs b/tests/source/configs/indent_style/block_generic.rs new file mode 100644 index 000000000000..2cf17be56eff --- /dev/null +++ b/tests/source/configs/indent_style/block_generic.rs @@ -0,0 +1,6 @@ +// rustfmt-indent_style: Block +// Generics indent + +fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, adipiscing: Adipiscing, consectetur: Consectetur, elit: Elit) -> T { + // body +} diff --git a/tests/source/configs/indent_style/block_struct_lit.rs b/tests/source/configs/indent_style/block_struct_lit.rs new file mode 100644 index 000000000000..47a6994f40ca --- /dev/null +++ b/tests/source/configs/indent_style/block_struct_lit.rs @@ -0,0 +1,6 @@ +// rustfmt-indent_style: Block +// Struct literal-style + +fn main() { + let lorem = Lorem { ipsum: dolor, sit: amet }; +} diff --git a/tests/source/configs/indent_style/block_trailing_comma_call/one.rs b/tests/source/configs/indent_style/block_trailing_comma_call/one.rs new file mode 100644 index 000000000000..6d48ea742fc7 --- /dev/null +++ b/tests/source/configs/indent_style/block_trailing_comma_call/one.rs @@ -0,0 +1,9 @@ +// rustfmt-version: One +// rustfmt-error_on_line_overflow: false +// rustfmt-indent_style: Block + +// rustfmt should not add trailing comma when rewriting macro. See #1528. +fn a() { + panic!("this is a long string that goes past the maximum line length causing rustfmt to insert a comma here:"); + foo(a, oooptoptoptoptptooptoptoptoptptooptoptoptoptptoptoptoptoptpt()); +} diff --git a/tests/source/configs/indent_style/block_trailing_comma_call/two.rs b/tests/source/configs/indent_style/block_trailing_comma_call/two.rs new file mode 100644 index 000000000000..7a62d722c6e0 --- /dev/null +++ b/tests/source/configs/indent_style/block_trailing_comma_call/two.rs @@ -0,0 +1,9 @@ +// rustfmt-version: Two +// rustfmt-error_on_line_overflow: false +// rustfmt-indent_style: Block + +// rustfmt should not add trailing comma when rewriting macro. See #1528. +fn a() { + panic!("this is a long string that goes past the maximum line length causing rustfmt to insert a comma here:"); + foo(a, oooptoptoptoptptooptoptoptoptptooptoptoptoptptoptoptoptoptpt()); +} diff --git a/tests/source/configs/indent_style/block_where_pred.rs b/tests/source/configs/indent_style/block_where_pred.rs new file mode 100644 index 000000000000..450491f02705 --- /dev/null +++ b/tests/source/configs/indent_style/block_where_pred.rs @@ -0,0 +1,6 @@ +// rustfmt-indent_style: Block +// Where predicate indent + +fn lorem() -> T where Ipsum: Eq, Dolor: Eq, Sit: Eq, Amet: Eq { + // body +} diff --git a/tests/source/configs/indent_style/default.rs b/tests/source/configs/indent_style/default.rs new file mode 100644 index 000000000000..f08f5c64460a --- /dev/null +++ b/tests/source/configs/indent_style/default.rs @@ -0,0 +1,6 @@ +// rustfmt-indent_style: Visual +// Where style + +fn lorem() -> T where Ipsum: Eq, Dolor: Eq, Sit: Eq, Amet: Eq { + // body +} diff --git a/tests/source/configs/indent_style/rfc_where.rs b/tests/source/configs/indent_style/rfc_where.rs new file mode 100644 index 000000000000..012840be28dc --- /dev/null +++ b/tests/source/configs/indent_style/rfc_where.rs @@ -0,0 +1,6 @@ +// rustfmt-indent_style: Block +// Where style + +fn lorem() -> T where Ipsum: Eq, Dolor: Eq, Sit: Eq, Amet: Eq { + // body +} diff --git a/tests/source/configs/indent_style/visual_args.rs b/tests/source/configs/indent_style/visual_args.rs new file mode 100644 index 000000000000..5aa28a62b9ce --- /dev/null +++ b/tests/source/configs/indent_style/visual_args.rs @@ -0,0 +1,32 @@ +// rustfmt-indent_style: Visual +// Function arguments layout + +fn lorem() {} + +fn lorem(ipsum: usize) {} + +fn lorem(ipsum: usize, dolor: usize, sit: usize, amet: usize, consectetur: usize, adipiscing: usize, elit: usize) { + // body +} + +// #1922 +extern "C" { + pub fn LAPACKE_csytrs_rook_work(matrix_layout: c_int, + uplo: c_char, + n: lapack_int, + nrhs: lapack_int, + a: *const lapack_complex_float, + lda: lapack_int, ipiv: *const lapack_int, + b: *mut lapack_complex_float, + ldb: lapack_int + )-> lapack_int; + + pub fn LAPACKE_csytrs_rook_work(matrix_layout: c_int, + uplo: c_char, + n: lapack_int, + nrhs: lapack_int, + lda: lapack_int, ipiv: *const lapack_int, + b: *mut lapack_complex_float, + ldb: lapack_int + ) -> lapack_int; +} diff --git a/tests/source/configs/indent_style/visual_array.rs b/tests/source/configs/indent_style/visual_array.rs new file mode 100644 index 000000000000..05bbf00b1d27 --- /dev/null +++ b/tests/source/configs/indent_style/visual_array.rs @@ -0,0 +1,6 @@ +// rustfmt-indent_style: Visual +// Array layout + +fn main() { + let lorem = vec!["ipsum","dolor","sit","amet","consectetur","adipiscing","elit"]; +} diff --git a/tests/source/configs/indent_style/visual_call.rs b/tests/source/configs/indent_style/visual_call.rs new file mode 100644 index 000000000000..9a679d6bb4c9 --- /dev/null +++ b/tests/source/configs/indent_style/visual_call.rs @@ -0,0 +1,6 @@ +// rustfmt-indent_style: Visual +// Function call style + +fn main() { + lorem("lorem", "ipsum", "dolor", "sit", "amet", "consectetur", "adipiscing", "elit"); +} diff --git a/tests/source/configs/indent_style/visual_chain.rs b/tests/source/configs/indent_style/visual_chain.rs new file mode 100644 index 000000000000..b74948753980 --- /dev/null +++ b/tests/source/configs/indent_style/visual_chain.rs @@ -0,0 +1,6 @@ +// rustfmt-indent_style: Visual +// Chain indent + +fn main() { + let lorem = ipsum.dolor().sit().amet().consectetur().adipiscing().elite(); +} diff --git a/tests/source/configs/indent_style/visual_generics.rs b/tests/source/configs/indent_style/visual_generics.rs new file mode 100644 index 000000000000..1f910d32d87a --- /dev/null +++ b/tests/source/configs/indent_style/visual_generics.rs @@ -0,0 +1,6 @@ +// rustfmt-indent_style: Visual +// Generics indent + +fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, adipiscing: Adipiscing, consectetur: Consectetur, elit: Elit) -> T { + // body +} diff --git a/tests/source/configs/indent_style/visual_struct_lit.rs b/tests/source/configs/indent_style/visual_struct_lit.rs new file mode 100644 index 000000000000..45538e70482d --- /dev/null +++ b/tests/source/configs/indent_style/visual_struct_lit.rs @@ -0,0 +1,6 @@ +// rustfmt-indent_style: Visual +// Struct literal-style + +fn main() { + let lorem = Lorem { ipsum: dolor, sit: amet }; +} diff --git a/tests/source/configs/indent_style/visual_trailing_comma.rs b/tests/source/configs/indent_style/visual_trailing_comma.rs new file mode 100644 index 000000000000..9738d397dbf6 --- /dev/null +++ b/tests/source/configs/indent_style/visual_trailing_comma.rs @@ -0,0 +1,7 @@ +// rustfmt-error_on_line_overflow: false +// rustfmt-indent_style: Visual + +// rustfmt should not add trailing comma when rewriting macro. See #1528. +fn a() { + panic!("this is a long string that goes past the maximum line length causing rustfmt to insert a comma here:"); +} diff --git a/tests/source/configs/indent_style/visual_where_pred.rs b/tests/source/configs/indent_style/visual_where_pred.rs new file mode 100644 index 000000000000..055806b68629 --- /dev/null +++ b/tests/source/configs/indent_style/visual_where_pred.rs @@ -0,0 +1,6 @@ +// rustfmt-indent_style: Visual +// Where predicate indent + +fn lorem() -> T where Ipsum: Eq, Dolor: Eq, Sit: Eq, Amet: Eq { + // body +} diff --git a/tests/source/configs/match_arm_blocks/false.rs b/tests/source/configs/match_arm_blocks/false.rs new file mode 100644 index 000000000000..53e37e13c4f2 --- /dev/null +++ b/tests/source/configs/match_arm_blocks/false.rs @@ -0,0 +1,11 @@ +// rustfmt-match_arm_blocks: false +// Wrap match-arms + +fn main() { + match lorem { + true => foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo(x), + false => { + println!("{}", sit) + } + } +} diff --git a/tests/source/configs/match_arm_blocks/true.rs b/tests/source/configs/match_arm_blocks/true.rs new file mode 100644 index 000000000000..a452b13cd27d --- /dev/null +++ b/tests/source/configs/match_arm_blocks/true.rs @@ -0,0 +1,11 @@ +// rustfmt-match_arm_blocks: true +// Wrap match-arms + +fn main() { + match lorem { + true => foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo(x), + false => { + println!("{}", sit) + } + } +} diff --git a/tests/source/configs/match_arm_leading_pipes/always.rs b/tests/source/configs/match_arm_leading_pipes/always.rs new file mode 100644 index 000000000000..162d812d8cff --- /dev/null +++ b/tests/source/configs/match_arm_leading_pipes/always.rs @@ -0,0 +1,27 @@ +// rustfmt-match_arm_leading_pipes: Always + +fn foo() { + match foo { + "foo" | "bar" => {} + "baz" + | "something relatively long" + | "something really really really realllllllllllllly long" => println!("x"), + "qux" => println!("y"), + _ => {} + } +} + +fn issue_3973() { + match foo { + "foo" | "bar" => {} + _ => {} + } +} + +fn bar() { + match baz { + "qux" => {} + "foo" | "bar" => {} + _ => {} + } +} diff --git a/tests/source/configs/match_arm_leading_pipes/never.rs b/tests/source/configs/match_arm_leading_pipes/never.rs new file mode 100644 index 000000000000..8a68fe21407e --- /dev/null +++ b/tests/source/configs/match_arm_leading_pipes/never.rs @@ -0,0 +1,28 @@ +// rustfmt-match_arm_leading_pipes: Never + +fn foo() { + match foo { + | "foo" | "bar" => {} + | "baz" + | "something relatively long" + | "something really really really realllllllllllllly long" => println!("x"), + | "qux" => println!("y"), + _ => {} + } +} + +fn issue_3973() { + match foo { + | "foo" + | "bar" => {} + _ => {} + } +} + +fn bar() { + match baz { + "qux" => {} + "foo" | "bar" => {} + _ => {} + } +} diff --git a/tests/source/configs/match_arm_leading_pipes/preserve.rs b/tests/source/configs/match_arm_leading_pipes/preserve.rs new file mode 100644 index 000000000000..5486877bde19 --- /dev/null +++ b/tests/source/configs/match_arm_leading_pipes/preserve.rs @@ -0,0 +1,36 @@ +// rustfmt-match_arm_leading_pipes: Preserve + +fn foo() { + match foo { + | "foo" | "bar" => {} + | "baz" + | "something relatively long" + | "something really really really realllllllllllllly long" => println!("x"), + | "qux" => println!("y"), + _ => {} + } +} + +fn issue_3973() { + match foo { + | "foo" + | "bar" => {} + _ => {} + } +} + +fn bar() { + match baz { + "qux" => { } + "foo" | "bar" => {} + _ => {} + } +} + +fn f(x: NonAscii) -> bool { + match x { + // foo + | Éfgh => true, + _ => false, + } +} \ No newline at end of file diff --git a/tests/source/configs/match_block_trailing_comma/false.rs b/tests/source/configs/match_block_trailing_comma/false.rs new file mode 100644 index 000000000000..70e02955fb01 --- /dev/null +++ b/tests/source/configs/match_block_trailing_comma/false.rs @@ -0,0 +1,11 @@ +// rustfmt-match_block_trailing_comma: false +// Match block trailing comma + +fn main() { + match lorem { + Lorem::Ipsum => { + println!("ipsum"); + } + Lorem::Dolor => println!("dolor"), + } +} diff --git a/tests/source/configs/match_block_trailing_comma/true.rs b/tests/source/configs/match_block_trailing_comma/true.rs new file mode 100644 index 000000000000..b9af3d47202f --- /dev/null +++ b/tests/source/configs/match_block_trailing_comma/true.rs @@ -0,0 +1,11 @@ +// rustfmt-match_block_trailing_comma: true +// Match block trailing comma + +fn main() { + match lorem { + Lorem::Ipsum => { + println!("ipsum"); + } + Lorem::Dolor => println!("dolor"), + } +} diff --git a/tests/source/configs/merge_derives/true.rs b/tests/source/configs/merge_derives/true.rs new file mode 100644 index 000000000000..18b8443f0d7b --- /dev/null +++ b/tests/source/configs/merge_derives/true.rs @@ -0,0 +1,46 @@ +// rustfmt-merge_derives: true +// Merge multiple derives to a single one. + +#[bar] +#[derive(Eq, PartialEq)] +#[foo] +#[derive(Debug)] +#[foobar] +#[derive(Copy, Clone)] +pub enum Foo {} + +#[derive(Eq, PartialEq)] +#[derive(Debug)] +#[foobar] +#[derive(Copy, Clone)] +pub enum Bar {} + +#[derive(Eq, PartialEq)] +#[derive(Debug)] +#[derive(Copy, Clone)] +pub enum FooBar {} + +mod foo { +#[bar] +#[derive(Eq, PartialEq)] +#[foo] +#[derive(Debug)] +#[foobar] +#[derive(Copy, Clone)] +pub enum Foo {} +} + +mod bar { +#[derive(Eq, PartialEq)] +#[derive(Debug)] +#[foobar] +#[derive(Copy, Clone)] +pub enum Bar {} +} + +mod foobar { +#[derive(Eq, PartialEq)] +#[derive(Debug)] +#[derive(Copy, Clone)] +pub enum FooBar {} +} diff --git a/tests/source/configs/normalize_comments/false.rs b/tests/source/configs/normalize_comments/false.rs new file mode 100644 index 000000000000..488962ed9362 --- /dev/null +++ b/tests/source/configs/normalize_comments/false.rs @@ -0,0 +1,13 @@ +// rustfmt-normalize_comments: false +// Normalize comments + +// Lorem ipsum: +fn dolor() -> usize {} + +/* sit amet: */ +fn adipiscing() -> usize {} + +// #652 +//////////////////////////////////////////////////////////////////////////////// +// Basic slice extension methods +//////////////////////////////////////////////////////////////////////////////// diff --git a/tests/source/configs/normalize_comments/true.rs b/tests/source/configs/normalize_comments/true.rs new file mode 100644 index 000000000000..c74a9808e61d --- /dev/null +++ b/tests/source/configs/normalize_comments/true.rs @@ -0,0 +1,13 @@ +// rustfmt-normalize_comments: true +// Normalize comments + +// Lorem ipsum: +fn dolor() -> usize {} + +/* sit amet: */ +fn adipiscing() -> usize {} + +// #652 +//////////////////////////////////////////////////////////////////////////////// +// Basic slice extension methods +//////////////////////////////////////////////////////////////////////////////// diff --git a/tests/source/configs/normalize_doc_attributes/false.rs b/tests/source/configs/normalize_doc_attributes/false.rs new file mode 100644 index 000000000000..f8eb64273c3d --- /dev/null +++ b/tests/source/configs/normalize_doc_attributes/false.rs @@ -0,0 +1,13 @@ +// rustfmt-normalize_doc_attributes: false +// Normalize doc attributes + +#![doc = " Example documentation"] + +#[doc = " Example item documentation"] +pub enum Foo {} + +#[doc = " Lots of space"] +pub enum Bar {} + +#[doc = "no leading space"] +pub mod FooBar {} diff --git a/tests/source/configs/normalize_doc_attributes/true.rs b/tests/source/configs/normalize_doc_attributes/true.rs new file mode 100644 index 000000000000..894c00a4dc05 --- /dev/null +++ b/tests/source/configs/normalize_doc_attributes/true.rs @@ -0,0 +1,13 @@ +// rustfmt-normalize_doc_attributes: true +// Normalize doc attributes + +#![doc = " Example documentation"] + +#[doc = " Example item documentation"] +pub enum Foo {} + +#[doc = " Lots of space"] +pub enum Bar {} + +#[doc = "no leading space"] +pub mod FooBar {} diff --git a/tests/source/configs/remove_nested_parens/remove_nested_parens.rs b/tests/source/configs/remove_nested_parens/remove_nested_parens.rs new file mode 100644 index 000000000000..87aed09c14a0 --- /dev/null +++ b/tests/source/configs/remove_nested_parens/remove_nested_parens.rs @@ -0,0 +1,5 @@ +// rustfmt-remove_nested_parens: true + +fn main() { + ((((((foo())))))); +} diff --git a/tests/source/configs/reorder_impl_items/false.rs b/tests/source/configs/reorder_impl_items/false.rs new file mode 100644 index 000000000000..beb99f0fb8e6 --- /dev/null +++ b/tests/source/configs/reorder_impl_items/false.rs @@ -0,0 +1,11 @@ +// rustfmt-reorder_impl_items: false + +struct Dummy; + +impl Iterator for Dummy { + fn next(&mut self) -> Option { + None + } + + type Item = i32; +} diff --git a/tests/source/configs/reorder_impl_items/true.rs b/tests/source/configs/reorder_impl_items/true.rs new file mode 100644 index 000000000000..612b1c84abdf --- /dev/null +++ b/tests/source/configs/reorder_impl_items/true.rs @@ -0,0 +1,11 @@ +// rustfmt-reorder_impl_items: true + +struct Dummy; + +impl Iterator for Dummy { + fn next(&mut self) -> Option { + None + } + + type Item = i32; +} diff --git a/tests/source/configs/reorder_imports/false.rs b/tests/source/configs/reorder_imports/false.rs new file mode 100644 index 000000000000..4b85684dc013 --- /dev/null +++ b/tests/source/configs/reorder_imports/false.rs @@ -0,0 +1,7 @@ +// rustfmt-reorder_imports: false +// Reorder imports + +use lorem; +use ipsum; +use dolor; +use sit; diff --git a/tests/source/configs/reorder_imports/true.rs b/tests/source/configs/reorder_imports/true.rs new file mode 100644 index 000000000000..2a40f6d069f9 --- /dev/null +++ b/tests/source/configs/reorder_imports/true.rs @@ -0,0 +1,19 @@ +// rustfmt-reorder_imports: true +// Reorder imports + +use lorem; +use ipsum; +use dolor; +use sit; + +fn foo() { + use C; + use B; + use A; + + bar(); + + use F; + use E; + use D; +} diff --git a/tests/source/configs/reorder_modules/dolor/mod.rs b/tests/source/configs/reorder_modules/dolor/mod.rs new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/tests/source/configs/reorder_modules/dolor/mod.rs @@ -0,0 +1 @@ + diff --git a/tests/source/configs/reorder_modules/false.rs b/tests/source/configs/reorder_modules/false.rs new file mode 100644 index 000000000000..56b1aa03ed79 --- /dev/null +++ b/tests/source/configs/reorder_modules/false.rs @@ -0,0 +1,7 @@ +// rustfmt-reorder_modules: false +// Reorder modules + +mod lorem; +mod ipsum; +mod dolor; +mod sit; diff --git a/tests/source/configs/reorder_modules/ipsum/mod.rs b/tests/source/configs/reorder_modules/ipsum/mod.rs new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/tests/source/configs/reorder_modules/ipsum/mod.rs @@ -0,0 +1 @@ + diff --git a/tests/source/configs/reorder_modules/lorem/mod.rs b/tests/source/configs/reorder_modules/lorem/mod.rs new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/tests/source/configs/reorder_modules/lorem/mod.rs @@ -0,0 +1 @@ + diff --git a/tests/source/configs/reorder_modules/sit/mod.rs b/tests/source/configs/reorder_modules/sit/mod.rs new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/tests/source/configs/reorder_modules/sit/mod.rs @@ -0,0 +1 @@ + diff --git a/tests/source/configs/reorder_modules/true.rs b/tests/source/configs/reorder_modules/true.rs new file mode 100644 index 000000000000..79b0ab1e3554 --- /dev/null +++ b/tests/source/configs/reorder_modules/true.rs @@ -0,0 +1,7 @@ +// rustfmt-reorder_modules: true +// Reorder modules + +mod lorem; +mod ipsum; +mod dolor; +mod sit; diff --git a/tests/source/configs/short_array_element_width_threshold/10.rs b/tests/source/configs/short_array_element_width_threshold/10.rs new file mode 100644 index 000000000000..7d0d70919a60 --- /dev/null +++ b/tests/source/configs/short_array_element_width_threshold/10.rs @@ -0,0 +1,11 @@ +// rustfmt-short_array_element_width_threshold: 10 + +fn main() { + pub const FORMAT_TEST: [u64; 5] = [ + 0x0000000000000000, + 0xaaaaaaaaaaaaaaaa, + 0xbbbbbbbbbbbbbbbb, + 0xcccccccccccccccc, + 0xdddddddddddddddd, + ]; +} \ No newline at end of file diff --git a/tests/source/configs/short_array_element_width_threshold/20.rs b/tests/source/configs/short_array_element_width_threshold/20.rs new file mode 100644 index 000000000000..8a93a51d6a28 --- /dev/null +++ b/tests/source/configs/short_array_element_width_threshold/20.rs @@ -0,0 +1,11 @@ +// rustfmt-short_array_element_width_threshold: 20 + +fn main() { + pub const FORMAT_TEST: [u64; 5] = [ + 0x0000000000000000, + 0xaaaaaaaaaaaaaaaa, + 0xbbbbbbbbbbbbbbbb, + 0xcccccccccccccccc, + 0xdddddddddddddddd, + ]; +} \ No newline at end of file diff --git a/tests/source/configs/short_array_element_width_threshold/greater_than_max_width.rs b/tests/source/configs/short_array_element_width_threshold/greater_than_max_width.rs new file mode 100644 index 000000000000..710b6fe7c4ba --- /dev/null +++ b/tests/source/configs/short_array_element_width_threshold/greater_than_max_width.rs @@ -0,0 +1,12 @@ +// rustfmt-max_width: 20 +// rustfmt-short_array_element_width_threshold: 30 + +fn main() { + pub const FORMAT_TEST: [u64; 5] = [ + 0x0000000000000000, + 0xaaaaaaaaaaaaaaaa, + 0xbbbbbbbbbbbbbbbb, + 0xcccccccccccccccc, + 0xdddddddddddddddd, + ]; +} diff --git a/tests/source/configs/single_line_let_else_max_width/100.rs b/tests/source/configs/single_line_let_else_max_width/100.rs new file mode 100644 index 000000000000..a73c9084bf2c --- /dev/null +++ b/tests/source/configs/single_line_let_else_max_width/100.rs @@ -0,0 +1,40 @@ +// rustfmt-single_line_let_else_max_width: 100 + +fn main() { + let Some(a) = opt else {}; + + let Some(b) = opt else { return }; + + let Some(c) = opt else { + return + }; + + let Some(c) = opt else { + // a comment should always force the block to be multi-lined + return + }; + + let Some(c) = opt else { /* a comment should always force the block to be multi-lined */ return }; + + let Some(d) = some_very_very_very_very_long_name else { return }; + + let Expr::Slice(ast::ExprSlice { lower, upper, step, range: _ }) = slice.as_ref() else { + return + }; + + let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true)? else { + return Ok(None) + }; + + let Some(doc_attr) = variant.attrs.iter().find(|attr| attr.path().is_ident("doc")) else { + return Err(Error::new(variant.span(), r#"expected a doc comment"#)) + }; + + let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true) else { + return Ok(None) + }; + + let Stmt::Expr(Expr::Call(ExprCall { args: some_args, .. }), _) = last_stmt else { + return Err(Error::new(last_stmt.span(), "expected last expression to be `Some(match (..) { .. })`")) + }; +} diff --git a/tests/source/configs/single_line_let_else_max_width/50.rs b/tests/source/configs/single_line_let_else_max_width/50.rs new file mode 100644 index 000000000000..87d0583c5520 --- /dev/null +++ b/tests/source/configs/single_line_let_else_max_width/50.rs @@ -0,0 +1,40 @@ +// rustfmt-single_line_let_else_max_width: 50 + +fn main() { + let Some(a) = opt else {}; + + let Some(b) = opt else { return }; + + let Some(c) = opt else { + return + }; + + let Some(c) = opt else { + // a comment should always force the block to be multi-lined + return + }; + + let Some(c) = opt else { /* a comment should always force the block to be multi-lined */ return }; + + let Some(d) = some_very_very_very_very_long_name else { return }; + + let Expr::Slice(ast::ExprSlice { lower, upper, step, range: _ }) = slice.as_ref() else { + return + }; + + let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true)? else { + return Ok(None) + }; + + let Some(doc_attr) = variant.attrs.iter().find(|attr| attr.path().is_ident("doc")) else { + return Err(Error::new(variant.span(), r#"expected a doc comment"#)) + }; + + let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true) else { + return Ok(None) + }; + + let Stmt::Expr(Expr::Call(ExprCall { args: some_args, .. }), _) = last_stmt else { + return Err(Error::new(last_stmt.span(), "expected last expression to be `Some(match (..) { .. })`")) + }; +} diff --git a/tests/source/configs/single_line_let_else_max_width/zero.rs b/tests/source/configs/single_line_let_else_max_width/zero.rs new file mode 100644 index 000000000000..afb9e5033073 --- /dev/null +++ b/tests/source/configs/single_line_let_else_max_width/zero.rs @@ -0,0 +1,40 @@ +// rustfmt-single_line_let_else_max_width: 0 + +fn main() { + let Some(a) = opt else {}; + + let Some(b) = opt else { return }; + + let Some(c) = opt else { + return + }; + + let Some(c) = opt else { + // a comment should always force the block to be multi-lined + return + }; + + let Some(c) = opt else { /* a comment should always force the block to be multi-lined */ return }; + + let Some(d) = some_very_very_very_very_long_name else { return }; + + let Expr::Slice(ast::ExprSlice { lower, upper, step, range: _ }) = slice.as_ref() else { + return + }; + + let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true)? else { + return Ok(None) + }; + + let Some(doc_attr) = variant.attrs.iter().find(|attr| attr.path().is_ident("doc")) else { + return Err(Error::new(variant.span(), r#"expected a doc comment"#)) + }; + + let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true) else { + return Ok(None) + }; + + let Stmt::Expr(Expr::Call(ExprCall { args: some_args, .. }), _) = last_stmt else { + return Err(Error::new(last_stmt.span(), "expected last expression to be `Some(match (..) { .. })`")) + }; +} diff --git a/tests/source/configs/skip_children/foo/mod.rs b/tests/source/configs/skip_children/foo/mod.rs new file mode 100644 index 000000000000..d7ff6cdb8290 --- /dev/null +++ b/tests/source/configs/skip_children/foo/mod.rs @@ -0,0 +1,3 @@ +fn skip_formatting_this() { + println ! ( "Skip this" ) ; +} diff --git a/tests/source/configs/skip_children/true.rs b/tests/source/configs/skip_children/true.rs new file mode 100644 index 000000000000..e51889dd4c91 --- /dev/null +++ b/tests/source/configs/skip_children/true.rs @@ -0,0 +1,4 @@ +// rustfmt-skip_children: true + +mod foo ; +mod void; diff --git a/tests/source/configs/space_before_colon/true.rs b/tests/source/configs/space_before_colon/true.rs new file mode 100644 index 000000000000..0a597602528a --- /dev/null +++ b/tests/source/configs/space_before_colon/true.rs @@ -0,0 +1,11 @@ +// rustfmt-space_before_colon: true +// Space before colon + +fn lorem(t : T) { + let ipsum: Dolor = sit; +} + +const LOREM : Lorem = Lorem { + ipsum : dolor, + sit : amet, +}; diff --git a/tests/source/configs/spaces_around_ranges/false.rs b/tests/source/configs/spaces_around_ranges/false.rs new file mode 100644 index 000000000000..1878c68a5a0c --- /dev/null +++ b/tests/source/configs/spaces_around_ranges/false.rs @@ -0,0 +1,34 @@ +// rustfmt-spaces_around_ranges: false +// Spaces around ranges + +fn main() { + let lorem = 0 .. 10; + let ipsum = 0 ..= 10; + + match lorem { + 1 .. 5 => foo(), + _ => bar, + } + + match lorem { + 1 ..= 5 => foo(), + _ => bar, + } + + match lorem { + 1 ... 5 => foo(), + _ => bar, + } +} + +fn half_open() { + match [5 .. 4, 99 .. 105, 43 .. 44] { + [_, 99 .., _] => {} + [_, .. 105, _] => {} + _ => {} + }; + + if let ..= 5 = 0 {} + if let .. 5 = 0 {} + if let 5 .. = 0 {} +} diff --git a/tests/source/configs/spaces_around_ranges/true.rs b/tests/source/configs/spaces_around_ranges/true.rs new file mode 100644 index 000000000000..0eadfb285157 --- /dev/null +++ b/tests/source/configs/spaces_around_ranges/true.rs @@ -0,0 +1,34 @@ +// rustfmt-spaces_around_ranges: true +// Spaces around ranges + +fn main() { + let lorem = 0..10; + let ipsum = 0..=10; + + match lorem { + 1..5 => foo(), + _ => bar, + } + + match lorem { + 1..=5 => foo(), + _ => bar, + } + + match lorem { + 1...5 => foo(), + _ => bar, + } +} + +fn half_open() { + match [5..4, 99..105, 43..44] { + [_, 99.., _] => {} + [_, ..105, _] => {} + _ => {} + }; + + if let ..=5 = 0 {} + if let ..5 = 0 {} + if let 5.. = 0 {} +} diff --git a/tests/source/configs/struct_field_align_threshold/20.rs b/tests/source/configs/struct_field_align_threshold/20.rs new file mode 100644 index 000000000000..81253c460376 --- /dev/null +++ b/tests/source/configs/struct_field_align_threshold/20.rs @@ -0,0 +1,383 @@ +// rustfmt-struct_field_align_threshold: 20 +// rustfmt-normalize_comments: true +// rustfmt-wrap_comments: true +// rustfmt-error_on_line_overflow: false + +struct Foo { + x: u32, + yy: u32, // comment + zzz: u32, +} + +pub struct Bar { + x: u32, + yy: u32, + zzz: u32, + + xxxxxxx: u32, +} + +fn main() { + let foo = Foo { + x: 0, + yy: 1, + zzz: 2, + }; + + let bar = Bar { + x: 0, + yy: 1, + zzz: 2, + + xxxxxxx: 3, + }; +} + + /// A Doc comment +#[AnAttribute] +pub struct Foo { + #[rustfmt::skip] + f : SomeType, // Comment beside a field + f: SomeType, // Comment beside a field + // Comment on a field + #[AnAttribute] + g: SomeOtherType, + /// A doc comment on a field + h: AThirdType, + pub i: TypeForPublicField +} + +// #1029 +pub struct Foo { + #[doc(hidden)] + // This will NOT get deleted! + bar: String, // hi +} + +// #1029 +struct X { + // `x` is an important number. + #[allow(unused)] // TODO: use + x: u32, +} + +// #410 +#[allow(missing_docs)] +pub struct Writebatch { + #[allow(dead_code)] //only used for holding the internal pointer + writebatch: RawWritebatch, + marker: PhantomData, +} + +struct Bar; + +struct NewType(Type, OtherType); + +struct +NewInt (pub i32, SomeType /* inline comment */, T /* sup */ + + + ); + +struct Qux<'a, + N: Clone + 'a, + E: Clone + 'a, + G: Labeller<'a, N, E> + GraphWalk<'a, N, E>, + W: Write + Copy> +( + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, // Comment + BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB, + #[AnAttr] + // Comment + /// Testdoc + G, + pub W, +); + +struct Tuple(/*Comment 1*/ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, + /* Comment 2 */ BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB,); + +// With a where-clause and generics. +pub struct Foo<'a, Y: Baz> + where X: Whatever +{ + f: SomeType, // Comment beside a field +} + +struct Baz { + + a: A, // Comment A + b: B, // Comment B + c: C, // Comment C + +} + +struct Baz { + a: A, // Comment A + + b: B, // Comment B + + + + + c: C, // Comment C +} + +struct Baz { + + a: A, + + b: B, + c: C, + + + + + d: D + +} + +struct Baz +{ + // Comment A + a: A, + + // Comment B +b: B, + // Comment C + c: C,} + +// Will this be a one-liner? +struct Tuple( + A, //Comment + B +); + +pub struct State time::Timespec> { now: F } + +pub struct State ()> { now: F } + +pub struct State { now: F } + +struct Palette { /// A map of indices in the palette to a count of pixels in approximately that color + foo: i32} + +// Splitting a single line comment into a block previously had a misalignment +// when the field had attributes +struct FieldsWithAttributes { + // Pre Comment + #[rustfmt::skip] pub host:String, // Post comment BBBBBBBBBBBBBB BBBBBBBBBBBBBBBB BBBBBBBBBBBBBBBB BBBBBBBBBBBBBBBBB BBBBBBBBBBB + //Another pre comment + #[attr1] + #[attr2] pub id: usize // CCCCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCCC CCCCCCCCCCCCCC CCCCCCCCCCCC +} + +struct Deep { + deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeep: node::Handle>, + Type, + NodeType>, +} + +struct Foo(T); +struct Foo(T) where T: Copy, T: Eq; +struct Foo(TTTTTTTTTTTTTTTTT, UUUUUUUUUUUUUUUUUUUUUUUU, TTTTTTTTTTTTTTTTTTT, UUUUUUUUUUUUUUUUUUU); +struct Foo(TTTTTTTTTTTTTTTTTT, UUUUUUUUUUUUUUUUUUUUUUUU, TTTTTTTTTTTTTTTTTTT) where T: PartialEq; +struct Foo(TTTTTTTTTTTTTTTTT, UUUUUUUUUUUUUUUUUUUUUUUU, TTTTTTTTTTTTTTTTTTTTT) where T: PartialEq; +struct Foo(TTTTTTTTTTTTTTTTT, UUUUUUUUUUUUUUUUUUUUUUUU, TTTTTTTTTTTTTTTTTTT, UUUUUUUUUUUUUUUUUUU) where T: PartialEq; +struct Foo(TTTTTTTTTTTTTTTTT, // Foo + UUUUUUUUUUUUUUUUUUUUUUUU /* Bar */, + // Baz + TTTTTTTTTTTTTTTTTTT, + // Qux (FIXME #572 - doc comment) + UUUUUUUUUUUUUUUUUUU); + +mod m { + struct X where T: Sized { + a: T, + } +} + +struct Foo(TTTTTTTTTTTTTTTTTTT, + /// Qux + UUUUUUUUUUUUUUUUUUU); + +struct Issue677 { + pub ptr: *const libc::c_void, + pub trace: fn( obj: + *const libc::c_void, tracer : *mut JSTracer ), +} + +struct Foo {} +struct Foo { + } +struct Foo { + // comment + } +struct Foo { + // trailing space -> + + + } +struct Foo { /* comment */ } +struct Foo( /* comment */ ); + +struct LongStruct { + a: A, + the_quick_brown_fox_jumps_over_the_lazy_dog:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, +} + +struct Deep { + deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeep: node::Handle>, + Type, + NodeType>, +} + +struct Foo(String); + +// #1364 +fn foo() { + convex_shape.set_point(0, &Vector2f { x: 400.0, y: 100.0 }); + convex_shape.set_point(1, &Vector2f { x: 500.0, y: 70.0 }); + convex_shape.set_point(2, &Vector2f { x: 450.0, y: 100.0 }); + convex_shape.set_point(3, &Vector2f { x: 580.0, y: 150.0 }); +} + +fn main() { + let x = Bar; + + // Comment + let y = Foo {a: x }; + + Foo { a: foo() /* comment*/, /* comment*/ b: bar(), ..something }; + + Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { a: f(), b: b(), }; + + Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { a: f(), b: b(), }; + + Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { + // Comment + a: foo(), // Comment + // Comment + b: bar(), // Comment + }; + + Foo { a:Bar, + b:f() }; + + Quux { x: if cond { bar(); }, y: baz() }; + + A { + // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Donec et mollis dolor. + first: item(), + // Praesent et diam eget libero egestas mattis sit amet vitae augue. + // Nam tincidunt congue enim, ut porta lorem lacinia consectetur. + second: Item + }; + + Some(Data::MethodCallData(MethodCallData { + span: sub_span.unwrap(), + scope: self.enclosing_scope(id), + ref_id: def_id, + decl_id: Some(decl_id), + })); + + Diagram { /* o This graph demonstrates how + * / \ significant whitespace is + * o o preserved. + * /|\ \ + * o o o o */ + graph: G, } +} + +fn matcher() { + TagTerminatedByteMatcher { + matcher: ByteMatcher { + pattern: b" { memb: T } + let foo = Foo:: { memb: 10 }; +} + +fn issue201() { + let s = S{a:0, .. b}; +} + +fn issue201_2() { + let s = S{a: S2{ .. c}, .. b}; +} + +fn issue278() { + let s = S { + a: 0, + // + b: 0, + }; + let s1 = S { + a: 0, + // foo + // + // bar + b: 0, + }; +} + +fn struct_exprs() { + Foo + { a : 1, b:f( 2)}; + Foo{a:1,b:f(2),..g(3)}; + LoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooongStruct { ..base }; + IntrinsicISizesContribution { content_intrinsic_sizes: IntrinsicISizes { minimum_inline_size: 0, }, }; +} + +fn issue123() { + Foo { a: b, c: d, e: f }; + + Foo { a: bb, c: dd, e: ff }; + + Foo { a: ddddddddddddddddddddd, b: cccccccccccccccccccccccccccccccccccccc }; +} + +fn issue491() { + Foo { + guard: None, + arm: 0, // Comment + }; + + Foo { + arm: 0, // Comment + }; + + Foo { a: aaaaaaaaaa, b: bbbbbbbb, c: cccccccccc, d: dddddddddd, /* a comment */ + e: eeeeeeeee }; +} + +fn issue698() { + Record { + ffffffffffffffffffffffffffields: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + }; + Record { + ffffffffffffffffffffffffffields: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + } +} + +fn issue835() { + MyStruct {}; + MyStruct { /* a comment */ }; + MyStruct { + // Another comment + }; + MyStruct {} +} + +fn field_init_shorthand() { + MyStruct { x, y, z }; + MyStruct { x, y, z, .. base }; + Foo { aaaaaaaaaa, bbbbbbbb, cccccccccc, dddddddddd, /* a comment */ + eeeeeeeee }; + Record { ffffffffffffffffffffffffffieldsaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa }; +} diff --git a/tests/source/configs/struct_lit_single_line/false.rs b/tests/source/configs/struct_lit_single_line/false.rs new file mode 100644 index 000000000000..17cad8dde290 --- /dev/null +++ b/tests/source/configs/struct_lit_single_line/false.rs @@ -0,0 +1,6 @@ +// rustfmt-struct_lit_single_line: false +// Struct literal multiline-style + +fn main() { + let lorem = Lorem { ipsum: dolor, sit: amet }; +} diff --git a/tests/source/configs/tab_spaces/2.rs b/tests/source/configs/tab_spaces/2.rs new file mode 100644 index 000000000000..5c2667bc2c79 --- /dev/null +++ b/tests/source/configs/tab_spaces/2.rs @@ -0,0 +1,11 @@ +// rustfmt-tab_spaces: 2 +// rustfmt-max_width: 30 +// rustfmt-indent_style: Block +// Tab spaces + +fn lorem() { +let ipsum = dolor(); +let sit = vec![ +"amet", "consectetur", "adipiscing", "elit." +]; +} diff --git a/tests/source/configs/tab_spaces/4.rs b/tests/source/configs/tab_spaces/4.rs new file mode 100644 index 000000000000..da61bbd42a7c --- /dev/null +++ b/tests/source/configs/tab_spaces/4.rs @@ -0,0 +1,11 @@ +// rustfmt-tab_spaces: 4 +// rustfmt-max_width: 30 +// rustfmt-indent_style: Block +// Tab spaces + +fn lorem() { +let ipsum = dolor(); +let sit = vec![ +"amet", "consectetur", "adipiscing", "elit." +]; +} diff --git a/tests/source/configs/trailing_comma/always.rs b/tests/source/configs/trailing_comma/always.rs new file mode 100644 index 000000000000..57e874cd822c --- /dev/null +++ b/tests/source/configs/trailing_comma/always.rs @@ -0,0 +1,7 @@ +// rustfmt-trailing_comma: Always +// Trailing comma + +fn main() { + let Lorem { ipsum, dolor, sit, } = amet; + let Lorem { ipsum, dolor, sit, amet, consectetur, adipiscing } = elit; +} diff --git a/tests/source/configs/trailing_comma/never.rs b/tests/source/configs/trailing_comma/never.rs new file mode 100644 index 000000000000..4da3b996f299 --- /dev/null +++ b/tests/source/configs/trailing_comma/never.rs @@ -0,0 +1,23 @@ +// rustfmt-trailing_comma: Never +// Trailing comma + +fn main() { + let Lorem { ipsum, dolor, sit, } = amet; + let Lorem { ipsum, dolor, sit, amet, consectetur, adipiscing } = elit; + + // #1544 + if let VrMsg::ClientReply {request_num: reply_req_num, value, ..} = msg { + let _ = safe_assert_eq!(reply_req_num, request_num, op); + return Ok((request_num, op, value)); + } + + // #1710 + pub struct FileInput { + input: StringInput, + file_name: OsString, + } + match len { + Some(len) => Ok(new(self.input, self.pos + len)), + None => Err(self), + } +} diff --git a/tests/source/configs/trailing_comma/vertical.rs b/tests/source/configs/trailing_comma/vertical.rs new file mode 100644 index 000000000000..c903e8221589 --- /dev/null +++ b/tests/source/configs/trailing_comma/vertical.rs @@ -0,0 +1,7 @@ +// rustfmt-trailing_comma: Vertical +// Trailing comma + +fn main() { + let Lorem { ipsum, dolor, sit, } = amet; + let Lorem { ipsum, dolor, sit, amet, consectetur, adipiscing } = elit; +} diff --git a/tests/source/configs/type_punctuation_density/compressed.rs b/tests/source/configs/type_punctuation_density/compressed.rs new file mode 100644 index 000000000000..223b9a2f0f02 --- /dev/null +++ b/tests/source/configs/type_punctuation_density/compressed.rs @@ -0,0 +1,37 @@ +// rustfmt-type_punctuation_density: Compressed +// Type punctuation density + +fn lorem() { + // body +} + +struct Foo +where U: Eq + Clone { + // body +} + +trait Foo<'a, T = usize> +where T: 'a + Eq + Clone +{ + type Bar: Eq + Clone; +} + +trait Foo: Eq + Clone { + // body +} + +impl Foo<'a> for Bar +where for<'a> T: 'a + Eq + Clone +{ + // body +} + +fn foo<'a, 'b, 'c>() +where 'a: 'b + 'c +{ + // body +} + +fn Foo + Foo>() { + let i = 6; +} diff --git a/tests/source/configs/type_punctuation_density/wide.rs b/tests/source/configs/type_punctuation_density/wide.rs new file mode 100644 index 000000000000..fe0c0816701b --- /dev/null +++ b/tests/source/configs/type_punctuation_density/wide.rs @@ -0,0 +1,37 @@ +// rustfmt-type_punctuation_density: Wide +// Type punctuation density + +fn lorem() { + // body +} + +struct Foo +where U: Eq + Clone { + // body +} + +trait Foo<'a, T = usize> +where T: 'a + Eq + Clone +{ + type Bar: Eq + Clone; +} + +trait Foo: Eq + Clone { + // body +} + +impl Foo<'a> for Bar +where for<'a> T: 'a + Eq + Clone +{ + // body +} + +fn foo<'a, 'b, 'c>() +where 'a: 'b + 'c +{ + // body +} + +fn Foo + Foo>() { + let i = 6; +} diff --git a/tests/source/configs/use_field_init_shorthand/false.rs b/tests/source/configs/use_field_init_shorthand/false.rs new file mode 100644 index 000000000000..4c2eb1de179c --- /dev/null +++ b/tests/source/configs/use_field_init_shorthand/false.rs @@ -0,0 +1,19 @@ +// rustfmt-use_field_init_shorthand: false +// Use field initialization shorthand if possible. + +fn main() { + let a = Foo { + x: x, + y: y, + z: z, + }; + + let b = Bar { + x: x, + y: y, + #[attr] + z: z, + #[rustfmt::skip] + skipped: skipped, + }; +} diff --git a/tests/source/configs/use_field_init_shorthand/true.rs b/tests/source/configs/use_field_init_shorthand/true.rs new file mode 100644 index 000000000000..dcde28d74e0f --- /dev/null +++ b/tests/source/configs/use_field_init_shorthand/true.rs @@ -0,0 +1,19 @@ +// rustfmt-use_field_init_shorthand: true +// Use field initialization shorthand if possible. + +fn main() { + let a = Foo { + x: x, + y: y, + z: z, + }; + + let b = Bar { + x: x, + y: y, + #[attr] + z: z, + #[rustfmt::skip] + skipped: skipped, + }; +} diff --git a/tests/source/configs/use_small_heuristics/default.rs b/tests/source/configs/use_small_heuristics/default.rs new file mode 100644 index 000000000000..95238c548446 --- /dev/null +++ b/tests/source/configs/use_small_heuristics/default.rs @@ -0,0 +1,35 @@ +// rustfmt-use_small_heuristics: Default + +enum Lorem { + Ipsum, + Dolor(bool), + Sit { + amet: Consectetur, + adipiscing: Elit, + }, +} + +fn main() { + lorem("lorem", "ipsum", "dolor", "sit", "amet", "consectetur", "adipiscing"); + + let lorem = Lorem { + ipsum: dolor, + sit: amet, + }; + + let lorem = if ipsum { + dolor + } else { + sit + }; +} + +fn format_let_else() { + let Some(a) = opt else {}; + + let Some(b) = opt else { return }; + + let Some(c) = opt else { return }; + + let Some(d) = some_very_very_very_very_long_name else { return }; +} diff --git a/tests/source/configs/use_small_heuristics/max.rs b/tests/source/configs/use_small_heuristics/max.rs new file mode 100644 index 000000000000..b79302e22ab2 --- /dev/null +++ b/tests/source/configs/use_small_heuristics/max.rs @@ -0,0 +1,35 @@ +// rustfmt-use_small_heuristics: Max + +enum Lorem { + Ipsum, + Dolor(bool), + Sit { + amet: Consectetur, + adipiscing: Elit, + }, +} + +fn main() { + lorem("lorem", "ipsum", "dolor", "sit", "amet", "consectetur", "adipiscing"); + + let lorem = Lorem { + ipsum: dolor, + sit: amet, + }; + + let lorem = if ipsum { + dolor + } else { + sit + }; +} + +fn format_let_else() { + let Some(a) = opt else {}; + + let Some(b) = opt else { return }; + + let Some(c) = opt else { return }; + + let Some(d) = some_very_very_very_very_long_name else { return }; +} diff --git a/tests/source/configs/use_small_heuristics/off.rs b/tests/source/configs/use_small_heuristics/off.rs new file mode 100644 index 000000000000..80bcdd898968 --- /dev/null +++ b/tests/source/configs/use_small_heuristics/off.rs @@ -0,0 +1,35 @@ +// rustfmt-use_small_heuristics: Off + +enum Lorem { + Ipsum, + Dolor(bool), + Sit { + amet: Consectetur, + adipiscing: Elit, + }, +} + +fn main() { + lorem("lorem", "ipsum", "dolor", "sit", "amet", "consectetur", "adipiscing"); + + let lorem = Lorem { + ipsum: dolor, + sit: amet, + }; + + let lorem = if ipsum { + dolor + } else { + sit + }; +} + +fn format_let_else() { + let Some(a) = opt else {}; + + let Some(b) = opt else { return }; + + let Some(c) = opt else { return }; + + let Some(d) = some_very_very_very_very_long_name else { return }; +} diff --git a/tests/source/configs/use_try_shorthand/false.rs b/tests/source/configs/use_try_shorthand/false.rs new file mode 100644 index 000000000000..de7f8b4a5e24 --- /dev/null +++ b/tests/source/configs/use_try_shorthand/false.rs @@ -0,0 +1,6 @@ +// rustfmt-use_try_shorthand: false +// Use try! shorthand + +fn main() { + let lorem = try!(ipsum.map(|dolor| dolor.sit())); +} diff --git a/tests/source/configs/use_try_shorthand/true.rs b/tests/source/configs/use_try_shorthand/true.rs new file mode 100644 index 000000000000..9015ec41e5e4 --- /dev/null +++ b/tests/source/configs/use_try_shorthand/true.rs @@ -0,0 +1,6 @@ +// rustfmt-use_try_shorthand: true +// Use try! shorthand + +fn main() { + let lorem = try!(ipsum.map(|dolor| dolor.sit())); +} diff --git a/tests/source/configs/where_single_line/true.rs b/tests/source/configs/where_single_line/true.rs new file mode 100644 index 000000000000..9de98283b5e5 --- /dev/null +++ b/tests/source/configs/where_single_line/true.rs @@ -0,0 +1,26 @@ +// rustfmt-where_single_line: true +// Where style + + +fn lorem_two_items() -> T where Ipsum: Eq, Lorem: Eq { + // body +} + +fn lorem_multi_line( + a: Aaaaaaaaaaaaaaa, + b: Bbbbbbbbbbbbbbbb, + c: Ccccccccccccccccc, + d: Ddddddddddddddddddddddddd, + e: Eeeeeeeeeeeeeeeeeee, +) -> T +where + Ipsum: Eq, +{ + // body +} + +fn lorem() -> T where Ipsum: Eq { + // body +} + +unsafe impl Sync for Foo where (): Send {} diff --git a/tests/source/configs/wrap_comments/false.rs b/tests/source/configs/wrap_comments/false.rs new file mode 100644 index 000000000000..48ecd88accbf --- /dev/null +++ b/tests/source/configs/wrap_comments/false.rs @@ -0,0 +1,8 @@ +// rustfmt-wrap_comments: false +// rustfmt-max_width: 50 +// rustfmt-error_on_line_overflow: false +// Wrap comments + +fn main() { + // 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. +} diff --git a/tests/source/configs/wrap_comments/true.rs b/tests/source/configs/wrap_comments/true.rs new file mode 100644 index 000000000000..39a79a4cacc4 --- /dev/null +++ b/tests/source/configs/wrap_comments/true.rs @@ -0,0 +1,15 @@ +// rustfmt-wrap_comments: true +// rustfmt-max_width: 50 +// Wrap comments + +fn main() { + // 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. +} + +fn code_block() { + // ```rust + // let x = 3; + // + // println!("x = {}", x); + // ``` +} diff --git a/tests/source/const_generics.rs b/tests/source/const_generics.rs new file mode 100644 index 000000000000..01b764dbe45d --- /dev/null +++ b/tests/source/const_generics.rs @@ -0,0 +1,44 @@ +struct Message { + field2: Vec< + "MessageEntity" + >, + field3: Vec< + 1 + >, + field4: Vec< + 2 , 3 + >, + +} + +struct RectangularArray { + array: [[T; WIDTH]; HEIGHT], +} + +fn main() { + const X: usize = 7; + let x: RectangularArray; + let y: RectangularArray; +} + +fn foo() { + const Y: usize = X * 2; + static Z: (usize, usize) = (X, X); + + struct Foo([i32; X]); +} + +type Foo = [i32; N + 1]; + +pub trait Foo: Bar<{Baz::COUNT}> { + const ASD: usize; +} + +// #4263 +fn const_generics_on_params< + // AAAA + const BBBB: usize, + /* CCCC */ + const DDDD: usize, + >() {} diff --git a/tests/source/control-brace-style-always-next-line.rs b/tests/source/control-brace-style-always-next-line.rs new file mode 100644 index 000000000000..9079fb46c0cd --- /dev/null +++ b/tests/source/control-brace-style-always-next-line.rs @@ -0,0 +1,44 @@ +// rustfmt-control_brace_style: AlwaysNextLine + +fn main() { + loop { + (); + (); + } + + + 'label: loop // loop comment + { + (); + } + + + cond = true; + while cond { + (); + } + + + 'while_label: while cond { // while comment + (); + } + + + for obj in iter { + for sub_obj in obj + { + 'nested_while_label: while cond { + (); + } + } + } + + match some_var { // match comment + pattern0 => val0, + pattern1 => val1, + pattern2 | pattern3 => { + do_stuff(); + val2 + }, + }; +} diff --git a/tests/source/control-brace-style-always-same-line.rs b/tests/source/control-brace-style-always-same-line.rs new file mode 100644 index 000000000000..45111aaab08c --- /dev/null +++ b/tests/source/control-brace-style-always-same-line.rs @@ -0,0 +1,42 @@ +fn main() { + loop { + (); + (); + } + + + 'label: loop // loop comment + { + (); + } + + + cond = true; + while cond { + (); + } + + + 'while_label: while cond { // while comment + (); + } + + + for obj in iter { + for sub_obj in obj + { + 'nested_while_label: while cond { + (); + } + } + } + + match some_var { // match comment + pattern0 => val0, + pattern1 => val1, + pattern2 | pattern3 => { + do_stuff(); + val2 + }, + }; +} diff --git a/tests/source/doc-attrib.rs b/tests/source/doc-attrib.rs new file mode 100644 index 000000000000..dde88c6e9552 --- /dev/null +++ b/tests/source/doc-attrib.rs @@ -0,0 +1,118 @@ +// rustfmt-wrap_comments: true +// rustfmt-normalize_doc_attributes: true + +// Only doc = "" attributes should be normalized +#![doc = " Example doc attribute comment"] +#![doc = " Example doc attribute comment with 10 leading spaces"] +#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", + html_favicon_url = "https://doc.rust-lang.org/favicon.ico", + html_root_url = "https://doc.rust-lang.org/nightly/", + html_playground_url = "https://play.rust-lang.org/", test(attr(deny(warnings))))] + + +// Long `#[doc = "..."]` +struct A { #[doc = " xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"] b: i32 } + + +#[doc = " The `nodes` and `edges` method each return instantiations of `Cow<[T]>` to leave implementers the freedom to create entirely new vectors or to pass back slices into internally owned vectors."] +struct B { b: i32 } + + +#[doc = " Level 1 comment"] +mod tests { + #[doc = " Level 2 comment"] + impl A { + #[doc = " Level 3 comment"] + fn f() { + #[doc = " Level 4 comment"] + fn g() { + } + } + } +} + +struct C { + #[doc = " item doc attrib comment"] + // regular item comment + b: i32, + + // regular item comment + #[doc = " item doc attrib comment"] + c: i32, +} + +// non-regression test for regular attributes, from #2647 +#[cfg(feature = "this_line_is_101_characters_long_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")] +pub fn foo() {} + +// path attrs +#[clippy::bar] +#[clippy::bar(a, b, c)] +pub fn foo() {} + +mod issue_2620 { + #[derive(Debug, StructOpt)] +#[structopt(about = "Display information about the character on FF Logs")] +pub struct Params { + #[structopt(help = "The server the character is on")] + server: String, + #[structopt(help = "The character's first name")] + first_name: String, + #[structopt(help = "The character's last name")] + last_name: String, + #[structopt( + short = "j", + long = "job", + help = "The job to look at", + parse(try_from_str) + )] + job: Option +} +} + +// non-regression test for regular attributes, from #2969 +#[cfg(not(all(feature="std", + any(target_os = "linux", target_os = "android", + target_os = "netbsd", + target_os = "dragonfly", + target_os = "haiku", + target_os = "emscripten", + target_os = "solaris", + target_os = "cloudabi", + target_os = "macos", target_os = "ios", + target_os = "freebsd", + target_os = "openbsd", + target_os = "redox", + target_os = "fuchsia", + windows, + all(target_arch = "wasm32", feature = "stdweb"), + all(target_arch = "wasm32", feature = "wasm-bindgen"), + ))))] +type Os = NoSource; + +// use cases from bindgen needing precise control over leading spaces +#[doc = "

"] +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct ContradictAccessors { + #[doc = "no leading spaces here"] + pub mBothAccessors: ::std::os::raw::c_int, + #[doc = "
"] + pub mNoAccessors: ::std::os::raw::c_int, + #[doc = "
"] + pub mUnsafeAccessors: ::std::os::raw::c_int, + #[doc = "
"] + pub mImmutableAccessor: ::std::os::raw::c_int, +} + +#[doc = " \\brief MPI structure"] +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct mbedtls_mpi { + #[doc = "< integer sign"] + pub s: ::std::os::raw::c_int, + #[doc = "< total # of limbs"] + pub n: ::std::os::raw::c_ulong, + #[doc = "< pointer to limbs"] + pub p: *mut mbedtls_mpi_uint, +} diff --git a/tests/source/doc-comment-with-example.rs b/tests/source/doc-comment-with-example.rs new file mode 100644 index 000000000000..e74ceefd1958 --- /dev/null +++ b/tests/source/doc-comment-with-example.rs @@ -0,0 +1,12 @@ +// rustfmt-format_code_in_doc_comments: true + +/// Foo +/// +/// # Example +/// ``` +/// # #![cfg_attr(not(dox), feature(cfg_target_feature, target_feature, stdsimd))] +/// # #![cfg_attr(not(dox), no_std)] +/// fn foo() { } +/// ``` +/// +fn foo() {} diff --git a/tests/source/doc.rs b/tests/source/doc.rs new file mode 100644 index 000000000000..3b25918b12ec --- /dev/null +++ b/tests/source/doc.rs @@ -0,0 +1,5 @@ +// rustfmt-normalize_comments: true +// Part of multiple.rs + +// sadfsdfa +//sdffsdfasdf diff --git a/tests/source/dyn_trait.rs b/tests/source/dyn_trait.rs new file mode 100644 index 000000000000..012643be92c2 --- /dev/null +++ b/tests/source/dyn_trait.rs @@ -0,0 +1,20 @@ +#![feature(dyn_trait)] + +fn main() { + // #2506 + // checks rustfmt doesn't remove dyn + trait MyTrait { + fn method(&self) -> u64; + } + fn f1(a: Box) {} + + // checks if line wrap works correctly + trait Very_______________________Long__________________Name_______________________________Trait { + fn method(&self) -> u64; + } + + fn f2(a: Box) {} + + // #2582 + let _: &dyn (::std::any::Any) = &msg; +} diff --git a/tests/source/else-if-brace-style-always-next-line.rs b/tests/source/else-if-brace-style-always-next-line.rs new file mode 100644 index 000000000000..7b4870fc65aa --- /dev/null +++ b/tests/source/else-if-brace-style-always-next-line.rs @@ -0,0 +1,54 @@ +// rustfmt-control_brace_style: AlwaysNextLine + +fn main() { + if false + { + (); + (); + } + + if false // lone if comment + { + (); + (); + } + + + let a = + if 0 > 1 { + unreachable!() + } + else + { + 0x0 + }; + + + if true + { + (); + } else if false { + (); + (); + } + else { + (); + (); + (); + } + + if true // else-if-chain if comment + { + (); + } + else if false // else-if-chain else-if comment + { + (); + (); + } else // else-if-chain else comment + { + (); + (); + (); + } +} diff --git a/tests/source/else-if-brace-style-always-same-line.rs b/tests/source/else-if-brace-style-always-same-line.rs new file mode 100644 index 000000000000..37c9417ea160 --- /dev/null +++ b/tests/source/else-if-brace-style-always-same-line.rs @@ -0,0 +1,52 @@ +fn main() { + if false + { + (); + (); + } + + if false // lone if comment + { + (); + (); + } + + + let a = + if 0 > 1 { + unreachable!() + } + else + { + 0x0 + }; + + + if true + { + (); + } else if false { + (); + (); + } + else { + (); + (); + (); + } + + if true // else-if-chain if comment + { + (); + } + else if false // else-if-chain else-if comment + { + (); + (); + } else // else-if-chain else comment + { + (); + (); + (); + } +} diff --git a/tests/source/else-if-brace-style-closing-next-line.rs b/tests/source/else-if-brace-style-closing-next-line.rs new file mode 100644 index 000000000000..3b885b3faaa5 --- /dev/null +++ b/tests/source/else-if-brace-style-closing-next-line.rs @@ -0,0 +1,54 @@ +// rustfmt-control_brace_style: ClosingNextLine + +fn main() { + if false + { + (); + (); + } + + if false // lone if comment + { + (); + (); + } + + + let a = + if 0 > 1 { + unreachable!() + } + else + { + 0x0 + }; + + + if true + { + (); + } else if false { + (); + (); + } + else { + (); + (); + (); + } + + if true // else-if-chain if comment + { + (); + } + else if false // else-if-chain else-if comment + { + (); + (); + } else // else-if-chain else comment + { + (); + (); + (); + } +} diff --git a/tests/source/empty-item-single-line-false.rs b/tests/source/empty-item-single-line-false.rs new file mode 100644 index 000000000000..20c5bc83b466 --- /dev/null +++ b/tests/source/empty-item-single-line-false.rs @@ -0,0 +1,46 @@ +// rustfmt-brace_style: AlwaysNextLine +// rustfmt-empty_item_single_line: false + +fn function() +{ + +} + +struct Struct +{ + +} + +enum Enum +{ + +} + +trait Trait +{ + +} + +impl Trait for T +{ + +} + +trait Trait2 +where + T: Copy + Display + Write + Read + FromStr, {} + +trait Trait3 +where + T: Something + + SomethingElse + + Sync + + Send + + Display + + Debug + + Copy + + Hash + + Debug + + Display + + Write + + Read, {} diff --git a/tests/source/empty_file.rs b/tests/source/empty_file.rs new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/source/enum.rs b/tests/source/enum.rs new file mode 100644 index 000000000000..a7b9616929cb --- /dev/null +++ b/tests/source/enum.rs @@ -0,0 +1,212 @@ +// rustfmt-wrap_comments: true +// Enums test + +#[atrr] +pub enum Test { + A, B(u32, + A /* comment */, + SomeType), + /// Doc comment + C, +} + +pub enum Foo<'a, Y: Baz> where X: Whatever +{ A, } + +enum EmtpyWithComment { + // Some comment +} + +// C-style enum +enum Bar { + A = 1, + #[someAttr(test)] + B = 2, // comment + C, +} + +enum LongVariants { +First(LOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONG, // comment +VARIANT), + // This is the second variant + Second +} + +enum StructLikeVariants { + Normal(u32, String, ), + StructLike { x: i32, // Test comment + // Pre-comment + #[Attr50] y: SomeType, // Another Comment + }, SL { a: A } +} + +enum X { + CreateWebGLPaintTask(Size2D, GLContextAttributes, IpcSender, usize), String>>), // This is a post comment +} + +pub enum EnumWithAttributes { + //This is a pre comment AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + TupleVar(usize, usize, usize), // AAAA AAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + // Pre Comment + #[rustfmt::skip] + SkippedItem(String,String,), // Post-comment + #[another_attr] + #[attr2] + ItemStruct {x: usize, y: usize}, // Comment AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + // And another + ForcedPreflight // AAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +} + +pub enum SingleTuple { + // Pre Comment AAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + Match(usize, usize, String) // Post-comment AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +} + +pub enum SingleStruct { + Match {name: String, loc: usize} // Post-comment +} + +pub enum GenericEnum +where I: Iterator { + // Pre Comment + Left {list: I, root: T}, // Post-comment + Right {list: I, root: T} // Post Comment +} + + +enum EmtpyWithComment { + // Some comment +} + +enum TestFormatFails { + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +} + +fn nested_enum_test() { + if true { + enum TestEnum { + One(usize, usize, usize, usize, usize, usize, usize, usize, usize, usize, usize, usize, usize, usize, usize, usize,), // AAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAA + Two // AAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAA + } + enum TestNestedFormatFail { + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + } + } +} + + pub struct EmtpyWithComment { + // FIXME: Implement this struct +} + +// #1115 +pub enum Bencoding<'i> { + Str(&'i [u8]), + Int(i64), + List(Vec>), + /// A bencoded dict value. The first element the slice of bytes in the source that the dict is + /// composed of. The second is the dict, decoded into an ordered map. + // TODO make Dict "structlike" AKA name the two values. + Dict(&'i [u8], BTreeMap<&'i [u8], Bencoding<'i>>), +} + +// #1261 +pub enum CoreResourceMsg { + SetCookieForUrl( + ServoUrl, + #[serde(deserialize_with = "::hyper_serde::deserialize", + serialize_with = "::hyper_serde::serialize")] + Cookie, + CookieSource + ), +} + +enum Loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong {} +enum Looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong {} +enum Loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong {} +enum Loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong { Foo } + +// #1046 +pub enum Entry<'a, K: 'a, V: 'a> { + Vacant( + #[ stable( feature = "rust1", since = "1.0.0" ) ] VacantEntry<'a, K, V>, + ), + Occupied( + #[ stable( feature = "rust1", since = "1.0.0" ) ] + OccupiedEntry<'a, K, V>, + ), +} + +// #2081 +pub enum ForegroundColor { + CYAN = (winapi::FOREGROUND_INTENSITY | winapi::FOREGROUND_GREEN | winapi::FOREGROUND_BLUE) as u16, +} + +// #2098 +pub enum E<'a> { + V ( < std::slice::Iter<'a, Xxxxxxxxxxxxxx> as Iterator> :: Item ) , +} + +// #1809 +enum State { + TryRecv { + pos: usize, + lap: u8, + closed_count: usize, + }, + Subscribe { pos: usize }, + IsReady { pos: usize, ready: bool }, + Unsubscribe { + pos: usize, + lap: u8, + id_woken: usize, + }, + FinalTryRecv { pos: usize, id_woken: usize }, + TimedOut, + Disconnected, +} + +// #2190 +#[derive(Debug, Fail)] +enum AnError { + #[fail(display = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")] + UnexpectedSingleToken { token: syn::Token }, +} + +// #2193 +enum WidthOf101 { + #[fail(display = ".....................................................")] Io(::std::io::Error), + #[fail(display = ".....................................................")] Ioo(::std::io::Error), + Xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx(::std::io::Error), + Xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx(::std::io::Error), +} + +// #2389 +pub enum QlError { + #[fail(display = "Parsing error: {}", 0)] LexError(parser::lexer::LexError), + #[fail(display = "Parsing error: {:?}", 0)] ParseError(parser::ParseError), + #[fail(display = "Validation error: {:?}", 0)] ValidationError(Vec), + #[fail(display = "Execution error: {}", 0)] ExecutionError(String), + // (from, to) + #[fail(display = "Translation error: from {} to {}", 0, 1)] TranslationError(String, String), + // (kind, input, expected) + #[fail(display = "aaaaaaaaaaaaCould not find {}: Found: {}, expected: {:?}", 0, 1, 2)] ResolveError(&'static str, String, Option), +} + +// #2594 +enum Foo {} +enum Bar { } + +// #3562 +enum PublishedFileVisibility { + Public = sys::ERemoteStoragePublishedFileVisibility_k_ERemoteStoragePublishedFileVisibilityPublic, + FriendsOnly = sys::ERemoteStoragePublishedFileVisibility_k_ERemoteStoragePublishedFileVisibilityFriendsOnly, + Private = sys::ERemoteStoragePublishedFileVisibility_k_ERemoteStoragePublishedFileVisibilityPrivate, +} + +// #3771 +//#![feature(arbitrary_enum_discriminant)] +#[repr(u32)] +pub enum E { + A { a: u32 } = 0x100, + B { field1: u32, field2: u8, field3: m::M } = 0x300 // comment +} diff --git a/tests/source/existential_type.rs b/tests/source/existential_type.rs new file mode 100644 index 000000000000..33bb9a951596 --- /dev/null +++ b/tests/source/existential_type.rs @@ -0,0 +1,23 @@ +// Opaque type. + + #![feature(type_alias_impl_trait)] + +pub type Adder +where + T: Clone, + F: Copy + = impl Fn(T) -> T; + +pub type Adderrr = impl Fn( T ) -> T; + +impl Foo for Bar { +type E = impl Trait; +} + +pub type Adder_without_impl +where + T: Clone, + F: Copy + = Fn(T) -> T; + +pub type Adderrr_without_impl = Fn( T ) -> T; diff --git a/tests/source/expr-block.rs b/tests/source/expr-block.rs new file mode 100644 index 000000000000..a3e6100b7f22 --- /dev/null +++ b/tests/source/expr-block.rs @@ -0,0 +1,300 @@ +// Test expressions with block formatting. + +fn arrays() { + [ ]; + let empty = []; + + let foo = [a_long_name, a_very_lng_name, a_long_name]; + + let foo = [a_long_name, a_very_lng_name, a_long_name, a_very_lng_name, a_long_name, a_very_lng_name, a_long_name, a_very_lng_name]; + + vec![a_long_name, a_very_lng_name, a_long_name, a_very_lng_name, a_long_name, a_very_lng_name, a_very_lng_name]; + + [a_long_name, a_very_lng_name, a_long_name, a_very_lng_name, a_long_name, a_very_lng_name, a_very_lng_name] +} + +fn arrays() { + let x = [0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 0, + 7, + 8, + 9, + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 0]; + + let y = [/* comment */ 1, 2 /* post comment */, 3]; + + let xy = [ strukt { test123: value_one_two_three_four, turbo: coolio(), } , /* comment */ 1 ]; + + let a =WeightedChoice::new(&mut [Weighted { + weight: x, + item: 0, + }, + Weighted { + weight: 1, + item: 1, + }, + Weighted { + weight: x, + item: 2, + }, + Weighted { + weight: 1, + item: 3, + }]); + + let z = [xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, yyyyyyyyyyyyyyyyyyyyyyyyyyy, zzzzzzzzzzzzzzzzz, q]; + + [ 1 + 3, 4 , 5, 6, 7, 7, fncall::>(3-1)] +} + +fn function_calls() { + let items = itemize_list(context.source_map, + args.iter(), + ")", + |item| item.span.lo(), + |item| item.span.hi(), + |item| { + item.rewrite(context, + Shape { + width: remaining_width, + ..nested_shape + }) + }, + span.lo(), + span.hi()); + + itemize_list(context.source_map, + args.iter(), + ")", + |item| item.span.lo(), + |item| item.span.hi(), + |item| { + item.rewrite(context, + Shape { + width: remaining_width, + ..nested_shape + }) + }, + span.lo(), + span.hi()) +} + +fn macros() { + baz!(do_not, add, trailing, commas, inside, of, function, like, macros, even, if_they, are, long); + + baz!(one_item_macro_which_is_also_loooooooooooooooooooooooooooooooooooooooooooooooong); + + let _ = match option { + None => baz!(function, like, macro_as, expression, which, is, loooooooooooooooong), + Some(p) => baz!(one_item_macro_as_expression_which_is_also_loooooooooooooooong), + }; +} + +fn issue_1450() { + if selfstate + .compare_exchandsfasdsdfgsdgsdfgsdfgsdfgsdfgsdfgfsfdsage_weak( + STATE_PARKED, + STATE_UNPARKED, + Release, + Relaxed, + Release, + Relaxed, + ) + .is_ok() { + return; + } +} + +fn foo() { + if real_total <= limit && !pre_line_comments && + !items.into_iter().any(|item| item.as_ref().is_multiline()) { + DefinitiveListTactic::Horizontal + } +} + +fn combine_block() { + foo( + Bar { + x: value, + y: value2, + }, + ); + + foo((Bar { + x: value, + y: value2, + },)); + + foo((1, 2, 3, Bar { + x: value, + y: value2, + })); + + foo((1, 2, 3, |x| { + let y = x + 1; + let z = y + 1; + z + })); + + let opt = Some( + Struct( + long_argument_one, + long_argument_two, + long_argggggggg, + ), + ); + + do_thing( + |param| { + action(); + foo(param) + }, + ); + + do_thing( + x, + |param| { + action(); + foo(param) + }, + ); + + do_thing( + x, + ( + 1, + 2, + 3, + |param| { + action(); + foo(param) + }, + ), + ); + + Ok( + some_function( + lllllllllong_argument_one, + lllllllllong_argument_two, + lllllllllllllllllllllllllllllong_argument_three, + ), + ); + + foo( + thing, + bar( + param2, + pparam1param1param1param1param1param1param1param1param1param1aram1, + param3, + ), + ); + + foo.map_or( + || { + Ok( + SomeStruct { + f1: 0, + f2: 0, + f3: 0, + }, + ) + }, + ); + + match opt { + Some(x) => somefunc(anotherfunc( + long_argument_one, + long_argument_two, + long_argument_three, + )), + Some(x) => |x| { + let y = x + 1; + let z = y + 1; + z + }, + Some(x) => (1, 2, |x| { + let y = x + 1; + let z = y + 1; + z + }), + Some(x) => SomeStruct { + f1: long_argument_one, + f2: long_argument_two, + f3: long_argument_three, + }, + None => Ok(SomeStruct { + f1: long_argument_one, + f2: long_argument_two, + f3: long_argument_three, + }), + }; + + match x { + y => func( + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, + ), + _ => func( + x, + yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy, + zzz, + ), + } +} + +fn issue_1862() { + foo( + /* bar = */ None , + something_something, + /* baz = */ None , + /* This comment waaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaay too long to be kept on the same line */ None , + /* com */ this_last_arg_is_tooooooooooooooooooooooooooooooooo_long_to_be_kept_with_the_pre_comment , + ) +} + +fn issue_3025() { + foo( + // This describes the argument below. + /* bar = */ None , + // This describes the argument below. + something_something, + // This describes the argument below. */ + None , + // This describes the argument below. + /* This comment waaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaay too long to be kept on the same line */ None , + // This describes the argument below. + /* com */ this_last_arg_is_tooooooooooooooooooooooooooooooooo_long_to_be_kept_with_the_pre_comment , + ) +} + +fn issue_1878() { + let channel: &str = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(2, &self))?; +} diff --git a/tests/source/expr-overflow-delimited.rs b/tests/source/expr-overflow-delimited.rs new file mode 100644 index 000000000000..cd80ca6fcebc --- /dev/null +++ b/tests/source/expr-overflow-delimited.rs @@ -0,0 +1,155 @@ +// rustfmt-overflow_delimited_expr: true + +fn combine_blocklike() { + do_thing( + |param| { + action(); + foo(param) + }, + ); + + do_thing( + x, + |param| { + action(); + foo(param) + }, + ); + + do_thing( + x, + + // I'll be discussing the `action` with your para(m)legal counsel + |param| { + action(); + foo(param) + }, + ); + + do_thing( + Bar { + x: value, + y: value2, + }, + ); + + do_thing( + x, + Bar { + x: value, + y: value2, + }, + ); + + do_thing( + x, + + // Let me tell you about that one time at the `Bar` + Bar { + x: value, + y: value2, + }, + ); + + do_thing( + &[ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ], + ); + + do_thing( + x, + &[ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ], + ); + + do_thing( + x, + + // Just admit it; my list is longer than can be folded on to one line + &[ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ], + ); + + do_thing( + vec![ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ], + ); + + do_thing( + x, + vec![ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ], + ); + + do_thing( + x, + + // Just admit it; my list is longer than can be folded on to one line + vec![ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ], + ); + + do_thing( + x, + ( + 1, + 2, + 3, + |param| { + action(); + foo(param) + }, + ), + ); +} + +fn combine_struct_sample() { + let identity = verify( + &ctx, + VerifyLogin { + type_: LoginType::Username, + username: args.username.clone(), + password: Some(args.password.clone()), + domain: None, + }, + )?; +} + +fn combine_macro_sample() { + rocket::ignite() + .mount( + "/", + routes![ + http::auth::login, + http::auth::logout, + http::cors::options, + http::action::dance, + http::action::sleep, + ], + ) + .launch(); +} diff --git a/tests/source/expr.rs b/tests/source/expr.rs new file mode 100644 index 000000000000..879c551ea490 --- /dev/null +++ b/tests/source/expr.rs @@ -0,0 +1,568 @@ +// rustfmt-normalize_comments: true +// rustfmt-wrap_comments: true +// Test expressions + +fn foo() -> bool { + let referenced = &5 ; + + let very_long_variable_name = ( a + first + simple + test ); + let very_long_variable_name = (a + first + simple + test + AAAAAAAAAAAAA + BBBBBBBBBBBBBBBBB + b + c); + + let is_internalxxxx = self.source_map.span_to_filename(s) == self.source_map.span_to_filename(m.inner); + + let some_val = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa * bbbb / (bbbbbb - + function_call(x, *very_long_pointer, y)) + + 1000 ; + +some_ridiculously_loooooooooooooooooooooong_function(10000 * 30000000000 + 40000 / 1002200000000 + - 50000 * sqrt(-1), + trivial_value); + (((((((((aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + a + + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + aaaaa))))))))) ; + + { for _ in 0..10 {} } + + {{{{}}}} + + if 1 + 2 > 0 { let result = 5; result } else { 4}; + + if let Some(x) = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa { + // Nothing + } + + if let Some(x) = (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) {} + + if let (some_very_large, + tuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuple) = 1 + + 2 + 3 { + } + + if let (some_very_large, + tuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuple) = 1111 + 2222 {} + + if let (some_very_large, tuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuple) = 1 + + 2 + 3 { + } + + if let ast::ItemKind::Trait(_, unsafety, ref generics, ref type_param_bounds, ref trait_items) = item.node + { + // nothing + } + + let test = if true { 5 } else { 3 }; + + if cond() { + something(); + } else if different_cond() { + something_else(); + } else { + // Check subformatting + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + } + + // #2884 + let _ = [0; {struct Foo; impl Foo {const fn get(&self) -> usize {5}}; Foo.get()}]; +} + +fn bar() { + let range = ( 111111111 + 333333333333333333 + 1111 + 400000000000000000) .. (2222 + 2333333333333333); + + let another_range = 5..some_func( a , b /* comment */); + + for _ in 1 ..{ call_forever(); } + + syntactically_correct(loop { sup( '?'); }, if cond { 0 } else { 1 }); + + let third = ..10; + let infi_range = .. ; + let foo = 1..; + let bar = 5 ; + let nonsense = (10 .. 0)..(0..10); + + loop{if true {break}} + + let x = (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa && + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + a); +} + +fn baz() { + unsafe /* {}{}{}{{{{}} */ { + let foo = 1u32; + } + + unsafe /* very looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong comment */ {} + + unsafe // So this is a very long comment. + // Multi-line, too. + // Will it still format correctly? + { + } + + unsafe { + // Regular unsafe block + } + + unsafe { + foo() + } + + unsafe { + foo(); + } + + // #2289 + let identifier_0 = unsafe { this_is_58_chars_long_and_line_is_93_chars_long_xxxxxxxxxx }; + let identifier_1 = unsafe { this_is_59_chars_long_and_line_is_94_chars_long_xxxxxxxxxxx }; + let identifier_2 = unsafe { this_is_65_chars_long_and_line_is_100_chars_long_xxxxxxxxxxxxxxxx }; + let identifier_3 = unsafe { this_is_66_chars_long_and_line_is_101_chars_long_xxxxxxxxxxxxxxxxx }; +} + +// Test some empty blocks. +fn qux() { + {} + // FIXME this one could be done better. + { /* a block with a comment */ } + { + + } + { + // A block with a comment. + } +} + +fn issue184(source: &str) { + for c in source.chars() { + if index < 'a' { + continue; + } + } +} + +fn arrays() { + let x = [0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 0, + 7, + 8, + 9, + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 0]; + + let y = [/* comment */ 1, 2 /* post comment */, 3]; + + let xy = [ strukt { test123: value_one_two_three_four, turbo: coolio(), } , /* comment */ 1 ]; + + let a =WeightedChoice::new(&mut [Weighted { + weightweight: x, + item: 0, + }, + Weighted { + weightweight: 1, + item: 1, + }, + Weighted { + weightweight: x, + item: 2, + }, + Weighted { + weightweight: 1, + item: 3, + }]); + + let z = [xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, yyyyyyyyyyyyyyyyyyyyyyyyyyy, zzzzzzzzzzzzzzzzzz, q]; + + [ 1 + 3, 4 , 5, 6, 7, 7, fncall::>(3-1)] +} + +fn returns() { + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa && return; + + return aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa; +} + +fn addrof() { + & mut(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb); + & (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb); + + // raw reference operator + & raw const a; + & raw mut b; +} + +fn casts() { + fn unpack(packed: u32) -> [u16; 2] { + [ + (packed >> 16) as u16, + (packed >> 0) as u16, + ] + } + + let some_trait_xxx = xxxxxxxxxxx + xxxxxxxxxxxxx + as SomeTraitXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + let slightly_longer_trait = yyyyyyyyy + yyyyyyyyyyy as SomeTraitYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY; +} + +fn indices() { + let x = (aaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb+cccccccccccccccc) [ x + y + z ]; + let y = (aaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + cccccccccccccccc)[ xxxxx + yyyyy + zzzzz ]; + let z = xxxxxxxxxx.x().y().zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz()[aaaaa]; + let z = xxxxxxxxxx.x().y().zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz()[aaaaa]; +} + +fn repeats() { + let x = [aaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb+cccccccccccccccc; x + y + z ]; + let y = [aaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + cccccccccccccccc; xxxxx + yyyyy + zzzzz ]; +} + +fn blocks() { + if 1 + 1 == 2 { + println!("yay arithmetix!"); + }; +} + +fn issue767() { + if false { + if false { + } else { + // A let binding here seems necessary to trigger it. + let _ = (); + } + } else if let false = false { + } +} + +fn ranges() { + let x = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa .. bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb; + let y = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ..= bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb; + let z = ..= x ; + + // #1766 + let x = [0. ..10.0]; + let x = [0. ..=10.0]; + + a ..= b + + // the expr below won't compile because inclusive ranges need a defined end + // let a = 0 ..= ; +} + +fn if_else() { + let exact = diff / + (if size == 0 { + 1 +} else { + size +}); + + let cx = tp1.x + + any * radius * + if anticlockwise { + 1.0 + } else { + -1.0 + }; +} + +fn complex_if_else() { + if let Some(x) = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx { + } else if let Some(x) = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx { + ha(); + } else if xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + xxxxxxxx { + yo(); + } else if let Some(x) = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx { + ha(); + } else if xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + xxxxxxxxx { + yo(); + } +} + +fn issue1106() { + { + if let hir::ItemEnum(ref enum_def, ref generics) = self.ast_map.expect_item(enum_node_id).node { + } + } + + for entry in + WalkDir::new(path) + .into_iter() + .filter_entry(|entry| exclusions.filter_entry(entry)) { + } +} + +fn issue1570() { + a_very_long_function_name({some_func(1, {1})}) +} + +fn issue1714() { + v = &mut {v}[mid..]; + let (left, right) = {v}.split_at_mut(mid); +} + +// Multi-lined index should be put on the next line if it fits in one line. +fn issue1749() { + { + { + { + if self.shape[(r as f32 + self.x_offset) as usize][(c as f32 + self.y_offset) as usize] != 0 { + // hello + } + } + } + } +} + +// #1172 +fn newlines_between_list_like_expr() { + foo( + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, + + yyyyyyyyyyyyyyyyyyyyyyyyyyyyyy, + + zzzzzzzzzzzzzzzzzzzzzzzzzzzzzz, + ); + + vec![ + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, + + yyyyyyyyyyyyyyyyyyyyyyyyyyyyyy, + + zzzzzzzzzzzzzzzzzzzzzzzzzzzzzz, + ]; + + match x { + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | + + yyyyyyyyyyyyyyyyyyyyyyyyyyyyyy | + + zzzzzzzzzzzzzzzzzzzzzzzzzzzzzz => foo(a, b, c), + _ => bar(), + }; +} + +fn issue2178() { + Ok(result.iter().map(|item| ls_util::rls_to_location(item)).collect()) +} + +// #2493 +impl Foo { +fn bar(&self) { + { + let x = match () { + () => { + let i; + i == self.install_config.storage.experimental_compressed_block_size as usize + } + }; + } +} +} + +fn dots() { + .. .. ..; // (.. (.. (..))) + ..= ..= ..; + (..) .. ..; // ((..) .. (..)) +} + +// #2676 +// A function call with a large single argument. +fn foo() { + let my_var = + Mutex::new(RpcClientType::connect(server_iddd).chain_err(|| "Unable to create RPC client")?); +} + +// #2704 +// Method call with prefix and suffix. +fn issue2704() { + // We should not combine the callee with a multi-lined method call. + let requires = requires.set(&requires0 + .concat(&requires1) + .concat(&requires2) + .distinct_total()); + let requires = requires.set(requires0 + .concat(&requires1) + .concat(&requires2) + .distinct_total() as u32); + let requires = requires.set(requires0 + .concat(&requires1) + .concat(&requires2) + .distinct_total()?); + let requires = requires.set(!requires0 + .concat(&requires1) + .concat(&requires2) + .distinct_total()); + // We should combine a small callee with an argument. + bar(vec![22] + .into_iter() + .map(|x| x * 2) + .filter(|_| true) + .collect()); + // But we should not combine a long callee with an argument. + barrrr(vec![22] + .into_iter() + .map(|x| x * 2) + .filter(|_| true) + .collect()); +} + +// #2782 +fn issue2782() { + {let f={let f={{match f{F(f,_)=>{{loop{let f={match f{F(f,_)=>{{match f{F(f,_)=>{{loop{let f={let f={match f{'-'=>F(f,()),}};};}}}}}}}};}}}}}};};} +} + +fn issue_2802() { + function_to_fill_this_line(some_arg, some_arg, some_arg) + * a_very_specific_length(specific_length_arg) * very_specific_length(Foo { + a: some_much_much_longer_value, + }) * some_value +} + +fn issue_3003() { + let mut path: PathBuf = [ + env!("CARGO_MANIFEST_DIR"), + "tests", + "support", + "dejavu-fonts-ttf-2.37", + "ttf", + ] + .iter() + .collect(); +} + +fn issue3226() { + { + { + { + return Err(ErrorKind::ManagementInterfaceError("Server exited unexpectedly").into()) + } + } + } + { + { + { + break Err(ErrorKind::ManagementInterfaceError("Server exited unexpectedlyy").into()) + } + } + } +} + +// #3457 +fn issue3457() { + { + { + { + { + { + { + { + { + { + { + { + { + { + { + { + { + { + { + { + { + { + { + { + { + println!("Test"); + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } +} + +// #3498 +static REPRO: &[usize] = &[#[cfg(feature = "zero")] + 0]; + +fn overflow_with_attr() { + foo(#[cfg(feature = "zero")] + 0); + foobar(#[cfg(feature = "zero")] + 0); + foobar(x, y, #[cfg(feature = "zero")] + {}); +} + + +// https://github.com/rust-lang/rustfmt/issues/3765 +fn foo() { + async { + // Do + // some + // work + } + .await; + + async { + // Do + // some + // work + } + .await; +} + +fn underscore() { + _= 1; + _; + [ _,a,_ ] = [1, 2, 3]; + (a, _) = (8, 9); + TupleStruct( _, a) = TupleStruct(2, 2); + + let _ : usize = foo(_, _); +} diff --git a/tests/source/extern.rs b/tests/source/extern.rs new file mode 100644 index 000000000000..f51ba6e98c9f --- /dev/null +++ b/tests/source/extern.rs @@ -0,0 +1,92 @@ +// rustfmt-normalize_comments: true + + extern crate foo ; + extern crate foo as bar ; + +extern crate futures; +extern crate dotenv; +extern crate chrono; + +extern crate foo; +extern crate bar; + +// #2315 +extern crate proc_macro2; +extern crate proc_macro; + +// #3128 +extern crate serde; // 1.0.78 +extern crate serde_derive; // 1.0.78 +extern crate serde_json; // 1.0.27 + + extern "C" { + fn c_func(x: *mut *mut libc::c_void); + + fn c_func(x: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX, y: YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY); + + #[test123] + fn foo() -> uint64_t; + +pub fn bar() ; + } + +extern { + fn DMR_GetDevice(pHDev: *mut HDEV, searchMode: DeviceSearchMode, pSearchString: *const c_char, devNr: c_uint, wildcard: c_char) -> TDMR_ERROR; + + fn quux() -> (); // Post comment + + pub type + Foo; + + type Bar; +} + +extern "Rust" { static ext: u32; + // Some comment. + pub static mut var : SomeType ; } + +extern "C" { + fn syscall(number: libc::c_long /* comment 1 */, /* comm 2 */ ... /* sup? */) -> libc::c_long; + + fn foo (x: *const c_char , ... ) -> +libc::c_long; + } + + extern { + pub fn freopen(filename: *const c_char, mode: *const c_char + , mode2: *const c_char + , mode3: *const c_char, + file: *mut FILE) + -> *mut FILE; + + + const fn foo( + + ) -> + *mut Bar; + unsafe fn foo( + + ) -> * + mut + Bar; + + pub(super) const fn foo() -> *mut Bar; + pub(crate) unsafe fn foo() -> *mut Bar; + } + +extern { + +} + +macro_rules! x { + ($tt:tt) => {}; +} + +extern "macros" { + x!(ident); + x!(#); + x![ident]; + x![#]; + x! {ident} + x! {#} +} diff --git a/tests/source/extern_not_explicit.rs b/tests/source/extern_not_explicit.rs new file mode 100644 index 000000000000..9d6c4c2a1cd5 --- /dev/null +++ b/tests/source/extern_not_explicit.rs @@ -0,0 +1,14 @@ +// rustfmt-force_explicit_abi: false + + extern "C" { + fn some_fn() -> (); + } + + extern "C" fn sup() { + + } + +type funky_func = extern "C" fn (unsafe extern "rust-call" fn(*const JSJitInfo, *mut JSContext, + HandleObject, *mut libc::c_void, u32, + *mut JSVal) + -> u8); diff --git a/tests/source/file-lines-1.rs b/tests/source/file-lines-1.rs new file mode 100644 index 000000000000..0164e30a854c --- /dev/null +++ b/tests/source/file-lines-1.rs @@ -0,0 +1,29 @@ +// rustfmt-file_lines: [{"file":"tests/source/file-lines-1.rs","range":[4,8]}] + +fn floaters() { + let x = Foo { + field1: val1, + field2: val2, + } + .method_call().method_call(); + + let y = if cond { + val1 + } else { + val2 + } + .method_call(); + + { + match x { + PushParam => { + // comment + stack.push(mparams[match cur.to_digit(10) { + Some(d) => d as usize - 1, + None => return Err("bad param number".to_owned()), + }] + .clone()); + } + } + } +} diff --git a/tests/source/file-lines-2.rs b/tests/source/file-lines-2.rs new file mode 100644 index 000000000000..6f44ec6e69d1 --- /dev/null +++ b/tests/source/file-lines-2.rs @@ -0,0 +1,29 @@ +// rustfmt-file_lines: [{"file":"tests/source/file-lines-2.rs","range":[10,15]}] + +fn floaters() { + let x = Foo { + field1: val1, + field2: val2, + } + .method_call().method_call(); + + let y = if cond { + val1 + } else { + val2 + } + .method_call(); + + { + match x { + PushParam => { + // comment + stack.push(mparams[match cur.to_digit(10) { + Some(d) => d as usize - 1, + None => return Err("bad param number".to_owned()), + }] + .clone()); + } + } + } +} diff --git a/tests/source/file-lines-3.rs b/tests/source/file-lines-3.rs new file mode 100644 index 000000000000..4b825b9f58ce --- /dev/null +++ b/tests/source/file-lines-3.rs @@ -0,0 +1,29 @@ +// rustfmt-file_lines: [{"file":"tests/source/file-lines-3.rs","range":[4,8]},{"file":"tests/source/file-lines-3.rs","range":[10,15]}] + +fn floaters() { + let x = Foo { + field1: val1, + field2: val2, + } + .method_call().method_call(); + + let y = if cond { + val1 + } else { + val2 + } + .method_call(); + + { + match x { + PushParam => { + // comment + stack.push(mparams[match cur.to_digit(10) { + Some(d) => d as usize - 1, + None => return Err("bad param number".to_owned()), + }] + .clone()); + } + } + } +} diff --git a/tests/source/file-lines-4.rs b/tests/source/file-lines-4.rs new file mode 100644 index 000000000000..83928bf6fecf --- /dev/null +++ b/tests/source/file-lines-4.rs @@ -0,0 +1,30 @@ +// rustfmt-file_lines: [] +// (Test that nothing is formatted if an empty array is specified.) + +fn floaters() { + let x = Foo { + field1: val1, + field2: val2, + } + .method_call().method_call(); + + let y = if cond { + val1 + } else { + val2 + } + .method_call(); + // aaaaaaaaaaaaa + { + match x { + PushParam => { + // comment + stack.push(mparams[match cur.to_digit(10) { + Some(d) => d as usize - 1, + None => return Err("bad param number".to_owned()), + }] + .clone()); + } + } + } +} diff --git a/tests/source/file-lines-5.rs b/tests/source/file-lines-5.rs new file mode 100644 index 000000000000..8ec2c67bc444 --- /dev/null +++ b/tests/source/file-lines-5.rs @@ -0,0 +1,17 @@ +// rustfmt-file_lines: [{"file":"tests/source/file-lines-5.rs","range":[3,5]}] + +struct A { +t: i64, +} + +mod foo { + fn bar() { + // test + let i = 12; + // test + } + // aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + fn baz() { + let j = 15; + } +} diff --git a/tests/source/file-lines-6.rs b/tests/source/file-lines-6.rs new file mode 100644 index 000000000000..2eacc8a0e752 --- /dev/null +++ b/tests/source/file-lines-6.rs @@ -0,0 +1,18 @@ +// rustfmt-file_lines: [{"file":"tests/source/file-lines-6.rs","range":[9,10]}] + +struct A { + t: i64, +} + +mod foo { + fn bar() { + // test + let i = 12; + // test + } + + fn baz() { +/// + let j = 15; + } +} diff --git a/tests/source/file-lines-7.rs b/tests/source/file-lines-7.rs new file mode 100644 index 000000000000..b227ac35dccd --- /dev/null +++ b/tests/source/file-lines-7.rs @@ -0,0 +1,24 @@ +// rustfmt-file_lines: [{"file":"tests/source/file-lines-7.rs","range":[8,15]}] + +struct A { + t: i64, +} + +mod foo { + fn bar() { + + + + // test + let i = 12; + // test + } + + fn baz() { + + + + /// + let j = 15; + } +} diff --git a/tests/source/file-lines-item.rs b/tests/source/file-lines-item.rs new file mode 100644 index 000000000000..fe52a7fa1761 --- /dev/null +++ b/tests/source/file-lines-item.rs @@ -0,0 +1,21 @@ +// rustfmt-file_lines: [{"file":"tests/source/file-lines-item.rs","range":[6,8]}] + +use foo::{c, b, a}; +use bar; + +fn foo() { + bar ( ) ; +} + +impl Drop for Context { + fn drop(&mut self) { + } +} + +impl Bar for Baz { + fn foo() { + bar( + baz, // Who knows? + ) + } +} diff --git a/tests/source/fn-custom-2.rs b/tests/source/fn-custom-2.rs new file mode 100644 index 000000000000..a3697c36d0f6 --- /dev/null +++ b/tests/source/fn-custom-2.rs @@ -0,0 +1,35 @@ +// Test different indents. + +fn foo(a: Aaaaaaaaaaaaaaa, b: Bbbbbbbbbbbbbbbb, c: Ccccccccccccccccc, d: Ddddddddddddddddddddddddd, e: Eeeeeeeeeeeeeeeeeee) { + foo(); +} + +fn bar<'a: 'bbbbbbbbbbbbbbbbbbbbbbbbbbb, TTTTTTTTTTTTT, UUUUUUUUUUUUUUUUUUUU: WWWWWWWWWWWWWWWWWWWWWWWW>(a: Aaaaaaaaaaaaaaa) { + bar(); +} + +fn baz() where X: TTTTTTTT { + baz(); +} + +fn qux() where X: TTTTTTTTTTTTTTTTTTTTTTTTTTTT, X: TTTTTTTTTTTTTTTTTTTTTTTTTTTT, X: TTTTTTTTTTTTTTTTTTTTTTTTTTTT, X: TTTTTTTTTTTTTTTTTTTTTTTTTTTT { + baz(); +} + +impl Foo { + fn foo(self, a: Aaaaaaaaaaaaaaa, b: Bbbbbbbbbbbbbbbb, c: Ccccccccccccccccc, d: Ddddddddddddddddddddddddd, e: Eeeeeeeeeeeeeeeeeee) { + foo(); + } + + fn bar<'a: 'bbbbbbbbbbbbbbbbbbbbbbbbbbb, TTTTTTTTTTTTT, UUUUUUUUUUUUUUUUUUUU: WWWWWWWWWWWWWWWWWWWWWWWW>(a: Aaaaaaaaaaaaaaa) { + bar(); + } + + fn baz() where X: TTTTTTTT { + baz(); + } +} + +struct Foo { + foo: Foo, +} diff --git a/tests/source/fn-custom-3.rs b/tests/source/fn-custom-3.rs new file mode 100644 index 000000000000..a5e0f9af26dd --- /dev/null +++ b/tests/source/fn-custom-3.rs @@ -0,0 +1,31 @@ +// Test different indents. + +fn foo(a: Aaaaaaaaaaaaaaa, b: Bbbbbbbbbbbbbbbb, c: Ccccccccccccccccc, d: Ddddddddddddddddddddddddd, e: Eeeeeeeeeeeeeeeeeee) { + foo(); +} + +fn bar<'a: 'bbbbbbbbbbbbbbbbbbbbbbbbbbb, TTTTTTTTTTTTT, UUUUUUUUUUUUUUUUUUUU: WWWWWWWWWWWWWWWWWWWWWWWW>(a: Aaaaaaaaaaaaaaa) { + bar(); +} + +fn qux() where X: TTTTTTTTTTTTTTTTTTTTTTTTTTTT, X: TTTTTTTTTTTTTTTTTTTTTTTTTTTT { + baz(); +} + +fn qux() where X: TTTTTTTTTTTTTTTTTTTTTTTTTTTT, X: TTTTTTTTTTTTTTTTTTTTTTTTTTTT, X: TTTTTTTTTTTTTTTTTTTTTTTTTTTT, X: TTTTTTTTTTTTTTTTTTTTTTTTTTTT { + baz(); +} + +impl Foo { + fn foo(self, a: Aaaaaaaaaaaaaaa, b: Bbbbbbbbbbbbbbbb, c: Ccccccccccccccccc, d: Ddddddddddddddddddddddddd, e: Eeeeeeeeeeeeeeeeeee) { + foo(); + } + + fn bar<'a: 'bbbbbbbbbbbbbbbbbbbbbbbbbbb, TTTTTTTTTTTTT, UUUUUUUUUUUUUUUUUUUU: WWWWWWWWWWWWWWWWWWWWWWWW>(a: Aaaaaaaaaaaaaaa) { + bar(); + } +} + +struct Foo { + foo: Foo, +} diff --git a/tests/source/fn-custom-4.rs b/tests/source/fn-custom-4.rs new file mode 100644 index 000000000000..6e18b6f9fe2b --- /dev/null +++ b/tests/source/fn-custom-4.rs @@ -0,0 +1,13 @@ +// Test different indents. + +fn qux() where X: TTTTTTTTTTTTTTTTTTTTTTTTTTTT, X: TTTTTTTTTTTTTTTTTTTTTTTTTTTT, X: TTTTTTTTTTTTTTTTTTTTTTTTTTTT, X: TTTTTTTTTTTTTTTTTTTTTTTTTTTT { + baz(); +} + +fn qux() where X: TTTTTTTTTTTTTTTTTTTTTTTTTTTT { + baz(); +} + +fn qux(a: Aaaaaaaaaaaaaaaaa) where X: TTTTTTTTTTTTTTTTTTTTTTTTTTTT, X: TTTTTTTTTTTTTTTTTTTTTTTTTTTT { + baz(); +} diff --git a/tests/source/fn-custom-6.rs b/tests/source/fn-custom-6.rs new file mode 100644 index 000000000000..807084575fa9 --- /dev/null +++ b/tests/source/fn-custom-6.rs @@ -0,0 +1,40 @@ +// rustfmt-brace_style: PreferSameLine +// Test different indents. + +fn foo(a: Aaaaaaaaaaaaaa, b: Bbbbbbbbbbbbbb) { + foo(); +} + +fn bar(a: Aaaaaaaaaaaaaa, b: Bbbbbbbbbbbbbb, c: Cccccccccccccccccc, d: Dddddddddddddddd, e: Eeeeeeeeeeeeeee) { + bar(); +} + +fn foo(a: Aaaaaaaaaaaaaa, b: Bbbbbbbbbbbbbb) -> String { + foo(); +} + +fn bar(a: Aaaaaaaaaaaaaa, b: Bbbbbbbbbbbbbb, c: Cccccccccccccccccc, d: Dddddddddddddddd, e: Eeeeeeeeeeeeeee) -> String { + bar(); +} + +fn foo(a: Aaaaaaaaaaaaaa, b: Bbbbbbbbbbbbbb) where T: UUUUUUUUUUU { + foo(); +} + +fn bar(a: Aaaaaaaaaaaaaa, b: Bbbbbbbbbbbbbb, c: Cccccccccccccccccc, d: Dddddddddddddddd, e: Eeeeeeeeeeeeeee) where T: UUUUUUUUUUU { + bar(); +} + +fn foo(a: Aaaaaaaaaaaaaa, b: Bbbbbbbbbbbbbb) -> String where T: UUUUUUUUUUU { + foo(); +} + +fn bar(a: Aaaaaaaaaaaaaa, b: Bbbbbbbbbbbbbb, c: Cccccccccccccccccc, d: Dddddddddddddddd, e: Eeeeeeeeeeeeeee) -> String where T: UUUUUUUUUUU { + bar(); +} + +trait Test { + fn foo(a: u8) {} + + fn bar(a: u8) -> String {} +} diff --git a/tests/source/fn-custom-7.rs b/tests/source/fn-custom-7.rs new file mode 100644 index 000000000000..3ecd8701727a --- /dev/null +++ b/tests/source/fn-custom-7.rs @@ -0,0 +1,24 @@ +// rustfmt-normalize_comments: true +// rustfmt-fn_params_layout: Vertical +// rustfmt-brace_style: AlwaysNextLine + +// Case with only one variable. +fn foo(a: u8) -> u8 { + bar() +} + +// Case with 2 variables and some pre-comments. +fn foo(a: u8 /* Comment 1 */, b: u8 /* Comment 2 */) -> u8 { + bar() +} + +// Case with 2 variables and some post-comments. +fn foo(/* Comment 1 */ a: u8, /* Comment 2 */ b: u8) -> u8 { + bar() +} + +trait Test { + fn foo(a: u8) {} + + fn bar(a: u8) -> String {} +} diff --git a/tests/source/fn-custom-8.rs b/tests/source/fn-custom-8.rs new file mode 100644 index 000000000000..0dd64868b2d5 --- /dev/null +++ b/tests/source/fn-custom-8.rs @@ -0,0 +1,48 @@ +// rustfmt-brace_style: PreferSameLine +// Test different indents. + +fn foo(a: Aaaaaaaaaaaaaa, b: Bbbbbbbbbbbbbb) { + foo(); +} + +fn bar(a: Aaaaaaaaaaaaaa, b: Bbbbbbbbbbbbbb, c: Cccccccccccccccccc, d: Dddddddddddddddd, e: Eeeeeeeeeeeeeee) { + bar(); +} + +fn foo(a: Aaaaaaaaaaaaaa, b: Bbbbbbbbbbbbbb) -> String { + foo(); +} + +fn bar(a: Aaaaaaaaaaaaaa, b: Bbbbbbbbbbbbbb, c: Cccccccccccccccccc, d: Dddddddddddddddd, e: Eeeeeeeeeeeeeee) -> String { + bar(); +} + +fn foo(a: Aaaaaaaaaaaaaa, b: Bbbbbbbbbbbbbb) where T: UUUUUUUUUUU { + foo(); +} + +fn bar(a: Aaaaaaaaaaaaaa, b: Bbbbbbbbbbbbbb, c: Cccccccccccccccccc, d: Dddddddddddddddd, e: Eeeeeeeeeeeeeee) where T: UUUUUUUUUUU { + bar(); +} + +fn foo(a: Aaaaaaaaaaaaaa, b: Bbbbbbbbbbbbbb) -> String where T: UUUUUUUUUUU { + foo(); +} + +fn bar(a: Aaaaaaaaaaaaaa, b: Bbbbbbbbbbbbbb, c: Cccccccccccccccccc, d: Dddddddddddddddd, e: Eeeeeeeeeeeeeee) -> String where T: UUUUUUUUUUU { + bar(); +} + +trait Test { + fn foo( + a: u8) { + + } + + fn bar(a: u8) + -> String { + + } + + fn bar(a: u8) -> String where Foo: foooo, Bar: barrr {} +} diff --git a/tests/source/fn-custom.rs b/tests/source/fn-custom.rs new file mode 100644 index 000000000000..64ef0ecfaae4 --- /dev/null +++ b/tests/source/fn-custom.rs @@ -0,0 +1,13 @@ +// rustfmt-fn_params_layout: Compressed +// Test some of the ways function signatures can be customised. + +// Test compressed layout of args. +fn foo(a: Aaaaaaaaaaaaaaa, b: Bbbbbbbbbbbbbbbb, c: Ccccccccccccccccc, d: Ddddddddddddddddddddddddd, e: Eeeeeeeeeeeeeeeeeee) { + foo(); +} + +impl Foo { + fn foo(self, a: Aaaaaaaaaaaaaaa, b: Bbbbbbbbbbbbbbbb, c: Ccccccccccccccccc, d: Ddddddddddddddddddddddddd, e: Eeeeeeeeeeeeeeeeeee) { + foo(); + } +} diff --git a/tests/source/fn-param-attributes.rs b/tests/source/fn-param-attributes.rs new file mode 100644 index 000000000000..3407a3b2ef2e --- /dev/null +++ b/tests/source/fn-param-attributes.rs @@ -0,0 +1,57 @@ +// https://github.com/rust-lang/rustfmt/issues/3623 + +fn foo(#[cfg(something)] x: i32, y: i32) -> i32 { + x + y +} + +fn foo_b(#[cfg(something)]x: i32, y: i32) -> i32 { + x + y +} + +fn add(#[cfg(something)]#[deny(C)] x: i32, y: i32) -> i32 { + x + y +} + +struct NamedSelfRefStruct {} +impl NamedSelfRefStruct { + fn foo( +#[cfg(something)] self: &Self, + ) {} +} + +struct MutStruct {} +impl MutStruct { + fn foo( + #[cfg(foo)]&mut self,#[deny(C)] b: i32, + ) {} +} + +fn main() { + let c = | + #[allow(C)]a: u32, + #[cfg(something)] b: i32, + #[cfg_attr(something, cfg(nothing))]#[deny(C)] c: i32, + | {}; + let _ = c(1, 2); +} + +pub fn bar( + /// bar +#[test] a: u32, + /// Bar + #[must_use] +/// Baz + #[no_mangle] b: i32, +) {} + + +fn abc( + #[foo] + #[bar] param: u32, +) { + // ... +} + +fn really_really_really_loooooooooooooooooooong(#[cfg(some_even_longer_config_feature_that_keeps_going_and_going_and_going_forever_and_ever_and_ever_on_and_on)] b: i32) { + // ... +} diff --git a/tests/source/fn-simple.rs b/tests/source/fn-simple.rs new file mode 100644 index 000000000000..12a50c013a91 --- /dev/null +++ b/tests/source/fn-simple.rs @@ -0,0 +1,74 @@ +// rustfmt-normalize_comments: true + +fn simple(/*pre-comment on a function!?*/ i: i32/*yes, it's possible! */ + ,response: NoWay /* hose */) { +fn op(x: Typ, key : &[u8], upd : Box) -> (memcache::Status, Result>)>) -> MapResult {} + + "cool"} + + +fn weird_comment(/* /*/ double level */ comment */ x: Hello /*/*/* triple, even */*/*/, +// Does this work? +y: World +) { + simple(/* does this preserve comments now? */ 42, NoWay) +} + +fn generic(arg: T) -> &SomeType + where T: Fn(// First arg + A, + // Second argument + B, C, D, /* pre comment */ E /* last comment */) -> &SomeType { + arg(a, b, c, d, e) +} + +fn foo() -> ! {} + +pub fn http_fetch_async(listener:Box< AsyncCORSResponseListener+Send >, script_chan: Box) { +} + +fn some_func>(val:T){} + +fn zzzzzzzzzzzzzzzzzzzz + (selff: Type, mut handle: node::Handle>, Type, NodeType>) + -> SearchStack<'a, K, V, Type, NodeType>{ +} + +unsafe fn generic_call(cx: *mut JSContext, argc: libc::c_uint, vp: *mut JSVal, + is_lenient: bool, + call: unsafe extern fn(*const JSJitInfo, *mut JSContext, + HandleObject, *mut libc::c_void, u32, + *mut JSVal) + -> u8) { + let f: fn ( _ , _ ) -> _ = panic!() ; +} + +pub fn start_export_thread(database: &Database, crypto_scheme: &C, block_size: usize, source_path: &Path) -> BonzoResult> {} + +pub fn waltz(cwd: &Path) -> CliAssert { + { + { + formatted_comment = rewrite_comment(comment, block_style, width, offset, formatting_fig); + } + } +} + +// #2003 +mod foo { + fn __bindgen_test_layout_i_open0_c_open1_char_a_open2_char_close2_close1_close0_instantiation() { + foo(); + } +} + +// #2082 +pub(crate) fn init() {} + +pub(crate) fn init() {} + +// #2630 +fn make_map String)>(records: &Vec, key_fn: F) -> HashMap {} + +// #2956 +fn bar(beans: Asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf, spam: bool, eggs: bool) -> bool{ + unimplemented!(); +} diff --git a/tests/source/fn-single-line/version_one.rs b/tests/source/fn-single-line/version_one.rs new file mode 100644 index 000000000000..469ab621567b --- /dev/null +++ b/tests/source/fn-single-line/version_one.rs @@ -0,0 +1,80 @@ +// rustfmt-fn_single_line: true +// rustfmt-version: One +// Test single-line functions. + +fn foo_expr() { + 1 +} + +fn foo_stmt() { + foo(); +} + +fn foo_decl_local() { + let z = 5; + } + +fn foo_decl_item(x: &mut i32) { + x = 3; +} + + fn empty() { + +} + +fn foo_return() -> String { + "yay" +} + +fn foo_where() -> T where T: Sync { + let x = 2; +} + +fn fooblock() { + { + "inner-block" + } +} + +fn fooblock2(x: i32) { + let z = match x { + _ => 2, + }; +} + +fn comment() { + // this is a test comment + 1 +} + +fn comment2() { + // multi-line comment + let z = 2; + 1 +} + +fn only_comment() { + // Keep this here +} + +fn aaaaaaaaaaaaaaaaa_looooooooooooooooooooooong_name() { + let z = "aaaaaaawwwwwwwwwwwwwwwwwwwwwwwwwwww"; +} + +fn lots_of_space () { + 1 +} + +fn mac() -> Vec { vec![] } + +trait CoolTypes { + fn dummy(&self) { + } +} + +trait CoolerTypes { fn dummy(&self) { +} +} + +fn Foo() where T: Bar { +} diff --git a/tests/source/fn-single-line/version_two.rs b/tests/source/fn-single-line/version_two.rs new file mode 100644 index 000000000000..bf381ff10651 --- /dev/null +++ b/tests/source/fn-single-line/version_two.rs @@ -0,0 +1,80 @@ +// rustfmt-fn_single_line: true +// rustfmt-version: Two +// Test single-line functions. + +fn foo_expr() { + 1 +} + +fn foo_stmt() { + foo(); +} + +fn foo_decl_local() { + let z = 5; + } + +fn foo_decl_item(x: &mut i32) { + x = 3; +} + + fn empty() { + +} + +fn foo_return() -> String { + "yay" +} + +fn foo_where() -> T where T: Sync { + let x = 2; +} + +fn fooblock() { + { + "inner-block" + } +} + +fn fooblock2(x: i32) { + let z = match x { + _ => 2, + }; +} + +fn comment() { + // this is a test comment + 1 +} + +fn comment2() { + // multi-line comment + let z = 2; + 1 +} + +fn only_comment() { + // Keep this here +} + +fn aaaaaaaaaaaaaaaaa_looooooooooooooooooooooong_name() { + let z = "aaaaaaawwwwwwwwwwwwwwwwwwwwwwwwwwww"; +} + +fn lots_of_space () { + 1 +} + +fn mac() -> Vec { vec![] } + +trait CoolTypes { + fn dummy(&self) { + } +} + +trait CoolerTypes { fn dummy(&self) { +} +} + +fn Foo() where T: Bar { +} diff --git a/tests/source/fn_args_indent-block.rs b/tests/source/fn_args_indent-block.rs new file mode 100644 index 000000000000..955f390cca2c --- /dev/null +++ b/tests/source/fn_args_indent-block.rs @@ -0,0 +1,77 @@ +// rustfmt-normalize_comments: true + +fn foo() { + foo(); +} + +fn foo(a: Aaaaaaaaaaaaaa, b: Bbbbbbbbbbbbbb) { + foo(); +} + +fn bar(a: Aaaaaaaaaaaaaa, b: Bbbbbbbbbbbbbb, c: Cccccccccccccccccc, d: Dddddddddddddddd, e: Eeeeeeeeeeeeeee) { + bar(); +} + +fn foo(a: Aaaaaaaaaaaaaa, b: Bbbbbbbbbbbbbb) -> String { + foo(); +} + +fn bar(a: Aaaaaaaaaaaaaa, b: Bbbbbbbbbbbbbb, c: Cccccccccccccccccc, d: Dddddddddddddddd, e: Eeeeeeeeeeeeeee) -> String { + bar(); +} + +fn foo(a: u8 /* Comment 1 */, b: u8 /* Comment 2 */) -> u8 { + bar() +} + +fn foo(a: u8 /* Comment 1 */, b: Bbbbbbbbbbbbbb, c: Cccccccccccccccccc, d: Dddddddddddddddd, e: Eeeeeeeeeeeeeee /* Comment 2 */) -> u8 { + bar() +} + +fn bar(a: Aaaaaaaaaaaaaa, b: Bbbbbbbbbbbbbb, c: Cccccccccccccccccc, d: Dddddddddddddddd, e: Eeeeeeeeeeeeeee) -> String where X: Fooooo, Y: Baaar { + bar(); +} + +fn foo() -> T { + foo(); +} + +fn foo() -> T where X: Foooo, Y: Baaar { + foo(); +} + +fn foo() where X: Foooo { +} + +fn foo() where X: Foooo, Y: Baaar { +} + +fn foo() -> (Loooooooooooooooooooooong, Reeeeeeeeeeeeeeeeeeeeeeeeturn, iiiiiiiiis, Looooooooooooooooong) { + foo(); +} + +fn foo() { + foo(); +} + +fn foo() { + foo(); +} + +fn foo() { + foo(); +} + +trait Test { + fn foo(a: u8) {} + + fn bar(a: Aaaaaaaaaaaaaa, b: Bbbbbbbbbbbbbb, c: Cccccccccccccccccc, d: Dddddddddddddddd, e: Eeeeeeeeeeeeeee) -> String {} +} + +fn foo(a: Aaaaaaaaaaaaaaaaaaaa, b: Bbbbbbbbbbbbbbbbb, c: Cccccccccccccccccc, d: Dddddddddddddddd) { + foo(); +} + +fn foo() -> (Looooooooooooooooooooooooooong, Reeeeeeeeeeeeeeeeeeeeeeeeeeeeeturn, iiiiiiiiiiiiiis, Loooooooooooooooooooooong) { + foo(); +} diff --git a/tests/source/fn_args_layout-vertical.rs b/tests/source/fn_args_layout-vertical.rs new file mode 100644 index 000000000000..fd6e3f0442ec --- /dev/null +++ b/tests/source/fn_args_layout-vertical.rs @@ -0,0 +1,33 @@ +// rustfmt-fn_params_layout: Vertical + +// Empty list should stay on one line. +fn do_bar( + +) -> u8 { + bar() +} + +// A single argument should stay on the same line. +fn do_bar( + a: u8) -> u8 { + bar() +} + +// Multiple arguments should each get their own line. +fn do_bar(a: u8, mut b: u8, c: &u8, d: &mut u8, closure: &Fn(i32) -> i32) -> i32 { + // This feature should not affect closures. + let bar = |x: i32, y: i32| -> i32 { x + y }; + bar(a, b) +} + +// If the first argument doesn't fit on the same line with the function name, +// the whole list should probably be pushed to the next line with hanging +// indent. That's not what happens though, so check current behaviour instead. +// In any case, it should maintain single argument per line. +fn do_this_that_and_the_other_thing( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: u8, + b: u8, c: u8, d: u8) { + this(); + that(); + the_other_thing(); +} diff --git a/tests/source/hard-tabs.rs b/tests/source/hard-tabs.rs new file mode 100644 index 000000000000..e4a0f4170075 --- /dev/null +++ b/tests/source/hard-tabs.rs @@ -0,0 +1,84 @@ +// rustfmt-normalize_comments: true +// rustfmt-wrap_comments: true +// rustfmt-hard_tabs: true + +fn main() { +let x = Bar; + +let y = Foo {a: x }; + +Foo { a: foo() /* comment*/, /* comment*/ b: bar(), ..something }; + +fn foo(a: i32, a: i32, a: i32, a: i32, a: i32, a: i32, a: i32, a: i32, a: i32, a: i32, a: i32) {} + +let str = "AAAAAAAAAAAAAAaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaAa"; + +if let (some_very_large, tuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuple) = 1 ++ 2 + 3 { +} + + if cond() { + something(); + } else if different_cond() { + something_else(); + } else { + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + } + +unsafe /* very looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong comment */ {} + +unsafe // So this is a very long comment. + // Multi-line, too. + // Will it still format correctly? +{ +} + +let chain = funktion_kall().go_to_next_line_with_tab().go_to_next_line_with_tab().go_to_next_line_with_tab(); + +let z = [xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, yyyyyyyyyyyyyyyyyyyyyyyyyyy, zzzzzzzzzzzzzzzzzz, q]; + +fn generic(arg: T) -> &SomeType + where T: Fn(// First arg + A, + // Second argument + B, C, D, /* pre comment */ E /* last comment */) -> &SomeType { + arg(a, b, c, d, e) +} + + loong_func().quux(move || { + if true { + 1 + } else { + 2 + } + }); + + fffffffffffffffffffffffffffffffffff(a, + { + SCRIPT_TASK_ROOT + .with(|root| { + *root.borrow_mut() = Some(&script_task); + }); + }); + a.b + .c + .d(); + + x().y(|| { + match cond() { + true => (), + false => (), + } + }); +} + +// #2296 +impl Foo { + // a comment + // on multiple lines + fn foo() { + // another comment + // on multiple lines + let x = true; + } +} diff --git a/tests/source/hello.rs b/tests/source/hello.rs new file mode 100644 index 000000000000..f892e6debb10 --- /dev/null +++ b/tests/source/hello.rs @@ -0,0 +1,6 @@ +// rustfmt-config: small_tabs.toml +// rustfmt-target: hello.rs + +// Smoke test - hello world. + +fn main() { println!("Hello world!"); } diff --git a/tests/source/hello2.rs b/tests/source/hello2.rs new file mode 100644 index 000000000000..48af7de38874 --- /dev/null +++ b/tests/source/hello2.rs @@ -0,0 +1,8 @@ +// rustfmt-config: small_tabs.toml +// rustfmt-target: hello.rs + +// Smoke test - hello world. + +fn main( ) { +println!("Hello world!"); +} diff --git a/tests/source/hex_literal_lower.rs b/tests/source/hex_literal_lower.rs new file mode 100644 index 000000000000..ce307b3aa521 --- /dev/null +++ b/tests/source/hex_literal_lower.rs @@ -0,0 +1,5 @@ +// rustfmt-hex_literal_case: Lower +fn main() { + let h1 = 0xCAFE_5EA7; + let h2 = 0xCAFE_F00Du32; +} diff --git a/tests/source/hex_literal_upper.rs b/tests/source/hex_literal_upper.rs new file mode 100644 index 000000000000..b1092ad71ba1 --- /dev/null +++ b/tests/source/hex_literal_upper.rs @@ -0,0 +1,5 @@ +// rustfmt-hex_literal_case: Upper +fn main() { + let h1 = 0xCaFE_5ea7; + let h2 = 0xCAFE_F00Du32; +} diff --git a/tests/source/if_while_or_patterns.rs b/tests/source/if_while_or_patterns.rs new file mode 100644 index 000000000000..f01df7e91586 --- /dev/null +++ b/tests/source/if_while_or_patterns.rs @@ -0,0 +1,27 @@ +#![feature(if_while_or_patterns)] + +fn main() { + if let 0 | 1 = 0 { + println!("hello, world"); + }; + + if let aaaaaaaaaaaaaaaaaaaaaaaaaa | bbbbbbbbbbbbbbbbbbbbbbbbbbb | cccccccccccccccc | d_100 = 0 { + println!("hello, world"); + } + + if let aaaaaaaaaaaaaaaaaaaaaaaaaa | bbbbbbbbbbbbbbbbbbbbbbb | ccccccccccccccccccccc | d_101 = 0 { + println!("hello, world"); + } + + if let aaaaaaaaaaaaaaaaaaaaaaaaaaaa | bbbbbbbbbbbbbbbbbbbbbbb | ccccccccccccccccccccc | d_103 = 0 { + println!("hello, world"); + } + + if let aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | bbbbbbbbbbbbbbbbbbbbbbb | ccccccccccccccccccccc | d_105 = 0 { + println!("hello, world"); + } + + while let xxx | xxx | xxx | xxx | xxx | xxx | xxx | xxx | xxx | xxx | xxx | xxx | xxx | xxx | xxx | xxx | xxx | xxx | xxx | xxx | xxx | xxx | xxx | xxx | xxx | xxx | xxx = foo_bar(bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, cccccccccccccccccccccccccccccccccccccccc) { + println!("hello, world"); + } +} diff --git a/tests/source/immovable_generators.rs b/tests/source/immovable_generators.rs new file mode 100644 index 000000000000..c57a1e144834 --- /dev/null +++ b/tests/source/immovable_generators.rs @@ -0,0 +1,7 @@ +#![feature(generators)] + +unsafe fn foo() { + let mut ga = static || { + yield 1; + }; +} diff --git a/tests/source/impls.rs b/tests/source/impls.rs new file mode 100644 index 000000000000..dcd1f0cd5b09 --- /dev/null +++ b/tests/source/impls.rs @@ -0,0 +1,178 @@ +// rustfmt-normalize_comments: true +impl Foo for Bar { fn foo() { "hi" } } + +pub impl Foo for Bar { + // Associated Constants + const Baz: i32 = 16; + // Associated Types + type FooBar = usize; + // Comment 1 + fn foo() { "hi" } + // Comment 2 + fn foo() { "hi" } + // Comment 3 +} + +#[inherent] +impl Visible for Bar { + pub const C: i32; + pub type T; + pub fn f(); + pub fn g() {} +} + +pub unsafe impl<'a, 'b, X, Y: Foo> !Foo<'a, X> for Bar<'b, Y> where X: Foo<'a, Z> { + fn foo() { "hi" } +} + +impl<'a, 'b, X, Y: Foo> Foo<'a, X> for Bar<'b, Y> where X: Fooooooooooooooooooooooooooooo<'a, Z> +{ + fn foo() { "hi" } +} + +impl<'a, 'b, X, Y: Foo> Foo<'a, X> for Bar<'b, Y> where X: Foooooooooooooooooooooooooooo<'a, Z> +{ + fn foo() { "hi" } +} + +impl Foo for Bar where T: Baz +{ +} + +impl Foo for Bar where T: Baz { /* Comment */ } + +impl Foo { + fn foo() {} +} + +impl Boo { + + // BOO + fn boo() {} + // FOO + + + +} + +mod a { + impl Foo { + // Hello! + fn foo() {} + } +} + + +mod b { + mod a { + impl Foo { + fn foo() {} + } + } +} + +impl Foo { add_fun!(); } + +impl Blah { + fn boop() {} + add_fun!(); +} + +impl X { fn do_parse( mut self : X ) {} } + +impl Y5000 { + fn bar(self: X< 'a , 'b >, y: Y) {} + + fn bad(&self, ( x, y): CoorT) {} + + fn turbo_bad(self: X< 'a , 'b > , ( x, y): CoorT) { + + } +} + +pub impl Foo for Bar where T: Foo +{ + fn foo() { "hi" } +} + +pub impl Foo for Bar where T: Foo, Z: Baz {} + +mod m { + impl PartialEq for S where T: PartialEq { + fn eq(&self, other: &Self) { + true + } + } + + impl PartialEq for S where T: PartialEq { } + } + +impl Handle, HandleType> { +} + +impl PartialEq for Handle, HandleType> { +} + +mod x { + impl Foo + where A: 'static, + B: 'static, + C: 'static, + D: 'static { } +} + +impl Issue1249 { + // Creates a new flow constructor. + fn foo() {} +} + +// #1600 +impl<#[may_dangle] K, #[may_dangle] V> Drop for RawTable { + fn drop() {} +} + +// #1168 +pub trait Number: Copy + Eq + Not + Shl + + Shr + + BitAnd + BitOr + BitAndAssign + BitOrAssign + + + +{ + // test + fn zero() -> Self; +} + +// #1642 +pub trait SomeTrait : Clone + Eq + PartialEq + Ord + PartialOrd + Default + Hash + Debug + Display + Write + Read + FromStr { + // comment +} + +// #1995 +impl Foo { + fn f( + S { + aaaaaaaaaa: aaaaaaaaaa, + bbbbbbbbbb: bbbbbbbbbb, + cccccccccc: cccccccccc, + }: S + ) -> u32{ + 1 + } +} + +// #2491 +impl<'a, 'b, 'c> SomeThing for (&'a mut SomethingLong, &'b mut SomethingLong, &'c mut SomethingLong) { + fn foo() {} +} + +// #2746 +impl<'seq1, 'seq2, 'body, 'scope, Channel> Adc12< Dual, MasterRunningDma<'seq1, 'body, 'scope, Channel>, SlaveRunningDma<'seq2, 'body, 'scope>, > where Channel: DmaChannel, {} + +// #4084 +impl const std::default::Default for Struct { + #[inline] + fn default() -> Self { + Self { f: 12.5 } + } +} diff --git a/tests/source/imports/imports-impl-only-use.rs b/tests/source/imports/imports-impl-only-use.rs new file mode 100644 index 000000000000..d290d8d91858 --- /dev/null +++ b/tests/source/imports/imports-impl-only-use.rs @@ -0,0 +1,4 @@ +#![feature(underscore_imports)] + +use attr; +use std::iter::Iterator as _; diff --git a/tests/source/imports/imports-reorder-lines-and-items.rs b/tests/source/imports/imports-reorder-lines-and-items.rs new file mode 100644 index 000000000000..b6380f31c610 --- /dev/null +++ b/tests/source/imports/imports-reorder-lines-and-items.rs @@ -0,0 +1,7 @@ +/// This comment should stay with `use std::str;` +use std::str; +use std::cmp::{d, c, b, a}; +use std::ddd::aaa; +use std::ddd::{d as p, c as g, b, a}; +// This comment should stay with `use std::ddd:bbb;` +use std::ddd::bbb; diff --git a/tests/source/imports/imports-reorder-lines.rs b/tests/source/imports/imports-reorder-lines.rs new file mode 100644 index 000000000000..2b018544eaeb --- /dev/null +++ b/tests/source/imports/imports-reorder-lines.rs @@ -0,0 +1,32 @@ +use std::str; +use std::cmp::{d, c, b, a}; +use std::cmp::{b, e, g, f}; +use std::ddd::aaa; +// This comment should stay with `use std::ddd;` +use std::ddd; +use std::ddd::bbb; + +mod test { +} + +use aaa::bbb; +use aaa; +use aaa::*; + +mod test {} +// If item names are equal, order by rename + +use test::{a as bb, b}; +use test::{a as aa, c}; + +mod test {} +// If item names are equal, order by rename - no rename comes before a rename + +use test::{a as bb, b}; +use test::{a, c}; + +mod test {} +// `self` always comes first + +use test::{a as aa, c}; +use test::{self as bb, b}; diff --git a/tests/source/imports/imports-reorder.rs b/tests/source/imports/imports-reorder.rs new file mode 100644 index 000000000000..cbe9d6ca78ad --- /dev/null +++ b/tests/source/imports/imports-reorder.rs @@ -0,0 +1,5 @@ +// rustfmt-normalize_comments: true + +use path::{C,/*A*/ A, B /* B */, self /* self */}; + +use {ab, ac, aa, Z, b}; diff --git a/tests/source/imports/imports.rs b/tests/source/imports/imports.rs new file mode 100644 index 000000000000..4dfc6ed94e34 --- /dev/null +++ b/tests/source/imports/imports.rs @@ -0,0 +1,107 @@ +// rustfmt-normalize_comments: true + +// Imports. + +// Long import. +use rustc_ast::ast::{ItemForeignMod, ItemImpl, ItemMac, ItemMod, ItemStatic, ItemDefaultImpl}; +use exceedingly::looooooooooooooooooooooooooooooooooooooooooooooooooooooooooong::import::path::{ItemA, ItemB}; +use exceedingly::loooooooooooooooooooooooooooooooooooooooooooooooooooooooong::import::path::{ItemA, ItemB}; + +use list::{ + // Some item + SomeItem /* Comment */, /* Another item */ AnotherItem /* Another Comment */, // Last Item + LastItem +}; + +use test::{ Other /* C */ , /* A */ self /* B */ }; + +use rustc_ast::{self}; +use {/* Pre-comment! */ + Foo, Bar /* comment */}; +use Foo::{Bar, Baz}; +pub use rustc_ast::ast::{Expr_, Expr, ExprAssign, ExprCall, ExprMethodCall, ExprPath}; + +use rustc_ast::some::{}; + +use self; +use std::io::{self}; +use std::io::self; + +mod Foo { + pub use rustc_ast::ast::{ + ItemForeignMod, + ItemImpl, + ItemMac, + ItemMod, + ItemStatic, + ItemDefaultImpl + }; + + mod Foo2 { + pub use rustc_ast::ast::{ItemForeignMod, ItemImpl, ItemMac, ItemMod, ItemStatic, self, ItemDefaultImpl}; + } +} + +fn test() { +use Baz::*; + use Qux; +} + +// Simple imports +use foo::bar::baz as baz ; +use bar::quux as kaas; +use foo; + +// With aliases. +use foo::{self as bar, baz}; +use foo::{self as bar}; +use foo::{qux as bar}; +use foo::{baz, qux as bar}; + +// With absolute paths +use ::foo; +use ::foo::{Bar}; +use ::foo::{Bar, Baz}; +use ::{Foo}; +use ::{Bar, Baz}; + +// Root globs +use *; +use ::*; + +// spaces used to cause glob imports to disappear (#1356) +use super:: * ; +use foo::issue_1356:: * ; + +// We shouldn't remove imports which have attributes attached (#1858) +#[cfg(unix)] +use self::unix::{}; + +// nested imports +use foo::{a, bar::{baz, qux, xxxxxxxxxxx, yyyyyyyyyyyyy, zzzzzzzzzzzzzzzz, foo::{a, b, cxxxxxxxxxxxxx, yyyyyyyyyyyyyy, zzzzzzzzzzzzzzzz}}, b, boo, c,}; + +use fooo::{baar::{foobar::{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy, zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz}}, z, bar, bar::*, x, y}; + +use exonum::{api::{Api, ApiError}, blockchain::{self, BlockProof, Blockchain, Transaction, TransactionSet}, crypto::{Hash, PublicKey}, helpers::Height, node::TransactionSend, storage::{ListProof, MapProof}}; + +// nested imports with a single sub-tree. +use a::{b::{c::*}}; +use a::{b::{c::{}}}; +use a::{b::{c::d}}; +use a::{b::{c::{xxx, yyy, zzz}}}; + +// #2645 +/// This line is not affected. +// This line is deleted. +use c; + +// #2670 +#[macro_use] +use imports_with_attr; + +// #2888 +use std::f64::consts::{SQRT_2, E, PI}; + +// #3273 +#[rustfmt::skip] +use std::fmt::{self, {Display, Formatter}}; diff --git a/tests/source/imports/imports_block_indent.rs b/tests/source/imports/imports_block_indent.rs new file mode 100644 index 000000000000..016deefe58c0 --- /dev/null +++ b/tests/source/imports/imports_block_indent.rs @@ -0,0 +1,2 @@ +// #2569 +use apns2::request::notification::{Notificatio, NotificationBuilder, Priority, SilentNotificationBuilder}; diff --git a/tests/source/imports/imports_granularity_crate.rs b/tests/source/imports/imports_granularity_crate.rs new file mode 100644 index 000000000000..f6f7761e82ee --- /dev/null +++ b/tests/source/imports/imports_granularity_crate.rs @@ -0,0 +1,65 @@ +// rustfmt-imports_granularity: Crate + +use a::{c,d,b}; +use a::{d, e, b, a, f}; +use a::{f, g, c}; + +#[doc(hidden)] +use a::b; +use a::c; +use a::d; + +use a::{c, d, e}; +#[doc(hidden)] +use a::b; +use a::d; + +pub use foo::bar; +use foo::{a, b, c}; +pub use foo::foobar; + +use a::{b::{c::*}}; +use a::{b::{c::{}}}; +use a::{b::{c::d}}; +use a::{b::{c::{xxx, yyy, zzz}}}; + +// https://github.com/rust-lang/rustfmt/issues/3808 +use d::{self}; +use e::{self as foo}; +use f::{self, b}; +use g::a; +use g::{self, b}; +use h::{a}; +use i::a::{self}; +use j::{a::{self}}; + +use {k::{a, b}, l::{a, b}}; +use {k::{c, d}, l::{c, d}}; + +use b::{f::g, h::{i, j} /* After b::h group */}; +use b::e; +use b::{/* Before b::l group */ l::{self, m, n::o, p::*}, q}; +use b::d; +use b::r; // After b::r +use b::q::{self /* After b::q::self */}; +use b::u::{ + a, + b, +}; +use b::t::{ + // Before b::t::a + a, + b, +}; +use b::s::{ + a, + b, // After b::s::b +}; +use b::v::{ + // Before b::v::a + a, + // Before b::v::b + b, +}; +use b::t::{/* Before b::t::self */ self}; +use b::c; diff --git a/tests/source/imports/imports_granularity_default-with-dups.rs b/tests/source/imports/imports_granularity_default-with-dups.rs new file mode 100644 index 000000000000..cbb21a9f1b38 --- /dev/null +++ b/tests/source/imports/imports_granularity_default-with-dups.rs @@ -0,0 +1,6 @@ +use crate::lexer; +use crate::lexer::tokens::TokenData; +use crate::lexer::{tokens::TokenData}; +use crate::lexer::self; +use crate::lexer::{self}; +use crate::lexer::{self, tokens::TokenData}; diff --git a/tests/source/imports/imports_granularity_item-with-dups-StdExternalCrate-no-reorder.rs b/tests/source/imports/imports_granularity_item-with-dups-StdExternalCrate-no-reorder.rs new file mode 100644 index 000000000000..e23705a884fe --- /dev/null +++ b/tests/source/imports/imports_granularity_item-with-dups-StdExternalCrate-no-reorder.rs @@ -0,0 +1,13 @@ +// rustfmt-imports_granularity: Item +// rustfmt-reorder_imports: false +// rustfmt-group_imports: StdExternalCrate + +use crate::lexer; +use crate::lexer; +use crate::lexer::tokens::TokenData; +use crate::lexer::{tokens::TokenData}; +use crate::lexer::self; +use crate::lexer; +use crate::lexer; +use crate::lexer::{self}; +use crate::lexer::{self, tokens::TokenData}; diff --git a/tests/source/imports/imports_granularity_item-with-dups.rs b/tests/source/imports/imports_granularity_item-with-dups.rs new file mode 100644 index 000000000000..3e9589c299f6 --- /dev/null +++ b/tests/source/imports/imports_granularity_item-with-dups.rs @@ -0,0 +1,11 @@ +// rustfmt-imports_granularity: Item + +use crate::lexer; +use crate::lexer; +use crate::lexer::tokens::TokenData; +use crate::lexer::{tokens::TokenData}; +use crate::lexer::self; +use crate::lexer; +use crate::lexer; +use crate::lexer::{self}; +use crate::lexer::{self, tokens::TokenData}; diff --git a/tests/source/imports/imports_granularity_item.rs b/tests/source/imports/imports_granularity_item.rs new file mode 100644 index 000000000000..b82c0d33cafd --- /dev/null +++ b/tests/source/imports/imports_granularity_item.rs @@ -0,0 +1,34 @@ +// rustfmt-imports_granularity: Item + +use a::{b, c, d}; +use a::{f::g, h::{i, j}}; +use a::{l::{self, m, n::o, p::*}}; +use a::q::{self}; + +use b::{f::g, h::{i, j} /* After b::h group */}; +use b::e; +use b::{/* Before b::l group */ l::{self, m, n::o, p::*}, q}; +use b::d; +use b::r; // After b::r +use b::q::{self /* After b::q::self */}; +use b::u::{ + a, + b, +}; +use b::t::{ + // Before b::t::a + a, + b, +}; +use b::s::{ + a, + b, // After b::s::b +}; +use b::v::{ + // Before b::v::a + a, + // Before b::v::b + b, +}; +use b::t::{/* Before b::t::self */ self}; +use b::c; diff --git a/tests/source/imports/imports_granularity_module.rs b/tests/source/imports/imports_granularity_module.rs new file mode 100644 index 000000000000..c7f68cea6d47 --- /dev/null +++ b/tests/source/imports/imports_granularity_module.rs @@ -0,0 +1,47 @@ +// rustfmt-imports_granularity: Module + +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}; +use foo::e; + +use bar::{ + // comment + a::b, + // more comment + c::d, + e::f, +}; + +use b::{f::g, h::{i, j} /* After b::h group */}; +use b::e; +use b::{/* Before b::l group */ l::{self, m, n::o, p::*}, q}; +use b::d; +use b::r; // After b::r +use b::q::{self /* After b::q::self */}; +use b::u::{ + a, + b, +}; +use b::t::{ + // Before b::t::a + a, + b, +}; +use b::s::{ + a, + b, // After b::s::b +}; +use b::v::{ + // Before b::v::a + a, + // Before b::v::b + b, +}; +use b::t::{/* Before b::t::self */ self}; +use b::c; diff --git a/tests/source/imports_granularity_one.rs b/tests/source/imports_granularity_one.rs new file mode 100644 index 000000000000..4d5a47956413 --- /dev/null +++ b/tests/source/imports_granularity_one.rs @@ -0,0 +1,88 @@ +// rustfmt-imports_granularity: One + +use b; +use a::ac::{aca, acb}; +use a::{aa::*, ab}; + +use a as x; +use b::ba; +use a::{aa, ab}; + +use a::aa::aaa; +use a::ab::aba as x; +use a::aa::*; + +use a::aa; +use a::ad::ada; +#[cfg(test)] +use a::{ab, ac::aca}; +use b; +#[cfg(test)] +use b::{ + ba, bb, + bc::bca::{bcaa, bcab}, +}; + +pub use a::aa; +pub use a::ae; +use a::{ab, ac, ad}; +use b::ba; +pub use b::{bb, bc::bca}; + +use a::aa::aaa; +use a::ac::{aca, acb}; +use a::{aa::*, ab}; +use b::{ + ba, + bb::{self, bba}, +}; + +use crate::a; +use crate::b::ba; +use c::ca; + +use super::a; +use c::ca; +use super::b::ba; + +use crate::a; +use super::b; +use c::{self, ca}; + +use a::{ + // some comment + aa::{aaa, aab}, + ab, + // another comment + ac::aca, +}; +use b as x; +use a::ad::ada; + +use b::{f::g, h::{i, j} /* After b::h group */}; +use b::e; +use b::{/* Before b::l group */ l::{self, m, n::o, p::*}, q}; +use b::d; +use b::r; // After b::r +use b::q::{self /* After b::q::self */}; +use b::u::{ + a, + b, +}; +use b::t::{ + // Before b::t::a + a, + b, +}; +use b::s::{ + a, + b, // After b::s::b +}; +use b::v::{ + // Before b::v::a + a, + // Before b::v::b + b, +}; +use b::t::{/* Before b::t::self */ self}; +use b::c; diff --git a/tests/source/imports_raw_identifiers/version_One.rs b/tests/source/imports_raw_identifiers/version_One.rs new file mode 100644 index 000000000000..bc4b5b135696 --- /dev/null +++ b/tests/source/imports_raw_identifiers/version_One.rs @@ -0,0 +1,5 @@ +// rustfmt-version:One + +use websocket::client::ClientBuilder; +use websocket::r#async::futures::Stream; +use websocket::result::WebSocketError; diff --git a/tests/source/imports_raw_identifiers/version_Two.rs b/tests/source/imports_raw_identifiers/version_Two.rs new file mode 100644 index 000000000000..88e7fbd01ca6 --- /dev/null +++ b/tests/source/imports_raw_identifiers/version_Two.rs @@ -0,0 +1,5 @@ +// rustfmt-version:Two + +use websocket::client::ClientBuilder; +use websocket::r#async::futures::Stream; +use websocket::result::WebSocketError; diff --git a/tests/source/invalid-rust-code-in-doc-comment.rs b/tests/source/invalid-rust-code-in-doc-comment.rs new file mode 100644 index 000000000000..835b0261b760 --- /dev/null +++ b/tests/source/invalid-rust-code-in-doc-comment.rs @@ -0,0 +1,20 @@ +// rustfmt-format_code_in_doc_comments: true + +/// ```rust +/// if (true) { … } +/// ``` +fn a() { +} + +/// ```rust +/// if foo() { +/// … +/// } +/// ``` +fn a() { +} + +/// ```rust +/// k1 == k2 ⇒ hash(k1) == hash(k2) +/// ``` +pub struct a ; diff --git a/tests/source/issue-1021.rs b/tests/source/issue-1021.rs new file mode 100644 index 000000000000..380e24cc0b02 --- /dev/null +++ b/tests/source/issue-1021.rs @@ -0,0 +1,22 @@ +// rustfmt-normalize_comments: true +fn main() { + match x { + S(true , .., true ) => (), + S(true , .. ) => (), + S(.., true ) => (), + S( .. ) => (), + S(_) => (), + S(/* .. */ .. ) => (), + S(/* .. */ .., true ) => (), + } + + match y { + (true , .., true ) => (), + (true , .. ) => (), + (.., true ) => (), + ( .. ) => (), + (_,) => (), + (/* .. */ .. ) => (), + (/* .. */ .., true ) => (), + } +} diff --git a/tests/source/issue-1049.rs b/tests/source/issue-1049.rs new file mode 100644 index 000000000000..bcfba41e7687 --- /dev/null +++ b/tests/source/issue-1049.rs @@ -0,0 +1,18 @@ +// Test overlong function signature +pub unsafe fn reborrow_mut(&mut X: Abcde) -> Handle, HandleType> { +} + +pub fn merge(mut X: Abcdef) -> Handle, K, V, marker::Internal>, marker::Edge> { +} + +impl Handle { + pub fn merge(a: Abcd) -> Handle, K, V, marker::Internal>, marker::Edge> { + } +} + +// Long function without return type that should not be reformatted. +fn veeeeeeeeeeeeeeeeeeeeery_long_name(a: FirstTypeeeeeeeeee, b: SecondTypeeeeeeeeeeeeeeeeeeeeeee) {} + +fn veeeeeeeeeeeeeeeeeeeeeery_long_name(a: FirstTypeeeeeeeeee, b: SecondTypeeeeeeeeeeeeeeeeeeeeeee) {} + +fn veeeeeeeeeeeeeeeeeeeeeeery_long_name(a: FirstTypeeeeeeeeee, b: SecondTypeeeeeeeeeeeeeeeeeeeeeee) {} diff --git a/tests/source/issue-1111.rs b/tests/source/issue-1111.rs new file mode 100644 index 000000000000..2e1a89ad78eb --- /dev/null +++ b/tests/source/issue-1111.rs @@ -0,0 +1 @@ +use bar; diff --git a/tests/source/issue-1120.rs b/tests/source/issue-1120.rs new file mode 100644 index 000000000000..e85c9af99d45 --- /dev/null +++ b/tests/source/issue-1120.rs @@ -0,0 +1,9 @@ +// rustfmt-reorder_imports: true + +// Ensure that a use at the start of an inline module is correctly formatted. +mod foo {use bar;} + +// Ensure that an indented `use` gets the correct indentation. +mod foo { + use bar; +} diff --git a/tests/source/issue-1124.rs b/tests/source/issue-1124.rs new file mode 100644 index 000000000000..35c2197fa346 --- /dev/null +++ b/tests/source/issue-1124.rs @@ -0,0 +1,15 @@ +// rustfmt-reorder_imports: true + +use d; use c; use b; use a; +// The previous line has a space after the `use a;` + +mod a { use d; use c; use b; use a; } + +use z; + +use y; + + + +use x; +use a; diff --git a/tests/source/issue-1127.rs b/tests/source/issue-1127.rs new file mode 100644 index 000000000000..b49db4e3f65d --- /dev/null +++ b/tests/source/issue-1127.rs @@ -0,0 +1,23 @@ +// rustfmt-max_width: 120 +// rustfmt-match_arm_blocks: false +// rustfmt-match_block_trailing_comma: true + +fn a_very_very_very_very_very_very_very_very_very_very_very_long_function_name() -> i32 { + 42 +} + +enum TestEnum { + AVeryVeryLongEnumName, + AnotherVeryLongEnumName, + TheLastVeryLongEnumName, +} + +fn main() { + let var = TestEnum::AVeryVeryLongEnumName; + let num = match var { + TestEnum::AVeryVeryLongEnumName => a_very_very_very_very_very_very_very_very_very_very_very_long_function_name(), + TestEnum::AnotherVeryLongEnumName => a_very_very_very_very_very_very_very_very_very_very_very_long_function_name(), + TestEnum::TheLastVeryLongEnumName => a_very_very_very_very_very_very_very_very_very_very_very_long_function_name(), + }; +} + diff --git a/tests/source/issue-1158.rs b/tests/source/issue-1158.rs new file mode 100644 index 000000000000..6742e17459ef --- /dev/null +++ b/tests/source/issue-1158.rs @@ -0,0 +1,3 @@ +trait T { + itemmacro!(this, is.now() .formatted(yay)); +} diff --git a/tests/source/issue-1177.rs b/tests/source/issue-1177.rs new file mode 100644 index 000000000000..3ac423c5aef9 --- /dev/null +++ b/tests/source/issue-1177.rs @@ -0,0 +1,7 @@ +// rustfmt-normalize_comments: true +fn main() { + // Line Comment + /* Block Comment */ + + let d = 5; +} diff --git a/tests/source/issue-1192.rs b/tests/source/issue-1192.rs new file mode 100644 index 000000000000..4e39fbf9a366 --- /dev/null +++ b/tests/source/issue-1192.rs @@ -0,0 +1,3 @@ +fn main() { + assert!(true) ; +} diff --git a/tests/source/issue-1210/a.rs b/tests/source/issue-1210/a.rs new file mode 100644 index 000000000000..6bb9964b4e35 --- /dev/null +++ b/tests/source/issue-1210/a.rs @@ -0,0 +1,12 @@ +// rustfmt-format_strings: true +// rustfmt-max_width: 50 + +impl Foo { + fn cxx(&self, target: &str) -> &Path { + match self.cxx.get(target) { + Some(p) => p.path(), + None => panic!("\n\ntarget `{}` is not configured as a host, + only as a target\n\n", target), + } + } +} diff --git a/tests/source/issue-1210/b.rs b/tests/source/issue-1210/b.rs new file mode 100644 index 000000000000..8c71ef98b7fc --- /dev/null +++ b/tests/source/issue-1210/b.rs @@ -0,0 +1,12 @@ +// rustfmt-format_strings: true +// rustfmt-max_width: 50 + +impl Foo { + fn cxx(&self, target: &str) -> &Path { + match self.cxx.get(target) { + Some(p) => p.path(), + None => panic!("\ntarget `{}`: is not, configured as a host, + only as a target\n\n", target), + } + } +} diff --git a/tests/source/issue-1210/c.rs b/tests/source/issue-1210/c.rs new file mode 100644 index 000000000000..c080cef950b3 --- /dev/null +++ b/tests/source/issue-1210/c.rs @@ -0,0 +1,5 @@ +// rustfmt-format_strings: true +// rustfmt-max_width: 50 + +const foo: String = "trailing_spaces!! + keep them! Amet neque. Praesent rhoncus eros non velit."; diff --git a/tests/source/issue-1210/d.rs b/tests/source/issue-1210/d.rs new file mode 100644 index 000000000000..783736bc3b2c --- /dev/null +++ b/tests/source/issue-1210/d.rs @@ -0,0 +1,4 @@ +// rustfmt-wrap_comments: true + +// trailing_spaces_in_comment!! +// remove those from above diff --git a/tests/source/issue-1210/e.rs b/tests/source/issue-1210/e.rs new file mode 100644 index 000000000000..9abada1d6d86 --- /dev/null +++ b/tests/source/issue-1210/e.rs @@ -0,0 +1,8 @@ +// rustfmt-format_strings: true +// rustfmt-max_width: 50 + +// explicit line breaks should be kept in order to preserve the layout + +const foo: String = "Suspendisse vel augue at felis tincidunt sollicitudin. Fusce arcu. + Duis et odio et leo + sollicitudin consequat. Aliquam lobortis. Phasellus condimentum."; diff --git a/tests/source/issue-1211.rs b/tests/source/issue-1211.rs new file mode 100644 index 000000000000..5818736bf6ba --- /dev/null +++ b/tests/source/issue-1211.rs @@ -0,0 +1,15 @@ +fn main() { + for iface in &ifaces { + match iface.addr { + get_if_addrs::IfAddr::V4(ref addr) => { + match addr.broadcast { + Some(ip) => { + sock.send_to(&buf, (ip, 8765)).expect("foobar"); + } + _ => () + } + } + _ => () + }; + } +} diff --git a/tests/source/issue-1216.rs b/tests/source/issue-1216.rs new file mode 100644 index 000000000000..d727c158ab8e --- /dev/null +++ b/tests/source/issue-1216.rs @@ -0,0 +1,5 @@ +// rustfmt-normalize_comments: true +enum E { + A, //* I am not a block comment (caused panic) + B, +} diff --git a/tests/source/issue-1239.rs b/tests/source/issue-1239.rs new file mode 100644 index 000000000000..913058257ed3 --- /dev/null +++ b/tests/source/issue-1239.rs @@ -0,0 +1,9 @@ +fn foo() { + let with_alignment = if condition__uses_alignment_for_first_if__0 || + condition__uses_alignment_for_first_if__1 || + condition__uses_alignment_for_first_if__2 { + } else if condition__no_alignment_for_later_else__0 || + condition__no_alignment_for_later_else__1 || + condition__no_alignment_for_later_else__2 { + }; +} diff --git a/tests/source/issue-1278.rs b/tests/source/issue-1278.rs new file mode 100644 index 000000000000..e25376561a94 --- /dev/null +++ b/tests/source/issue-1278.rs @@ -0,0 +1,9 @@ +// rustfmt-indent_style = "block" + +#![feature(pub_restricted)] + +mod inner_mode { + pub(super) fn func_name(abc: i32) -> i32 { + abc + } +} diff --git a/tests/source/issue-1350.rs b/tests/source/issue-1350.rs new file mode 100644 index 000000000000..1baa1985a7ea --- /dev/null +++ b/tests/source/issue-1350.rs @@ -0,0 +1,16 @@ +// rustfmt-max_width: 120 +// rustfmt-comment_width: 110 + +impl Struct { + fn fun() { + let result = match ::deserialize(&json) { + Ok(v) => v, + Err(e) => { + match ::deserialize(&json) { + Ok(v) => return Err(Error::with_json(v)), + Err(e2) => return Err(Error::with_json(e)), + } + } + }; + } +} diff --git a/tests/source/issue-1366.rs b/tests/source/issue-1366.rs new file mode 100644 index 000000000000..9d2964fc77cc --- /dev/null +++ b/tests/source/issue-1366.rs @@ -0,0 +1,12 @@ +fn main() { + fn f() -> Option { + Some("fffffffsssssssssddddssssfffffddddff").map(|s| s).map(|s| s.to_string()).map(|res| { + match Some(res) { + Some(ref s) if s == "" => 41, + Some(_) => 42, + _ => 43, + } + }) + } + println!("{:?}", f()) +} diff --git a/tests/source/issue-1468.rs b/tests/source/issue-1468.rs new file mode 100644 index 000000000000..4d0d4f0eb985 --- /dev/null +++ b/tests/source/issue-1468.rs @@ -0,0 +1,27 @@ +fn issue1468() { +euc_jp_decoder_functions!({ +let trail_minus_offset = byte.wrapping_sub(0xA1); +// Fast-track Hiragana (60% according to Lunde) +// and Katakana (10% according to Lunde). +if jis0208_lead_minus_offset == 0x03 && +trail_minus_offset < 0x53 { +// Hiragana +handle.write_upper_bmp(0x3041 + trail_minus_offset as u16) +} else if jis0208_lead_minus_offset == 0x04 && +trail_minus_offset < 0x56 { +// Katakana +handle.write_upper_bmp(0x30A1 + trail_minus_offset as u16) +} else if trail_minus_offset > (0xFE - 0xA1) { +if byte < 0x80 { +return (DecoderResult::Malformed(1, 0), +unread_handle_trail.unread(), +handle.written()); +} +return (DecoderResult::Malformed(2, 0), +unread_handle_trail.consumed(), +handle.written()); +} else { +unreachable!(); +} +}); +} diff --git a/tests/source/issue-1693.rs b/tests/source/issue-1693.rs new file mode 100644 index 000000000000..0622ce502351 --- /dev/null +++ b/tests/source/issue-1693.rs @@ -0,0 +1,3 @@ +fn issue1693() { + let pixel_data = vec![(f16::from_f32(0.82), f16::from_f32(1.78), f16::from_f32(0.21)); 256 * 256]; +} diff --git a/tests/source/issue-1800.rs b/tests/source/issue-1800.rs new file mode 100644 index 000000000000..eae226532589 --- /dev/null +++ b/tests/source/issue-1800.rs @@ -0,0 +1,3 @@ +#![doc(html_root_url = "http://example.com")] +#[cfg(feature = "foo")] +fn a() {} diff --git a/tests/source/issue-1914.rs b/tests/source/issue-1914.rs new file mode 100644 index 000000000000..447296c4b874 --- /dev/null +++ b/tests/source/issue-1914.rs @@ -0,0 +1,6 @@ +// rustfmt-max_width: 80 + +extern "C" { +#[link_name = "_ZN7MyClass26example_check_no_collisionE"] + pub static mut MyClass_example_check_no_collision : * const :: std :: os :: raw :: c_int ; +} diff --git a/tests/source/issue-2025.rs b/tests/source/issue-2025.rs new file mode 100644 index 000000000000..c6f61b4e3e19 --- /dev/null +++ b/tests/source/issue-2025.rs @@ -0,0 +1,8 @@ + + + + +// See if rustfmt removes empty lines on top of the file. +pub fn foo() { + println!("hello, world"); +} diff --git a/tests/source/issue-2111.rs b/tests/source/issue-2111.rs new file mode 100644 index 000000000000..ccd113696e10 --- /dev/null +++ b/tests/source/issue-2111.rs @@ -0,0 +1,26 @@ +// An import with single line comments. +use super::{ + SCHEMA_VERSIONS, + LodaModel, + ModelProperties, + StringMap, + ModelSelector, + RequestDescription, + MethodDescription, + ModelBehaviour, + ModelRequestGraph, + DelayChoice, + Holding, + Destinations, + ModelEdges, + Switch, + // ModelMetaData, + // Generated, + // SecondsString, + // DateString, + // ModelConfiguration, + // ModelRequests, + // RestResponse, + // RestResponseCode, + // UniformHolding +}; diff --git a/tests/source/issue-2164.rs b/tests/source/issue-2164.rs new file mode 100644 index 000000000000..6c288e1bd62a --- /dev/null +++ b/tests/source/issue-2164.rs @@ -0,0 +1,4 @@ +// A stress test against code generated by bindgen. +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct emacs_env_25 { pub size : isize , pub private_members : * mut emacs_env_private , pub make_global_ref : :: std :: option :: Option < unsafe extern "C" fn ( env : * mut emacs_env , any_reference : emacs_value ) -> emacs_value > , pub free_global_ref : :: std :: option :: Option < unsafe extern "C" fn ( env : * mut emacs_env , global_reference : emacs_value ) > , pub non_local_exit_check : :: std :: option :: Option < unsafe extern "C" fn ( env : * mut emacs_env ) -> emacs_funcall_exit > , pub non_local_exit_clear : :: std :: option :: Option < unsafe extern "C" fn ( env : * mut emacs_env ) > , pub non_local_exit_get : :: std :: option :: Option < unsafe extern "C" fn ( env : * mut emacs_env , non_local_exit_symbol_out : * mut emacs_value , non_local_exit_data_out : * mut emacs_value ) -> emacs_funcall_exit > , pub non_local_exit_signal : :: std :: option :: Option < unsafe extern "C" fn ( env : * mut emacs_env , non_local_exit_symbol : emacs_value , non_local_exit_data : emacs_value ) > , pub non_local_exit_throw : :: std :: option :: Option < unsafe extern "C" fn ( env : * mut emacs_env , tag : emacs_value , value : emacs_value ) > , pub make_function : :: std :: option :: Option < unsafe extern "C" fn ( env : * mut emacs_env , min_arity : isize , max_arity : isize , function : :: std :: option :: Option < unsafe extern "C" fn ( env : * mut emacs_env , nargs : isize , args : * mut emacs_value , arg1 : * mut ::libc :: c_void ) -> emacs_value > , documentation : * const ::libc :: c_char , data : * mut ::libc :: c_void ) -> emacs_value > , pub funcall : :: std :: option :: Option < unsafe extern "C" fn ( env : * mut emacs_env , function : emacs_value , nargs : isize , args : * mut emacs_value ) -> emacs_value > , pub intern : :: std :: option :: Option < unsafe extern "C" fn ( env : * mut emacs_env , symbol_name : * const ::libc :: c_char ) -> emacs_value > , pub type_of : :: std :: option :: Option < unsafe extern "C" fn ( env : * mut emacs_env , value : emacs_value ) -> emacs_value > , pub is_not_nil : :: std :: option :: Option < unsafe extern "C" fn ( env : * mut emacs_env , value : emacs_value ) -> bool > , pub eq : :: std :: option :: Option < unsafe extern "C" fn ( env : * mut emacs_env , a : emacs_value , b : emacs_value ) -> bool > , pub extract_integer : :: std :: option :: Option < unsafe extern "C" fn ( env : * mut emacs_env , value : emacs_value ) -> intmax_t > , pub make_integer : :: std :: option :: Option < unsafe extern "C" fn ( env : * mut emacs_env , value : intmax_t ) -> emacs_value > , pub extract_float : :: std :: option :: Option < unsafe extern "C" fn ( env : * mut emacs_env , value : emacs_value ) -> f64 > , pub make_float : :: std :: option :: Option < unsafe extern "C" fn ( env : * mut emacs_env , value : f64 ) -> emacs_value > , pub copy_string_contents : :: std :: option :: Option < unsafe extern "C" fn ( env : * mut emacs_env , value : emacs_value , buffer : * mut ::libc :: c_char , size_inout : * mut isize ) -> bool > , pub make_string : :: std :: option :: Option < unsafe extern "C" fn ( env : * mut emacs_env , contents : * const ::libc :: c_char , length : isize ) -> emacs_value > , pub make_user_ptr : :: std :: option :: Option < unsafe extern "C" fn ( env : * mut emacs_env , fin : :: std :: option :: Option < unsafe extern "C" fn ( arg1 : * mut ::libc :: c_void ) > , ptr : * mut ::libc :: c_void ) -> emacs_value > , pub get_user_ptr : :: std :: option :: Option < unsafe extern "C" fn ( env : * mut emacs_env , uptr : emacs_value ) -> * mut ::libc :: c_void > , pub set_user_ptr : :: std :: option :: Option < unsafe extern "C" fn ( env : * mut emacs_env , uptr : emacs_value , ptr : * mut ::libc :: c_void ) > , pub get_user_finalizer : :: std :: option :: Option < unsafe extern "C" fn ( arg1 : * mut ::libc :: c_void , env : * mut emacs_env , uptr : emacs_value ) -> :: std :: option :: Option < unsafe extern "C" fn ( arg1 : * mut ::libc :: c_void , env : * mut emacs_env , uptr : emacs_value ) > > , pub set_user_finalizer : :: std :: option :: Option < unsafe extern "C" fn ( env : * mut emacs_env , uptr : emacs_value , fin : :: std :: option :: Option < unsafe extern "C" fn ( arg1 : * mut ::libc :: c_void ) > ) > , pub vec_get : :: std :: option :: Option < unsafe extern "C" fn ( env : * mut emacs_env , vec : emacs_value , i : isize ) -> emacs_value > , pub vec_set : :: std :: option :: Option < unsafe extern "C" fn ( env : * mut emacs_env , vec : emacs_value , i : isize , val : emacs_value ) > , pub vec_size : :: std :: option :: Option < unsafe extern "C" fn ( env : * mut emacs_env , vec : emacs_value ) -> isize > , } diff --git a/tests/source/issue-2179/one.rs b/tests/source/issue-2179/one.rs new file mode 100644 index 000000000000..d23947931fff --- /dev/null +++ b/tests/source/issue-2179/one.rs @@ -0,0 +1,36 @@ +// rustfmt-version: One +// rustfmt-error_on_line_overflow: false + +fn issue_2179() { + let (opts, rustflags, clear_env_rust_log) = + { + // We mustn't lock configuration for the whole build process + let rls_config = rls_config.lock().unwrap(); + + let opts = CargoOptions::new(&rls_config); + trace!("Cargo compilation options:\n{:?}", opts); + let rustflags = prepare_cargo_rustflags(&rls_config); + + // Warn about invalid specified bin target or package depending on current mode + // TODO: Return client notifications along with diagnostics to inform the user + if !rls_config.workspace_mode { + let cur_pkg_targets = ws.current().unwrap().targets(); + + if let &Some(ref build_bin) = rls_config.build_bin.as_ref() { + let mut bins = cur_pkg_targets.iter().filter(|x| x.is_bin()); + if let None = bins.find(|x| x.name() == build_bin) { + warn!("cargo - couldn't find binary `{}` specified in `build_bin` configuration", build_bin); + } + } + } else { + for package in &opts.package { + if let None = ws.members().find(|x| x.name() == package) { + warn!("cargo - couldn't find member package `{}` specified in `analyze_package` configuration", package); + } + } + } + + (opts, rustflags, rls_config.clear_env_rust_log) + }; + +} diff --git a/tests/source/issue-2179/two.rs b/tests/source/issue-2179/two.rs new file mode 100644 index 000000000000..f4cc9cc488bf --- /dev/null +++ b/tests/source/issue-2179/two.rs @@ -0,0 +1,36 @@ +// rustfmt-version: Two +// rustfmt-error_on_line_overflow: false + +fn issue_2179() { + let (opts, rustflags, clear_env_rust_log) = + { + // We mustn't lock configuration for the whole build process + let rls_config = rls_config.lock().unwrap(); + + let opts = CargoOptions::new(&rls_config); + trace!("Cargo compilation options:\n{:?}", opts); + let rustflags = prepare_cargo_rustflags(&rls_config); + + // Warn about invalid specified bin target or package depending on current mode + // TODO: Return client notifications along with diagnostics to inform the user + if !rls_config.workspace_mode { + let cur_pkg_targets = ws.current().unwrap().targets(); + + if let &Some(ref build_bin) = rls_config.build_bin.as_ref() { + let mut bins = cur_pkg_targets.iter().filter(|x| x.is_bin()); + if let None = bins.find(|x| x.name() == build_bin) { + warn!("cargo - couldn't find binary `{}` specified in `build_bin` configuration", build_bin); + } + } + } else { + for package in &opts.package { + if let None = ws.members().find(|x| x.name() == package) { + warn!("cargo - couldn't find member package `{}` specified in `analyze_package` configuration", package); + } + } + } + + (opts, rustflags, rls_config.clear_env_rust_log) + }; + +} diff --git a/tests/source/issue-2256.rs b/tests/source/issue-2256.rs new file mode 100644 index 000000000000..a206e8db6cf4 --- /dev/null +++ b/tests/source/issue-2256.rs @@ -0,0 +1,12 @@ +// こんにちは +use std::{}; +use std::borrow::Cow; + +/* comment 1 */ use std::{}; +/* comment 2 */ use std::{}; + + + + + +/* comment 3 */ use std::{}; diff --git a/tests/source/issue-2342.rs b/tests/source/issue-2342.rs new file mode 100644 index 000000000000..f86d24a146b4 --- /dev/null +++ b/tests/source/issue-2342.rs @@ -0,0 +1,5 @@ +// rustfmt-max_width: 80 + +struct Foo { + #[cfg(feature = "serde")] bytes: [[u8; 17]; 5], // Same size as signature::ED25519_PKCS8_V2_LEN +} diff --git a/tests/source/issue-2445.rs b/tests/source/issue-2445.rs new file mode 100644 index 000000000000..deef429dbee0 --- /dev/null +++ b/tests/source/issue-2445.rs @@ -0,0 +1,21 @@ +test!(RunPassPretty { + // comment + path: "tests/run-pass/pretty", + mode: "pretty", + suite: "run-pass", + default: false, + host: true // should, force, , no trailing comma here +}); + +test!(RunPassPretty { + // comment + path: "tests/run-pass/pretty", + mode: "pretty", + suite: "run-pass", + default: false, + host: true, // should, , preserve, the trailing comma +}); + +test!(Test{ + field: i32, // comment +}); diff --git a/tests/source/issue-2446.rs b/tests/source/issue-2446.rs new file mode 100644 index 000000000000..ad649d95c549 --- /dev/null +++ b/tests/source/issue-2446.rs @@ -0,0 +1,11 @@ +enum Issue2446 { + V { + f: u8, // x + }, +} + +enum Issue2446TrailingCommentsOnly { + V { + f: u8, /* */ + } +} diff --git a/tests/source/issue-2479.rs b/tests/source/issue-2479.rs new file mode 100644 index 000000000000..df50236d05d3 --- /dev/null +++ b/tests/source/issue-2479.rs @@ -0,0 +1,2 @@ +// Long attributes. +# [ derive ( Clone , Copy , Debug , PartialEq ) ] pub enum POLARITYR { # [ doc = "Task mode: No effect on pin from OUT[n] task. Event mode: no IN[n] event generated on pin activity." ] NONE , # [ doc = "Task mode: Set pin from OUT[n] task. Event mode: Generate IN[n] event when rising edge on pin." ] LOTOHI , # [ doc = "Task mode: Clear pin from OUT[n] task. Event mode: Generate IN[n] event when falling edge on pin." ] HITOLO , # [ doc = "Task mode: Toggle pin from OUT[n]. Event mode: Generate IN[n] when any change on pin." ] TOGGLE } diff --git a/tests/source/issue-2482/a.rs b/tests/source/issue-2482/a.rs new file mode 100644 index 000000000000..fbbcb52a878a --- /dev/null +++ b/tests/source/issue-2482/a.rs @@ -0,0 +1,9 @@ +// rustfmt-reorder_modules: true + +// Do not reorder inline modules. + +mod c; +mod a { + fn a() {} +} +mod b; diff --git a/tests/source/issue-2482/b.rs b/tests/source/issue-2482/b.rs new file mode 100644 index 000000000000..40a8d942126c --- /dev/null +++ b/tests/source/issue-2482/b.rs @@ -0,0 +1 @@ +pub fn b() {} diff --git a/tests/source/issue-2482/c.rs b/tests/source/issue-2482/c.rs new file mode 100644 index 000000000000..d937545511ef --- /dev/null +++ b/tests/source/issue-2482/c.rs @@ -0,0 +1 @@ +pub fn c() {} diff --git a/tests/source/issue-2496.rs b/tests/source/issue-2496.rs new file mode 100644 index 000000000000..0ebd4b510ece --- /dev/null +++ b/tests/source/issue-2496.rs @@ -0,0 +1,16 @@ +// rustfmt-indent_style: Visual +fn main() { + match option { + None => some_function(first_reasonably_long_argument, + second_reasonably_long_argument), + } +} + +fn main() { + match option { + None => { + some_function(first_reasonably_long_argument, + second_reasonably_long_argument) + } + } +} diff --git a/tests/source/issue-2520.rs b/tests/source/issue-2520.rs new file mode 100644 index 000000000000..5a23f10430d1 --- /dev/null +++ b/tests/source/issue-2520.rs @@ -0,0 +1,13 @@ +// rustfmt-normalize_comments: true +// rustfmt-format_code_in_doc_comments: true + +//! ```rust +//! println!( "hello, world" ); +//! ``` + +#![deny( missing_docs )] + +//! ```rust +//! println!("hello, world"); + +#![deny( missing_docs )] diff --git a/tests/source/issue-2523.rs b/tests/source/issue-2523.rs new file mode 100644 index 000000000000..491d5c38fc22 --- /dev/null +++ b/tests/source/issue-2523.rs @@ -0,0 +1,18 @@ +// rustfmt-normalize_comments: true +// rustfmt-format_code_in_doc_comments: true + +// Do not unindent macro calls in comment with unformattable syntax. +//! ```rust +//! let x = 3 ; +//! some_macro!(pub fn fn foo() ( +//! println!("Don't unindent me!"); +//! )); +//! ``` + +// Format items that appear as arguments of macro call. +//! ```rust +//! let x = 3 ; +//! some_macro!(pub fn foo() { +//! println!("Don't unindent me!"); +//! }); +//! ``` diff --git a/tests/source/issue-2582.rs b/tests/source/issue-2582.rs new file mode 100644 index 000000000000..bba8ce1504ce --- /dev/null +++ b/tests/source/issue-2582.rs @@ -0,0 +1 @@ + fn main() {} diff --git a/tests/source/issue-2641.rs b/tests/source/issue-2641.rs new file mode 100644 index 000000000000..c7ad60674fd5 --- /dev/null +++ b/tests/source/issue-2641.rs @@ -0,0 +1,3 @@ +macro_rules! a { + () => {{}} +} diff --git a/tests/source/issue-2644.rs b/tests/source/issue-2644.rs new file mode 100644 index 000000000000..fa9d16f444dd --- /dev/null +++ b/tests/source/issue-2644.rs @@ -0,0 +1,11 @@ +// rustfmt-max_width: 80 +fn foo(e: Enum) { + match e { + Enum::Var { + element1, + element2, + } => { + return; + } + } +} diff --git a/tests/source/issue-2728.rs b/tests/source/issue-2728.rs new file mode 100644 index 000000000000..6cb41b75b6d0 --- /dev/null +++ b/tests/source/issue-2728.rs @@ -0,0 +1,8 @@ +// rustfmt-wrap_comments: true +// rustfmt-newline_style: Windows + +//! ```rust +//! extern crate uom; +//! ``` + +fn main() {} diff --git a/tests/source/issue-2761.rs b/tests/source/issue-2761.rs new file mode 100644 index 000000000000..bc312319034b --- /dev/null +++ b/tests/source/issue-2761.rs @@ -0,0 +1,15 @@ +const DATA: &'static [u8] = &[ + 0x42, 0x50, 0x54, 0x44, //type + 0x23, 0x00, 0x00, 0x00, //size + 0x00, 0x00, 0x04, 0x00, //flags + 0xEC, 0x0C, 0x00, 0x00, //id + 0x00, 0x00, 0x00, 0x00, //revision + 0x2B, 0x00, //version + 0x00, 0x00, //unknown + 0x42, 0x50, 0x54, 0x4E, //field type + 0x1D, 0x00, //field size + 0x19, 0x00, 0x00, 0x00, //decompressed field size + 0x75, 0xc5, 0x21, 0x0d, 0x00, 0x00, 0x08, 0x05, 0xd1, 0x6c, //field data (compressed) + 0x6c, 0xdc, 0x57, 0x48, 0x3c, 0xfd, 0x5b, 0x5c, 0x02, 0xd4, //field data (compressed) + 0x6b, 0x32, 0xb5, 0xdc, 0xa3 //field data (compressed) +]; diff --git a/tests/source/issue-2781.rs b/tests/source/issue-2781.rs new file mode 100644 index 000000000000..2c15b29b6dcd --- /dev/null +++ b/tests/source/issue-2781.rs @@ -0,0 +1,11 @@ +pub // Oh, no. A line comment. +struct Foo {} + +pub /* Oh, no. A block comment. */ struct Foo {} + +mod inner { +pub // Oh, no. A line comment. +struct Foo {} + +pub /* Oh, no. A block comment. */ struct Foo {} +} diff --git a/tests/source/issue-2794.rs b/tests/source/issue-2794.rs new file mode 100644 index 000000000000..c3f9c0412a46 --- /dev/null +++ b/tests/source/issue-2794.rs @@ -0,0 +1,7 @@ +// rustfmt-indent_style: Block +// rustfmt-imports_indent: Block +// rustfmt-imports_layout: Vertical + +use std::{ + env, fs, io::{Read, Write}, +}; diff --git a/tests/source/issue-2835.rs b/tests/source/issue-2835.rs new file mode 100644 index 000000000000..2219b0b38e40 --- /dev/null +++ b/tests/source/issue-2835.rs @@ -0,0 +1,7 @@ +// rustfmt-brace_style: AlwaysNextLine +// rustfmt-fn_single_line: true + +fn lorem() -> i32 +{ + 42 +} diff --git a/tests/source/issue-2863.rs b/tests/source/issue-2863.rs new file mode 100644 index 000000000000..1bda857be766 --- /dev/null +++ b/tests/source/issue-2863.rs @@ -0,0 +1,25 @@ +// rustfmt-reorder_impl_items: true + +impl IntoIterator for SafeVec { + type F = impl Trait; + type IntoIter = self::IntoIter; + type Item = T; + // comment on foo() + fn foo() {println!("hello, world");} + type Bar = u32; + fn foo1() {println!("hello, world");} + type FooBar = u32; + fn foo2() {println!("hello, world");} + fn foo3() {println!("hello, world");} + const SomeConst: i32 = 100; + fn foo4() {println!("hello, world");} + fn foo5() {println!("hello, world");} + // comment on FoooooBar + type FoooooBar = u32; + fn foo6() {println!("hello, world");} + fn foo7() {println!("hello, world");} + type BarFoo = u32; + type E = impl Trait; + const AnotherConst: i32 = 100; + fn foo8() {println!("hello, world");} +} diff --git a/tests/source/issue-2869.rs b/tests/source/issue-2869.rs new file mode 100644 index 000000000000..d18adfb462e9 --- /dev/null +++ b/tests/source/issue-2869.rs @@ -0,0 +1,41 @@ +// rustfmt-struct_field_align_threshold: 50 + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "PascalCase")] +struct AuditLog1 { + creation_time: String, + id: String, + operation: String, + organization_id: String, + record_type: u32, + result_status: Option, + #[serde(rename = "ClientIP")] + client_ip: Option, + object_id: String, + actor: Option>, + actor_context_id: Option, + actor_ip_address: Option, + azure_active_directory_event_type: Option, + + #[serde(rename = "very")] + aaaaa: String, + #[serde(rename = "cool")] + bb: i32, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "PascalCase")] +struct AuditLog2 { + creation_time: String, + id: String, + operation: String, + organization_id: String, + record_type: u32, + result_status: Option, + client_ip: Option, + object_id: String, + actor: Option>, + actor_context_id: Option, + actor_ip_address: Option, + azure_active_directory_event_type: Option, +} diff --git a/tests/source/issue-2896.rs b/tests/source/issue-2896.rs new file mode 100644 index 000000000000..f648e64b1e37 --- /dev/null +++ b/tests/source/issue-2896.rs @@ -0,0 +1,161 @@ +extern crate rand; +extern crate timely; +extern crate differential_dataflow; + +use rand::{Rng, SeedableRng, StdRng}; + +use timely::dataflow::operators::*; + +use differential_dataflow::AsCollection; +use differential_dataflow::operators::*; +use differential_dataflow::input::InputSession; + +// mod loglikelihoodratio; + +fn main() { + + // define a new timely dataflow computation. + timely::execute_from_args(std::env::args().skip(6), move |worker| { + + // capture parameters of the experiment. + let users: usize = std::env::args().nth(1).unwrap().parse().unwrap(); + let items: usize = std::env::args().nth(2).unwrap().parse().unwrap(); + let scale: usize = std::env::args().nth(3).unwrap().parse().unwrap(); + let batch: usize = std::env::args().nth(4).unwrap().parse().unwrap(); + let noisy: bool = std::env::args().nth(5).unwrap() == "noisy"; + + let index = worker.index(); + let peers = worker.peers(); + + let (input, probe) = worker.dataflow(|scope| { + + // input of (user, item) collection. + let (input, occurrences) = scope.new_input(); + let occurrences = occurrences.as_collection(); + + //TODO adjust code to only work with upper triangular half of cooccurrence matrix + + /* Compute the cooccurrence matrix C = A'A from the binary interaction matrix A. */ + let cooccurrences = + occurrences + .join_map(&occurrences, |_user, &item_a, &item_b| (item_a, item_b)) + .filter(|&(item_a, item_b)| item_a != item_b) + .count(); + + /* compute the rowsums of C indicating how often we encounter individual items. */ + let row_sums = + occurrences + .map(|(_user, item)| item) + .count(); + + // row_sums.inspect(|record| println!("[row_sums] {:?}", record)); + + /* Join the cooccurrence pairs with the corresponding row sums. */ + let mut cooccurrences_with_row_sums = cooccurrences + .map(|((item_a, item_b), num_cooccurrences)| (item_a, (item_b, num_cooccurrences))) + .join_map(&row_sums, |&item_a, &(item_b, num_cooccurrences), &row_sum_a| { + assert!(row_sum_a > 0); + (item_b, (item_a, num_cooccurrences, row_sum_a)) + }) + .join_map(&row_sums, |&item_b, &(item_a, num_cooccurrences, row_sum_a), &row_sum_b| { + assert!(row_sum_a > 0); + assert!(row_sum_b > 0); + (item_a, (item_b, num_cooccurrences, row_sum_a, row_sum_b)) + }); + + // cooccurrences_with_row_sums + // .inspect(|record| println!("[cooccurrences_with_row_sums] {:?}", record)); + + // //TODO compute top-k "similar items" per item + // /* Compute LLR scores for each item pair. */ + // let llr_scores = cooccurrences_with_row_sums.map( + // |(item_a, (item_b, num_cooccurrences, row_sum_a, row_sum_b))| { + + // println!( + // "[llr_scores] item_a={} item_b={}, num_cooccurrences={} row_sum_a={} row_sum_b={}", + // item_a, item_b, num_cooccurrences, row_sum_a, row_sum_b); + + // let k11: isize = num_cooccurrences; + // let k12: isize = row_sum_a as isize - k11; + // let k21: isize = row_sum_b as isize - k11; + // let k22: isize = 10000 - k12 - k21 + k11; + + // let llr_score = loglikelihoodratio::log_likelihood_ratio(k11, k12, k21, k22); + + // ((item_a, item_b), llr_score) + // }); + + if noisy { + cooccurrences_with_row_sums = + cooccurrences_with_row_sums + .inspect(|x| println!("change: {:?}", x)); + } + + let probe = + cooccurrences_with_row_sums + .probe(); +/* + // produce the (item, item) collection + let cooccurrences = occurrences + .join_map(&occurrences, |_user, &item_a, &item_b| (item_a, item_b)); + // count the occurrences of each item. + let counts = cooccurrences + .map(|(item_a,_)| item_a) + .count(); + // produce ((item1, item2), count1, count2, count12) tuples + let cooccurrences_with_counts = cooccurrences + .join_map(&counts, |&item_a, &item_b, &count_item_a| (item_b, (item_a, count_item_a))) + .join_map(&counts, |&item_b, &(item_a, count_item_a), &count_item_b| { + ((item_a, item_b), count_item_a, count_item_b) + }); + let probe = cooccurrences_with_counts + .inspect(|x| println!("change: {:?}", x)) + .probe(); +*/ + (input, probe) + }); + + let seed: &[_] = &[1, 2, 3, index]; + let mut rng1: StdRng = SeedableRng::from_seed(seed); // rng for edge additions + let mut rng2: StdRng = SeedableRng::from_seed(seed); // rng for edge deletions + + let mut input = InputSession::from(input); + + for count in 0 .. scale { + if count % peers == index { + let user = rng1.gen_range(0, users); + let item = rng1.gen_range(0, items); + // println!("[INITIAL INPUT] ({}, {})", user, item); + input.insert((user, item)); + } + } + + // load the initial data up! + while probe.less_than(input.time()) { worker.step(); } + + for round in 1 .. { + + for element in (round * batch) .. ((round + 1) * batch) { + if element % peers == index { + // advance the input timestamp. + input.advance_to(round * batch); + // insert a new item. + let user = rng1.gen_range(0, users); + let item = rng1.gen_range(0, items); + if noisy { println!("[INPUT: insert] ({}, {})", user, item); } + input.insert((user, item)); + // remove an old item. + let user = rng2.gen_range(0, users); + let item = rng2.gen_range(0, items); + if noisy { println!("[INPUT: remove] ({}, {})", user, item); } + input.remove((user, item)); + } + } + + input.advance_to(round * batch); + input.flush(); + + while probe.less_than(input.time()) { worker.step(); } + } + }).unwrap(); +} diff --git a/tests/source/issue-2916.rs b/tests/source/issue-2916.rs new file mode 100644 index 000000000000..ccb1f8486c5e --- /dev/null +++ b/tests/source/issue-2916.rs @@ -0,0 +1,2 @@ +a_macro!(name, +) ; diff --git a/tests/source/issue-2917/packed_simd.rs b/tests/source/issue-2917/packed_simd.rs new file mode 100644 index 000000000000..afa9e67c8ab6 --- /dev/null +++ b/tests/source/issue-2917/packed_simd.rs @@ -0,0 +1,63 @@ +// rustfmt-wrap_comments: true +//! Implements `From` and `Into` for vector types. + +macro_rules! impl_from_vector { + ([$elem_ty:ident; $elem_count:expr]: $id:ident | $test_tt:tt | $source:ident) => { + impl From<$source> for $id { + #[inline] + fn from(source: $source) -> Self { + fn static_assert_same_number_of_lanes() + where + T: crate::sealed::Simd, + U: crate::sealed::Simd, + { + } + use llvm::simd_cast; + static_assert_same_number_of_lanes::<$id, $source>(); + Simd(unsafe { simd_cast(source.0) }) + } + } + + // FIXME: `Into::into` is not inline, but due to + // the blanket impl in `std`, which is not + // marked `default`, we cannot override it here with + // specialization. + /* + impl Into<$id> for $source { + #[inline] + fn into(self) -> $id { + unsafe { simd_cast(self) } + } + } + */ + + test_if!{ + $test_tt: + interpolate_idents! { + mod [$id _from_ $source] { + use super::*; + #[test] + fn from() { + assert_eq!($id::lanes(), $source::lanes()); + let source: $source = Default::default(); + let vec: $id = Default::default(); + + let e = $id::from(source); + assert_eq!(e, vec); + + let e: $id = source.into(); + assert_eq!(e, vec); + } + } + } + } + }; +} + +macro_rules! impl_from_vectors { + ([$elem_ty:ident; $elem_count:expr]: $id:ident | $test_tt:tt | $($source:ident),*) => { + $( + impl_from_vector!([$elem_ty; $elem_count]: $id | $test_tt | $source); + )* + } +} diff --git a/tests/source/issue-2922.rs b/tests/source/issue-2922.rs new file mode 100644 index 000000000000..44fae0b645d0 --- /dev/null +++ b/tests/source/issue-2922.rs @@ -0,0 +1,9 @@ +// rustfmt-indent_style: Visual +struct Functions { + RunListenServer: unsafe extern "C" fn(*mut c_void, + *mut c_char, + *mut c_char, + *mut c_char, + *mut c_void, + *mut c_void) -> c_int, +} diff --git a/tests/source/issue-2927-2.rs b/tests/source/issue-2927-2.rs new file mode 100644 index 000000000000..d87761fdc9c3 --- /dev/null +++ b/tests/source/issue-2927-2.rs @@ -0,0 +1,7 @@ +// rustfmt-edition: 2015 +#![feature(rust_2018_preview, uniform_paths)] +use futures::prelude::*; +use http_03::cli::Cli; +use hyper::{service::service_fn_ok, Body, Response, Server}; +use ::log::{error, info, log}; +use structopt::StructOpt; diff --git a/tests/source/issue-2927.rs b/tests/source/issue-2927.rs new file mode 100644 index 000000000000..a7df32084f3b --- /dev/null +++ b/tests/source/issue-2927.rs @@ -0,0 +1,7 @@ +// rustfmt-edition: 2018 +#![feature(rust_2018_preview, uniform_paths)] +use futures::prelude::*; +use http_03::cli::Cli; +use hyper::{service::service_fn_ok, Body, Response, Server}; +use ::log::{error, info, log}; +use structopt::StructOpt; diff --git a/tests/source/issue-2930.rs b/tests/source/issue-2930.rs new file mode 100644 index 000000000000..962c3e4fe04e --- /dev/null +++ b/tests/source/issue-2930.rs @@ -0,0 +1,5 @@ +// rustfmt-indent_style: Visual +fn main() { + let (first_variable, second_variable) = (this_is_something_with_an_extraordinarily_long_name, + this_variable_name_is_also_pretty_long); +} diff --git a/tests/source/issue-2936.rs b/tests/source/issue-2936.rs new file mode 100644 index 000000000000..55b5c56e6984 --- /dev/null +++ b/tests/source/issue-2936.rs @@ -0,0 +1,19 @@ +struct AStruct { + A: u32, + B: u32, + C: u32, +} + +impl Something for AStruct { + fn a_func() { + match a_val { + ContextualParseError::InvalidMediaRule(ref err) => { + let err: &CStr = match err.kind { + ParseErrorKind::Custom(StyleParseErrorKind::MediaQueryExpectedFeatureName(..)) => { + cstr!("PEMQExpectedFeatureName") + }, + }; + } + }; + } +} diff --git a/tests/source/issue-2955.rs b/tests/source/issue-2955.rs new file mode 100644 index 000000000000..525e070a57ee --- /dev/null +++ b/tests/source/issue-2955.rs @@ -0,0 +1,6 @@ +// rustfmt-condense_wildcard_suffixes: true +fn main() { + match (1, 2, 3) { + (_, _, _) => (), + } +} diff --git a/tests/source/issue-2973.rs b/tests/source/issue-2973.rs new file mode 100644 index 000000000000..5256dd7c9601 --- /dev/null +++ b/tests/source/issue-2973.rs @@ -0,0 +1,158 @@ +#[cfg(test)] +mod test { + summary_test! { + tokenize_recipe_interpolation_eol, + "foo: # some comment + {{hello}} +", + "foo: \ + {{hello}} \ +{{ahah}}", + "N:#$>^{N}$<.", + } + + summary_test! { + tokenize_strings, + r#"a = "'a'" + '"b"' + "'c'" + '"d"'#echo hello"#, + r#"N="+'+"+'#."#, + } + + summary_test! { + tokenize_recipe_interpolation_eol, + "foo: # some comment + {{hello}} +", + "N:#$>^{N}$<.", + } + + summary_test! { + tokenize_recipe_interpolation_eof, + "foo: # more comments + {{hello}} +# another comment +", + "N:#$>^{N}$<#$.", + } + + summary_test! { + tokenize_recipe_complex_interpolation_expression, + "foo: #lol\n {{a + b + \"z\" + blarg}}", + "N:#$>^{N+N+\"+N}<.", + } + + summary_test! { + tokenize_recipe_multiple_interpolations, + "foo:,#ok\n {{a}}0{{b}}1{{c}}", + "N:,#$>^{N}_{N}_{N}<.", + } + + summary_test! { + tokenize_junk, + "bob + +hello blah blah blah : a b c #whatever + ", + "N$$NNNN:NNN#$.", + } + + summary_test! { + tokenize_empty_lines, + " +# this does something +hello: + asdf + bsdf + + csdf + + dsdf # whatever + +# yolo + ", + "$#$N:$>^_$^_$$^_$$^_$$<#$.", + } + + summary_test! { + tokenize_comment_before_variable, + " +# +A='1' +echo: + echo {{A}} + ", + "$#$N='$N:$>^_{N}$<.", + } + + summary_test! { + tokenize_interpolation_backticks, + "hello:\n echo {{`echo hello` + `echo goodbye`}}", + "N:$>^_{`+`}<.", + } + + summary_test! { + tokenize_assignment_backticks, + "a = `echo hello` + `echo goodbye`", + "N=`+`.", + } + + summary_test! { + tokenize_multiple, + " +hello: + a + b + + c + + d + +# hello +bob: + frank + ", + + "$N:$>^_$^_$$^_$$^_$$<#$N:$>^_$<.", + } + + summary_test! { + tokenize_comment, + "a:=#", + "N:=#." + } + + summary_test! { + tokenize_comment_with_bang, + "a:=#foo!", + "N:=#." + } + + summary_test! { + tokenize_order, + r" +b: a + @mv a b + +a: + @touch F + @touch a + +d: c + @rm c + +c: b + @mv b c", + "$N:N$>^_$$^_$^_$$^_$$^_<.", + } + + summary_test! { + tokenize_parens, + r"((())) )abc(+", + "((())))N(+.", + } + + summary_test! { + crlf_newline, + "#\r\n#asdf\r\n", + "#$#$.", + } +} diff --git a/tests/source/issue-2977/impl.rs b/tests/source/issue-2977/impl.rs new file mode 100644 index 000000000000..8d7bb9414eb7 --- /dev/null +++ b/tests/source/issue-2977/impl.rs @@ -0,0 +1,44 @@ +macro_rules! atomic_bits { + // the println macro cannot be rewritten because of the asm macro + ($type:ty, $ldrex:expr, $strex:expr) => { + impl AtomicBits for $type { + unsafe fn load_excl(address: usize) -> Self { + let raw: $type; + asm!($ldrex + : "=r"(raw) + : "r"(address) + : + : "volatile"); + raw + } + + unsafe fn store_excl(self, address: usize) -> bool { + let status: $type; + println!("{}", + status); + status == 0 + } + } + }; + + // the println macro should be rewritten here + ($type:ty) => { + fn some_func(self) { + let status: $type; + println!("{}", status); + } + }; + + // unrewritale macro in func + ($type:ty, $ldrex:expr) => { + unsafe fn load_excl(address: usize) -> Self { + let raw: $type; + asm!($ldrex + : "=r"(raw) + : "r"(address) + : + : "volatile"); + raw + } + } +} diff --git a/tests/source/issue-2977/trait.rs b/tests/source/issue-2977/trait.rs new file mode 100644 index 000000000000..ae20668cd75f --- /dev/null +++ b/tests/source/issue-2977/trait.rs @@ -0,0 +1,44 @@ +macro_rules! atomic_bits { + // the println macro cannot be rewritten because of the asm macro + ($type:ty, $ldrex:expr, $strex:expr) => { + trait $type { + unsafe fn load_excl(address: usize) -> Self { + let raw: $type; + asm!($ldrex + : "=r"(raw) + : "r"(address) + : + : "volatile"); + raw + } + + unsafe fn store_excl(self, address: usize) -> bool { + let status: $type; + println!("{}", + status); + status == 0 + } + } + }; + + // the println macro should be rewritten here + ($type:ty) => { + fn some_func(self) { + let status: $type; + println!("{}", status); + } + }; + + // unrewritale macro in func + ($type:ty, $ldrex:expr) => { + unsafe fn load_excl(address: usize) -> Self { + let raw: $type; + asm!($ldrex + : "=r"(raw) + : "r"(address) + : + : "volatile"); + raw + } + } +} diff --git a/tests/source/issue-2985.rs b/tests/source/issue-2985.rs new file mode 100644 index 000000000000..bde4da8314c1 --- /dev/null +++ b/tests/source/issue-2985.rs @@ -0,0 +1,35 @@ +// rustfmt-indent_style: Visual +fn foo() { + { + { + let extra_encoder_settings = extra_encoder_settings.iter() + .filter_map(|&(name, value)| { + value.split() + .next() + .something() + .something2() + .something3() + .something4() + }); + let extra_encoder_settings = extra_encoder_settings.iter() + .filter_map(|&(name, value)| { + value.split() + .next() + .something() + .something2() + .something3() + .something4() + }) + .something(); + if let Some(subpod) = pod.subpods.iter().find(|s| { + !s.plaintext + .as_ref() + .map(String::as_ref) + .unwrap_or("") + .is_empty() + }) { + do_something(); + } + } + } +} diff --git a/tests/source/issue-2995.rs b/tests/source/issue-2995.rs new file mode 100644 index 000000000000..accf7c3a1ad5 --- /dev/null +++ b/tests/source/issue-2995.rs @@ -0,0 +1,7 @@ +fn issue_2995() { + // '\u{2028}' is inserted in the code below. + + [0, 
1]; + [0, 
/* */ 1]; + 
[
0
,
1
]
; +} diff --git a/tests/source/issue-3029.rs b/tests/source/issue-3029.rs new file mode 100644 index 000000000000..a7ac5c32b066 --- /dev/null +++ b/tests/source/issue-3029.rs @@ -0,0 +1,94 @@ +fn keep_if() { + { + { + { + EvaluateJSReply::NumberValue( + if FromJSValConvertible::from_jsval(cx, rval.handle(), ()) { + unimplemented!(); + }, + ) + } + } + } +} + +fn keep_if_let() { + { + { + { + EvaluateJSReply::NumberValue( + if let Some(e) = FromJSValConvertible::from_jsval(cx, rval.handle(), ()) { + unimplemented!(); + }, + ) + } + } + } +} + +fn keep_for() { + { + { + { + EvaluateJSReply::NumberValue( + for conv in FromJSValConvertible::from_jsval(cx, rval.handle(), ()) { + unimplemented!(); + }, + ) + } + } + } +} + +fn keep_loop() { + { + { + { + EvaluateJSReply::NumberValue(loop { + FromJSValConvertible::from_jsval(cx, rval.handle(), ()); + }) + } + } + } +} + +fn keep_while() { + { + { + { + EvaluateJSReply::NumberValue( + while FromJSValConvertible::from_jsval(cx, rval.handle(), ()) { + unimplemented!(); + }, + ) + } + } + } +} + +fn keep_while_let() { + { + { + { + EvaluateJSReply::NumberValue( + while let Some(e) = FromJSValConvertible::from_jsval(cx, rval.handle(), ()) { + unimplemented!(); + }, + ) + } + } + } +} + +fn keep_match() { + { + { + EvaluateJSReply::NumberValue( + match FromJSValConvertible::from_jsval(cx, rval.handle(), ()) { + Ok(ConversionResult::Success(v)) => v, + _ => unreachable!(), + }, + ) + } + } +} diff --git a/tests/source/issue-3038.rs b/tests/source/issue-3038.rs new file mode 100644 index 000000000000..0fbb05ddc0e6 --- /dev/null +++ b/tests/source/issue-3038.rs @@ -0,0 +1,20 @@ +impl HTMLTableElement { + fn func() { + if number_of_row_elements == 0 { + if let Some(last_tbody) = node.rev_children() + .filter_map(DomRoot::downcast::) + .find(|n| n.is::() && n.local_name() == &local_name!("tbody")) { + last_tbody.upcast::().AppendChild(new_row.upcast::()) + .expect("InsertRow failed to append first row."); + } + } + + if number_of_row_elements == 0 { + if let Some(last_tbody) = node + .find(|n| n.is::() && n.local_name() == &local_name!("tbody")) { + last_tbody.upcast::().AppendChild(new_row.upcast::()) + .expect("InsertRow failed to append first row."); + } + } + } +} diff --git a/tests/source/issue-3049.rs b/tests/source/issue-3049.rs new file mode 100644 index 000000000000..43742683e4cd --- /dev/null +++ b/tests/source/issue-3049.rs @@ -0,0 +1,45 @@ +// rustfmt-indent_style: Visual +fn main() { + something.aaaaaaaaaaaa() + .aaaaaaaaaaaa() + .aaaaaaaaaaaa() + .aaaaaaaaaaaa() + .aaaaaaaaaaaa() + .aaaaaaaaaaaa() + .aaaaaaaaaaaa() + .bench_function(|| { + let x = hello(); + }); + + something.aaaaaaaaaaaa() + .aaaaaaaaaaaa() + .aaaaaaaaaaaa() + .aaaaaaaaaaaa() + .aaaaaaaaaaaa() + .aaaaaaaaaaaa() + .aaaaaaaaaaaa() + .bench_function(arg, || { + let x = hello(); + }); + + something.aaaaaaaaaaaa() + .aaaaaaaaaaaa() + .aaaaaaaaaaaa() + .aaaaaaaaaaaa() + .aaaaaaaaaaaa() + .aaaaaaaaaaaa() + .aaaaaaaaaaaa() + .bench_function(arg, + || { + let x = hello(); + }, + arg); + + AAAAAAAAAAA.function(|| { + let _ = (); + }); + + AAAAAAAAAAA.chain().function(|| { + let _ = (); + }) +} diff --git a/tests/source/issue-3055/original.rs b/tests/source/issue-3055/original.rs new file mode 100644 index 000000000000..45e58473a4a8 --- /dev/null +++ b/tests/source/issue-3055/original.rs @@ -0,0 +1,43 @@ +// rustfmt-wrap_comments: true +// rustfmt-format_code_in_doc_comments: true + +/// Vestibulum elit nibh, rhoncus non, euismod sit amet, pretium eu, enim. Nunc commodo ultricies dui. +/// +/// Should not format with text attribute +/// ```text +/// .--------------. +/// | v +/// Park <- Idle -> Poll -> Probe -> Download -> Install -> Reboot +/// ^ ^ ' ' ' +/// ' ' ' ' ' +/// ' `--------' ' ' +/// `---------------' ' ' +/// `--------------------------' ' +/// `-------------------------------------' +/// ``` +/// +/// Should not format with ignore attribute +/// ```text +/// .--------------. +/// | v +/// Park <- Idle -> Poll -> Probe -> Download -> Install -> Reboot +/// ^ ^ ' ' ' +/// ' ' ' ' ' +/// ' `--------' ' ' +/// `---------------' ' ' +/// `--------------------------' ' +/// `-------------------------------------' +/// ``` +/// +/// Should format with rust attribute +/// ```rust +/// let x = +/// 42; +/// ``` +/// +/// Should format with no attribute as it defaults to rust +/// ``` +/// let x = +/// 42; +/// ``` +fn func() {} diff --git a/tests/source/issue-3059.rs b/tests/source/issue-3059.rs new file mode 100644 index 000000000000..49a75cd67abb --- /dev/null +++ b/tests/source/issue-3059.rs @@ -0,0 +1,7 @@ +// rustfmt-wrap_comments: true +// rustfmt-max_width: 80 + +/// Vestibulum elit nibh, rhoncus non, euismod sit amet, pretium eu, enim. Nunc commodo ultricies dui. +/// Cras gravida rutrum massa. Donec accumsan mattis turpis. Quisque sem. Quisque elementum sapien +/// iaculis augue. In dui sem, congue sit amet, feugiat quis, lobortis at, eros. +fn func4() {} diff --git a/tests/source/issue-3066.rs b/tests/source/issue-3066.rs new file mode 100644 index 000000000000..4d1ece43defc --- /dev/null +++ b/tests/source/issue-3066.rs @@ -0,0 +1,7 @@ +// rustfmt-indent_style: Visual +fn main() { + Struct { field: aaaaaaaaaaa }; + Struct { field: aaaaaaaaaaaa, }; + Struct { field: value, + field2: value2, }; +} diff --git a/tests/source/issue-3131.rs b/tests/source/issue-3131.rs new file mode 100644 index 000000000000..c4cb2d8c03c0 --- /dev/null +++ b/tests/source/issue-3131.rs @@ -0,0 +1,8 @@ +fn main() { + match 3 { + t if match t { + _ => true, + } => {}, + _ => {} + } +} diff --git a/tests/source/issue-3153.rs b/tests/source/issue-3153.rs new file mode 100644 index 000000000000..2836ce97cf8d --- /dev/null +++ b/tests/source/issue-3153.rs @@ -0,0 +1,9 @@ +// rustfmt-wrap_comments: true + +/// This may panic if: +/// - there are fewer than `max_header_bytes` bytes preceding the body +/// - there are fewer than `max_footer_bytes` bytes following the body +/// - the sum of the body bytes and post-body bytes is less than the sum +/// of `min_body_and_padding_bytes` and `max_footer_bytes` (in other +/// words, the minimum body and padding byte requirement is not met) +fn foo() {} diff --git a/tests/source/issue-3158.rs b/tests/source/issue-3158.rs new file mode 100644 index 000000000000..315073db6af5 --- /dev/null +++ b/tests/source/issue-3158.rs @@ -0,0 +1,74 @@ +// rustfmt-format_code_in_doc_comments: true + +/// Should format +/// ```rust +/// assert!( false ); +/// ``` +/// +/// Should format +/// ```rust,should_panic +/// assert!( false ); +/// ``` +/// +/// Should format +/// ```rust,should_panic,edition2018 +/// assert!( false ); +/// ``` +/// +/// Should format +/// ```rust , should_panic , edition2018 +/// assert!( false ); +/// ``` +/// +/// Should not format +/// ```ignore +/// assert!( false ); +/// ``` +/// +/// Should not format (not all are rust) +/// ```rust,ignore +/// assert!( false ); +/// ``` +/// +/// Should not format (rust compile_fail) +/// ```compile_fail +/// assert!( false ); +/// ``` +/// +/// Should not format (rust compile_fail) +/// ```rust,compile_fail +/// assert!( false ); +/// ``` +/// +/// Various unspecified ones that should format +/// ``` +/// assert!( false ); +/// ``` +/// +/// ```, +/// assert!( false ); +/// ``` +/// +/// ```,,,,, +/// assert!( false ); +/// ``` +/// +/// ```,,, rust ,, +/// assert!( false ); +/// ``` +/// +/// Should not format +/// ```,,, rust , ignore, +/// assert!( false ); +/// ``` +/// +/// Few empty ones +/// ``` +/// ``` +/// +/// ```rust +/// ``` +/// +/// ```ignore +/// ``` +fn foo() {} diff --git a/tests/source/issue-3194.rs b/tests/source/issue-3194.rs new file mode 100644 index 000000000000..b80ce346b6c3 --- /dev/null +++ b/tests/source/issue-3194.rs @@ -0,0 +1,13 @@ +mod m { struct S where A: B; } + +mod n { struct Foo where A: B { foo: usize } } + +mod o { enum Bar where A: B { Bar } } + +mod with_comments { + mod m { struct S /* before where */ where A: B; /* after where */ } + + mod n { struct Foo /* before where */ where A: B /* after where */ { foo: usize } } + + mod o { enum Bar /* before where */ where A: B /* after where */ { Bar } } +} diff --git a/tests/source/issue-3198.rs b/tests/source/issue-3198.rs new file mode 100644 index 000000000000..48cb24a00259 --- /dev/null +++ b/tests/source/issue-3198.rs @@ -0,0 +1,99 @@ +impl TestTrait { + fn foo_one_pre(/* Important comment1 */ + self) { + } + + fn foo_one_post(self + /* Important comment1 */) { + } + + fn foo_pre( + /* Important comment1 */ + self, + /* Important comment2 */ + a: i32, + ) { + } + + fn foo_post( + self + /* Important comment1 */, + a: i32 + /* Important comment2 */, + ) { + } + + fn bar_pre( + /* Important comment1 */ + &mut self, + /* Important comment2 */ + a: i32, + ) { + } + + fn bar_post( + &mut self + /* Important comment1 */, + a: i32 + /* Important comment2 */, + ) { + } + + fn baz_pre( + /* Important comment1 */ + self: X< 'a , 'b >, + /* Important comment2 */ + a: i32, + ) { + } + + fn baz_post( + self: X< 'a , 'b > + /* Important comment1 */, + a: i32 + /* Important comment2 */, + ) { + } + + fn baz_tree_pre( + /* Important comment1 */ + self: X< 'a , 'b >, + /* Important comment2 */ + a: i32, + /* Important comment3 */ + b: i32, + ) { + } + + fn baz_tree_post( + self: X< 'a , 'b > + /* Important comment1 */, + a: i32 + /* Important comment2 */, + b: i32 + /* Important comment3 */,){ + } + + fn multi_line( + self: X<'a, 'b>, /* Important comment1-1 */ + /* Important comment1-2 */ + a: i32, /* Important comment2 */ + b: i32, /* Important comment3 */ + ) { + } + + fn two_line_comment( + self: X<'a, 'b>, /* Important comment1-1 + Important comment1-2 */ + a: i32, /* Important comment2 */ + b: i32, /* Important comment3 */ + ) { + } + + fn no_first_line_comment( + self: X<'a, 'b>, + /* Important comment2 */a: i32, + /* Important comment3 */b: i32, + ) { + } +} diff --git a/tests/source/issue-3213/version_one.rs b/tests/source/issue-3213/version_one.rs new file mode 100644 index 000000000000..f9f4cab55e32 --- /dev/null +++ b/tests/source/issue-3213/version_one.rs @@ -0,0 +1,9 @@ +// rustfmt-version: One + +fn foo() { + match 0 { + 0 => return AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, + 1 => AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, + _ => "", + }; +} diff --git a/tests/source/issue-3213/version_two.rs b/tests/source/issue-3213/version_two.rs new file mode 100644 index 000000000000..0f068c19d744 --- /dev/null +++ b/tests/source/issue-3213/version_two.rs @@ -0,0 +1,9 @@ +// rustfmt-version: Two + +fn foo() { + match 0 { + 0 => return AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, + 1 => AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, + _ => "", + }; +} diff --git a/tests/source/issue-3217.rs b/tests/source/issue-3217.rs new file mode 100644 index 000000000000..e68ca2c5907f --- /dev/null +++ b/tests/source/issue-3217.rs @@ -0,0 +1,6 @@ +fn main() { + let mut res = 0; + 's_39: { if res == 0i32 { println!("Hello, world!"); } } + 's_40: loop { println!("res = {}", res); res += 1; if res == 3i32 { break 's_40; } } + let toto = || { if true { 42 } else { 24 } }; +} diff --git a/tests/source/issue-3227/two.rs b/tests/source/issue-3227/two.rs new file mode 100644 index 000000000000..c1572c00d57a --- /dev/null +++ b/tests/source/issue-3227/two.rs @@ -0,0 +1,13 @@ +// rustfmt-version: Two + +fn main() { + thread::spawn(|| { + while true { + println!("iteration"); + } + }); + + thread::spawn(|| loop { + println!("iteration"); + }); +} diff --git a/tests/source/issue-3234.rs b/tests/source/issue-3234.rs new file mode 100644 index 000000000000..120740a72339 --- /dev/null +++ b/tests/source/issue-3234.rs @@ -0,0 +1,14 @@ +macro_rules! fuzz_target { + (|$data:ident: &[u8]| $body:block) => {}; +} + +fuzz_target!(|data: &[u8]| { + + if let Ok(app_img) = AppImage::parse(data) { + if let Ok(app_img) = app_img.sign_for_secureboot(include_str!("../../test-data/signing-key")) { + assert!(app_img.is_signed()); + Gbl::from_app_image(app_img).to_bytes(); + } + } + +}); diff --git a/tests/source/issue-3241.rs b/tests/source/issue-3241.rs new file mode 100644 index 000000000000..090284a21fdc --- /dev/null +++ b/tests/source/issue-3241.rs @@ -0,0 +1,11 @@ +// rustfmt-edition: 2018 + +use ::ignore; +use ::ignore::some::more; +use ::{foo, bar}; +use ::*; +use ::baz::{foo, bar}; + +fn main() { + println!("Hello, world!"); +} diff --git a/tests/source/issue-3253/bar.rs b/tests/source/issue-3253/bar.rs new file mode 100644 index 000000000000..eaeffd3ad00c --- /dev/null +++ b/tests/source/issue-3253/bar.rs @@ -0,0 +1,4 @@ +// Empty + fn empty() { + +} diff --git a/tests/source/issue-3253/foo.rs b/tests/source/issue-3253/foo.rs new file mode 100644 index 000000000000..4ebe5326b3c8 --- /dev/null +++ b/tests/source/issue-3253/foo.rs @@ -0,0 +1,6 @@ +pub fn hello( ) + { +println!("Hello World!"); + + } + diff --git a/tests/source/issue-3253/lib.rs b/tests/source/issue-3253/lib.rs new file mode 100644 index 000000000000..3eef586bd679 --- /dev/null +++ b/tests/source/issue-3253/lib.rs @@ -0,0 +1,14 @@ +#[macro_use] +extern crate cfg_if; + +cfg_if! { + if #[cfg(target_family = "unix")] { + mod foo; + #[path = "paths/bar_foo.rs"] + mod bar_foo; + } else { + mod bar; + #[path = "paths/foo_bar.rs"] + mod foo_bar; + } +} diff --git a/tests/source/issue-3253/paths/bar_foo.rs b/tests/source/issue-3253/paths/bar_foo.rs new file mode 100644 index 000000000000..da19f9dfaa16 --- /dev/null +++ b/tests/source/issue-3253/paths/bar_foo.rs @@ -0,0 +1,3 @@ +fn foo_decl_item(x: &mut i32) { + x = 3; +} diff --git a/tests/source/issue-3253/paths/excluded.rs b/tests/source/issue-3253/paths/excluded.rs new file mode 100644 index 000000000000..5c63eb83203e --- /dev/null +++ b/tests/source/issue-3253/paths/excluded.rs @@ -0,0 +1,6 @@ +// This module is not imported in the cfg_if macro in lib.rs so it is ignored +// while the foo and bar mods are formatted. +// Check the corresponding file in tests/target/issue-3253/paths/excluded.rs +trait CoolerTypes { fn dummy(&self) { +} +} diff --git a/tests/source/issue-3253/paths/foo_bar.rs b/tests/source/issue-3253/paths/foo_bar.rs new file mode 100644 index 000000000000..fbb5d92c6e33 --- /dev/null +++ b/tests/source/issue-3253/paths/foo_bar.rs @@ -0,0 +1,4 @@ + + +fn Foo() where T: Bar { +} diff --git a/tests/source/issue-3265.rs b/tests/source/issue-3265.rs new file mode 100644 index 000000000000..e927cf2be466 --- /dev/null +++ b/tests/source/issue-3265.rs @@ -0,0 +1,14 @@ +// rustfmt-newline_style: Windows +#[cfg(test)] +mod test { + summary_test! { + tokenize_recipe_interpolation_eol, + "foo: # some comment + {{hello}} +", + "foo: \ + {{hello}} \ +{{ahah}}", + "N:#$>^{N}$<.", + } +} diff --git a/tests/source/issue-3270/one.rs b/tests/source/issue-3270/one.rs new file mode 100644 index 000000000000..3c2e27e2293d --- /dev/null +++ b/tests/source/issue-3270/one.rs @@ -0,0 +1,12 @@ +// rustfmt-version: One + +pub fn main() { + /* let s = String::from( + " +hello +world +", + ); */ + + assert_eq!(s, "\nhello\nworld\n"); +} diff --git a/tests/source/issue-3270/two.rs b/tests/source/issue-3270/two.rs new file mode 100644 index 000000000000..0eb756471e74 --- /dev/null +++ b/tests/source/issue-3270/two.rs @@ -0,0 +1,12 @@ +// rustfmt-version: Two + +pub fn main() { + /* let s = String::from( + " +hello +world +", + ); */ + + assert_eq!(s, "\nhello\nworld\n"); +} diff --git a/tests/source/issue-3272/v1.rs b/tests/source/issue-3272/v1.rs new file mode 100644 index 000000000000..f4c1b7c992bf --- /dev/null +++ b/tests/source/issue-3272/v1.rs @@ -0,0 +1,15 @@ +// rustfmt-version: One + +fn main() { + assert!(HAYSTACK + .par_iter() + .find_any(|&&x| x[0] % 1000 == 999) + .is_some()); + + assert( + HAYSTACK + .par_iter() + .find_any(|&&x| x[0] % 1000 == 999) + .is_some(), + ); +} diff --git a/tests/source/issue-3272/v2.rs b/tests/source/issue-3272/v2.rs new file mode 100644 index 000000000000..0148368edc8e --- /dev/null +++ b/tests/source/issue-3272/v2.rs @@ -0,0 +1,15 @@ +// rustfmt-version: Two + +fn main() { + assert!(HAYSTACK + .par_iter() + .find_any(|&&x| x[0] % 1000 == 999) + .is_some()); + + assert( + HAYSTACK + .par_iter() + .find_any(|&&x| x[0] % 1000 == 999) + .is_some(), + ); +} diff --git a/tests/source/issue-3278/version_one.rs b/tests/source/issue-3278/version_one.rs new file mode 100644 index 000000000000..580679fbae34 --- /dev/null +++ b/tests/source/issue-3278/version_one.rs @@ -0,0 +1,8 @@ +// rustfmt-version: One + +pub fn parse_conditional<'a, I: 'a>( +) -> impl Parser + 'a +where + I: Stream, +{ +} diff --git a/tests/source/issue-3278/version_two.rs b/tests/source/issue-3278/version_two.rs new file mode 100644 index 000000000000..c17b1742d396 --- /dev/null +++ b/tests/source/issue-3278/version_two.rs @@ -0,0 +1,8 @@ +// rustfmt-version: Two + +pub fn parse_conditional<'a, I: 'a>() +-> impl Parser + 'a +where + I: Stream, +{ +} diff --git a/tests/source/issue-3295/two.rs b/tests/source/issue-3295/two.rs new file mode 100644 index 000000000000..0eaf022249be --- /dev/null +++ b/tests/source/issue-3295/two.rs @@ -0,0 +1,13 @@ +// rustfmt-version: Two +pub enum TestEnum { + a, + b, +} + +fn the_test(input: TestEnum) { + match input { + TestEnum::a => String::from("aaa"), + TestEnum::b => String::from("this is a very very very very very very very very very very very very very very very ong string"), + + }; +} diff --git a/tests/source/issue-3302.rs b/tests/source/issue-3302.rs new file mode 100644 index 000000000000..c037584fd710 --- /dev/null +++ b/tests/source/issue-3302.rs @@ -0,0 +1,43 @@ +// rustfmt-version: Two + +macro_rules! moo1 { + () => { + bar! { +" +" + } + }; +} + +macro_rules! moo2 { + () => { + bar! { + " +" + } + }; +} + +macro_rules! moo3 { + () => { + 42 + /* + bar! { + " + toto +tata" + } + */ + }; +} + +macro_rules! moo4 { + () => { + bar! { +" + foo + bar +baz" + } + }; +} diff --git a/tests/source/issue-3343.rs b/tests/source/issue-3343.rs new file mode 100644 index 000000000000..5670b04f5d6c --- /dev/null +++ b/tests/source/issue-3343.rs @@ -0,0 +1,47 @@ +// rustfmt-inline_attribute_width: 50 + +#[cfg(feature = "alloc")] +use core::slice; + +#[cfg(feature = "alloc")] +use total_len_is::_50__; + +#[cfg(feature = "alloc")] +use total_len_is::_51___; + +#[cfg(feature = "alloc")] +extern crate len_is_50_; + +#[cfg(feature = "alloc")] +extern crate len_is_51__; + +/// this is a comment to test is_sugared_doc property +use core::convert; + +#[fooooo] +#[barrrrr] +use total_len_is_::_51______; + +#[cfg(not(all( + feature = "std", + any( + target_os = "linux", + target_os = "android", + target_os = "netbsd", + target_os = "dragonfly", + target_os = "haiku", + target_os = "emscripten", + target_os = "solaris", + target_os = "cloudabi", + target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "openbsd", + target_os = "redox", + target_os = "fuchsia", + windows, + all(target_arch = "wasm32", feature = "stdweb"), + all(target_arch = "wasm32", feature = "wasm-bindgen"), + ) +)))] +use core::slice; diff --git a/tests/source/issue-3423.rs b/tests/source/issue-3423.rs new file mode 100644 index 000000000000..fbe8e5c372eb --- /dev/null +++ b/tests/source/issue-3423.rs @@ -0,0 +1,5 @@ +/* a nice comment with a trailing whitespace */ +fn foo() {} + +/* a nice comment with a trailing tab */ +fn bar() {} diff --git a/tests/source/issue-3434/lib.rs b/tests/source/issue-3434/lib.rs new file mode 100644 index 000000000000..7e396b383888 --- /dev/null +++ b/tests/source/issue-3434/lib.rs @@ -0,0 +1,57 @@ +#![rustfmt::skip::macros(skip_macro_mod)] + +mod no_entry; + +#[rustfmt::skip::macros(html, skip_macro)] +fn main() { + let macro_result1 = html! {
+this should be skipped
+ } + .to_string(); + + let macro_result2 = not_skip_macro! {
+this should be mangled
+ } + .to_string(); + + skip_macro! { +this should be skipped +}; + + foo(); +} + +fn foo() { + let macro_result1 = html! {
+this should be mangled
+ } + .to_string(); +} + +fn bar() { + let macro_result1 = skip_macro_mod! {
+this should be skipped
+ } + .to_string(); +} + +fn visitor_made_from_same_context() { + let pair = ( + || { + foo!(
+this should be mangled
+ ); + skip_macro_mod!(
+this should be skipped
+ ); + }, + || { + foo!(
+this should be mangled
+ ); + skip_macro_mod!(
+this should be skipped
+ ); + }, + ); +} diff --git a/tests/source/issue-3434/no_entry.rs b/tests/source/issue-3434/no_entry.rs new file mode 100644 index 000000000000..0838829fed34 --- /dev/null +++ b/tests/source/issue-3434/no_entry.rs @@ -0,0 +1,18 @@ +#[rustfmt::skip::macros(another_macro)] +fn foo() { + another_macro!( +This should be skipped. + ); +} + +fn bar() { + skip_macro_mod!( +This should be skipped. + ); +} + +fn baz() { + let macro_result1 = no_skip_macro! {
+this should be mangled
+ }.to_string(); +} diff --git a/tests/source/issue-3434/not_skip_macro.rs b/tests/source/issue-3434/not_skip_macro.rs new file mode 100644 index 000000000000..1d7d73c523d6 --- /dev/null +++ b/tests/source/issue-3434/not_skip_macro.rs @@ -0,0 +1,8 @@ +#[this::is::not::skip::macros(ouch)] + +fn main() { + let macro_result1 = ouch! {
+this should be mangled
+ } + .to_string(); +} diff --git a/tests/source/issue-3465.rs b/tests/source/issue-3465.rs new file mode 100644 index 000000000000..0bc95ad4616f --- /dev/null +++ b/tests/source/issue-3465.rs @@ -0,0 +1,42 @@ +fn main() { + ((((((((((((((((((((((((((((((((((((((((((0) + 1) + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1); +} diff --git a/tests/source/issue-3494/crlf.rs b/tests/source/issue-3494/crlf.rs new file mode 100644 index 000000000000..9ce457c7b068 --- /dev/null +++ b/tests/source/issue-3494/crlf.rs @@ -0,0 +1,8 @@ +// rustfmt-file_lines: [{"file":"tests/source/issue-3494/crlf.rs","range":[4,5]}] + +pub fn main() +{ +let world1 = "world"; println!("Hello, {}!", world1); +let world2 = "world"; println!("Hello, {}!", world2); +let world3 = "world"; println!("Hello, {}!", world3); +} diff --git a/tests/source/issue-3494/lf.rs b/tests/source/issue-3494/lf.rs new file mode 100644 index 000000000000..bdbe69cef4a6 --- /dev/null +++ b/tests/source/issue-3494/lf.rs @@ -0,0 +1,8 @@ +// rustfmt-file_lines: [{"file":"tests/source/issue-3494/lf.rs","range":[4,5]}] + +pub fn main() +{ +let world1 = "world"; println!("Hello, {}!", world1); +let world2 = "world"; println!("Hello, {}!", world2); +let world3 = "world"; println!("Hello, {}!", world3); +} diff --git a/tests/source/issue-3508.rs b/tests/source/issue-3508.rs new file mode 100644 index 000000000000..821e947c707c --- /dev/null +++ b/tests/source/issue-3508.rs @@ -0,0 +1,29 @@ +fn foo(foo2: F) +where + F: Fn( + // this comment is deleted +), +{ +} + +fn foo_block(foo2: F) +where + F: Fn( + /* this comment is deleted */ + ), +{ +} + +fn bar( + bar2: impl Fn( + // this comment is deleted + ), +) { +} + +fn bar_block( + bar2: impl Fn( + /* this comment is deleted */ + ), +) { +} diff --git a/tests/source/issue-3515.rs b/tests/source/issue-3515.rs new file mode 100644 index 000000000000..9f760cb94e8f --- /dev/null +++ b/tests/source/issue-3515.rs @@ -0,0 +1,6 @@ +// rustfmt-reorder_imports: false + +use std :: fmt :: { self , Display } ; +use std :: collections :: HashMap ; + +fn main() {} diff --git a/tests/source/issue-3532.rs b/tests/source/issue-3532.rs new file mode 100644 index 000000000000..ec0c01610cd4 --- /dev/null +++ b/tests/source/issue-3532.rs @@ -0,0 +1,7 @@ +fn foo(a: T) { + match a { +1 => {} + 0 => {} + // _ => panic!("doesn't format!"), + } +} diff --git a/tests/source/issue-3585/extern_crate.rs b/tests/source/issue-3585/extern_crate.rs new file mode 100644 index 000000000000..6716983ba0f3 --- /dev/null +++ b/tests/source/issue-3585/extern_crate.rs @@ -0,0 +1,12 @@ +// rustfmt-inline_attribute_width: 100 + +#[macro_use] +extern crate static_assertions; + +#[cfg(unix)] +extern crate static_assertions; + +// a comment before the attribute +#[macro_use] +// some comment after +extern crate static_assertions; diff --git a/tests/source/issue-3585/reorder_imports_disabled.rs b/tests/source/issue-3585/reorder_imports_disabled.rs new file mode 100644 index 000000000000..45b1bb9fdd9d --- /dev/null +++ b/tests/source/issue-3585/reorder_imports_disabled.rs @@ -0,0 +1,12 @@ +// rustfmt-inline_attribute_width: 100 +// rustfmt-reorder_imports: false + +#[cfg(unix)] +extern crate crateb; +#[cfg(unix)] +extern crate cratea; + +#[cfg(unix)] +use crateb; +#[cfg(unix)] +use cratea; diff --git a/tests/source/issue-3585/reorder_imports_enabled.rs b/tests/source/issue-3585/reorder_imports_enabled.rs new file mode 100644 index 000000000000..9f433e5ca21b --- /dev/null +++ b/tests/source/issue-3585/reorder_imports_enabled.rs @@ -0,0 +1,12 @@ +// rustfmt-inline_attribute_width: 100 +// rustfmt-reorder_imports: true + +#[cfg(unix)] +extern crate crateb; +#[cfg(unix)] +extern crate cratea; + +#[cfg(unix)] +use crateb; +#[cfg(unix)] +use cratea; diff --git a/tests/source/issue-3585/use.rs b/tests/source/issue-3585/use.rs new file mode 100644 index 000000000000..e71ba9008ce4 --- /dev/null +++ b/tests/source/issue-3585/use.rs @@ -0,0 +1,7 @@ +// rustfmt-inline_attribute_width: 100 + +#[macro_use] +use static_assertions; + +#[cfg(unix)] +use static_assertions; diff --git a/tests/source/issue-3636.rs b/tests/source/issue-3636.rs new file mode 100644 index 000000000000..edfa03012f29 --- /dev/null +++ b/tests/source/issue-3636.rs @@ -0,0 +1,10 @@ +// rustfmt-file_lines: [{"file":"tests/source/issue-3636.rs","range":[4,7]},{"file":"tests/target/issue-3636.rs","range":[3,6]}] + +fn foo() { + let x = + 42; + let y = + 42; + let z = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + let z = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"; +} diff --git a/tests/source/issue-3639.rs b/tests/source/issue-3639.rs new file mode 100644 index 000000000000..7b16b2dfde06 --- /dev/null +++ b/tests/source/issue-3639.rs @@ -0,0 +1,5 @@ +trait Foo where {} +struct Bar where {} +struct Bax where; +struct Baz(String) where; +impl<> Foo<> for Bar<> where {} diff --git a/tests/source/issue-3651.rs b/tests/source/issue-3651.rs new file mode 100644 index 000000000000..c153e99d0f01 --- /dev/null +++ b/tests/source/issue-3651.rs @@ -0,0 +1,4 @@ +fn f() -> Box< + dyn FnMut() -> Thing< WithType = LongItemName, Error = LONGLONGLONGLONGLONGONGEvenLongerErrorNameLongerLonger>, +>{ +} \ No newline at end of file diff --git a/tests/source/issue-3665/lib.rs b/tests/source/issue-3665/lib.rs new file mode 100644 index 000000000000..e049fbc5680e --- /dev/null +++ b/tests/source/issue-3665/lib.rs @@ -0,0 +1,33 @@ +#![rustfmt::skip::attributes(skip_mod_attr)] + +mod sub_mod; + +#[rustfmt::skip::attributes(other, skip_attr)] +fn main() { + #[other(should, +skip, + this, format)] + struct S {} + + #[skip_attr(should, skip, +this, format,too)] + fn doesnt_mater() {} + + #[skip_mod_attr(should, skip, +this, format, + enerywhere)] + fn more() {} + + #[not_skip(not, +skip, me)] + struct B {} +} + +#[other(should, not, skip, +this, format, here)] +fn foo() {} + +#[skip_mod_attr(should, skip, +this, format,in, master, + and, sub, module)] +fn bar() {} diff --git a/tests/source/issue-3665/not_skip_attribute.rs b/tests/source/issue-3665/not_skip_attribute.rs new file mode 100644 index 000000000000..14985259a50f --- /dev/null +++ b/tests/source/issue-3665/not_skip_attribute.rs @@ -0,0 +1,4 @@ +#![this::is::not::skip::attribute(ouch)] + +#[ouch(not, skip, me)] +fn main() {} diff --git a/tests/source/issue-3665/sub_mod.rs b/tests/source/issue-3665/sub_mod.rs new file mode 100644 index 000000000000..75fb24b4a65d --- /dev/null +++ b/tests/source/issue-3665/sub_mod.rs @@ -0,0 +1,14 @@ +#[rustfmt::skip::attributes(more_skip)] +#[more_skip(should, + skip, +this, format)] +fn foo() {} + +#[skip_mod_attr(should, skip, +this, format,in, master, + and, sub, module)] +fn bar() {} + +#[skip_attr(should, not, + skip, this, attribute, here)] +fn baz() {} diff --git a/tests/source/issue-3672.rs b/tests/source/issue-3672.rs new file mode 100644 index 000000000000..82616bd4253a --- /dev/null +++ b/tests/source/issue-3672.rs @@ -0,0 +1,4 @@ +fn main() { + let x = 5;; + +} \ No newline at end of file diff --git a/tests/source/issue-3675.rs b/tests/source/issue-3675.rs new file mode 100644 index 000000000000..f16efb2dcac1 --- /dev/null +++ b/tests/source/issue-3675.rs @@ -0,0 +1,5 @@ + fn main() { + println!("{}" + // comment + , 111); + } \ No newline at end of file diff --git a/tests/source/issue-3701/one.rs b/tests/source/issue-3701/one.rs new file mode 100644 index 000000000000..a7f0bd3aa17a --- /dev/null +++ b/tests/source/issue-3701/one.rs @@ -0,0 +1,12 @@ +// rustfmt-version: One + +fn build_sorted_static_get_entry_names( + mut entries: Vec<(u8, &'static str)>, +) -> (impl Fn( + AlphabeticalTraversal, + Box>, +) -> BoxFuture<'static, Result, Status>> + + Send + + Sync + + 'static) { +} diff --git a/tests/source/issue-3701/two.rs b/tests/source/issue-3701/two.rs new file mode 100644 index 000000000000..8e15c58b8b25 --- /dev/null +++ b/tests/source/issue-3701/two.rs @@ -0,0 +1,12 @@ +// rustfmt-version: Two + +fn build_sorted_static_get_entry_names( + mut entries: Vec<(u8, &'static str)>, +) -> (impl Fn( + AlphabeticalTraversal, + Box>, +) -> BoxFuture<'static, Result, Status>> + + Send + + Sync + + 'static) { +} diff --git a/tests/source/issue-3709.rs b/tests/source/issue-3709.rs new file mode 100644 index 000000000000..73c2a624e524 --- /dev/null +++ b/tests/source/issue-3709.rs @@ -0,0 +1,10 @@ +// rustfmt-edition: 2018 + +macro_rules! token { + ($t:tt) => {}; +} + +fn main() { + token!(dyn); + token!(dyn ); +} diff --git a/tests/source/issue-3740.rs b/tests/source/issue-3740.rs new file mode 100644 index 000000000000..2769a8cc9b15 --- /dev/null +++ b/tests/source/issue-3740.rs @@ -0,0 +1,10 @@ +impl IntoNormalized for Vector + where + Vector: Div>, + for<'a> &'a Vector: IntoLength, +{ + type Output = Vector; + fn into_normalized(self) -> Self::Output { + + } +} diff --git a/tests/source/issue-3750.rs b/tests/source/issue-3750.rs new file mode 100644 index 000000000000..1189a99d2b6d --- /dev/null +++ b/tests/source/issue-3750.rs @@ -0,0 +1,16 @@ +// rustfmt-imports_granularity: Crate + +pub mod foo { + pub mod bar { + pub struct Bar; + } + + pub fn bar() {} +} + +use foo::bar; +use foo::bar::Bar; + +fn main() { + bar(); +} diff --git a/tests/source/issue-3751.rs b/tests/source/issue-3751.rs new file mode 100644 index 000000000000..1343f80e66e5 --- /dev/null +++ b/tests/source/issue-3751.rs @@ -0,0 +1,10 @@ +// rustfmt-format_code_in_doc_comments: true + +//! Empty pound line +//! +//! ```rust +//! # +//! # fn main() { +//! foo ( ) ; +//! # } +//! ``` diff --git a/tests/source/issue-3779/ice.rs b/tests/source/issue-3779/ice.rs new file mode 100644 index 000000000000..cde21412d945 --- /dev/null +++ b/tests/source/issue-3779/ice.rs @@ -0,0 +1,3 @@ +pub fn bar() { + 1x; +} diff --git a/tests/source/issue-3779/lib.rs b/tests/source/issue-3779/lib.rs new file mode 100644 index 000000000000..16e9d48337bd --- /dev/null +++ b/tests/source/issue-3779/lib.rs @@ -0,0 +1,9 @@ +// rustfmt-unstable: true +// rustfmt-config: issue-3779.toml + +#[path = "ice.rs"] +mod ice; + +fn foo() { +println!("abc") ; + } diff --git a/tests/source/issue-3786.rs b/tests/source/issue-3786.rs new file mode 100644 index 000000000000..54f8211ed4f8 --- /dev/null +++ b/tests/source/issue-3786.rs @@ -0,0 +1,12 @@ +fn main() { + let _ = +r#" +this is a very long string exceeded maximum width in this case maximum 100. (current this line width is about 115) +"#; + + let _with_newline = + +r#" +this is a very long string exceeded maximum width in this case maximum 100. (current this line width is about 115) +"#; +} diff --git a/tests/source/issue-3787.rs b/tests/source/issue-3787.rs new file mode 100644 index 000000000000..bcdc131a05e9 --- /dev/null +++ b/tests/source/issue-3787.rs @@ -0,0 +1,9 @@ +// rustfmt-wrap_comments: true + +//! URLs in items +//! * [This is a link with a very loooooooooooooooooooooooooooooooooooooooooong URL.](https://example.com/This/is/a/link/with/a/very/loooooooooooooooooooooooooooooooooooooooooong/URL) +//! * This is a [link](https://example.com/This/is/a/link/with/a/very/loooooooooooooooooooooooooooooooooooooooooong/URL) with a very loooooooooooooooooooooooooooooooooooooooooong URL. +//! * there is no link here: In hac habitasse platea dictumst. Maecenas in ligula. Duis tincidunt odio sollicitudin quam. Nullam non mauris. Phasellus lacinia, velit sit amet bibendum euismod, leo diam interdum ligula, eu scelerisque sem purus in tellus. +fn main() { + println!("Hello, world!"); +} diff --git a/tests/source/issue-3840/version-one_hard-tabs.rs b/tests/source/issue-3840/version-one_hard-tabs.rs new file mode 100644 index 000000000000..bf7ea7da0ebc --- /dev/null +++ b/tests/source/issue-3840/version-one_hard-tabs.rs @@ -0,0 +1,15 @@ +// rustfmt-hard_tabs: true + +impl + FromEvent, A: Widget2, B: Widget2, C: for<'a> CtxFamily<'a>> Widget2 for WidgetEventLifter +{ + type Ctx = C; + type Event = Vec; +} + +mod foo { + impl + FromEvent, A: Widget2, B: Widget2, C: for<'a> CtxFamily<'a>> Widget2 for WidgetEventLifter + { + type Ctx = C; + type Event = Vec; + } +} diff --git a/tests/source/issue-3840/version-one_soft-tabs.rs b/tests/source/issue-3840/version-one_soft-tabs.rs new file mode 100644 index 000000000000..3fc26224d503 --- /dev/null +++ b/tests/source/issue-3840/version-one_soft-tabs.rs @@ -0,0 +1,13 @@ +impl + FromEvent, A: Widget2, B: Widget2, C: for<'a> CtxFamily<'a>> Widget2 for WidgetEventLifter +{ + type Ctx = C; + type Event = Vec; +} + +mod foo { + impl + FromEvent, A: Widget2, B: Widget2, C: for<'a> CtxFamily<'a>> Widget2 for WidgetEventLifter + { + type Ctx = C; + type Event = Vec; + } +} diff --git a/tests/source/issue-3840/version-two_hard-tabs.rs b/tests/source/issue-3840/version-two_hard-tabs.rs new file mode 100644 index 000000000000..7b505fda87c2 --- /dev/null +++ b/tests/source/issue-3840/version-two_hard-tabs.rs @@ -0,0 +1,16 @@ +// rustfmt-hard_tabs: true +// rustfmt-version: Two + +impl + FromEvent, A: Widget2, B: Widget2, C: for<'a> CtxFamily<'a>> Widget2 for WidgetEventLifter +{ + type Ctx = C; + type Event = Vec; +} + +mod foo { + impl + FromEvent, A: Widget2, B: Widget2, C: for<'a> CtxFamily<'a>> Widget2 for WidgetEventLifter + { + type Ctx = C; + type Event = Vec; + } +} diff --git a/tests/source/issue-3840/version-two_soft-tabs.rs b/tests/source/issue-3840/version-two_soft-tabs.rs new file mode 100644 index 000000000000..39c8ef31292c --- /dev/null +++ b/tests/source/issue-3840/version-two_soft-tabs.rs @@ -0,0 +1,15 @@ +// rustfmt-version: Two + +impl + FromEvent, A: Widget2, B: Widget2, C: for<'a> CtxFamily<'a>> Widget2 for WidgetEventLifter +{ + type Ctx = C; + type Event = Vec; +} + +mod foo { + impl + FromEvent, A: Widget2, B: Widget2, C: for<'a> CtxFamily<'a>> Widget2 for WidgetEventLifter + { + type Ctx = C; + type Event = Vec; + } +} diff --git a/tests/source/issue-3987/format_macro_bodies_true.rs b/tests/source/issue-3987/format_macro_bodies_true.rs new file mode 100644 index 000000000000..9af114fbe574 --- /dev/null +++ b/tests/source/issue-3987/format_macro_bodies_true.rs @@ -0,0 +1,26 @@ +// rustfmt-format_macro_bodies: true + +// with comments +macro_rules! macros { + () => {{ + Struct { + field: ( + 42 + //comment 1 + 42 + //comment 2 + ), + }; + }}; +} + +// without comments +macro_rules! macros { + () => {{ + Struct { + field: ( + 42 + + 42 + ), + }; + }}; +} diff --git a/tests/source/issue-4018.rs b/tests/source/issue-4018.rs new file mode 100644 index 000000000000..9a91dd9a3066 --- /dev/null +++ b/tests/source/issue-4018.rs @@ -0,0 +1,13 @@ +fn main() { + ; + /* extra comment */ ; +} + +fn main() { + println!(""); + // comment 1 + // comment 2 + // comment 3 + // comment 4 + ; +} diff --git a/tests/source/issue-4036/one.rs b/tests/source/issue-4036/one.rs new file mode 100644 index 000000000000..9f9675f51631 --- /dev/null +++ b/tests/source/issue-4036/one.rs @@ -0,0 +1,11 @@ +// rustfmt-format_strings: true + +macro_rules! test { + () => { + fn from() { + None.expect( + "We asserted that `buffer.len()` is exactly `$n` so we can expect `ApInt::from_iter` to be successful.", + ) + } + }; +} diff --git a/tests/source/issue-4036/three.rs b/tests/source/issue-4036/three.rs new file mode 100644 index 000000000000..e1865dd0868b --- /dev/null +++ b/tests/source/issue-4036/three.rs @@ -0,0 +1,12 @@ +// rustfmt-format_strings: true +// rustfmt-hard_tabs: true + +macro_rules! test { + () => { + fn from() { + None.expect( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", + ) + } + }; +} diff --git a/tests/source/issue-4036/two.rs b/tests/source/issue-4036/two.rs new file mode 100644 index 000000000000..fa54d2e3e09c --- /dev/null +++ b/tests/source/issue-4036/two.rs @@ -0,0 +1,11 @@ +// rustfmt-format_strings: true + +macro_rules! test { + () => { + fn from() { + None.expect( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", + ) + } + }; +} diff --git a/tests/source/issue-4041.rs b/tests/source/issue-4041.rs new file mode 100644 index 000000000000..274b80f1bc5d --- /dev/null +++ b/tests/source/issue-4041.rs @@ -0,0 +1,5 @@ +// rustfmt-wrap_comments: true +//! List: +//! - Sub list: +//! + very long #1 blah blah blah blah blah blah blah blah blah blah blah blah foo baar baxxxxxxxx long line 1231421230912i3091238192038 +//! + very long #2 blah blah blah blah blah blah blah blah blah blah blah blah diff --git a/tests/source/issue-4079.rs b/tests/source/issue-4079.rs new file mode 100644 index 000000000000..eb1ce5ed2a5b --- /dev/null +++ b/tests/source/issue-4079.rs @@ -0,0 +1,8 @@ +// rustfmt-wrap_comments: true + +/*! + * Lorem ipsum dolor sit amet, consectetur adipiscing elit. In lacinia + * ullamcorper lorem, non hendrerit enim convallis ut. Curabitur id sem volutpat + */ + +/*! Lorem ipsum dolor sit amet, consectetur adipiscing elit. In lacinia ullamcorper lorem, non hendrerit enim convallis ut. Curabitur id sem volutpat */ diff --git a/tests/source/issue-4120.rs b/tests/source/issue-4120.rs new file mode 100644 index 000000000000..c9ce838c51ad --- /dev/null +++ b/tests/source/issue-4120.rs @@ -0,0 +1,85 @@ +fn main() { + let x = if true { + 1 + // In if + } else { + 0 + // In else + }; + + let x = if true { + 1 + /* In if */ + } else { + 0 + /* In else */ + }; + + let z = if true { + if true { + 1 + + // In if level 2 + } else { + 2 + } + } else { + 3 + }; + + let a = if true { + 1 + // In if + } else { + 0 + // In else + }; + + let a = if true { + 1 + + // In if + } else { + 0 + // In else + }; + + let b = if true { + 1 + + // In if + } else { + 0 + // In else + }; + + let c = if true { + 1 + + // In if + } else { + 0 + // In else + }; + for i in 0..2 { + println!("Something"); + // In for + } + + for i in 0..2 { + println!("Something"); + /* In for */ + } + + extern "C" { + fn first(); + + // In foreign mod + } + + extern "C" { + fn first(); + + /* In foreign mod */ + } +} diff --git a/tests/source/issue-4243.rs b/tests/source/issue-4243.rs new file mode 100644 index 000000000000..d8a27f7a4a40 --- /dev/null +++ b/tests/source/issue-4243.rs @@ -0,0 +1,21 @@ +fn main() { + type A: AA /*AA*/ + /*AB*/ AB ++ AC = AA +/*AA*/ + + /*AB*/ AB+AC; + + type B: BA /*BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA*/+/*BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB*/ BB + + BC = BA /*BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA*/ + /*BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB*/ BB+ BC; + + type C: CA // CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +// CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + + + // CBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB + // CBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB + CB + CC = CA // CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + // CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + + + // CBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB + // CBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB + CB+ CC; +} diff --git a/tests/source/issue-4244.rs b/tests/source/issue-4244.rs new file mode 100644 index 000000000000..34b51085e131 --- /dev/null +++ b/tests/source/issue-4244.rs @@ -0,0 +1,16 @@ +pub struct SS {} + +pub type A /* A Comment */ = SS; + +pub type B // Comment +// B += SS; + +pub type C + /* Comment C */ = SS; + +pub trait D { + type E /* Comment E */ = SS; +} + +type F<'a: 'static, T: Ord + 'static>: Eq + PartialEq where T: 'static + Copy /* x */ = Vec; diff --git a/tests/source/issue-4245.rs b/tests/source/issue-4245.rs new file mode 100644 index 000000000000..57d7e192d0af --- /dev/null +++ b/tests/source/issue-4245.rs @@ -0,0 +1,26 @@ + + +fn a(a: & // Comment + // Another comment + 'a File) {} + +fn b(b: & /* Another Comment */'a File) {} + +fn c(c: &'a /*Comment */ mut /*Comment */ File){} + +fn d(c: & // Comment +'b // Multi Line +// Comment +mut // Multi Line +// Comment +File +) {} + +fn e(c: &// Comment +File) {} + +fn d(c: &// Comment +mut // Multi Line +// Comment +File +) {} diff --git a/tests/source/issue-4312.rs b/tests/source/issue-4312.rs new file mode 100644 index 000000000000..b36b0efdb9e2 --- /dev/null +++ b/tests/source/issue-4312.rs @@ -0,0 +1,22 @@ +// issue 4312 +fn main() { + /* " */ + println!("Hello, world!"); + /* abc " */ + println!("Hello, world!"); + /* " abc */ + println!("Hello, world!"); + let y = 4; + let x = match 1 + y == 3 { + True => 3, + False => 4, + /* " unreachable */ + }; +} + +// issue 4806 +enum X { + A, + B, + /*"*/ +} diff --git a/tests/source/issue-4382.rs b/tests/source/issue-4382.rs new file mode 100644 index 000000000000..cbf0c4ed6d46 --- /dev/null +++ b/tests/source/issue-4382.rs @@ -0,0 +1,4 @@ +pub const NAME_MAX: usize = { + #[cfg(target_os = "linux")] { 1024 } + #[cfg(target_os = "freebsd")] { 255 } +}; diff --git a/tests/source/issue-4398.rs b/tests/source/issue-4398.rs new file mode 100644 index 000000000000..b0095aaac791 --- /dev/null +++ b/tests/source/issue-4398.rs @@ -0,0 +1,19 @@ +impl Struct { + /// Documentation for `foo` + #[rustfmt::skip] // comment on why use a skip here + pub fn foo(&self) {} +} + +impl Struct { + /// Documentation for `foo` + #[rustfmt::skip] // comment on why use a skip here + pub fn foo(&self) {} +} + +/// Documentation for `Struct` +#[rustfmt::skip] // comment +impl Struct { + /// Documentation for `foo` + #[rustfmt::skip] // comment on why use a skip here + pub fn foo(&self) {} +} diff --git a/tests/source/issue-4427.rs b/tests/source/issue-4427.rs new file mode 100644 index 000000000000..e14e039b98f5 --- /dev/null +++ b/tests/source/issue-4427.rs @@ -0,0 +1,31 @@ +const A: usize = + // Some constant + 2; + +const B: usize = +/* constant */ +3; + +const C : usize + = /* foo */5; + +const D: usize = // baz +/* Some constant */ + /* ba */ + { 3 + // foo + }; +const E: usize= /* foo */5; +const F: usize = +{ + 7 + }; +const G: usize = /* foooooooooooooooooooooooooooooooooooooooooooooooooooooooo0000000000000000xx00 */ 5; + const H: usize = /* asdfasdf */ match G > 1 { + true => 1, + false => 3, + }; + + pub static FOO_BAR: Vec = //f + { + vec![]}; diff --git a/tests/source/issue-447.rs b/tests/source/issue-447.rs new file mode 100644 index 000000000000..7c542cb58c2d --- /dev/null +++ b/tests/source/issue-447.rs @@ -0,0 +1,39 @@ +// rustfmt-normalize_comments: true + +fn main() { + if /* shouldn't be dropped + shouldn't be dropped */ + + cond /* shouldn't be dropped + shouldn't be dropped */ + + { + } /* shouldn't be dropped + shouldn't be dropped */ + + else /* shouldn't be dropped + shouldn't be dropped */ + + if /* shouldn't be dropped + shouldn't be dropped */ + + cond /* shouldn't be dropped + shouldn't be dropped */ + + { + } /* shouldn't be dropped + shouldn't be dropped */ + + else /* shouldn't be dropped + shouldn't be dropped */ + + { + } + + if /* shouldn't be dropped + shouldn't be dropped */ + let Some(x) = y/* shouldn't be dropped + shouldn't be dropped */ + { + } +} diff --git a/tests/source/issue-4530.rs b/tests/source/issue-4530.rs new file mode 100644 index 000000000000..9d2882abb3c1 --- /dev/null +++ b/tests/source/issue-4530.rs @@ -0,0 +1,4 @@ +// rustfmt-version: Two +fn main() { + let [aaaaaaaaaaaaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, cccccccccccccccccccccccccc, ddddddddddddddddddddddddd] = panic!(); +} diff --git a/tests/source/issue-4577.rs b/tests/source/issue-4577.rs new file mode 100644 index 000000000000..79975dd73ffc --- /dev/null +++ b/tests/source/issue-4577.rs @@ -0,0 +1,20 @@ +fn main() { + let s: String = "ABAABBAA".chars() + .filter(|c| { + if *c == 'A' { + true + } + else { + false + } + }) + .map(|c| -> char { + if c == 'A' { + '0' + } else { + '1' + } + }).collect(); + + println!("{}", s); +} diff --git a/tests/source/issue-4603.rs b/tests/source/issue-4603.rs new file mode 100644 index 000000000000..ba0803e0ecab --- /dev/null +++ b/tests/source/issue-4603.rs @@ -0,0 +1,47 @@ +// Formatting when original macro snippet is used + +// Original issue #4603 code +#![feature(or_patterns)] +macro_rules! t_or_f { + () => { + (true // some comment + | false) + }; +} + +// Other test cases variations +macro_rules! RULES { + () => { + ( + xxxxxxx // COMMENT + | yyyyyyy + ) + }; +} +macro_rules! RULES { + () => { + (xxxxxxx // COMMENT + | yyyyyyy) + }; +} + +fn main() { + macro_rules! RULES { + () => { + (xxxxxxx // COMMENT + | yyyyyyy) + }; + } +} + +macro_rules! RULES { + () => { + (xxxxxxx /* COMMENT */ | yyyyyyy) + }; +} +macro_rules! RULES { + () => { + (xxxxxxx /* COMMENT */ + | yyyyyyy) + }; +} diff --git a/tests/source/issue-4615/minimum_example.rs b/tests/source/issue-4615/minimum_example.rs new file mode 100644 index 000000000000..89af5d1239dd --- /dev/null +++ b/tests/source/issue-4615/minimum_example.rs @@ -0,0 +1,4 @@ +info!(//debug + "{}: sending function_code={:04x} data={:04x} crc=0x{:04X} data={:02X?}", + self.name, function_code, data, crc, output_cmd +); diff --git a/tests/source/issue-4643.rs b/tests/source/issue-4643.rs new file mode 100644 index 000000000000..382072d9004b --- /dev/null +++ b/tests/source/issue-4643.rs @@ -0,0 +1,23 @@ +// output doesn't get corrupted when using comments within generic type parameters of a trait + +pub trait Something< + A, + // some comment + B, + C +> { + fn a(&self, x: A) -> i32; + fn b(&self, x: B) -> i32; + fn c(&self, x: C) -> i32; +} + +pub trait SomethingElse< + A, + /* some comment */ + B, + C +> { + fn a(&self, x: A) -> i32; + fn b(&self, x: B) -> i32; + fn c(&self, x: C) -> i32; +} diff --git a/tests/source/issue-4646.rs b/tests/source/issue-4646.rs new file mode 100644 index 000000000000..ee0f23220c97 --- /dev/null +++ b/tests/source/issue-4646.rs @@ -0,0 +1,20 @@ +trait Foo { + fn bar(&self) + // where + // Self: Bar + ; +} + +trait Foo { + fn bar(&self) + // where + // Self: Bar +; +} + +trait Foo { + fn bar(&self) + // where + // Self: Bar + ; +} diff --git a/tests/source/issue-4656/format_me_please.rs b/tests/source/issue-4656/format_me_please.rs new file mode 100644 index 000000000000..7de753016497 --- /dev/null +++ b/tests/source/issue-4656/format_me_please.rs @@ -0,0 +1,2 @@ + +pub fn hello( ) { } diff --git a/tests/source/issue-4656/lib.rs b/tests/source/issue-4656/lib.rs new file mode 100644 index 000000000000..5dac91b8aab2 --- /dev/null +++ b/tests/source/issue-4656/lib.rs @@ -0,0 +1,7 @@ +extern crate cfg_if; + +cfg_if::cfg_if! { + if #[cfg(target_family = "unix")] { + mod format_me_please; + } +} diff --git a/tests/source/issue-4656/lib2.rs b/tests/source/issue-4656/lib2.rs new file mode 100644 index 000000000000..b17fffc58e1d --- /dev/null +++ b/tests/source/issue-4656/lib2.rs @@ -0,0 +1,3 @@ +its_a_macro! { + // Contents +} diff --git a/tests/source/issue-4689/one.rs b/tests/source/issue-4689/one.rs new file mode 100644 index 000000000000..d048eb10fb15 --- /dev/null +++ b/tests/source/issue-4689/one.rs @@ -0,0 +1,149 @@ +// rustfmt-version: One + +// Based on the issue description +pub trait PrettyPrinter<'tcx>: +Printer< +'tcx, +Error = fmt::Error, +Path = Self, +Region = Self, +Type = Self, +DynExistential = Self, +Const = Self, +> +{ +// +} +pub trait PrettyPrinter<'tcx>: +Printer< +'tcx, +Error = fmt::Error, +Path = Self, +Region = Self, +Type = Self, +DynExistential = Self, +Const = Self, +> + fmt::Write +{ +// +} +pub trait PrettyPrinter<'tcx>: +Printer< +'tcx, +Error = fmt::Error, +Path = Self, +Region = Self, +Type = Self, +DynExistential = Self, +Const = Self, +> + fmt::Write1 + fmt::Write2 +{ +// +} +pub trait PrettyPrinter<'tcx>: +fmt::Write + Printer< +'tcx, +Error = fmt::Error, +Path = Self, +Region = Self, +Type = Self, +DynExistential = Self, +Const = Self, +> +{ +// +} +pub trait PrettyPrinter<'tcx>: +fmt::Write + Printer1< +'tcx, +Error = fmt::Error, +Path = Self, +Region = Self, +Type = Self, +DynExistential = Self, +Const = Self, +> + Printer2< +'tcx, +Error = fmt::Error, +Path = Self, +Region = Self, +Type = Self, +DynExistential = Self, +Const = Self, +> +{ +// +} + +// Some test cases to ensure other cases formatting were not changed +fn f() -> Box< +FnMut() -> Thing< +WithType = LongItemName, +Error = LONGLONGLONGLONGLONGONGEvenLongerErrorNameLongerLonger, +>, +> { +} +fn f() -> Box< +FnMut() -> Thing< +WithType = LongItemName, +Error = LONGLONGLONGLONGLONGONGEvenLongerErrorNameLongerLonger, +> + fmt::Write1 ++ fmt::Write2, +> { +} + +fn foo(foo2: F) +where +F: Fn( +// this comment is deleted +) +{ +} +fn foo(foo2: F) +where +F: Fn( +// this comment is deleted +) + fmt::Write +{ +} + +fn elaborate_bounds(mut mk_cand: F) +where +F: for<> FnMut( +&mut ProbeContext<>, +ty::PolyTraitRefffffffffffffffffffffffffffffffff<>, +tyyyyyyyyyyyyyyyyyyyyy::AssociatedItem, +), +{ +} +fn elaborate_bounds(mut mk_cand: F) +where +F: for<> FnMut( +&mut ProbeContext<>, +ty::PolyTraitRefffffffffffffffffffffffffffffffff<>, +tyyyyyyyyyyyyyyyyyyyyy::AssociatedItem, +) + fmt::Write, +{ +} + +fn build_sorted_static_get_entry_names( +mut entries: entryyyyyyyy, +) -> ( +impl Fn( +AlphabeticalTraversal, +Seconddddddddddddddddddddddddddddddddddd +) -> Parammmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm ++ Sendddddddddddddddddddddddddddddddddddddddddddd +) { +} + +pub trait SomeTrait: +Cloneeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee ++ Eqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq +{ +} + +trait B = where +for<'b> &'b Self: Send ++ Cloneeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee ++ Copyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy; diff --git a/tests/source/issue-4689/two.rs b/tests/source/issue-4689/two.rs new file mode 100644 index 000000000000..ea7feda825d4 --- /dev/null +++ b/tests/source/issue-4689/two.rs @@ -0,0 +1,149 @@ +// rustfmt-version: Two + +// Based on the issue description +pub trait PrettyPrinter<'tcx>: +Printer< +'tcx, +Error = fmt::Error, +Path = Self, +Region = Self, +Type = Self, +DynExistential = Self, +Const = Self, +> +{ +// +} +pub trait PrettyPrinter<'tcx>: +Printer< +'tcx, +Error = fmt::Error, +Path = Self, +Region = Self, +Type = Self, +DynExistential = Self, +Const = Self, +> + fmt::Write +{ +// +} +pub trait PrettyPrinter<'tcx>: +Printer< +'tcx, +Error = fmt::Error, +Path = Self, +Region = Self, +Type = Self, +DynExistential = Self, +Const = Self, +> + fmt::Write1 + fmt::Write2 +{ +// +} +pub trait PrettyPrinter<'tcx>: +fmt::Write + Printer< +'tcx, +Error = fmt::Error, +Path = Self, +Region = Self, +Type = Self, +DynExistential = Self, +Const = Self, +> +{ +// +} +pub trait PrettyPrinter<'tcx>: +fmt::Write + Printer1< +'tcx, +Error = fmt::Error, +Path = Self, +Region = Self, +Type = Self, +DynExistential = Self, +Const = Self, +> + Printer2< +'tcx, +Error = fmt::Error, +Path = Self, +Region = Self, +Type = Self, +DynExistential = Self, +Const = Self, +> +{ +// +} + +// Some test cases to ensure other cases formatting were not changed +fn f() -> Box< +FnMut() -> Thing< +WithType = LongItemName, +Error = LONGLONGLONGLONGLONGONGEvenLongerErrorNameLongerLonger, +>, +> { +} +fn f() -> Box< +FnMut() -> Thing< +WithType = LongItemName, +Error = LONGLONGLONGLONGLONGONGEvenLongerErrorNameLongerLonger, +> + fmt::Write1 ++ fmt::Write2, +> { +} + +fn foo(foo2: F) +where +F: Fn( +// this comment is deleted +) +{ +} +fn foo(foo2: F) +where +F: Fn( +// this comment is deleted +) + fmt::Write +{ +} + +fn elaborate_bounds(mut mk_cand: F) +where +F: for<> FnMut( +&mut ProbeContext<>, +ty::PolyTraitRefffffffffffffffffffffffffffffffff<>, +tyyyyyyyyyyyyyyyyyyyyy::AssociatedItem, +), +{ +} +fn elaborate_bounds(mut mk_cand: F) +where +F: for<> FnMut( +&mut ProbeContext<>, +ty::PolyTraitRefffffffffffffffffffffffffffffffff<>, +tyyyyyyyyyyyyyyyyyyyyy::AssociatedItem, +) + fmt::Write, +{ +} + +fn build_sorted_static_get_entry_names( +mut entries: entryyyyyyyy, +) -> ( +impl Fn( +AlphabeticalTraversal, +Seconddddddddddddddddddddddddddddddddddd +) -> Parammmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm ++ Sendddddddddddddddddddddddddddddddddddddddddddd +) { +} + +pub trait SomeTrait: +Cloneeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee ++ Eqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq +{ +} + +trait B = where +for<'b> &'b Self: Send ++ Cloneeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee ++ Copyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy; diff --git a/tests/source/issue-4791/buggy.rs b/tests/source/issue-4791/buggy.rs new file mode 100644 index 000000000000..4760022eeaf0 --- /dev/null +++ b/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/tests/source/issue-4791/trailing_comma.rs b/tests/source/issue-4791/trailing_comma.rs new file mode 100644 index 000000000000..c56c70faeae4 --- /dev/null +++ b/tests/source/issue-4791/trailing_comma.rs @@ -0,0 +1,14 @@ +// rustfmt-struct_field_align_threshold: 30 +// rustfmt-trailing_comma: Always + +struct Foo { + group_a: u8, + + group_b: u8 +} + +struct Bar { + group_a: u8, + + group_b: u8, +} diff --git a/tests/source/issue-4816/lib.rs b/tests/source/issue-4816/lib.rs new file mode 100644 index 000000000000..43d540c4a5d7 --- /dev/null +++ b/tests/source/issue-4816/lib.rs @@ -0,0 +1,10 @@ +#![feature(const_generics_defaults)] +struct Foo; +struct Bar; +struct Lots; +struct NamesRHard; +struct FooBar< + const LessThan100ButClose: usize = {1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1} +>; +struct FooBarrrrrrrr; diff --git a/tests/source/issue-4926/deeply_nested_struct.rs b/tests/source/issue-4926/deeply_nested_struct.rs new file mode 100644 index 000000000000..e55e41bd1a58 --- /dev/null +++ b/tests/source/issue-4926/deeply_nested_struct.rs @@ -0,0 +1,35 @@ + +// rustfmt-struct_field_align_threshold: 30 + +struct X { + a: i32, + b: i32, + c: i32, +} + +fn test(x: X) { + let d = { + let e = { + let f = { + let g = { + let h = { + let i = { + let j = { + matches!( + x, + X { a: 1_000, b: 1_000, .. } + ) + }; + j + }; + i + }; + h + }; + g + }; + f + }; + e + }; +} \ No newline at end of file diff --git a/tests/source/issue-4926/deeply_nested_struct_with_long_field_names.rs b/tests/source/issue-4926/deeply_nested_struct_with_long_field_names.rs new file mode 100644 index 000000000000..516699fa2b8b --- /dev/null +++ b/tests/source/issue-4926/deeply_nested_struct_with_long_field_names.rs @@ -0,0 +1,43 @@ +// rustfmt-struct_field_align_threshold: 30 + +struct X { + really_really_long_field_a: i32, + really_really_really_long_field_b: i32, + really_really_really_really_long_field_c: i32, + really_really_really_really_really_long_field_d: i32, + really_really_really_really_really_really_long_field_e: i32, + f: i32, +} + +fn test(x: X) { + let d = { + let e = { + let f = { + let g = { + let h = { + let i = { + let j = { + matches!( + x, + X { + really_really_long_field_a: 10, + really_really_really_long_field_b: 10, + really_really_really_really_long_field_c: 10, + really_really_really_really_really_long_field_d: 10, + really_really_really_really_really_really_long_field_e: 10, .. + } + ) + }; + j + }; + i + }; + h + }; + g + }; + f + }; + e + }; +} diff --git a/tests/source/issue-4926/deeply_nested_struct_with_many_fields.rs b/tests/source/issue-4926/deeply_nested_struct_with_many_fields.rs new file mode 100644 index 000000000000..38fd6f02cf06 --- /dev/null +++ b/tests/source/issue-4926/deeply_nested_struct_with_many_fields.rs @@ -0,0 +1,44 @@ +// rustfmt-struct_field_align_threshold: 30 + +struct X { + a: i32, + b: i32, + c: i32, + d: i32, + e: i32, + f: i32, + g: i32, + h: i32, + i: i32, + j: i32, + k: i32, +} + +fn test(x: X) { + let d = { + let e = { + let f = { + let g = { + let h = { + let i = { + let j = { + matches!( + x, + X { + a: 1_000, b: 1_000, c: 1_000, d: 1_000, e: 1_000, f: 1_000, g: 1_000, h: 1_000, i: 1_000, j: 1_000, .. + } + ) + }; + j + }; + i + }; + h + }; + g + }; + f + }; + e + }; +} diff --git a/tests/source/issue-4926/enum_struct_field.rs b/tests/source/issue-4926/enum_struct_field.rs new file mode 100644 index 000000000000..336378537df3 --- /dev/null +++ b/tests/source/issue-4926/enum_struct_field.rs @@ -0,0 +1,35 @@ +// rustfmt-struct_field_align_threshold: 30 +// rustfmt-enum_discrim_align_threshold: 30 +// rustfmt-imports_layout: HorizontalVertical + +#[derive(Default)] +struct InnerStructA { bbbbbbbbb: i32, cccccccc: i32 } + +enum SomeEnumNamedD { + E(InnerStructA), + F { + ggggggggggggggggggggggggg: bool, + h: bool, + } +} + +impl SomeEnumNamedD { + fn f_variant() -> Self { + Self::F { ggggggggggggggggggggggggg: true, h: true } + } +} + +fn main() { + let kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk = SomeEnumNamedD::f_variant(); + let something_we_care_about = matches!( + kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk, + SomeEnumNamedD::F { + ggggggggggggggggggggggggg: true, + .. + } + ); + + if something_we_care_about { + println!("Yup it happened"); + } +} diff --git a/tests/source/issue-4926/minimum_example.rs b/tests/source/issue-4926/minimum_example.rs new file mode 100644 index 000000000000..2c3045dea489 --- /dev/null +++ b/tests/source/issue-4926/minimum_example.rs @@ -0,0 +1,10 @@ +// rustfmt-struct_field_align_threshold: 30 + +struct X { a: i32, b: i32 } + +fn test(x: X) { + let y = matches!(x, X { + a: 1, + .. + }); +} diff --git a/tests/source/issue-4926/struct_with_long_field_names.rs b/tests/source/issue-4926/struct_with_long_field_names.rs new file mode 100644 index 000000000000..b8a37f0714ee --- /dev/null +++ b/tests/source/issue-4926/struct_with_long_field_names.rs @@ -0,0 +1,21 @@ +// rustfmt-struct_field_align_threshold: 30 + +struct X { + really_really_long_field_a: i32, + really_really_really_long_field_b: i32, + really_really_really_really_long_field_c: i32, + really_really_really_really_really_long_field_d: i32, + really_really_really_really_really_really_long_field_e: i32, + f: i32, +} + +fn test(x: X) { + let y = matches!(x, X { + really_really_long_field_a: 10, + really_really_really_long_field_b: 10, + really_really_really_really_long_field_c: 10, + really_really_really_really_really_long_field_d: 10, + really_really_really_really_really_really_long_field_e: 10, + .. + }); +} diff --git a/tests/source/issue-4926/struct_with_many_fields.rs b/tests/source/issue-4926/struct_with_many_fields.rs new file mode 100644 index 000000000000..4adfd3b30629 --- /dev/null +++ b/tests/source/issue-4926/struct_with_many_fields.rs @@ -0,0 +1,21 @@ +// rustfmt-struct_field_align_threshold: 30 + +struct X { + a: i32, + b: i32, + c: i32, + d: i32, + e: i32, + f: i32, + g: i32, + h: i32, + i: i32, + j: i32, + k: i32, +} + +fn test(x: X) { + let y = matches!(x, X { + a: 1_000, b: 1_000, c: 1_000, d: 1_000, e: 1_000, f: 1_000, g: 1_000, h: 1_000, i: 1_000, j: 1_000, .. + }); +} \ No newline at end of file diff --git a/tests/source/issue-4984/minimum_example.rs b/tests/source/issue-4984/minimum_example.rs new file mode 100644 index 000000000000..677f87377169 --- /dev/null +++ b/tests/source/issue-4984/minimum_example.rs @@ -0,0 +1,2 @@ +#[derive(/*Debug, */Clone)] +struct Foo; diff --git a/tests/source/issue-4984/multi_line_derive.rs b/tests/source/issue-4984/multi_line_derive.rs new file mode 100644 index 000000000000..73921dd17354 --- /dev/null +++ b/tests/source/issue-4984/multi_line_derive.rs @@ -0,0 +1,20 @@ +#[derive( +/* ---------- Some really important comment that just had to go inside the derive --------- */ +Debug, Clone, Eq, PartialEq, +)] +struct Foo { + a: i32, + b: T, +} + +#[derive( +/* + Some really important comment that just had to go inside the derive. + Also had to be put over multiple lines +*/ +Debug, Clone, Eq, PartialEq, +)] +struct Bar { + a: i32, + b: T, +} diff --git a/tests/source/issue-4984/multiple_comments_within.rs b/tests/source/issue-4984/multiple_comments_within.rs new file mode 100644 index 000000000000..eb474a723d01 --- /dev/null +++ b/tests/source/issue-4984/multiple_comments_within.rs @@ -0,0 +1,8 @@ +#[derive( +/* ---------- Some really important comment that just had to go inside the derive --------- */ +Debug, Clone,/* Another comment */Eq, PartialEq, +)] +struct Foo { + a: i32, + b: T, +} diff --git a/tests/source/issue-5011.rs b/tests/source/issue-5011.rs new file mode 100644 index 000000000000..b48292164e43 --- /dev/null +++ b/tests/source/issue-5011.rs @@ -0,0 +1,12 @@ +pub(crate) struct ASlash( + // hello + i32 +); + +pub(crate) struct AStar( + /* hello */ + i32 +); + +pub(crate) struct BStar(/* hello */ i32); + diff --git a/tests/source/issue-5023.rs b/tests/source/issue-5023.rs new file mode 100644 index 000000000000..ae1c723eff76 --- /dev/null +++ b/tests/source/issue-5023.rs @@ -0,0 +1,22 @@ +// rustfmt-wrap_comments: true + +/// A comment to test special unicode characters on boundaries +/// 是,是,是,是,是,是,是,是,是,是,是,是 it should break right here this goes to the next line +fn main() { + if xxx { + let xxx = xxx + .into_iter() + .filter(|(xxx, xxx)| { + if let Some(x) = Some(1) { + // xxxxxxxxxxxxxxxxxx, xxxxxxxxxxxx, xxxxxxxxxxxxxxxxxxxx xxx xxxxxxx, xxxxx xxx + // xxxxxxxxxx. xxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxx xxx xxxxxxx + // 是sdfadsdfxxxxxxxxx,sdfaxxxxxx_xxxxx_masdfaonxxx, + if false { + return true; + } + } + false + }) + .collect(); + } +} diff --git a/tests/source/issue-5030.rs b/tests/source/issue-5030.rs new file mode 100644 index 000000000000..08ffaac7d1dc --- /dev/null +++ b/tests/source/issue-5030.rs @@ -0,0 +1,22 @@ +// rustfmt-imports_granularity: Item +// rustfmt-group_imports: One + +// Confirm that attributes are duplicated to all items in the use statement +#[cfg(feature = "foo")] +use std::collections::{ + HashMap, + HashSet, +}; + +// Separate the imports below from the ones above +const A: usize = 0; + +// Copying attrs works with import grouping as well +#[cfg(feature = "foo")] +use std::collections::{ + HashMap, + HashSet, +}; + +#[cfg(feature = "spam")] +use qux::{bar, baz}; diff --git a/tests/source/issue-5042/multi-line_comment_with_trailing_comma.rs b/tests/source/issue-5042/multi-line_comment_with_trailing_comma.rs new file mode 100644 index 000000000000..5d171f32a1ae --- /dev/null +++ b/tests/source/issue-5042/multi-line_comment_with_trailing_comma.rs @@ -0,0 +1,24 @@ +fn main() { + // 5042 deals with trailing commas, not the indentation issue of these comments + // When a future PR fixes the inentation issues these test can be updated + let _ = std::ops::Add::add(10, 20 + // ... + // ..., + ); + + let _ = std::ops::Add::add(10, 20 + /* ... */ + // ..., + ); + + let _ = std::ops::Add::add(10, 20 + // ..., + // ..., + ); + + let _ = std::ops::Add::add(10, 20 + // ..., + /* ... + */, + ); +} diff --git a/tests/source/issue-5042/multi-line_comment_without_trailing_comma.rs b/tests/source/issue-5042/multi-line_comment_without_trailing_comma.rs new file mode 100644 index 000000000000..b8a824b34b79 --- /dev/null +++ b/tests/source/issue-5042/multi-line_comment_without_trailing_comma.rs @@ -0,0 +1,24 @@ +fn main() { + // 5042 deals with trailing commas, not the indentation issue of these comments + // When a future PR fixes the inentation issues these test can be updated + let _ = std::ops::Add::add(10, 20 + // ... + // ... + ); + + let _ = std::ops::Add::add(10, 20 + /* ... */ + // ... + ); + + let _ = std::ops::Add::add(10, 20 + // ... + // ... + ); + + let _ = std::ops::Add::add(10, 20 + // ... + /* ... + */ + ); +} diff --git a/tests/source/issue-5042/single-line_comment_with_trailing_comma.rs b/tests/source/issue-5042/single-line_comment_with_trailing_comma.rs new file mode 100644 index 000000000000..bd765b7b41f4 --- /dev/null +++ b/tests/source/issue-5042/single-line_comment_with_trailing_comma.rs @@ -0,0 +1,9 @@ +fn main() { + let _ = std::ops::Add::add(10, 20 + // ..., + ); + + let _ = std::ops::Add::add(10, 20 + /* ... */, + ); +} diff --git a/tests/source/issue-5042/single-line_comment_without_trailing_comma.rs b/tests/source/issue-5042/single-line_comment_without_trailing_comma.rs new file mode 100644 index 000000000000..2ed8de875add --- /dev/null +++ b/tests/source/issue-5042/single-line_comment_without_trailing_comma.rs @@ -0,0 +1,10 @@ +fn main() { + let _ = std::ops::Add::add(10, 20 + // ... + ); + + let _ = std::ops::Add::add(10, 20 + /* ... */ + ); +} + diff --git a/tests/source/issue-5088/deeply_nested_long_comment_wrap_comments_true.rs b/tests/source/issue-5088/deeply_nested_long_comment_wrap_comments_true.rs new file mode 100644 index 000000000000..09f68cae4240 --- /dev/null +++ b/tests/source/issue-5088/deeply_nested_long_comment_wrap_comments_true.rs @@ -0,0 +1,33 @@ +// rustfmt-wrap_comments: true + +fn main() { + { + { + { + { + { + { + { + { + { + { + { + // - aaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa bbbbbbbbbb bbbbbbbbb bbbbbbbbb ccc cccccccccc ccccccc cccccccc + + // * aaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa bbbbbbbbbb bbbbbbbbb bbbbbbbbb ccc cccccccccc ccccccc cccccccc + + /* - aaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa bbbbbbbbbb bbbbbbbbb bbbbbbbbb ccc cccccccccc ccccccc cccccccc */ + + /* * aaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa bbbbbbbbbb bbbbbbbbb bbbbbbbbb ccc cccccccccc ccccccc cccccccc */ + }; + }; + }; + }; + }; + }; + }; + }; + }; + }; + }; +} diff --git a/tests/source/issue-5088/start_with_empty_comment_very_long_itemized_block_wrap_comments_true.rs b/tests/source/issue-5088/start_with_empty_comment_very_long_itemized_block_wrap_comments_true.rs new file mode 100644 index 000000000000..75f748000f9b --- /dev/null +++ b/tests/source/issue-5088/start_with_empty_comment_very_long_itemized_block_wrap_comments_true.rs @@ -0,0 +1,19 @@ +// rustfmt-wrap_comments: true + +// +// - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +// - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + +// +// * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +// * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + +/* + * - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/ +/* + * - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/ + +/* + * * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/ +/* + * * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/ diff --git a/tests/source/issue-5088/very_long_comment_wrap_comments_true.rs b/tests/source/issue-5088/very_long_comment_wrap_comments_true.rs new file mode 100644 index 000000000000..00437f00216b --- /dev/null +++ b/tests/source/issue-5088/very_long_comment_wrap_comments_true.rs @@ -0,0 +1,13 @@ +// rustfmt-wrap_comments: true + +// - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +// - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + +// * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +// * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + +/* - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/ +/* - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/ + +/* * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/ +/* * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/ diff --git a/tests/source/issue-510.rs b/tests/source/issue-510.rs new file mode 100644 index 000000000000..4c60859e6cf3 --- /dev/null +++ b/tests/source/issue-510.rs @@ -0,0 +1,37 @@ +impl ISizeAndMarginsComputer for AbsoluteNonReplaced { +fn solve_inline_size_constraints(&self, +block: &mut BlockFlow, +input: &ISizeConstraintInput) +-> ISizeConstraintSolution { +let (inline_start,inline_size,margin_inline_start,margin_inline_end) = +match (inline_startssssssxxxxxxsssssxxxxxxxxxssssssxxx,inline_startssssssxxxxxxsssssxxxxxxxxxssssssxxx) { +(MaybeAuto::Auto, MaybeAuto::Auto, MaybeAuto::Auto) => { +let margin_start = inline_start_margin.specified_or_zero(); +let margin_end = inline_end_margin.specified_or_zero(); +// Now it is the same situation as inline-start Specified and inline-end +// and inline-size Auto. +// +// Set inline-end to zero to calculate inline-size. +let inline_size = block.get_shrink_to_fit_inline_size(available_inline_size - +(margin_start + margin_end)); +(Au(0), inline_size, margin_start, margin_end) +} +}; + + let (inline_start, inline_size, margin_inline_start, margin_inline_end) = + match (inline_start, inline_end, computed_inline_size) { + (MaybeAuto::Auto, MaybeAuto::Auto, MaybeAuto::Auto) => { + let margin_start = inline_start_margin.specified_or_zero(); + let margin_end = inline_end_margin.specified_or_zero(); + // Now it is the same situation as inline-start Specified and inline-end + // and inline-size Auto. + // + // Set inline-end to zero to calculate inline-size. + let inline_size = + block.get_shrink_to_fit_inline_size(available_inline_size - + (margin_start + margin_end)); + (Au(0), inline_size, margin_start, margin_end) + } + }; +} +} diff --git a/tests/source/issue-5157/indented_itemized_markdown_blockquote.rs b/tests/source/issue-5157/indented_itemized_markdown_blockquote.rs new file mode 100644 index 000000000000..5c1d79a74309 --- /dev/null +++ b/tests/source/issue-5157/indented_itemized_markdown_blockquote.rs @@ -0,0 +1,4 @@ +// rustfmt-wrap_comments: true + +/// > For each sample received, the middleware internally maintains a sample_state relative to each DataReader. The sample_state can either be READ or NOT_READ. +fn block_quote() {} diff --git a/tests/source/issue-5157/nested_itemized_markdown_blockquote.rs b/tests/source/issue-5157/nested_itemized_markdown_blockquote.rs new file mode 100644 index 000000000000..cf200d04e08e --- /dev/null +++ b/tests/source/issue-5157/nested_itemized_markdown_blockquote.rs @@ -0,0 +1,10 @@ +// rustfmt-wrap_comments: true + +/// > For each sample received, the middleware internally maintains a sample_state relative to each DataReader. The sample_state can either be READ or NOT_READ. +/// +/// > > For each sample received, the middleware internally maintains a sample_state relative to each DataReader. The sample_state can either be READ or NOT_READ. +/// +/// > > > For each sample received, the middleware internally maintains a sample_state relative to each DataReader. The sample_state can either be READ or NOT_READ. +/// +/// > > > > > > > > For each sample received, the middleware internally maintains a sample_state relative to each DataReader. The sample_state can either be READ or NOT_READ. +fn block_quote() {} diff --git a/tests/source/issue-5157/support_itemized_markdown_blockquote.rs b/tests/source/issue-5157/support_itemized_markdown_blockquote.rs new file mode 100644 index 000000000000..eb436402e4e0 --- /dev/null +++ b/tests/source/issue-5157/support_itemized_markdown_blockquote.rs @@ -0,0 +1,4 @@ +// rustfmt-wrap_comments: true + +/// > For each sample received, the middleware internally maintains a sample_state relative to each DataReader. The sample_state can either be READ or NOT_READ. +fn block_quote() {} diff --git a/tests/source/issue-5234.rs b/tests/source/issue-5234.rs new file mode 100644 index 000000000000..67266f485d3b --- /dev/null +++ b/tests/source/issue-5234.rs @@ -0,0 +1,51 @@ +// rustfmt-format_code_in_doc_comments: true + +/// ``` +/// ``` +fn foo() {} + +/// ``` +///Something +/// ``` +fn foo() {} + +/// ``` +/// +/// ``` +fn foo() {} + + +/// /// ``` +fn foo() {} + +/// /// ``` +/// ``` +/// +/// ``` +/// ``` +fn foo() {} + +fn foo() { +/// ``` +/// +/// ``` +struct bar {} +} + +/// ``` +/// fn com(){ +/// let i = 5; +/// +/// let j = 6; +/// } +/// ``` +fn foo() {} + +fn foo() { +/// ``` +///fn com(){ +///let i = 5; +///} +/// ``` +struct bar {} +} diff --git a/tests/source/issue-5238/markdown_header_wrap_comments_false.rs b/tests/source/issue-5238/markdown_header_wrap_comments_false.rs new file mode 100644 index 000000000000..229c6e5753d2 --- /dev/null +++ b/tests/source/issue-5238/markdown_header_wrap_comments_false.rs @@ -0,0 +1,11 @@ +// rustfmt-wrap_comments: false + +/// no markdown header so rustfmt should wrap this comment when `format_code_in_doc_comments = true` and `wrap_comments = true` +fn not_documented_with_markdown_header() { + // This is just a normal inline comment so rustfmt should wrap this comment when `wrap_comments = true` +} + +/// # We're using a markdown header here so rustfmt should refuse to wrap this comment in all circumstances +fn documented_with_markdown_header() { + // # We're using a markdown header in an inline comment. rustfmt should be able to wrap this comment when `wrap_comments = true` +} diff --git a/tests/source/issue-5238/markdown_header_wrap_comments_true.rs b/tests/source/issue-5238/markdown_header_wrap_comments_true.rs new file mode 100644 index 000000000000..c547ff35c691 --- /dev/null +++ b/tests/source/issue-5238/markdown_header_wrap_comments_true.rs @@ -0,0 +1,11 @@ +// rustfmt-wrap_comments: true + +/// no markdown header so rustfmt should wrap this comment when `format_code_in_doc_comments = true` and `wrap_comments = true` +fn not_documented_with_markdown_header() { + // This is just a normal inline comment so rustfmt should wrap this comment when `wrap_comments = true` +} + +/// # We're using a markdown header here so rustfmt should refuse to wrap this comment in all circumstances +fn documented_with_markdown_header() { + // # We're using a markdown header in an inline comment. rustfmt should be able to wrap this comment when `wrap_comments = true` +} diff --git a/tests/source/issue-5260.rs b/tests/source/issue-5260.rs new file mode 100644 index 000000000000..c0606817290c --- /dev/null +++ b/tests/source/issue-5260.rs @@ -0,0 +1,14 @@ +// rustfmt-wrap_comments: true + +/// [MyType](VeryLongPathToMyType::NoLineBreak::Here::Okay::ThatWouldBeNice::Thanks) +fn documented_with_longtype() { + // # We're using a long type link, rustfmt should not break line + // on the type when `wrap_comments = true` +} + +/// VeryLongPathToMyType::JustMyType::But::VeryVery::Long::NoLineBreak::Here::Okay::ThatWouldBeNice::Thanks +fn documented_with_verylongtype() { + // # We're using a long type link, rustfmt should not break line + // on the type when `wrap_comments = true` +} + diff --git a/tests/source/issue-5270/merge_derives_true.rs b/tests/source/issue-5270/merge_derives_true.rs new file mode 100644 index 000000000000..b31bbf095e73 --- /dev/null +++ b/tests/source/issue-5270/merge_derives_true.rs @@ -0,0 +1,62 @@ +// rustfmt-merge_derives:true + +#[rustfmt::skip::attributes(derive)] +#[allow(dead_code)] +#[derive(StructField)] +#[derive(Clone)] +struct DoNotMergeDerives { + field: String, +} + +#[allow(dead_code)] +#[derive(StructField)] +#[rustfmt::skip::attributes(derive)] +#[derive(Clone)] +struct DoNotMergeDerivesSkipInMiddle { + field: String, +} + +#[allow(dead_code)] +#[derive(StructField)] +#[derive(Clone)] +#[rustfmt::skip::attributes(derive)] +struct DoNotMergeDerivesSkipAtEnd { + field: String, +} + +#[allow(dead_code)] +#[derive(StructField)] +#[derive(Clone)] +struct MergeDerives { + field: String, +} + +mod inner_attribute_derive_skip { + #![rustfmt::skip::attributes(derive)] + + #[allow(dead_code)] + #[derive(StructField)] + #[derive(Clone)] + struct DoNotMergeDerives { + field: String, + } +} + +#[rustfmt::skip::attributes(derive)] +mod outer_attribute_derive_skip { + #[allow(dead_code)] + #[derive(StructField)] + #[derive(Clone)] + struct DoNotMergeDerives { + field: String, + } +} + +mod no_derive_skip { + #[allow(dead_code)] + #[derive(StructField)] + #[derive(Clone)] + struct MergeDerives { + field: String, + } +} diff --git a/tests/source/issue-539.rs b/tests/source/issue-539.rs new file mode 100644 index 000000000000..d70682e3bee4 --- /dev/null +++ b/tests/source/issue-539.rs @@ -0,0 +1,5 @@ +// rustfmt-normalize_comments: true +/* + FIXME (#3300): Should allow items to be anonymous. Right now + we just use dummy names for anon items. + */ diff --git a/tests/source/issue-5488.rs b/tests/source/issue-5488.rs new file mode 100644 index 000000000000..d361632e29ef --- /dev/null +++ b/tests/source/issue-5488.rs @@ -0,0 +1,17 @@ +// rustfmt-use_field_init_shorthand: true + +struct MyStruct(u32); +struct AnotherStruct { + a: u32, +} + +fn main() { + // Since MyStruct is a tuple struct, it should not be shorthanded to + // MyStruct { 0 } even if use_field_init_shorthand is enabled. + let instance = MyStruct { 0: 0 }; + + // Since AnotherStruct is not a tuple struct, the shorthand should + // apply. + let a = 10; + let instance = AnotherStruct { a: a }; +} diff --git a/tests/source/issue-5586.rs b/tests/source/issue-5586.rs new file mode 100644 index 000000000000..9cf6c1d58ddc --- /dev/null +++ b/tests/source/issue-5586.rs @@ -0,0 +1,164 @@ +// rustfmt-version: Two +fn main() { + // sample 1 + { + { + { + { + { + let push_ident = + if let Some(&node_id) = subgraph_nodes.get(pull_to_push_idx) { + self.node_id_as_ident(node_id, false) + } else { + // Entire subgraph is pull (except for a single send/push handoff output). + assert_eq!( + 1, + send_ports.len(), + "If entire subgraph is pull, should have only one handoff output." +); + send_ports[0].clone() + }; + } + } + } + } + } + + // sample 2 + { + { + { + { + { + let push_ident = if let Some(&node_id) = + subgraph_nodes.get(pull_to_push_idx) + { + self.node_id_as_ident(node_id, false) + } else { + // Entire subgraph is pull (except for a single send/push handoff output). + assert_eq!( + 1, + send_ports.len(), + "If entire subgraph is pull, should have only one handoff output." + ); + send_ports[0].clone() + }; + } + } + } + } + } + + // sample 3 + { + { + { + { + { + let push_ident = + if let Some(&node_id) = subgraph_nodes.get(pull_to_push_idx) { + self.node_id_as_ident(node_id, false) + } else { + // Entire subgraph is pull (except for a single send/push handoff output). + assert_eq!( + 1, + send_ports.len(), + "If entire subgraph is pull, should have only one handoff output." + ); + send_ports[0].clone() + }; + } + } + } + } + } + + // sample 4 + {{{{{ + let push_ident = + if let Some(&node_id) = subgraph_nodes.get(pull_to_push_idx) { + self.node_id_as_ident(node_id, false) + } else { + // Entire subgraph is pull (except for a single send/push handoff output). + assert_eq!( + 1, + send_ports.len(), + "If entire subgraph is pull, should have only one handoff output." + ); + send_ports[0].clone() + }; + }}}}} + + // sample 5 + { + { + { + { + { + let push_ident = + if let Some(&node_id) = subgraph_nodes.get(pull_to_push_idx) { + self.node_id_as_ident(node_id, false) + } else { + // Entire subgraph is pull (except for a single send/push handoff output). + assert_eq!( + 1, + send_ports.len(), + "If entire subgraph is pull, should have only one handoff output." + ); + send_ports[0].clone() + }; + } + } + } + } + } + + // sample 6 + { + { + { + { + { + let push_ident = if let Some(&node_id) = + subgraph_nodes.get(pull_to_push_idx) + { + self.node_id_as_ident(node_id, false) + } else { + // Entire subgraph is pull (except for a single send/push handoff output). + assert_eq!( + 1, + send_ports.len(), + "If entire subgraph is pull, should have only one handoff output." + ); + send_ports[0].clone() + }; + } + } + } + } + } + + // sample 7 + { + { + { + { + { + let push_ident = + if let Some(&node_id) = subgraph_nodes.get(pull_to_push_idx) { + self.node_id_as_ident(node_id, false) + } else { + // Entire subgraph is pull (except for a single send/push handoff output). + assert_eq!( + 1, + send_ports.len(), + "If entire subgraph is pull, should have only one handoff output." + ); + send_ports[0].clone() + }; + } + } + } + } + } +} diff --git a/tests/source/issue-683.rs b/tests/source/issue-683.rs new file mode 100644 index 000000000000..fd99015ea51c --- /dev/null +++ b/tests/source/issue-683.rs @@ -0,0 +1,5 @@ +// rustfmt-normalize_comments: true +/* + * FIXME (#3300): Should allow items to be anonymous. Right now + * we just use dummy names for anon items. + */ diff --git a/tests/source/issue-811.rs b/tests/source/issue-811.rs new file mode 100644 index 000000000000..b7a89b5d0f9d --- /dev/null +++ b/tests/source/issue-811.rs @@ -0,0 +1,19 @@ +trait FooTrait: Sized { + type Bar: BarTrait; +} + +trait BarTrait: Sized { + type Baz; + fn foo(); +} + +type Foo = <>::Bar as BarTrait>::Baz; +type Bar = >::Baz; + +fn some_func, U>() { + <>::Bar as BarTrait>::foo(); +} + +fn some_func>() { + >::foo(); +} diff --git a/tests/source/issue-850.rs b/tests/source/issue-850.rs new file mode 100644 index 000000000000..c939716a6a88 --- /dev/null +++ b/tests/source/issue-850.rs @@ -0,0 +1 @@ +const unsafe fn x() {} diff --git a/tests/source/issue-855.rs b/tests/source/issue-855.rs new file mode 100644 index 000000000000..8f33fa685ae9 --- /dev/null +++ b/tests/source/issue-855.rs @@ -0,0 +1,20 @@ +fn main() { + 'running: loop { + for event in event_pump.poll_iter() { + match event { + Event::Quit {..} | Event::KeyDown { keycode: Some(Keycode::Escape), .. } => break 'running, + } + } + } +} + +fn main2() { + 'running: loop { + for event in event_pump.poll_iter() { + match event { + Event::Quit {..} | + Event::KeyDownXXXXXXXXXXXXX { keycode: Some(Keycode::Escape), .. } => break 'running, + } + } + } +} diff --git a/tests/source/issue-913.rs b/tests/source/issue-913.rs new file mode 100644 index 000000000000..25b9d42fd414 --- /dev/null +++ b/tests/source/issue-913.rs @@ -0,0 +1,20 @@ +mod client { + impl Client { + fn test(self) -> Result<()> { + let next_state = match self.state { + State::V5(v5::State::Command(v5::coand::State::WriteVersion(ref mut response))) => { + let x = reformat . meeee() ; + } + }; + + let next_state = match self.state { + State::V5(v5::State::Command(v5::comand::State::WriteVersion(ref mut response))) => { + // The pattern cannot be formatted in a way that the match stays + // within the column limit. The rewrite should therefore be + // skipped. + let x = dont . reformat . meeee(); + } + }; + } + } +} diff --git a/tests/source/issue-945.rs b/tests/source/issue-945.rs new file mode 100644 index 000000000000..37d703c4679f --- /dev/null +++ b/tests/source/issue-945.rs @@ -0,0 +1,5 @@ +impl Bar { default const unsafe fn foo() { "hi" } } + +impl Baz { default unsafe extern "C" fn foo() { "hi" } } + +impl Foo for Bar { default fn foo() { "hi" } } diff --git a/tests/source/issue-977.rs b/tests/source/issue-977.rs new file mode 100644 index 000000000000..fe16387b7b23 --- /dev/null +++ b/tests/source/issue-977.rs @@ -0,0 +1,7 @@ +// rustfmt-normalize_comments: true + +trait NameC { /* comment */ } +struct FooC { /* comment */ } +enum MooC { /* comment */ } +mod BarC { /* comment */ } +extern { /* comment */ } diff --git a/tests/source/issue_1306.rs b/tests/source/issue_1306.rs new file mode 100644 index 000000000000..03b78e34108c --- /dev/null +++ b/tests/source/issue_1306.rs @@ -0,0 +1,29 @@ +// rustfmt-max_width: 160 +// rustfmt-fn_call_width: 96 +// rustfmt-fn_args_layout: Compressed +// rustfmt-trailing_comma: Always +// rustfmt-wrap_comments: true + +fn foo() { + for elem in try!(gen_epub_book::ops::parse_descriptor_file(&mut try!(File::open(&opts.source_file.1).map_err(|_| { + gen_epub_book::Error::Io { + desc: "input file", + op: "open", + more: None, + } + })), + "input file")) { + println!("{}", elem); + } +} + +fn write_content() { + io::copy(try!(File::open(in_f).map_err(|_| { + Error::Io { + desc: "Content", + op: "open", + more: None, + } + })), + w); +} diff --git a/tests/source/issue_3245.rs b/tests/source/issue_3245.rs new file mode 100644 index 000000000000..0279246ed6ac --- /dev/null +++ b/tests/source/issue_3245.rs @@ -0,0 +1,4 @@ +fn main() { + let x = 1; + ;let y = 3; +} diff --git a/tests/source/issue_3561.rs b/tests/source/issue_3561.rs new file mode 100644 index 000000000000..8f6cd8f9fbce --- /dev/null +++ b/tests/source/issue_3561.rs @@ -0,0 +1,6 @@ +fn main() {;7 +} + +fn main() { + ;7 +} diff --git a/tests/source/issue_3839.rs b/tests/source/issue_3839.rs new file mode 100644 index 000000000000..3933d31ee1a3 --- /dev/null +++ b/tests/source/issue_3839.rs @@ -0,0 +1,8 @@ +struct Foo { + a: i32, +/* +asd +*/ + // foo +b: i32, +} diff --git a/tests/source/issue_3844.rs b/tests/source/issue_3844.rs new file mode 100644 index 000000000000..15441b2b05c4 --- /dev/null +++ b/tests/source/issue_3844.rs @@ -0,0 +1,3 @@ +fn main() { +|| {{}}; +} diff --git a/tests/source/issue_3853.rs b/tests/source/issue_3853.rs new file mode 100644 index 000000000000..c41309bc7882 --- /dev/null +++ b/tests/source/issue_3853.rs @@ -0,0 +1,52 @@ +fn by_ref_with_block_before_ident() { +if let Some(ref /*def*/ state)= foo{ + println!( + "asdfasdfasdf"); } +} + +fn mut_block_before_ident() { +if let Some(mut /*def*/ state ) =foo{ + println!( + "123" ); } +} + +fn ref_and_mut_blocks_before_ident() { +if let Some(ref /*abc*/ + mut /*def*/ state ) = foo { + println!( + "deefefefefefwea" ); } +} + +fn sub_pattern() { + let foo @ /*foo*/ +bar(f) = 42; +} + +fn no_prefix_block_before_ident() { +if let Some( + /*def*/ state ) = foo { + println!( + "129387123123" ); } +} + +fn issue_3853() { +if let Some(ref /*mut*/ state) = foo { + } +} + +fn double_slash_comment_between_lhs_and_rhs() { + if let Some(e) = + // self.foo.bar(e, tx) + packet.transaction.state.committed + { + // body + println!( + "a2304712836123"); + } +} + +fn block_comment_between_lhs_and_rhs() { +if let Some(ref /*def*/ mut /*abc*/ state)= /*abc*/foo{ + println!( + "asdfasdfasdf"); } +} diff --git a/tests/source/issue_3868.rs b/tests/source/issue_3868.rs new file mode 100644 index 000000000000..6c46c3c9e180 --- /dev/null +++ b/tests/source/issue_3868.rs @@ -0,0 +1,13 @@ +fn foo() { + ; +} + +fn bar() { + for _ in 0..1 { + ; + } +} + +fn baz() { + (); + } \ No newline at end of file diff --git a/tests/source/issue_4032.rs b/tests/source/issue_4032.rs new file mode 100644 index 000000000000..11ded074c345 --- /dev/null +++ b/tests/source/issue_4032.rs @@ -0,0 +1,4 @@ +fn a1(#[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa] a: u8) {} +fn b1(#[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa] bb: u8) {} +fn a2(#[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa] a: u8) {} +fn b2(#[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa] bb: u8) {} diff --git a/tests/source/issue_4057.rs b/tests/source/issue_4057.rs new file mode 100644 index 000000000000..7cd80734bd99 --- /dev/null +++ b/tests/source/issue_4057.rs @@ -0,0 +1,15 @@ +// rustfmt-format_code_in_doc_comments: true + +/// ``` +/// # #[rustversion::since(1.36)] +/// # fn dox() { +/// # use std::pin::Pin; +/// # type Projection<'a> = &'a (); +/// # type ProjectionRef<'a> = &'a (); +/// # trait Dox { +/// fn project_ex (self: Pin<&mut Self>) -> Projection<'_>; +/// fn project_ref(self: Pin<&Self>) -> ProjectionRef<'_>; +/// # } +/// # } +/// ``` +struct Foo; diff --git a/tests/source/issue_4086.rs b/tests/source/issue_4086.rs new file mode 100644 index 000000000000..ffa6442e930f --- /dev/null +++ b/tests/source/issue_4086.rs @@ -0,0 +1,2 @@ +#[cfg(any())] +extern "C++" {} diff --git a/tests/source/issue_4257.rs b/tests/source/issue_4257.rs new file mode 100644 index 000000000000..9482512efca0 --- /dev/null +++ b/tests/source/issue_4257.rs @@ -0,0 +1,10 @@ +trait Trait { + type Type<'a> where T: 'a; + fn foo(x: &T) -> Self::Type<'_>; +} +impl Trait for () { + type Type<'a> where T: 'a = &'a T; + fn foo(x: &T) -> Self::Type<'_> { + x + } +} diff --git a/tests/source/issue_4322.rs b/tests/source/issue_4322.rs new file mode 100644 index 000000000000..b28cc7cdd12f --- /dev/null +++ b/tests/source/issue_4322.rs @@ -0,0 +1,3 @@ +trait Bar { + type X<'a> where Self: 'a; +} diff --git a/tests/source/issue_4374.rs b/tests/source/issue_4374.rs new file mode 100644 index 000000000000..2a45a022e7e7 --- /dev/null +++ b/tests/source/issue_4374.rs @@ -0,0 +1,13 @@ +fn a(_f: F) -> () +where + F: FnOnce() -> (), +{ +} +fn main() { + a(|| { + #[allow(irrefutable_let_patterns)] + while let _ = 0 { + break; + } + }); +} \ No newline at end of file diff --git a/tests/source/issue_4475.rs b/tests/source/issue_4475.rs new file mode 100644 index 000000000000..241dc91d7ba6 --- /dev/null +++ b/tests/source/issue_4475.rs @@ -0,0 +1,27 @@ +fn main() { + #[cfg(debug_assertions)] + { println!("DEBUG"); } +} + +fn main() { + #[cfg(feature = "foo")] + { + /* + let foo = 0 + */ + } +} + +fn main() { + #[cfg(feature = "foo")] + { /* let foo = 0; */ } +} + +fn main() { + #[foo] + #[bar] + #[baz] + { + // let foo = 0; + } +} \ No newline at end of file diff --git a/tests/source/issue_4528.rs b/tests/source/issue_4528.rs new file mode 100644 index 000000000000..85f6d8c0387c --- /dev/null +++ b/tests/source/issue_4528.rs @@ -0,0 +1,8 @@ +#![allow(clippy::no_effect)] + +extern "C" { + // N.B., mutability can be easily incorrect in FFI calls -- as + // in C, the default is mutable pointers. + fn ffi(c: *mut u8); + fn int_ffi(c: *mut i32); +} \ No newline at end of file diff --git a/tests/source/issue_4579.rs b/tests/source/issue_4579.rs new file mode 100644 index 000000000000..73f345233ba7 --- /dev/null +++ b/tests/source/issue_4579.rs @@ -0,0 +1,15 @@ +// rustfmt-hard_tabs: true + +#[macro_export] +macro_rules! main { + () => { + #[spirv(fragment)] + pub fn main_fs( + mut out_color: ::spirv_std::storage_class::Output, + #[spirv(descriptor_set = 1)]iChannelResolution: ::spirv_std::storage_class::UniformConstant< + [::spirv_std::glam::Vec3A; 4], + >, + ) { + } + }; +} diff --git a/tests/source/issue_4584.rs b/tests/source/issue_4584.rs new file mode 100644 index 000000000000..695c5590559d --- /dev/null +++ b/tests/source/issue_4584.rs @@ -0,0 +1,19 @@ +// rustfmt-indent_style: Visual + +#[derive(Debug,)] +pub enum Case { + Upper, + Lower +} + +#[derive(Debug, Clone, PartialEq, Eq,)] +pub enum Case { + Upper, + Lower +} + +// NB - This formatting looks potentially off the desired state, but is +// consistent with current behavior. Included here to provide a line wrapped +// derive case with the changes applied to resolve issue #4584 +#[derive(Add, Sub, Mul, Div, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Debug, Hash, Serialize, Mul,)] +struct Foo {} \ No newline at end of file diff --git a/tests/source/issue_4636.rs b/tests/source/issue_4636.rs new file mode 100644 index 000000000000..ea7079f6c730 --- /dev/null +++ b/tests/source/issue_4636.rs @@ -0,0 +1,13 @@ +pub trait PrettyPrinter<'tcx>: + Printer< + 'tcx, + Error = fmt::Error, + Path = Self, + Region = Self, + Type = Self, + DynExistential = Self, + Const = Self, + > + fmt::Write + { + // + } \ No newline at end of file diff --git a/tests/source/issue_4675.rs b/tests/source/issue_4675.rs new file mode 100644 index 000000000000..66613eed000c --- /dev/null +++ b/tests/source/issue_4675.rs @@ -0,0 +1,8 @@ +macro_rules! foo { + ($s:ident ( $p:pat )) => { + Foo { + name: Name::$s($p), + .. + } + }; +} \ No newline at end of file diff --git a/tests/source/issue_4823.rs b/tests/source/issue_4823.rs new file mode 100644 index 000000000000..a008dd3d8381 --- /dev/null +++ b/tests/source/issue_4823.rs @@ -0,0 +1,5 @@ +macro_rules! m { +() => { +type Type; +}; +} diff --git a/tests/source/issue_4854.rs b/tests/source/issue_4854.rs new file mode 100644 index 000000000000..35d6e21affee --- /dev/null +++ b/tests/source/issue_4854.rs @@ -0,0 +1,113 @@ +struct Struct { + // Multiline comment + // should be formatted + // properly. +} + +struct Struct2 { + // This formatting +// Should be changed +} + +struct Struct3( + // This + // is + // correct +); + +struct Struct4( + // This +// is +// not +// correct +); + +struct Struct5 { + /* + Comment block + with many lines. + */ +} + +struct Struct6( + /* + Comment block + with many lines. + */ +); + +struct Struct7 { + /* +Invalid +format +*/ +} + +struct Struct8( + /* +Invalid +format +*/ +); + +struct Struct9 { /* bar */ } + +struct Struct10 { /* bar +baz +*/ } + +mod module { + struct Struct { + // Multiline comment + // should be formatted + // properly. + } + + struct Struct2 { + // This formatting +// Should be changed + } + + struct Struct3( + // This + // is + // correct + ); + + struct Struct4( + // This + // is + // not +// correct + ); + + struct Struct5 { + /* + Comment block + with many lines. + */ + } + + struct Struct6( + /* + Comment block + with many lines. + */ + ); + + struct Struct7 { + /* +Invalid +format +*/ + } + + struct Struct8( + /* +Invalid +format +*/ + ); + + struct Struct9 { /* bar */ } +} diff --git a/tests/source/issue_4911.rs b/tests/source/issue_4911.rs new file mode 100644 index 000000000000..c254db7b509c --- /dev/null +++ b/tests/source/issue_4911.rs @@ -0,0 +1,5 @@ +#![feature(min_type_alias_impl_trait)] + +impl SomeTrait for SomeType { + type SomeGAT<'a> where Self: 'a = impl SomeOtherTrait; +} \ No newline at end of file diff --git a/tests/source/issue_4943.rs b/tests/source/issue_4943.rs new file mode 100644 index 000000000000..307d9a4a1aba --- /dev/null +++ b/tests/source/issue_4943.rs @@ -0,0 +1,7 @@ +impl SomeStruct { + fn process(v: T) -> ::R + where Self: GAT = T> + { + SomeStruct::do_something(v) + } +} diff --git a/tests/source/issue_4954.rs b/tests/source/issue_4954.rs new file mode 100644 index 000000000000..8011c601b654 --- /dev/null +++ b/tests/source/issue_4954.rs @@ -0,0 +1,5 @@ +trait Foo { + type Arg<'a>; +} + +struct Bar(T) where for<'a> T: Foo = ()>; diff --git a/tests/source/issue_4963.rs b/tests/source/issue_4963.rs new file mode 100644 index 000000000000..32e1f6cd41bf --- /dev/null +++ b/tests/source/issue_4963.rs @@ -0,0 +1,5 @@ +mod test { + extern "C" {fn test();} +} + +extern "C" {fn test();} \ No newline at end of file diff --git a/tests/source/issue_5027.rs b/tests/source/issue_5027.rs new file mode 100644 index 000000000000..67beeb23b711 --- /dev/null +++ b/tests/source/issue_5027.rs @@ -0,0 +1,7 @@ +// rustfmt-version: Two + +pub type Iter<'a, D> = impl DoubleEndedIterator)>+ ExactSizeIterator+ 'a; + +trait FOo {pub type Iter<'a, D> = impl DoubleEndedIterator)>+ ExactSizeIterator+ 'a;} + +impl Bar {pub type Iter<'a, D> = impl DoubleEndedIterator)>+ ExactSizeIterator+ 'a;} \ No newline at end of file diff --git a/tests/source/issue_5086.rs b/tests/source/issue_5086.rs new file mode 100644 index 000000000000..1644c9d2ccbb --- /dev/null +++ b/tests/source/issue_5086.rs @@ -0,0 +1,2 @@ +#[cfg(any())] + type Type : Bound ; \ No newline at end of file diff --git a/tests/source/issue_5686.rs b/tests/source/issue_5686.rs new file mode 100644 index 000000000000..3bf96f73b2cd --- /dev/null +++ b/tests/source/issue_5686.rs @@ -0,0 +1,40 @@ +#[repr(u8)] +enum MyEnum { + UnitWithExplicitDiscriminant = 0, + EmptyStructSingleLineBlockComment { + /* Comment */ + } = 1, + EmptyStructMultiLineBlockComment { + /* + * Comment + */ + } = 2, + EmptyStructLineComment { + // comment + } = 3, + EmptyTupleSingleLineBlockComment( + /* Comment */ + ) = 4, + EmptyTupleMultiLineBlockComment( + /* + * Comment + */ + ) = 5, + EmptyTupleLineComment( + // comment + ) = 6, +} + +enum Animal { + Dog(/* tuple variant closer in comment -> ) */) = 1, + #[hello(world)] + Cat(/* tuple variant close in leading attribute */) = 2, + Bee(/* tuple variant closer on associated field attribute */ #[hello(world)] usize) = 3, + Fox(/* tuple variant closer on const fn call */) = some_const_fn(), + Ant(/* tuple variant closer on macro call */) = some_macro!(), + Snake {/* stuct variant closer in comment -> } */} = 6, + #[hell{world}] + Cobra {/* struct variant close in leading attribute */} = 6, + Eagle {/* struct variant closer on associated field attribute */ #[hell{world}]value: Sting} = 7, + Koala {/* struct variant closer on macro call */} = some_macro!{} +} diff --git a/tests/source/item-brace-style-always-next-line.rs b/tests/source/item-brace-style-always-next-line.rs new file mode 100644 index 000000000000..0fb6405120aa --- /dev/null +++ b/tests/source/item-brace-style-always-next-line.rs @@ -0,0 +1,64 @@ +// rustfmt-brace_style: AlwaysNextLine + +mod M { + enum A { + A, + } + + struct B { + b: i32, + } + + // For empty enums and structs, the brace remains on the same line. + enum C {} + + struct D {} + + enum A where T: Copy { + A, + } + + struct B where T: Copy { + b: i32, + } + + // For empty enums and structs, the brace remains on the same line. + enum C where T: Copy {} + + struct D where T: Copy {} +} + + +fn function() +{ + +} + +trait Trait +{ + +} + +impl Trait for T +{ + +} + +trait Trait2 +where + T: Copy + Display + Write + Read + FromStr, {} + +trait Trait3 +where + T: Something + + SomethingElse + + Sync + + Send + + Display + + Debug + + Copy + + Hash + + Debug + + Display + + Write + + Read, {} diff --git a/tests/source/item-brace-style-prefer-same-line.rs b/tests/source/item-brace-style-prefer-same-line.rs new file mode 100644 index 000000000000..dff89b8b66b5 --- /dev/null +++ b/tests/source/item-brace-style-prefer-same-line.rs @@ -0,0 +1,29 @@ +// rustfmt-brace_style: PreferSameLine + +mod M { + enum A + { + A, + } + + struct B + { + b: i32, + } + + enum C {} + + struct D {} + + enum A where T: Copy { + A, + } + + struct B where T: Copy { + b: i32, + } + + enum C where T: Copy {} + + struct D where T: Copy {} +} diff --git a/tests/source/item-brace-style-same-line-where.rs b/tests/source/item-brace-style-same-line-where.rs new file mode 100644 index 000000000000..1d034089f694 --- /dev/null +++ b/tests/source/item-brace-style-same-line-where.rs @@ -0,0 +1,29 @@ +mod M { + enum A + { + A, + } + + struct B + { + b: i32, + } + + // For empty enums and structs, the brace remains on the same line. + enum C {} + + struct D {} + + enum A where T: Copy { + A, + } + + struct B where T: Copy { + b: i32, + } + + // For empty enums and structs, the brace remains on the same line. + enum C where T: Copy {} + + struct D where T: Copy {} +} diff --git a/tests/source/itemized-blocks/no_wrap.rs b/tests/source/itemized-blocks/no_wrap.rs new file mode 100644 index 000000000000..e5699e766848 --- /dev/null +++ b/tests/source/itemized-blocks/no_wrap.rs @@ -0,0 +1,81 @@ +// rustfmt-normalize_comments: true +// rustfmt-format_code_in_doc_comments: true + +//! This is an itemized markdown list (see also issue #3224): +//! * Outer +//! * Outer +//! * Inner +//! * Inner with lots of text so that it could be reformatted something something something lots of text so that it could be reformatted something something something +//! +//! This example shows how to configure fern to output really nicely colored logs +//! - when the log level is error, the whole line is red +//! - when the log level is warn, the whole line is yellow +//! - when the log level is info, the level name is green and the rest of the line is white +//! - when the log level is debug, the whole line is white +//! - when the log level is trace, the whole line is gray ("bright black") +//! +//! This is a numbered markdown list (see also issue #5416): +//! 1. Long long long long long long long long long long long long long long long long long line +//! 2. Another very long long long long long long long long long long long long long long long line +//! 3. Nested list +//! 1. Long long long long long long long long long long long long long long long long line +//! 2. Another very long long long long long long long long long long long long long long line +//! 4. Last item +//! +//! Using the ')' instead of '.' character after the number: +//! 1) Long long long long long long long long long long long long long long long long long line +//! 2) Another very long long long long long long long long long long long long long long long line +//! +//! Deep list that mixes various bullet and number formats: +//! 1. First level with a long long long long long long long long long long long long long long +//! long long long line +//! 2. First level with another very long long long long long long long long long long long long +//! long long long line +//! * Second level with a long long long long long long long long long long long long long +//! long long long line +//! * Second level with another very long long long long long long long long long long long +//! long long long line +//! 1) Third level with a long long long long long long long long long long long long long +//! long long long line +//! 2) Third level with another very long long long long long long long long long long +//! long long long long line +//! - Forth level with a long long long long long long long long long long long long +//! long long long long line +//! - Forth level with another very long long long long long long long long long long +//! long long long long line +//! 3) One more item at the third level +//! 4) Last item of the third level +//! * Last item of second level +//! 3. Last item of first level + +/// All the parameters ***except for `from_theater`*** should be inserted as sent by the remote +/// theater, i.e., as passed to [`Theater::send`] on the remote actor: +/// * `from` is the sending (remote) [`ActorId`], as reported by the remote theater by theater-specific means +/// * `to` is the receiving (local) [`ActorId`], as requested by the remote theater +/// * `tag` is a tag that identifies the message type +/// * `msg` is the (serialized) message +/// All the parameters ***except for `from_theater`*** should be inserted as sent by the remote +/// theater, i.e., as passed to [`Theater::send`] on the remote actor +fn func1() {} + +/// All the parameters ***except for `from_theater`*** should be inserted as sent by the remote +/// theater, i.e., as passed to [`Theater::send`] on the remote actor: +/// * `from` is the sending (remote) [`ActorId`], as reported by the remote theater by theater-specific means +/// * `to` is the receiving (local) [`ActorId`], as requested by the remote theater +/// * `tag` is a tag that identifies the message type +/// * `msg` is the (serialized) message +/// ``` +/// let x = 42; +/// ``` +fn func2() {} + +/// Look: +/// +/// ``` +/// let x = 42; +/// ``` +/// * `from` is the sending (remote) [`ActorId`], as reported by the remote theater by theater-specific means +/// * `to` is the receiving (local) [`ActorId`], as requested by the remote theater +/// * `tag` is a tag that identifies the message type +/// * `msg` is the (serialized) message +fn func3() {} diff --git a/tests/source/itemized-blocks/rewrite_fail.rs b/tests/source/itemized-blocks/rewrite_fail.rs new file mode 100644 index 000000000000..f99c2cc5ff9c --- /dev/null +++ b/tests/source/itemized-blocks/rewrite_fail.rs @@ -0,0 +1,11 @@ +// rustfmt-wrap_comments: true +// rustfmt-max_width: 50 + +// This example shows how to configure fern to output really nicely colored logs +// - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +// - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +// - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +// - when the log level is info, the level name is green and the rest of the line is white +// - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +// - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +fn func1() {} diff --git a/tests/source/itemized-blocks/urls.rs b/tests/source/itemized-blocks/urls.rs new file mode 100644 index 000000000000..2eaaafbbc4aa --- /dev/null +++ b/tests/source/itemized-blocks/urls.rs @@ -0,0 +1,22 @@ +// rustfmt-wrap_comments: true +// rustfmt-max_width: 79 + +//! CMSIS: Cortex Microcontroller Software Interface Standard +//! +//! The version 5 of the standard can be found at: +//! +//! http://arm-software.github.io/CMSIS_5/Core/html/index.html +//! +//! The API reference of the standard can be found at: +//! +//! - example -- http://example.org -- something something something something something something +//! - something something something something something something more -- http://example.org +//! - http://example.org/something/something/something/something/something/something and the rest +//! - Core function access -- http://arm-software.github.io/CMSIS_5/Core/html/group__Core__Register__gr.html +//! - Intrinsic functions for CPU instructions -- http://arm-software.github.io/CMSIS_5/Core/html/group__intrinsic__CPU__gr.html +//! - Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Vestibulum sem lacus, commodo vitae. +//! +//! The reference C implementation used as the base of this Rust port can be +//! found at +//! +//! https://github.com/ARM-software/CMSIS_5/blob/5.3.0/CMSIS/Core/Include/cmsis_gcc.h diff --git a/tests/source/itemized-blocks/wrap.rs b/tests/source/itemized-blocks/wrap.rs new file mode 100644 index 000000000000..768461a43f95 --- /dev/null +++ b/tests/source/itemized-blocks/wrap.rs @@ -0,0 +1,89 @@ +// rustfmt-wrap_comments: true +// rustfmt-format_code_in_doc_comments: true +// rustfmt-max_width: 50 + +//! This is an itemized markdown list (see also issue #3224): +//! * Outer +//! * Outer +//! * Inner +//! * Inner with lots of text so that it could be reformatted something something something lots of text so that it could be reformatted something something something +//! +//! This example shows how to configure fern to output really nicely colored logs +//! - when the log level is error, the whole line is red +//! - when the log level is warn, the whole line is yellow +//! - when the log level is info, the level name is green and the rest of the line is white +//! - when the log level is debug, the whole line is white +//! - when the log level is trace, the whole line is gray ("bright black") +//! +//! This is a numbered markdown list (see also issue #5416): +//! 1. Long long long long long long long long long long long long long long long long long line +//! 2. Another very long long long long long long long long long long long long long long long line +//! 3. Nested list +//! 1. Long long long long long long long long long long long long long long long long line +//! 2. Another very long long long long long long long long long long long long long long line +//! 4. Last item +//! +//! Using the ')' instead of '.' character after the number: +//! 1) Long long long long long long long long long long long long long long long long long line +//! 2) Another very long long long long long long long long long long long long long long long line +//! +//! Deep list that mixes various bullet and number formats: +//! 1. First level with a long long long long long long long long long long long long long long +//! long long long line +//! 2. First level with another very long long long long long long long long long long long long +//! long long long line +//! * Second level with a long long long long long long long long long long long long long +//! long long long line +//! * Second level with another very long long long long long long long long long long long +//! long long long line +//! 1) Third level with a long long long long long long long long long long long long long +//! long long long line +//! 2) Third level with another very long long long long long long long long long long +//! long long long long line +//! - Forth level with a long long long long long long long long long long long long +//! long long long long line +//! - Forth level with another very long long long long long long long long long long +//! long long long long line +//! 3) One more item at the third level +//! 4) Last item of the third level +//! * Last item of second level +//! 3. Last item of first level + +// This example shows how to configure fern to output really nicely colored logs +// - when the log level is error, the whole line is red +// - when the log level is warn, the whole line is yellow +// - when the log level is info, the level name is green and the rest of the line is white +// - when the log level is debug, the whole line is white +// - when the log level is trace, the whole line is gray ("bright black") + +/// All the parameters ***except for `from_theater`*** should be inserted as sent by the remote +/// theater, i.e., as passed to [`Theater::send`] on the remote actor: +/// * `from` is the sending (remote) [`ActorId`], as reported by the remote theater by theater-specific means +/// * `to` is the receiving (local) [`ActorId`], as requested by the remote theater +/// * `tag` is a tag that identifies the message type +/// * `msg` is the (serialized) message +/// All the parameters ***except for `from_theater`*** should be inserted as sent by the remote +/// theater, i.e., as passed to [`Theater::send`] on the remote actor +fn func1() {} + +/// All the parameters ***except for `from_theater`*** should be inserted as sent by the remote +/// theater, i.e., as passed to [`Theater::send`] on the remote actor: +/// * `from` is the sending (remote) [`ActorId`], as reported by the remote theater by theater-specific means +/// * `to` is the receiving (local) [`ActorId`], as requested by the remote theater +/// * `tag` is a tag that identifies the message type +/// * `msg` is the (serialized) message +/// ``` +/// let x = 42; +/// ``` +fn func2() {} + +/// Look: +/// +/// ``` +/// let x = 42; +/// ``` +/// * `from` is the sending (remote) [`ActorId`], as reported by the remote theater by theater-specific means +/// * `to` is the receiving (local) [`ActorId`], as requested by the remote theater +/// * `tag` is a tag that identifies the message type +/// * `msg` is the (serialized) message +fn func3() {} diff --git a/tests/source/label_break.rs b/tests/source/label_break.rs new file mode 100644 index 000000000000..2c79fd35e70c --- /dev/null +++ b/tests/source/label_break.rs @@ -0,0 +1,28 @@ +// format with label break value. +fn main() { + +'empty_block: {} + +'block: { + do_thing(); + if condition_not_met() { + break 'block; + } + do_next_thing(); + if condition_not_met() { + break 'block; + } + do_last_thing(); +} + +let result = 'block: { + if foo() { + // comment + break 'block 1; + } + if bar() { /* comment */ + break 'block 2; + } + 3 +}; +} \ No newline at end of file diff --git a/tests/source/large-block.rs b/tests/source/large-block.rs new file mode 100644 index 000000000000..09e9169f3408 --- /dev/null +++ b/tests/source/large-block.rs @@ -0,0 +1,5 @@ +fn issue1351() { + std_fmt_Arguments_new_v1_std_rt_begin_panic_fmt_sdfasfasdfasdf({ + static __STATIC_FMTSTR: &'static [&'static str] = &[]; + }); +} diff --git a/tests/source/large_vec.rs b/tests/source/large_vec.rs new file mode 100644 index 000000000000..34d5bf399544 --- /dev/null +++ b/tests/source/large_vec.rs @@ -0,0 +1,29 @@ +// See #1470. + +impl Environment { + pub fn new_root() -> Rc> { + let mut env = Environment::new(); + let builtin_functions = &[("println", + Function::NativeVoid(CallSign { + num_params: 0, + variadic: true, + param_types: vec![], + }, + native_println)), + ("run_http_server", + Function::NativeVoid(CallSign { + num_params: 1, + variadic: false, + param_types: + vec![Some(ConstraintType::Function)], + }, + native_run_http_server)), + ("len", + Function::NativeReturning(CallSign { + num_params: 1, + variadic: false, + param_types: vec![None], + }, + native_len))]; + } +} diff --git a/tests/source/lazy_static.rs b/tests/source/lazy_static.rs new file mode 100644 index 000000000000..38fefbcbef5e --- /dev/null +++ b/tests/source/lazy_static.rs @@ -0,0 +1,45 @@ +// Format `lazy_static!`. + +lazy_static! { +static ref CONFIG_NAME_REGEX: regex::Regex = +regex::Regex::new(r"^## `([^`]+)`").expect("Failed creating configuration pattern"); +static ref CONFIG_VALUE_REGEX: regex::Regex = regex::Regex::new(r#"^#### `"?([^`"]+)"?`"#) +.expect("Failed creating configuration value pattern"); +} + +// We need to be able to format `lazy_static!` without known syntax. +lazy_static!( + xxx, +yyyy , + zzzzz +); + +lazy_static!{ +} + +// #2354 +lazy_static ! { +pub static ref Sbase64_encode_string : :: lisp :: LispSubrRef = { +let subr = :: remacs_sys :: Lisp_Subr { +header : :: remacs_sys :: Lisp_Vectorlike_Header { +size : ( +( :: remacs_sys :: PseudovecType :: PVEC_SUBR as :: libc :: ptrdiff_t ) << :: +remacs_sys :: PSEUDOVECTOR_AREA_BITS ) , } , function : self :: +Fbase64_encode_string as * const :: libc :: c_void , min_args : 1i16 , +max_args : 2i16 , symbol_name : ( b"base64-encode-string\x00" ) . as_ptr ( ) +as * const :: libc :: c_char , intspec : :: std :: ptr :: null ( ) , doc : :: +std :: ptr :: null ( ) , lang : :: remacs_sys :: Lisp_Subr_Lang_Rust , } ; +unsafe { +let ptr = :: remacs_sys :: xmalloc ( +:: std :: mem :: size_of :: < :: remacs_sys :: Lisp_Subr > ( ) ) as * mut :: +remacs_sys :: Lisp_Subr ; :: std :: ptr :: copy_nonoverlapping ( +& subr , ptr , 1 ) ; :: std :: mem :: forget ( subr ) ; :: lisp :: ExternalPtr +:: new ( ptr ) } } ; } + + +lazy_static! { +static ref FOO: HashMap Result, Either> +),> = HashMap::new(); +} diff --git a/tests/source/let_else.rs b/tests/source/let_else.rs new file mode 100644 index 000000000000..85b3604ad3c7 --- /dev/null +++ b/tests/source/let_else.rs @@ -0,0 +1,162 @@ +// rustfmt-single_line_let_else_max_width: 100 + +fn main() { + // Although this won't compile it still parses so make sure we can format empty else blocks + let Some(x) = opt else {}; + + // let-else may be formatted on a single line if they are "short" + // and only contain a single expression + let Some(x) = opt else { return }; + + let Some(x) = opt else { + return + }; + + let Some(x) = opt else { return; }; + + let Some(x) = opt else { + // nope + return; + }; + + let Some(x) = opt else { let y = 1; return y }; + + let Some(x) = y.foo("abc", fairly_long_identifier, "def", "123456", "string", "cheese") else { bar() }; + + let Some(x) = abcdef().foo("abc", some_really_really_really_long_ident, "ident", "123456").bar().baz().qux("fffffffffffffffff") else { foo_bar() }; +} + +fn with_comments_around_else_keyword() { + let Some(x) = opt /* pre else keyword block-comment */ else { return }; + + let Some(x) = opt else /* post else keyword block-comment */ { return }; + + let Some(x) = opt /* pre else keyword block-comment */ else /* post else keyword block-comment */ { return }; + + let Some(x) = opt // pre else keyword line-comment + else { return }; + + let Some(x) = opt else + // post else keyword line-comment + { return }; + + let Some(x) = opt // pre else keyword line-comment + else + // post else keyword line-comment + { return }; + +} + +fn unbreakable_initializer_expr_pre_formatting_let_else_length_near_max_width() { + // Pre Formatting: + // The length of `(indent)let pat = init else block;` is 100 (max_width) + // Post Formatting: + // The formatting is left unchanged! + let Some(x) = some_really_really_really_really_really_really_really_long_name_A else { return }; + + // Pre Formatting: + // The length of `(indent)let pat = init else block;` is 100 (max_width) + // Post Formatting: + // The else keyword and opening brace remain on the same line as the initializer expr, + // and the else block is formatted over multiple lines because we can't fit the + // else block on the same line as the initializer expr. + let Some(x) = some_really_really_really_really_really_really_really_long_name___B else {return}; + + // Pre Formatting: + // The length of `(indent)let pat = init else block;` is 100 (max_width) + // Post Formatting: + // The else keyword and opening brace remain on the same line as the initializer expr, + // and the else block is formatted over multiple lines because we can't fit the + // else block on the same line as the initializer expr. + let Some(x) = some_really_really_really_really_long_name_____C else {some_divergent_function()}; + + // Pre Formatting: + // The length of `(indent)let pat = init else block;` is 101 (> max_width) + // Post Formatting: + // The else keyword and opening brace remain on the same line as the initializer expr, + // and the else block is formatted over multiple lines because we can't fit the + // else block on the same line as the initializer expr. + let Some(x) = some_really_really_really_really_really_really_really_long_name__D else { return }; +} + +fn unbreakable_initializer_expr_pre_formatting_length_up_to_opening_brace_near_max_width() { + // Pre Formatting: + // The length of `(indent)let pat = init else {` is 99 (< max_width) + // Post Formatting: + // The else keyword and opening brace remain on the same line as the initializer expr, + // and the else block is formatted over multiple lines because we can't fit the + // else block on the same line as the initializer expr. + let Some(x) = some_really_really_really_really_really_really_really_really_long_name___E else {return}; + + // Pre Formatting: + // The length of `(indent)let pat = init else {` is 101 (> max_width) + // Post Formatting: + // The else keyword and opening brace cannot fit on the same line as the initializer expr. + // They are formatted on the next line. + let Some(x) = some_really_really_really_really_really_really_really_really_long_name_____F else {return}; +} + +fn unbreakable_initializer_expr_pre_formatting_length_through_initializer_expr_near_max_width() { + // Pre Formatting: + // The length of `(indent)let pat = init` is 99 (< max_width) + // Post Formatting: + // The else keyword and opening brace cannot fit on the same line as the initializer expr. + // They are formatted on the next line. + let Some(x) = some_really_really_really_really_really_really_really_really_really_long_name___G else {return}; + + // Pre Formatting: + // The length of `(indent)let pat = init` is 100 (max_width) + // Post Formatting: + // Break after the `=` and put the initializer expr on it's own line. + // Because the initializer expr is multi-lined the else is placed on it's own line. + let Some(x) = some_really_really_really_really_really_really_really_really_really_long_name____H else {return}; + + // Pre Formatting: + // The length of `(indent)let pat = init` is 109 (> max_width) + // Post Formatting: + // Break after the `=` and put the initializer expr on it's own line. + // Because the initializer expr is multi-lined the else is placed on it's own line. + // The initializer expr has a length of 91, which when indented on the next line + // The `(indent)init` line has a lengh of 99. This is the max length that the `init` can be + // before we start running into max_width issues. I suspect this is becuase the shape is + // accounting for the `;` at the end of the `let-else` statement. + let Some(x) = some_really_really_really_really_really_really_really_really_really_really_long_name______I else {return}; + + // Pre Formatting: + // The length of `(indent)let pat = init` is 110 (> max_width) + // Post Formatting: + // Max length issues prevent us from formatting. + // The initializer expr has a length of 92, which if it would be indented on the next line + // the `(indent)init` line has a lengh of 100 which == max_width of 100. + // One might expect formatting to succeed, but I suspect the reason we hit max_width issues is + // because the Shape is accounting for the `;` at the end of the `let-else` statement. + let Some(x) = some_really_really_really_really_really_really_really_really_really_really_really_long_nameJ else {return}; +} + +fn long_patterns() { + let Foo {x: Bar(..), y: FooBar(..), z: Baz(..)} = opt else { + return; + }; + + // with version=One we don't wrap long array patterns + let [aaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbb, cccccccccccccccccc, dddddddddddddddddd] = opt else { + return; + }; + + let ("aaaaaaaaaaaaaaaaaaa" | "bbbbbbbbbbbbbbbbb" | "cccccccccccccccccccccccc" | "dddddddddddddddd" | "eeeeeeeeeeeeeeee") = opt else { + return; + }; + + let Some(Ok((Message::ChangeColor(super::color::Color::Rgb(r, g, b)), Point { x, y, z }))) = opt else { + return; + }; +} + +fn with_trailing_try_operator() { + // Currently the trailing ? forces the else on the next line + // This may be revisited in style edition 2024 + let Some(next_bucket) = ranking_rules[cur_ranking_rule_index].next_bucket(ctx, logger, &ranking_rule_universes[cur_ranking_rule_index])? else { return }; + + // Maybe this is a workaround? + let Ok(Some(next_bucket)) = ranking_rules[cur_ranking_rule_index].next_bucket(ctx, logger, &ranking_rule_universes[cur_ranking_rule_index]) else { return }; +} diff --git a/tests/source/long-fn-1/version_one.rs b/tests/source/long-fn-1/version_one.rs new file mode 100644 index 000000000000..d6832c2af095 --- /dev/null +++ b/tests/source/long-fn-1/version_one.rs @@ -0,0 +1,21 @@ +// rustfmt-version: One +// Tests that a function which is almost short enough, but not quite, gets +// formatted correctly. + +impl Foo { + fn some_input(&mut self, input: Input, input_path: Option, ) -> (Input, Option) {} + + fn some_inpu(&mut self, input: Input, input_path: Option) -> (Input, Option) {} +} + +// #1843 +#[allow(non_snake_case)] +pub extern "C" fn Java_com_exonum_binding_storage_indices_ValueSetIndexProxy_nativeContainsByHash() -> bool { + false +} + +// #3009 +impl Something { + fn my_function_name_is_way_to_long_but_used_as_a_case_study_or_an_example_its_fine( +) -> Result< (), String > {} +} diff --git a/tests/source/long-fn-1/version_two.rs b/tests/source/long-fn-1/version_two.rs new file mode 100644 index 000000000000..f402a26e8b6a --- /dev/null +++ b/tests/source/long-fn-1/version_two.rs @@ -0,0 +1,21 @@ +// rustfmt-version: Two +// Tests that a function which is almost short enough, but not quite, gets +// formatted correctly. + +impl Foo { + fn some_input(&mut self, input: Input, input_path: Option, ) -> (Input, Option) {} + + fn some_inpu(&mut self, input: Input, input_path: Option) -> (Input, Option) {} +} + +// #1843 +#[allow(non_snake_case)] +pub extern "C" fn Java_com_exonum_binding_storage_indices_ValueSetIndexProxy_nativeContainsByHash() -> bool { + false +} + +// #3009 +impl Something { + fn my_function_name_is_way_to_long_but_used_as_a_case_study_or_an_example_its_fine( +) -> Result< (), String > {} +} diff --git a/tests/source/long-match-arms-brace-newline.rs b/tests/source/long-match-arms-brace-newline.rs new file mode 100644 index 000000000000..927ada0ffb2e --- /dev/null +++ b/tests/source/long-match-arms-brace-newline.rs @@ -0,0 +1,15 @@ +// rustfmt-format_strings: true +// rustfmt-max_width: 80 +// rustfmt-control_brace_style: AlwaysNextLine + +fn main() { + match x { + aaaaaaaa::Bbbbb::Ccccccccccccc(_, Some(ref x)) if x == + "aaaaaaaaaaa \ + aaaaaaa \ + aaaaaa" => { + Ok(()) + } + _ => Err(x), + } +} diff --git a/tests/source/long-use-statement-issue-3154.rs b/tests/source/long-use-statement-issue-3154.rs new file mode 100644 index 000000000000..339382b5bbf2 --- /dev/null +++ b/tests/source/long-use-statement-issue-3154.rs @@ -0,0 +1,3 @@ +// rustfmt-reorder_imports: false + +pub use self :: super :: super :: super :: root::mozilla::detail::StringClassFlags as nsTStringRepr_ClassFlags ; diff --git a/tests/source/long_field_access.rs b/tests/source/long_field_access.rs new file mode 100644 index 000000000000..7aa626221aea --- /dev/null +++ b/tests/source/long_field_access.rs @@ -0,0 +1,3 @@ +fn f() { + block_flow.base.stacking_relative_position_of_display_port = self.base.stacking_relative_position_of_display_port; +} diff --git a/tests/source/loop.rs b/tests/source/loop.rs new file mode 100644 index 000000000000..6e92cdc6c2c0 --- /dev/null +++ b/tests/source/loop.rs @@ -0,0 +1,29 @@ + +fn main() { + loop + { return some_val;} + +let x = loop { do_forever(); }; + + 'label : loop { + // Just comments + } + + 'a: while loooooooooooooooooooooooooooooooooong_variable_name + another_value > some_other_value{} + + while aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa > bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb { + } + + while aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa {} + + 'b: for xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx in some_iter(arg1, arg2) { + // do smth + } + + while let Some(i) = x.find('s') + { + x.update(); + continue; + continue 'foo; + } +} diff --git a/tests/source/macro_not_expr.rs b/tests/source/macro_not_expr.rs new file mode 100644 index 000000000000..d8de4dce38f2 --- /dev/null +++ b/tests/source/macro_not_expr.rs @@ -0,0 +1,7 @@ +macro_rules! test { + ($($t:tt)*) => {} +} + +fn main() { + test!( a : B => c d ); +} diff --git a/tests/source/macro_rules.rs b/tests/source/macro_rules.rs new file mode 100644 index 000000000000..5aaca0c83fa3 --- /dev/null +++ b/tests/source/macro_rules.rs @@ -0,0 +1,301 @@ +// rustfmt-format_macro_matchers: true + +macro_rules! m { + () => (); + ( $ x : ident ) => (); + ( $ m1 : ident , $ m2 : ident , $ x : ident ) => (); + ( $($beginning:ident),*;$middle:ident;$($end:ident),* ) => (); + ( $($beginning: ident),*; $middle: ident; $($end: ident),*; $($beginning: ident),*; $middle: ident; $($end: ident),* ) => {}; + ( $ name : ident ( $ ( $ dol : tt $ var : ident ) * ) $ ( $ body : tt ) * ) => (); + ( $( $ i : ident : $ ty : ty , $def : expr , $stb : expr , $ ( $ dstring : tt ) , + ) ; + $ ( ; ) * + $( $ i : ident : $ ty : ty , $def : expr , $stb : expr , $ ( $ dstring : tt ) , + ) ; + $ ( ; ) * + ) => {}; + ( $foo: tt foo [$ attr : meta] $name: ident ) => {}; + ( $foo: tt [$ attr: meta] $name: ident ) => {}; + ( $foo: tt &'a [$attr : meta] $name: ident ) => {}; + ( $foo: tt foo # [ $attr : meta] $name: ident ) => {}; + ( $foo: tt # [ $attr : meta] $name: ident) => {}; + ( $foo: tt &'a # [ $attr : meta] $name: ident ) => {}; + ( $ x : tt foo bar foo bar foo bar $ y : tt => x*y*z $ z : tt , $ ( $a: tt ) , * ) => {}; +} + + +macro_rules! impl_a_method { + ($n:ident ( $a:ident : $ta:ty ) -> $ret:ty { $body:expr }) => { + fn $n($a:$ta) -> $ret { $body } + macro_rules! $n { ($va:expr) => { $n($va) } } + }; + ($n:ident ( $a:ident : $ta:ty, $b:ident : $tb:ty ) -> $ret:ty { $body:expr }) => { + fn $n($a:$ta, $b:$tb) -> $ret { $body } + macro_rules! $n { ($va:expr, $vb:expr) => { $n($va, $vb) } } + }; + ($n:ident ( $a:ident : $ta:ty, $b:ident : $tb:ty, $c:ident : $tc:ty ) -> $ret:ty { $body:expr }) => { + fn $n($a:$ta, $b:$tb, $c:$tc) -> $ret { $body } + macro_rules! $n { ($va:expr, $vb:expr, $vc:expr) => { $n($va, $vb, $vc) } } + }; + ($n:ident ( $a:ident : $ta:ty, $b:ident : $tb:ty, $c:ident : $tc:ty, $d:ident : $td:ty ) -> $ret:ty { $body:expr }) => { + fn $n($a:$ta, $b:$tb, $c:$tc, $d:$td) -> $ret { $body } + macro_rules! $n { ($va:expr, $vb:expr, $vc:expr, $vd:expr) => { $n($va, $vb, $vc, $vd) } } + }; +} + +macro_rules! m { + // a + ($expr :expr, $( $func : ident ) * ) => { + { + let x = $expr; + $func ( + x + ) + } + }; + + /* b */ + + () => {/* c */}; + + (@tag) => + { + + }; + +// d +( $item:ident ) => { + mod macro_item { struct $item ; } +}; +} + +macro m2 { + // a + ($expr :expr, $( $func : ident ) * ) => { + { + let x = $expr; + $func ( + x + ) + } + } + + /* b */ + + () => {/* c */} + + (@tag) => + { + + } + +// d +( $item:ident ) => { + mod macro_item { struct $item ; } +} +} + +// #2438, #2476 +macro_rules! m { + () => { + fn foo() { + this_line_is_98_characters_long_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx( + ); + } + } +} +macro_rules! m { + () => { + fn foo() { + this_line_is_99_characters_long_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx( +); + } + }; +} +macro_rules! m { + () => { + fn foo() { + this_line_is_100_characters_long_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx( +); + } + }; +} +macro_rules! m { + () => { + fn foo() { + this_line_is_101_characters_long_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx( + ); + } + }; +} + +// #2439 +macro_rules! m { + ($line0_xxxxxxxxxxxxxxxxx: expr, $line1_xxxxxxxxxxxxxxxxx: expr, $line2_xxxxxxxxxxxxxxxxx: expr, $line3_xxxxxxxxxxxxxxxxx: expr,) => {}; +} + +// #2466 +// Skip formatting `macro_rules!` that are not using `{}`. +macro_rules! m ( + () => () +); +macro_rules! m [ + () => () +]; + +// #2470 +macro foo($type_name: ident, $docs: expr) { + #[allow(non_camel_case_types)] + #[doc=$docs] + #[derive(Debug, Clone, Copy)] + pub struct $type_name; +} + +// #2534 +macro_rules! foo { + ($a:ident : $b:ty) => {}; + ($a:ident $b:ident $c:ident) => {}; +} + +// #2538 +macro_rules! add_message_to_notes { + ($msg:expr) => {{ + let mut lines = message.lines(); + notes.push_str(&format!("\n{}: {}", level, lines.next().unwrap())); + for line in lines { + notes.push_str(&format!( + "\n{:indent$}{line}", + "", + indent = level.len() + 2, + line = line, + )); + } + }} +} + +// #2560 +macro_rules! binary { + ($_self:ident,$expr:expr, $lhs:expr,$func:ident) => { + while $_self.matched($expr) { + let op = $_self.get_binary_op()?; + + let rhs = Box::new($_self.$func()?); + + $lhs = Spanned { + span: $lhs.get_span().to(rhs.get_span()), + value: Expression::Binary { + lhs: Box::new($lhs), + op, + rhs, + }, + } + } + }; +} + +// #2558 +macro_rules! m { + ($x:) => {}; + ($($foo:expr)()?) => {}; +} + +// #2749 +macro_rules! foo { + ($(x)* {}) => {}; + ($(x)* ()) => {}; + ($(x)* []) => {}; +} +macro_rules! __wundergraph_expand_sqlite_mutation { + ( $mutation_name:ident $((context = $($context:tt)*))*{ $( $entity_name:ident( $(insert = $insert:ident,)* $(update = $update:ident,)* $(delete = $($delete:tt)+)* ), )* } ) => {}; +} + +// #2607 +macro_rules! bench { + ($ty:ident) => { + criterion_group!( + name = benches; + config = ::common_bench::reduced_samples(); + targets = call, map; + ); + }; +} + +// #2770 +macro_rules! save_regs { + () => { + asm!("push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11" + :::: "intel", "volatile"); + }; +} + +// #2721 +macro_rules! impl_as_byte_slice_arrays { + ($n:expr,) => {}; + ($n:expr, $N:ident, $($NN:ident,)*) => { + impl_as_byte_slice_arrays!($n - 1, $($NN,)*); + + impl AsByteSliceMut for [T; $n] where [T]: AsByteSliceMut { + fn as_byte_slice_mut(&mut self) -> &mut [u8] { + self[..].as_byte_slice_mut() + } + + fn to_le(&mut self) { + self[..].to_le() + } + } + }; + (!div $n:expr,) => {}; + (!div $n:expr, $N:ident, $($NN:ident,)*) => { + impl_as_byte_slice_arrays!(!div $n / 2, $($NN,)*); + + impl AsByteSliceMut for [T; $n] where [T]: AsByteSliceMut { + fn as_byte_slice_mut(&mut self) -> &mut [u8] { + self[..].as_byte_slice_mut() + } + + fn to_le(&mut self) { + self[..].to_le() + } + } + }; +} + +// #2919 +fn foo() { + { + macro_rules! touch_value { + ($func:ident, $value:expr) => {{ + let result = API::get_cached().$func(self, key.as_ptr(), $value, ffi::VSPropAppendMode::paTouch); + let result = API::get_cached().$func(self, key.as_ptr(), $value, ffi::VSPropAppend); + let result = API::get_cached().$func(self, key.as_ptr(), $value, ffi::VSPropAppendM); + let result = APIIIIIIIII::get_cached().$func(self, key.as_ptr(), $value, ffi::VSPropAppendM); + let result = API::get_cached().$func(self, key.as_ptr(), $value, ffi::VSPropAppendMMMMMMMMMM); + debug_assert!(result == 0); + }}; + } + } +} + +// #2642 +macro_rules! template { + ($name: expr) => { + format_args!(r##" +"http://example.com" + +# test +"##, $name) + } +} + +macro_rules! template { + () => { + format_args!(r" +// + +") + } +} diff --git a/tests/source/macros.rs b/tests/source/macros.rs new file mode 100644 index 000000000000..3b286579ca8f --- /dev/null +++ b/tests/source/macros.rs @@ -0,0 +1,486 @@ +// rustfmt-normalize_comments: true +// rustfmt-format_macro_matchers: true +itemmacro!(this, is.now() .formatted(yay)); + +itemmacro!(really, long.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbb() .is.formatted()); + +itemmacro!{this, is.brace().formatted()} + +fn main() { + foo! ( ); + + foo!(,); + + bar!( a , b , c ); + + bar!( a , b , c , ); + + baz!(1+2+3, quux. kaas()); + + quux!(AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB); + + kaas!(/* comments */ a /* post macro */, b /* another */); + + trailingcomma!( a , b , c , ); + // Preserve trailing comma only when necessary. + ok!(file.seek( + SeekFrom::Start( + table.map(|table| fixture.offset(table)).unwrap_or(0), + ) + )); + + noexpr!( i am not an expression, OK? ); + + vec! [ a , b , c]; + + vec! [AAAAAA, AAAAAA, AAAAAA, AAAAAA, AAAAAA, AAAAAA, AAAAAA, AAAAAA, AAAAAA, + BBBBB, 5, 100-30, 1.33, b, b, b]; + + vec! [a /* comment */]; + + // Trailing spaces after a comma + vec![ + a, + ]; + + vec![a; b]; + vec!(a; b); + vec!{a; b}; + + vec![a, b; c]; + vec![a; b, c]; + + vec![a; (|x| { let y = x + 1; let z = y + 1; z })(2)]; + vec![a; xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx]; + vec![a; unsafe { + x + 1 + }]; + + unknown_bracket_macro__comma_should_not_be_stripped![ + a, + ]; + + foo(makro!(1, 3)); + + hamkaas!{ () }; + + macrowithbraces! {dont, format, me} + + x!(fn); + + some_macro!( + + ); + + some_macro![ + ]; + + some_macro!{ + // comment + }; + + some_macro!{ + // comment + }; + + some_macro!( + // comment + not function like + ); + + // #1712 + let image = gray_image!( + 00, 01, 02; + 10, 11, 12; + 20, 21, 22); + + // #1092 + chain!(input, a:take!(max_size), || []); + + // #2727 + foo!("bar") +; +} + +impl X { + empty_invoc!{} + empty_invoc! {} +} + +fn issue_1279() { + println!("dsfs"); // a comment +} + +fn issue_1555() { + let hello = &format!("HTTP/1.1 200 OK\r\nServer: {}\r\n\r\n{}", + "65454654654654654654654655464", + "4"); +} + +fn issue1178() { + macro_rules! foo { + (#[$attr:meta] $name:ident) => {} + } + + foo!(#[doc = "bar"] baz); +} + +fn issue1739() { + sql_function!(add_rss_item, + add_rss_item_t, + (a: types::Integer, + b: types::Timestamptz, + c: types::Text, + d: types::Text, + e: types::Text)); + + w.slice_mut(s![.., init_size[1] - extreeeeeeeeeeeeeeeeeeeeeeeem..init_size[1], ..]) + .par_map_inplace(|el| *el = 0.); +} + +fn issue_1885() { + let threads = people.into_iter().map(|name| { + chan_select! { + rx.recv() => {} + } + }).collect::>(); +} + +fn issue_1917() { + mod x { + quickcheck! { + fn test(a: String, s: String, b: String) -> TestResult { + if a.find(&s).is_none() { + + TestResult::from_bool(true) + } else { + TestResult::discard() + } + } + } + } +} + +fn issue_1921() { + // Macro with tabs. + lazy_static! { + static ref ONE: u32 = 1; + static ref TWO: u32 = 2; + static ref THREE: u32 = 3; + static ref FOUR: u32 = { + let mut acc = 1; + acc += 1; + acc += 2; + acc + } +} +} + +// #1577 +fn issue1577() { + let json = json!({ + "foo": "bar", + }); +} + +// #3174 +fn issue_3174() { + let data = + if let Some(debug) = error.debug_info() { + json!({ + "errorKind": format!("{:?}", error.err_kind()), + "debugMessage": debug.message, + }) + } else { + json!({"errorKind": format!("{:?}", error.err_kind())}) + }; +} + +gfx_pipeline!(pipe { + vbuf: gfx::VertexBuffer = (), + out: gfx::RenderTarget = "Target0", +}); + +// #1919 +#[test] +fn __bindgen_test_layout_HandleWithDtor_open0_int_close0_instantiation() { + assert_eq!( + ::std::mem::size_of::>(), + 8usize, + concat!( + "Size of template specialization: ", + stringify ! ( HandleWithDtor < :: std :: os :: raw :: c_int > ) + ) + ); + assert_eq ! ( :: std :: mem :: align_of :: < HandleWithDtor < :: std :: os :: raw :: c_int > > ( ) , 8usize , concat ! ( "Alignment of template specialization: " , stringify ! ( HandleWithDtor < :: std :: os :: raw :: c_int > ) ) ); +} + +// #878 +macro_rules! try_opt { + ($expr:expr) => (match $expr { + Some(val) => val, + + None => { return None; } + }) +} + +// #2214 +// macro call whose argument is an array with trailing comma. +fn issue2214() { +make_test!(str_searcher_ascii_haystack, "bb", "abbcbbd", [ + Reject(0, 1), + Match (1, 3), + Reject(3, 4), + Match (4, 6), + Reject(6, 7), +]); +} + +fn special_case_macros() { + let p = eprint!(); + let q = eprint!("{}", 1); + let r = eprint!("{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); + let s = eprint!("{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26); + + let q = eprintln!("{}", 1); + let r = eprintln!("{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); + let s = eprintln!("{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26); + + let q = format!("{}", 1); + let r = format!("{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); + let s = format!("{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26); + + let q = format_args!("{}", 1); + let r = format_args!("{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); + let s = format_args!("{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26); + + let q = print!("{}", 1); + let r = print!("{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); + let s = print!("{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26); + + let q = println!("{}", 1); + let r = println!("{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); + let s = println!("{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26); + + let q = unreachable!("{}", 1); + let r = unreachable!("{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); + let s = unreachable!("{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26); + + debug!("{}", 1); + debug!("{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); + debug!("{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26); + + error!("{}", 1); + error!("{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); + error!("{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26); + + info!("{}", 1); + info!("{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); + info!("{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26); + + panic!("{}", 1); + panic!("{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); + panic!("{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26); + + warn!("{}", 1); + warn!("{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); + warn!("{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26); + + assert!(); + assert!(result == 42); + assert!(result == 42, "Ahoy there, {}!", target); + assert!(result == 42, "Arr! While plunderin' the hold, we got '{}' when given '{}' (we expected '{}')", result, input, expected); + assert!(result == 42, "{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26); + + assert_eq!(); + assert_eq!(left); + assert_eq!(left, right); + assert_eq!(left, right, "Ahoy there, {}!", target); + assert_eq!(left, right, "Arr! While plunderin' the hold, we got '{}' when given '{}' (we expected '{}')", result, input, expected); + assert_eq!(first_realllllllllllly_long_variable_that_doesnt_fit_one_one_line, second_reallllllllllly_long_variable_that_doesnt_fit_one_one_line, "Arr! While plunderin' the hold, we got '{}' when given '{}' (we expected '{}')", result, input, expected); + assert_eq!(left + 42, right, "Arr! While plunderin' the hold, we got '{}' when given '{}' (we expected '{}')", result, input, expected); + assert_eq!(left, right, "{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26); + + write!(&mut s, "Ahoy there, {}!", target); + write!(&mut s, "Arr! While plunderin' the hold, we got '{}' when given '{}' (we expected '{}')", result, input, expected); + write!(&mut s, "{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26); + + writeln!(&mut s, "Ahoy there, {}!", target); + writeln!(&mut s, "Arr! While plunderin' the hold, we got '{}' when given '{}' (we expected '{}')", result, input, expected); + writeln!(&mut s, "{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26); +} + +// #1209 +impl Foo { + /// foo + pub fn foo(&self) -> Bar {} +} + +// #819 +fn macro_in_pattern_position () { + let x = match y { + foo!( ) => (), + bar!( a, b, + c) => (), + bar!(a + , b + , c + ,) => (), + baz!( 1 + 2 + 3, quux.kaas( ) + ) => (), + quux!(AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB) => (), + }; +} + +macro foo() { + + +} + +pub macro bar($x:ident+$y:expr; ) { + fn foo($x: Foo) { + long_function(a_long_argument_to_a_long_function_is_what_this_is(AAAAAAAAAAAAAAAAAAAAAAAAAAAA), + $x.bar($y)); + } +} + +macro foo() { + // a comment + fn foo() { + // another comment + bar(); + } +} + +// #2574 +macro_rules! test { + () => {{}} +} + +macro lex_err($kind: ident $(, $body: expr)*) { + Err(QlError::LexError(LexError::$kind($($body,)*))) +} + +// Preserve trailing comma on item-level macro with `()` or `[]`. +methods![ get, post, delete, ]; +methods!( get, post, delete, ); + +// #2588 +macro_rules! m { + () => { + r#" + test + "# + }; +} +fn foo() { + f!{r#" + test + "#}; +} + +// #2591 +fn foo() { + match 0u32 { + 0 => (), + _ => unreachable!(/* obviously */), + } +} + +fn foo() { + let _ = column!(/* here */); +} + +// #2616 +// Preserve trailing comma when using mixed layout for macro call. +fn foo() { + foo!(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); + foo!(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,); +} + +// #2830 +// Preserve trailing comma-less/ness inside nested macro. +named!( + do_parse_gsv, + map_res!( + do_parse!( + number_of_sentences: map_res!(digit, parse_num::) + >> char!(',') + >> sentence_index: map_res!(digit, parse_num::) + >> char!(',') + >> total_number_of_sats: map_res!(digit, parse_num::) + >> char!(',') + >> sat0: opt!(complete!(parse_gsv_sat_info)) + >> sat1: opt!(complete!(parse_gsv_sat_info)) + >> sat2: opt!(complete!(parse_gsv_sat_info)) + >> sat3: opt!(complete!(parse_gsv_sat_info)) + >> ( + number_of_sentences, + sentence_index, + total_number_of_sats, + sat0, + sat1, + sat2, + sat3 + ) + ), + construct_gsv_data + ) +); + +// #2857 +convert_args!(vec!(1, 2, 3)); + +// #3031 +thread_local!( +/// TLV Holds a set of JSTraceables that need to be rooted + static ROOTED_TRACEABLES: RefCell = + RefCell::new(RootedTraceableSet::new()) ; +) ; + +thread_local![ + /// TLV Holds a set of JSTraceables that need to be rooted + static ROOTED_TRACEABLES: RefCell = + RefCell::new(RootedTraceableSet::new()) ; + + /// TLV Holds a set of JSTraceables that need to be rooted + static ROOTED_TRACEABLES: RefCell = + RefCell::new(RootedTraceableSet::new(0)) ; + + /// TLV Holds a set of JSTraceables that need to be rooted + static ROOTED_TRACEABLES: RefCell = + RefCell::new(RootedTraceableSet::new(), xxx, yyy) ; + + /// TLV Holds a set of JSTraceables that need to be rooted +static ROOTED_TRACEABLES: RefCell = + RefCell::new(RootedTraceableSet::new(1234)) ; + +] ; + +fn issue3004() { + foo!(|_| { ( ) }); + stringify!(( foo+ )); +} + +// #3331 +pub fn fold_abi(_visitor: &mut V, _i: Abi) -> Abi { + Abi { + extern_token: Token ! [ extern ](tokens_helper(_visitor, &_i.extern_token.span)), + name: (_i.name).map(|it| _visitor.fold_lit_str(it)), + } +} + +// #3463 +x ! {()} + +// #3746 +f!(match a { + 4 => + &[ + (3, false), // Missing + (4, true) // I-frame + ] [..], +}); + +// #3583 +foo!(|x = y|); diff --git a/tests/source/markdown-comment-with-options.rs b/tests/source/markdown-comment-with-options.rs new file mode 100644 index 000000000000..2c4d6a5cc2be --- /dev/null +++ b/tests/source/markdown-comment-with-options.rs @@ -0,0 +1,17 @@ +// rustfmt-wrap_comments: true + +// Preserve two trailing whitespaces in doc comment, +// but trim any whitespaces in normal comment. + +//! hello world +//! hello world + +/// hello world +/// hello world +/// hello world +fn foo() { + // hello world + // hello world + let x = 3; + println!("x = {}", x); +} diff --git a/tests/source/markdown-comment.rs b/tests/source/markdown-comment.rs new file mode 100644 index 000000000000..1ec26562fe28 --- /dev/null +++ b/tests/source/markdown-comment.rs @@ -0,0 +1,15 @@ +// Preserve two trailing whitespaces in doc comment, +// but trim any whitespaces in normal comment. + +//! hello world +//! hello world + +/// hello world +/// hello world +/// hello world +fn foo() { + // hello world + // hello world + let x = 3; + println!("x = {}", x); +} diff --git a/tests/source/match-block-trailing-comma.rs b/tests/source/match-block-trailing-comma.rs new file mode 100644 index 000000000000..baa05b79c161 --- /dev/null +++ b/tests/source/match-block-trailing-comma.rs @@ -0,0 +1,22 @@ +// rustfmt-match_block_trailing_comma: true +// Match expressions, no unwrapping of block arms or wrapping of multiline +// expressions. + +fn foo() { + match x { + a => { + "line1"; + "line2" + } + ThisIsA::Guard if true => { + "line1"; + "line2" + } + ThisIsA::ReallyLongPattern(ThatWillForce::TheGuard, ToWrapOnto::TheFollowingLine) if true => { + "line1"; + "line2" + } + b => (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb), + } +} diff --git a/tests/source/match-flattening.rs b/tests/source/match-flattening.rs new file mode 100644 index 000000000000..935ece53b83b --- /dev/null +++ b/tests/source/match-flattening.rs @@ -0,0 +1,21 @@ +fn main() { + match option { + None => if condition { + true + } else { + false + }, + } +} + +fn main() { + match option { + None => { + if condition { + true + } else { + false + } + } + } +} diff --git a/tests/source/match-nowrap-trailing-comma.rs b/tests/source/match-nowrap-trailing-comma.rs new file mode 100644 index 000000000000..134d2fdf9a4a --- /dev/null +++ b/tests/source/match-nowrap-trailing-comma.rs @@ -0,0 +1,15 @@ +// rustfmt-match_arm_blocks: false +// rustfmt-match_block_trailing_comma: true +// Match expressions, no unwrapping of block arms or wrapping of multiline +// expressions. + +fn foo() { + match x { + a => { + "line1"; + "line2" + } + b => (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb), + } +} diff --git a/tests/source/match-nowrap.rs b/tests/source/match-nowrap.rs new file mode 100644 index 000000000000..db22cd9f0106 --- /dev/null +++ b/tests/source/match-nowrap.rs @@ -0,0 +1,12 @@ +// rustfmt-match_arm_blocks: false +// Match expressions, no unwrapping of block arms or wrapping of multiline +// expressions. + +fn foo() { + match x { + a => { foo() } + b => + (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb), + } +} diff --git a/tests/source/match.rs b/tests/source/match.rs new file mode 100644 index 000000000000..b5dc9957a2ca --- /dev/null +++ b/tests/source/match.rs @@ -0,0 +1,589 @@ +// rustfmt-normalize_comments: true +// Match expressions. + +fn foo() { + // A match expression. + match x { + // Some comment. + a => foo(), + b if 0 < 42 => foo(), + c => { // Another comment. + // Comment. + an_expression; + foo() + } + Foo(ref bar) => + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + Pattern1 | Pattern2 | Pattern3 => false, + Paternnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn | + Paternnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn => { + blah + } + Patternnnnnnnnnnnnnnnnnnn | + Patternnnnnnnnnnnnnnnnnnn | + Patternnnnnnnnnnnnnnnnnnn | + Patternnnnnnnnnnnnnnnnnnn => meh, + + Patternnnnnnnnnnnnnnnnnnn | + Patternnnnnnnnnnnnnnnnnnn if looooooooooooooooooong_guard => meh, + + Patternnnnnnnnnnnnnnnnnnnnnnnnn | + Patternnnnnnnnnnnnnnnnnnnnnnnnn if looooooooooooooooooooooooooooooooooooooooong_guard => + meh, + + // Test that earlier patterns can take the guard space + (aaaa, bbbbb, ccccccc, aaaaa, bbbbbbbb, cccccc, aaaa, bbbbbbbb, cccccc, dddddd) | + Patternnnnnnnnnnnnnnnnnnnnnnnnn if loooooooooooooooooooooooooooooooooooooooooong_guard => {} + + _ => {} + ast::PathParameters::AngleBracketedParameters(ref data) if data.lifetimes.len() > 0 || + data.types.len() > 0 || + data.bindings.len() > 0 => {} + } + + let whatever = match something { + /// DOC COMMENT! + Some(_) => 42, + // Comment on an attribute. + #[an_attribute] + // Comment after an attribute. + None => 0, + #[rustfmt::skip] + Blurb => { } + }; +} + +// Test that a match on an overflow line is laid out properly. +fn main() { + let sub_span = + match xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx { + Some(sub_span) => Some(sub_span), + None => sub_span, + }; +} + +// Test that one-line bodies align. +fn main() { + match r { + Variableeeeeeeeeeeeeeeeee => ( "variable", + vec!("id", "name", "qualname", + "value", "type", "scopeid"), + true, + true), + Enummmmmmmmmmmmmmmmmmmmm => ("enum", + vec!("id","qualname","scopeid","value"), + true, + true), + Variantttttttttttttttttttttttt => ("variant", + vec!("id", + "name", + "qualname", + "type", + "value", + "scopeid"), + true, + true), + }; + + match x{ + y=>{/*Block with comment. Preserve me.*/ } + z=>{stmt();} } +} + +fn matches() { + match 1 { + -1 => 10, + 1 => 1, // foo + 2 => 2, + // bar + 3 => 3, + _ => 0 // baz + } +} + +fn match_skip() { + let _ = match Some(1) { + #[rustfmt::skip] + Some( n ) => n, + None => 1, + }; +} + +fn issue339() { + match a { + b => {} + c => { } + d => { + } + e => { + + + + } + // collapsing here is safe + ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff => { + } + // collapsing here exceeds line length + ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffg => { + } + h => { // comment above block + } + i => { + } // comment below block + j => { + // comment inside block + } + j2 => { + // comments inside... + } // ... and after + // TODO uncomment when vertical whitespace is handled better + // k => { + // + // // comment with WS above + // } + // l => { + // // comment with ws below + // + // } + m => { + } n => { } o => + { + + } + p => { // Don't collapse me + } q => { } r => + { + + } + s => 0, // s comment + // t comment + t => 1, + u => 2, + v => { + } /* funky block + * comment */ + // final comment + } +} + +fn issue355() { + match mac { + a => println!("a", b), + b => vec!(1, 2), + c => vec!(3; 4), + d => { + println!("a", b) + } + e => { + vec!(1, 2) + } + f => { + vec!(3; 4) + } + h => println!("a", b), // h comment + i => vec!(1, 2), // i comment + j => vec!(3; 4), // j comment + // k comment + k => println!("a", b), + // l comment + l => vec!(1, 2), + // m comment + m => vec!(3; 4), + // Rewrite splits macro + nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn => println!("a", b), + // Rewrite splits macro + oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo => vec!(1, 2), + // Macro support fails to recognise this macro as splittable + // We push the whole expr to a new line, TODO split this macro as well + pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp => vec!(3; 4), + // q, r and s: Rewrite splits match arm + qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq => println!("a", b), + rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr => vec!(1, 2), + ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss => vec!(3; 4), + // Funky bracketing styles + t => println!{"a", b}, + u => vec!{1, 2}, + v => vec!{3; 4}, + w => println!["a", b], + x => vec![1, 2], + y =>vec![3; 4], + // Brackets with comments + tc => println!{"a", b}, // comment + uc => vec!{1, 2}, // comment + vc =>vec!{3; 4}, // comment + wc =>println!["a", b], // comment + xc => vec![1,2], // comment + yc => vec![3; 4], // comment + yd => + looooooooooooooooooooooooooooooooooooooooooooooooooooooooong_func(aaaaaaaaaa, + bbbbbbbbbb, + cccccccccc, + dddddddddd), + } +} + +fn issue280() { + { + match x { + CompressionMode::DiscardNewline | CompressionMode::CompressWhitespaceNewline => ch == + '\n', + ast::ItemConst(ref typ, ref expr) => self.process_static_or_const_item(item, + &typ, + &expr), + } + } +} + +fn issue383() { + match resolution.last_private {LastImport{..} => false, _ => true}; +} + +fn issue507() { + match 1 { + 1 => unsafe { std::intrinsics::abort() }, + _ => (), + } +} + +fn issue508() { + match s.type_id() { + Some(NodeTypeId::Element(ElementTypeId::HTMLElement( + HTMLElementTypeId::HTMLCanvasElement))) => true, + Some(NodeTypeId::Element(ElementTypeId::HTMLElement( + HTMLElementTypeId::HTMLObjectElement))) => s.has_object_data(), + Some(NodeTypeId::Element(_)) => false, + } +} + +fn issue496() {{{{ + match def { + def::DefConst(def_id) | def::DefAssociatedConst(def_id) => + match const_eval::lookup_const_by_id(cx.tcx, def_id, Some(self.pat.id)) { + Some(const_expr) => { x }}}}}}} + +fn issue494() { + { + match stmt.node { + hir::StmtExpr(ref expr, id) | hir::StmtSemi(ref expr, id) => + result.push( + StmtRef::Mirror( + Box::new(Stmt { span: stmt.span, + kind: StmtKind::Expr { + scope: cx.tcx.region_maps.node_extent(id), + expr: expr.to_ref() } }))), + } + } +} + +fn issue386() { + match foo { + BiEq | BiLt | BiLe | BiNe | BiGt | BiGe => + true, + BiAnd | BiOr | BiAdd | BiSub | BiMul | BiDiv | BiRem | + BiBitXor | BiBitAnd | BiBitOr | BiShl | BiShr => + false, + } +} + +fn guards() { + match foo { + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa if foooooooooooooo && barrrrrrrrrrrr => {} + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa if foooooooooooooo && barrrrrrrrrrrr => {} + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + if fooooooooooooooooooooo && + (bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb || cccccccccccccccccccccccccccccccccccccccc) => {} + } +} + +fn issue1371() { + Some(match type_ { + sfEvtClosed => Closed, + sfEvtResized => { + let e = unsafe { *event.size.as_ref() }; + + Resized { + width: e.width, + height: e.height, + } + } + sfEvtLostFocus => LostFocus, + sfEvtGainedFocus => GainedFocus, + sfEvtTextEntered => { + TextEntered { + unicode: unsafe { + ::std::char::from_u32((*event.text.as_ref()).unicode) + .expect("Invalid unicode encountered on TextEntered event") + }, + } + } + sfEvtKeyPressed => { + let e = unsafe { event.key.as_ref() }; + + KeyPressed { + code: unsafe { ::std::mem::transmute(e.code) }, + alt: e.alt.to_bool(), + ctrl: e.control.to_bool(), + shift: e.shift.to_bool(), + system: e.system.to_bool(), + } + } + sfEvtKeyReleased => { + let e = unsafe { event.key.as_ref() }; + + KeyReleased { + code: unsafe { ::std::mem::transmute(e.code) }, + alt: e.alt.to_bool(), + ctrl: e.control.to_bool(), + shift: e.shift.to_bool(), + system: e.system.to_bool(), + } + } + }) +} + +fn issue1395() { + let bar = Some(true); + let foo = Some(true); + let mut x = false; + bar.and_then(|_| { + match foo { + None => None, + Some(b) => { + x = true; + Some(b) + } + } + }); +} + +fn issue1456() { + Ok(Recording { + artists: match reader.evaluate(".//mb:recording/mb:artist-credit/mb:name-credit")? { + Nodeset(nodeset) => { + let res: Result, ReadError> = nodeset + .iter() + .map(|node| { + XPathNodeReader::new(node, &context).and_then(|r| ArtistRef::from_xml(&r)) + }) + .collect(); + res? + } + _ => Vec::new(), + }, + }) +} + +fn issue1460() { + let _ = match foo { + REORDER_BUFFER_CHANGE_INTERNAL_SPEC_INSERT => "internal_spec_insert_internal_spec_insert_internal_spec_insert", + _ => "reorder_something", + }; +} + +fn issue525() { + foobar(f, "{}", match *self { + TaskState::Started => "started", + TaskState::Success => "success", + TaskState::Failed => "failed", + }); +} + +// #1838, #1839 +fn match_with_near_max_width() { + let (this_line_uses_99_characters_and_is_formatted_properly, x012345) = match some_expression { + _ => unimplemented!(), + }; + + let (should_be_formatted_like_the_line_above_using_100_characters, x0) = match some_expression { + _ => unimplemented!(), + }; + + let (should_put_the_brace_on_the_next_line_using_101_characters, x0000) = match some_expression + { + _ => unimplemented!(), + }; + match m { + Variant::Tag | Variant::Tag2 | Variant::Tag3 | Variant::Tag4 | Variant::Tag5 | Variant::Tag6 => + {} + } +} + +fn match_with_trailing_spaces() { + match x { + + Some(..) => 0, + None => 1, + } +} + +fn issue_2099() { + let a = match x { +}; + let b = match x { + + }; + + match x {} +} + +// #2021 +impl<'tcx> Const<'tcx> { + pub fn from_constval<'a>() -> Const<'tcx> { + let val = match *cv { + ConstVal::Variant(_) | ConstVal::Aggregate(..) | ConstVal::Unevaluated(..) => bug!("MIR must not use `{:?}` (aggregates are expanded to MIR rvalues)", cv), + }; + } +} + +// #2151 +fn issue_2151() { + match either { + x => { + + }y => () + } +} + +// #2152 +fn issue_2152() { + match m { + "aaaaaaaaaaaaa" | "bbbbbbbbbbbbb" | "cccccccccccccccccccccccccccccccccccccccccccc" if true => {} + "bind" | "writev" | "readv" | "sendmsg" | "recvmsg" if android && (aarch64 || x86_64) => true, + } +} + +// #2376 +// Preserve block around expressions with condition. +fn issue_2376() { + let mut x = None; + match x { + Some(0) => { + for i in 1..11 { + x = Some(i); + } + } + Some(ref mut y) => { + while *y < 10 { + *y += 1; + } + } + None => { + while let None = x { + x = Some(10); + } + } + } +} + +// #2621 +// Strip leading `|` in match arm patterns +fn issue_2621() { + let x = Foo::A; + match x { + Foo::A => println!("No vert single condition"), + Foo::B | Foo::C => println!("Center vert two conditions"), + | Foo::D => println!("Preceding vert single condition"), + | Foo::E + | Foo::F => println!("Preceding vert over two lines"), + Foo::G | + Foo::H => println!("Trailing vert over two lines"), + // Comment on its own line + | Foo::I => println!("With comment"), // Comment after line + } +} + +fn issue_2377() { + match tok { + Tok::Not + | Tok::BNot + | Tok::Plus + | Tok::Minus + | Tok::PlusPlus + | Tok::MinusMinus + | Tok::Void + | Tok::Delete if prec <= 16 => { + // code here... + } + Tok::TypeOf if prec <= 16 => {} + } +} + +// #3040 +fn issue_3040() { + { + match foo { + DevtoolScriptControlMsg::WantsLiveNotifications(id, to_send) => { + match documents.find_window(id) { + Some(window) => devtools::handle_wants_live_notifications(window.upcast(), to_send), + None => return warn!("Message sent to closed pipeline {}.", id), + } + } + } + } +} + +// #3030 +fn issue_3030() { + match input.trim().parse::() { + Ok(val) + if !( + // A valid number is the same as what rust considers to be valid, + // except for +1., NaN, and Infinity. + val.is_infinite() || val + .is_nan() || input.ends_with(".") || input.starts_with("+") + ) + => { + } + } +} + +fn issue_3005() { + match *token { + Token::Dimension { + value, ref unit, .. + } if num_context.is_ok(context.parsing_mode, value) => + { + return NoCalcLength::parse_dimension(context, value, unit) + .map(LengthOrPercentage::Length) + .map_err(|()| location.new_unexpected_token_error(token.clone())); + }, + } +} + +// #3774 +fn issue_3774() { + { + { + { + match foo { + Lam(_, _, _) | Pi(_, _, _) | Let(_, _, _, _) | Embed(_) | Var(_) => unreachab(), + Lam(_, _, _) | Pi(_, _, _) | Let(_, _, _, _) | Embed(_) | Var(_) => unreacha!(), + Lam(_, _, _) | Pi(_, _, _) | Let(_, _, _, _) | Embed(_) | Var(_) => unreachabl(), + Lam(_, _, _) | Pi(_, _, _) | Let(_, _, _, _) | Embed(_) | Var(_) => unreachae!(), + Lam(_, _, _) | Pi(_, _, _) | Let(_, _, _, _) | Embed(_) | Var(_) => unreachable(), + Lam(_, _, _) | Pi(_, _, _) | Let(_, _, _, _) | Embed(_) | Var(_) => unreachable!(), + Lam(_, _, _) | Pi(_, _, _) | Let(_, _, _, _) | Embed(_) | Var(_) => rrunreachable!(), + } + } + } + } +} + +// #4109 +fn issue_4109() { + match () { + _ => { +#[cfg(debug_assertions)] +{ +println!("Foo"); +} +} +} + +match () { +_ => { +#[allow(unsafe_code)] +unsafe {} +} +} +} diff --git a/tests/source/match_overflow_expr.rs b/tests/source/match_overflow_expr.rs new file mode 100644 index 000000000000..91275a894294 --- /dev/null +++ b/tests/source/match_overflow_expr.rs @@ -0,0 +1,53 @@ +// rustfmt-overflow_delimited_expr: true + +fn main() { + println!( + "Foobar: {}", + match "input" { + "a" => "", + "b" => "", + "c" => "", + "d" => "", + "e" => "", + "f" => "", + "g" => "", + "h" => "", + "i" => "", + "j" => "", + "k" => "", + "l" => "", + "m" => "", + "n" => "", + "o" => "", + "p" => "", + "q" => "", + "r" => "Rust", + } + ); +} + +fn main() { + println!( + "Very Long Input String Which Makes It Impossible To Fit On The Same Line: {}", + match "input" { + "a" => "", + "b" => "", + "c" => "", + "d" => "", + "e" => "", + "f" => "", + "g" => "", + "h" => "", + "i" => "", + "j" => "", + "k" => "", + "l" => "", + "m" => "", + "n" => "", + "o" => "", + "p" => "", + "q" => "", + "r" => "Rust", + } + ); +} diff --git a/tests/source/max-line-length-in-chars.rs b/tests/source/max-line-length-in-chars.rs new file mode 100644 index 000000000000..d49fbb7e30e5 --- /dev/null +++ b/tests/source/max-line-length-in-chars.rs @@ -0,0 +1,4 @@ +// rustfmt-max_width: 25 + +// абвгдеёжзийклмнопрст +fn main() {} diff --git a/tests/source/merge_imports_true_compat.rs b/tests/source/merge_imports_true_compat.rs new file mode 100644 index 000000000000..bcea9435129f --- /dev/null +++ b/tests/source/merge_imports_true_compat.rs @@ -0,0 +1,4 @@ +// rustfmt-merge_imports: true + +use a::b; +use a::c; \ No newline at end of file diff --git a/tests/source/mod-1.rs b/tests/source/mod-1.rs new file mode 100644 index 000000000000..427a355b6bee --- /dev/null +++ b/tests/source/mod-1.rs @@ -0,0 +1,29 @@ +// Deeply indented modules. + + mod foo { mod bar { mod baz {} } } + +mod foo { + mod bar { + mod baz { + fn foo() { bar() } + } + } + + mod qux { + + } +} + +mod boxed { pub use std::boxed::{Box, HEAP}; } + +pub mod x { + pub fn freopen(filename: *const c_char, + mode: *const c_char, + mode2: *const c_char, + mode3: *const c_char, + file: *mut FILE) + -> *mut FILE{} +} + + mod y { // sup boooooiiii + } diff --git a/tests/source/mod-2.rs b/tests/source/mod-2.rs new file mode 100644 index 000000000000..7202e00203ec --- /dev/null +++ b/tests/source/mod-2.rs @@ -0,0 +1,4 @@ +// Some nested mods + +#[cfg(test)] mod nestedmod ; +pub mod no_new_line_beginning; diff --git a/tests/source/mod_skip_child.rs b/tests/source/mod_skip_child.rs new file mode 100644 index 000000000000..d48c4a37e810 --- /dev/null +++ b/tests/source/mod_skip_child.rs @@ -0,0 +1,2 @@ +// rustfmt-skip_children: true +mod nested_skipped; diff --git a/tests/source/multiple.rs b/tests/source/multiple.rs new file mode 100644 index 000000000000..f89f4f68da9b --- /dev/null +++ b/tests/source/multiple.rs @@ -0,0 +1,134 @@ +// rustfmt-normalize_comments: true +// rustfmt-wrap_comments: true +// rustfmt-format_strings: true +// Test of lots of random stuff. +// FIXME split this into multiple, self-contained tests. + + +#[attr1] extern crate foo; +#[attr2] #[attr3] extern crate foo; +#[attr1]extern crate foo; +#[attr2]#[attr3]extern crate foo; + +use std::cell::*; +use std::{any, ascii, self, borrow, boxed, char, borrow, boxed, char, borrow, borrow, boxed, char, borrow, boxed, char, borrow, boxed, char, borrow, boxed, char, borrow, boxed, char, borrow, boxed, char, borrow, boxed, char, borrow, boxed, char}; + +mod doc; +mod other; + + +// sfdgfffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffff + + fn foo(a: isize, + b: u32, /* blah blah */ + c: f64) { + +} + +fn foo()->Box where 'a: 'b, for<'a> D<'b>: 'a { + hello!() +} + +fn baz<'a: 'b /* comment on 'a */, T: SomsssssssssssssssssssssssssssssssssssssssssssssssssssssseType /* comment on T */>(a: A, b: B /* comment on b */, c: C) -> Bob { + #[attr1] extern crate foo; + #[attr2] #[attr3] extern crate foo; + #[attr1]extern crate foo; + #[attr2]#[attr3]extern crate foo; +} + +#[rustfmt::skip] +fn qux(a: dadsfa, // Comment 1 + b: sdfasdfa, // Comment 2 + c: dsfdsafa) // Comment 3 +{ + +} + +/// Blah blah blah. +impl Bar { + fn foo(&mut self, a: sdfsdfcccccccccccccccccccccccccccccccccccccccccccccccccc, // comment on a + b: sdfasdfsdfasfs /*closing comment*/ ) -> isize {} + + /// Blah blah blah. + pub fn f2(self) { + (foo, bar) + } + + #[an_attribute] + fn f3(self) -> Dog { + } +} + +/// The `nodes` and `edges` method each return instantiations of +/// `Cow<[T]>` to leave implementers the freedom to create + +/// entirely new vectors or to pass back slices into internally owned +/// vectors. +pub trait GraphWalk<'a, N, E> { + /// Returns all the nodes in this graph. + fn nodes(&'a self) -> Nodes<'a, N>; + /// Returns all of the edges in this graph. + fn edges(&'a self) -> Edges<'a, E>; + /// The source node for `edge`. + fn source(&'a self, edge: &E) -> N; + /// The target node for `edge`. + fn target(&'a self, edge: &E) -> N; +} + +/// A Doc comment +#[AnAttribute] +pub struct Foo { + #[rustfmt::skip] + f : SomeType, // Comment beside a field + f : SomeType, // Comment beside a field + // Comment on a field + g: SomeOtherType, + /// A doc comment on a field + h: AThirdType,} + +struct Bar; + +// With a where-clause and generics. +pub struct Foo<'a, Y: Baz> + where X: Whatever +{ + f: SomeType, // Comment beside a field +} + +fn foo(ann: &'a (PpAnn+'a)) {} + +fn main() { + for i in 0i32..4 { + println!("{}", i); + } + + + while true { + hello(); + } + + let rc = Cell::new(42usize,42usize, Cell::new(42usize, remaining_widthremaining_widthremaining_widthremaining_width), 42usize); + let rc = RefCell::new(42usize,remaining_width, remaining_width); // a comment + let x = "Hello!!!!!!!!! abcd abcd abcd abcd abcd abcd\n abcd abcd abcd abcd abcd abcd abcd abcd abcd \ + abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd \ + abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd"; + let s = expand(a + , + b); } + +fn deconstruct() -> (SocketAddr, Method, Headers, + RequestUri, HttpVersion, + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) { +} + +fn deconstruct(foo: Bar) -> (SocketAddr, Method, Headers, + RequestUri, HttpVersion, + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) { +} + +#[rustfmt::skip] +mod a{ +fn foo(x: T) { + let x: T = dfasdf; +} +} diff --git a/tests/source/negative-impl.rs b/tests/source/negative-impl.rs new file mode 100644 index 000000000000..da242d4f3dca --- /dev/null +++ b/tests/source/negative-impl.rs @@ -0,0 +1,7 @@ +impl ! Display for JoinHandle { } + +impl ! Box < JoinHandle > { } + +impl ! std :: fmt :: Display for JoinHandle < T : std :: future :: Future + std :: marker :: Send + std :: marker :: Sync > { } + +impl ! JoinHandle < T : std :: future :: Future < Output > + std :: marker :: Send + std :: marker :: Sync + 'static > + 'static { } diff --git a/tests/source/nested-if-else.rs b/tests/source/nested-if-else.rs new file mode 100644 index 000000000000..9a54789ddcd6 --- /dev/null +++ b/tests/source/nested-if-else.rs @@ -0,0 +1,11 @@ +fn issue1518() { + Some(Object { + field: if a { + a_thing + } else if b { + b_thing + } else { + c_thing + }, + }) +} diff --git a/tests/source/nested_skipped/mod.rs b/tests/source/nested_skipped/mod.rs new file mode 100644 index 000000000000..44b25ca8797d --- /dev/null +++ b/tests/source/nested_skipped/mod.rs @@ -0,0 +1,3 @@ +fn ugly() { +92; +} diff --git a/tests/source/nestedmod/mod.rs b/tests/source/nestedmod/mod.rs new file mode 100644 index 000000000000..d04e49570a4f --- /dev/null +++ b/tests/source/nestedmod/mod.rs @@ -0,0 +1,13 @@ + +mod mod2a; +mod mod2b; + +mod mymod1 { + use mod2a::{Foo,Bar}; +mod mod3a; +} + +#[path="mod2c.rs"] +mod mymod2; + +mod submod2; diff --git a/tests/source/nestedmod/mod2a.rs b/tests/source/nestedmod/mod2a.rs new file mode 100644 index 000000000000..5df457a83165 --- /dev/null +++ b/tests/source/nestedmod/mod2a.rs @@ -0,0 +1,4 @@ +// This is an empty file containing only +// comments + +// ................... diff --git a/tests/source/nestedmod/mod2b.rs b/tests/source/nestedmod/mod2b.rs new file mode 100644 index 000000000000..f128e2da6dbf --- /dev/null +++ b/tests/source/nestedmod/mod2b.rs @@ -0,0 +1,3 @@ + +#[path="mod2a.rs"] +mod c; diff --git a/tests/source/nestedmod/mod2c.rs b/tests/source/nestedmod/mod2c.rs new file mode 100644 index 000000000000..eda6b233e4be --- /dev/null +++ b/tests/source/nestedmod/mod2c.rs @@ -0,0 +1,3 @@ +// A standard mod + +fn a( ) {} diff --git a/tests/source/nestedmod/mymod1/mod3a.rs b/tests/source/nestedmod/mymod1/mod3a.rs new file mode 100644 index 000000000000..f28bde5e56db --- /dev/null +++ b/tests/source/nestedmod/mymod1/mod3a.rs @@ -0,0 +1,2 @@ +// Another mod +fn a( ) { } diff --git a/tests/source/nestedmod/submod2/a.rs b/tests/source/nestedmod/submod2/a.rs new file mode 100644 index 000000000000..0eaf08f0d2ca --- /dev/null +++ b/tests/source/nestedmod/submod2/a.rs @@ -0,0 +1,6 @@ +// Yet Another mod +// Nested + +use c::a; + +fn foo( ) { } diff --git a/tests/source/nestedmod/submod2/mod.rs b/tests/source/nestedmod/submod2/mod.rs new file mode 100644 index 000000000000..52f8be910227 --- /dev/null +++ b/tests/source/nestedmod/submod2/mod.rs @@ -0,0 +1,5 @@ +// Another mod + +mod a; + +use a::a; diff --git a/tests/source/no_arg_with_commnet.rs b/tests/source/no_arg_with_commnet.rs new file mode 100644 index 000000000000..ea4ee0f1eee0 --- /dev/null +++ b/tests/source/no_arg_with_commnet.rs @@ -0,0 +1,2 @@ +fn foo( /* cooment */ +) {} diff --git a/tests/source/no_new_line_beginning.rs b/tests/source/no_new_line_beginning.rs new file mode 100644 index 000000000000..f79c691f0853 --- /dev/null +++ b/tests/source/no_new_line_beginning.rs @@ -0,0 +1,2 @@ +fn main() { +} diff --git a/tests/source/normalize_doc_attributes_should_not_imply_format_doc_comments.rs b/tests/source/normalize_doc_attributes_should_not_imply_format_doc_comments.rs new file mode 100644 index 000000000000..a97705bfb3bf --- /dev/null +++ b/tests/source/normalize_doc_attributes_should_not_imply_format_doc_comments.rs @@ -0,0 +1,15 @@ +// rustfmt-normalize_doc_attributes: true + +/// Foo +/// +/// # Example +/// ``` +/// # #![cfg_attr(not(dox), feature(cfg_target_feature, target_feature, stdsimd))] +/// # #![cfg_attr(not(dox), no_std)] +/// fn foo() { } +/// ``` +/// +fn foo() {} + +#[doc = "Bar documents"] +fn bar() {} diff --git a/tests/source/normalize_multiline_doc_attribute.rs b/tests/source/normalize_multiline_doc_attribute.rs new file mode 100644 index 000000000000..3564e3e7ad05 --- /dev/null +++ b/tests/source/normalize_multiline_doc_attribute.rs @@ -0,0 +1,12 @@ +// rustfmt-unstable: true +// rustfmt-normalize_doc_attributes: true + +#[doc = "This comment +is split +on multiple lines"] +fn foo() {} + +#[doc = " B1"] +#[doc = ""] +#[doc = " A1"] +fn bar() {} diff --git a/tests/source/one_line_if_v1.rs b/tests/source/one_line_if_v1.rs new file mode 100644 index 000000000000..d3dcbe6787a1 --- /dev/null +++ b/tests/source/one_line_if_v1.rs @@ -0,0 +1,42 @@ +// rustfmt-version: One + +fn plain_if(x: bool) -> u8 { + if x { + 0 + } else { + 1 + } +} + +fn paren_if(x: bool) -> u8 { + (if x { 0 } else { 1 }) +} + +fn let_if(x: bool) -> u8 { + let x = if x { + foo() + } else { + bar() + }; + x +} + +fn return_if(x: bool) -> u8 { + return if x { + 0 + } else { + 1 + }; +} + +fn multi_if() { + use std::io; + if x { foo() } else { bar() } + if x { foo() } else { bar() } +} + +fn middle_if() { + use std::io; + if x { foo() } else { bar() } + let x = 1; +} diff --git a/tests/source/one_line_if_v2.rs b/tests/source/one_line_if_v2.rs new file mode 100644 index 000000000000..40c834959f90 --- /dev/null +++ b/tests/source/one_line_if_v2.rs @@ -0,0 +1,42 @@ +// rustfmt-version: Two + +fn plain_if(x: bool) -> u8 { + if x { + 0 + } else { + 1 + } +} + +fn paren_if(x: bool) -> u8 { + (if x { 0 } else { 1 }) +} + +fn let_if(x: bool) -> u8 { + let x = if x { + foo() + } else { + bar() + }; + x +} + +fn return_if(x: bool) -> u8 { + return if x { + 0 + } else { + 1 + }; +} + +fn multi_if() { + use std::io; + if x { foo() } else { bar() } + if x { foo() } else { bar() } +} + +fn middle_if() { + use std::io; + if x { foo() } else { bar() } + let x = 1; +} diff --git a/tests/source/other.rs b/tests/source/other.rs new file mode 100644 index 000000000000..dfce84fcdc47 --- /dev/null +++ b/tests/source/other.rs @@ -0,0 +1,5 @@ +// Part of multiple.rs + +fn bob() { + println!("hello other!"); +} diff --git a/tests/source/paren.rs b/tests/source/paren.rs new file mode 100644 index 000000000000..04e5ab7a5549 --- /dev/null +++ b/tests/source/paren.rs @@ -0,0 +1,6 @@ +fn main() { + let x = (((1))); + let y = (/* comment */((2))); + let z = (((3)/* comment */)); + let a = (((4/* comment */))); +} diff --git a/tests/source/path_clarity/foo.rs b/tests/source/path_clarity/foo.rs new file mode 100644 index 000000000000..cd247fabfe34 --- /dev/null +++ b/tests/source/path_clarity/foo.rs @@ -0,0 +1,2 @@ +// rustfmt-edition: 2018 +mod bar; diff --git a/tests/source/path_clarity/foo/bar.rs b/tests/source/path_clarity/foo/bar.rs new file mode 100644 index 000000000000..8c1be504c090 --- /dev/null +++ b/tests/source/path_clarity/foo/bar.rs @@ -0,0 +1,3 @@ +pub fn fn_in_bar( ) { + println!( "foo/bar.rs" ); +} diff --git a/tests/source/paths.rs b/tests/source/paths.rs new file mode 100644 index 000000000000..ebc26f146e4a --- /dev/null +++ b/tests/source/paths.rs @@ -0,0 +1,25 @@ +// rustfmt-normalize_comments: true + +fn main() { + let constellation_chan = Constellation:: ::start( + compositor_proxy, + resource_task, + image_cache_task,font_cache_task, + time_profiler_chan, + mem_profiler_chan, + devtools_chan, + storage_task, + supports_clipboard + ); + + Quux::::some_func(); + + < *mut JSObject >:: relocate(entry); + + let x: Foo
; + let x: Foo/*::*/; +} + +fn op(foo: Bar, key : &[u8], upd : Fn(Option<&memcache::Item> , Baz ) -> Result) -> MapResult {} diff --git a/tests/source/pattern-condense-wildcards.rs b/tests/source/pattern-condense-wildcards.rs new file mode 100644 index 000000000000..69c3fa3cbad8 --- /dev/null +++ b/tests/source/pattern-condense-wildcards.rs @@ -0,0 +1,12 @@ +// rustfmt-normalize_comments: true +// rustfmt-condense_wildcard_suffixes: true + +fn main() { + match x { + Butt (_,_) => "hah", + Tup (_) => "nah", + Quad (_,_, x,_) => " also no rewrite", + Quad (x, _, _, _) => "condense me pls", + Weird (x, _, _, /* don't condense before */ _, _, _) => "pls work", + } +} diff --git a/tests/source/pattern.rs b/tests/source/pattern.rs new file mode 100644 index 000000000000..f06d03cadf2f --- /dev/null +++ b/tests/source/pattern.rs @@ -0,0 +1,90 @@ +// rustfmt-normalize_comments: true +#![feature(exclusive_range_pattern)] +use core::u8::MAX; + +fn main() { + let z = match x { + "pat1" => 1, + ( ref x, ref mut y /*comment*/) => 2, + }; + + if let < T as Trait > :: CONST = ident { + do_smth(); + } + + let Some ( ref xyz /* comment! */) = opt; + + if let None = opt2 { panic!("oh noes"); } + + let foo@bar (f) = 42; + let a::foo ( ..) = 42; + let [ ] = 42; + let [a, b,c ] = 42; + let [ a,b,c ] = 42; + let [a, b, c, d,e,f, g] = 42; + let foo { } = 42; + let foo {..} = 42; + let foo { x, y: ref foo, .. } = 42; + let foo { x, yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy: ref foo, .. } = 42; + let foo { x, yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy: ref foo, } = 42; + let foo { x, yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy: ref foo, .. }; + let foo { x, yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy: ref foo, }; + + match b"12" { + [0, + 1..MAX + ] => {} + _ => {} + } +} + +impl<'a,'b> ResolveGeneratedContentFragmentMutator<'a,'b> { + fn mutate_fragment(&mut self, fragment: &mut Fragment) { + match **info { + GeneratedContentInfo::ContentItem( + ContentItem::Counter( + ref counter_name, + counter_style + ) + ) => {}}} +} + +fn issue_1319() { + if let (Event { .. }, .. ) = ev_state {} +} + +fn issue_1874() { + if let Some(()) = x { +y + } +} + +fn combine_patterns() { + let x = match y { + Some( + Some( + Foo { + z: Bar(..), + a: Bar(..), + b: Bar(..), + }, + ), + ) => z, + _ => return, + }; +} + +fn slice_patterns() { + match b"123" { + [0, ..] => {} + [0, foo] => {} + _ => {} + } +} + +fn issue3728() { + let foo = | + (c,) + | c; + foo((1,)); +} diff --git a/tests/source/preserves_carriage_return_for_unix.rs b/tests/source/preserves_carriage_return_for_unix.rs new file mode 100644 index 000000000000..e5e0b2865989 --- /dev/null +++ b/tests/source/preserves_carriage_return_for_unix.rs @@ -0,0 +1,2 @@ +// rustfmt-newline_style: Unix +// Foo Bar diff --git a/tests/source/preserves_carriage_return_for_windows.rs b/tests/source/preserves_carriage_return_for_windows.rs new file mode 100644 index 000000000000..1085360ee59e --- /dev/null +++ b/tests/source/preserves_carriage_return_for_windows.rs @@ -0,0 +1,2 @@ +// rustfmt-newline_style: Windows +// Foo Bar diff --git a/tests/source/pub-restricted.rs b/tests/source/pub-restricted.rs new file mode 100644 index 000000000000..5683acbf3aa9 --- /dev/null +++ b/tests/source/pub-restricted.rs @@ -0,0 +1,51 @@ +pub( super ) enum WriteState { + WriteId { + id: U64Writer, + size: U64Writer, + payload: Option>, + }, + WriteSize { + size: U64Writer, + payload: Option>, + }, + WriteData(Writer), +} + +pub( crate ) enum WriteState { + WriteId { + id: U64Writer, + size: U64Writer, + payload: Option>, + }, + WriteSize { + size: U64Writer, + payload: Option>, + }, + WriteData(Writer), +} + +pub(in ::global:: path :: to::some_mod ) enum WriteState { + WriteId { + id: U64Writer, + size: U64Writer, + payload: Option>, + }, + WriteSize { + size: U64Writer, + payload: Option>, + }, + WriteData(Writer), +} + +pub( in local:: path :: to::some_mod ) enum WriteState { + WriteId { + id: U64Writer, + size: U64Writer, + payload: Option>, + }, + WriteSize { + size: U64Writer, + payload: Option>, + }, + WriteData(Writer), +} diff --git a/tests/source/remove_blank_lines.rs b/tests/source/remove_blank_lines.rs new file mode 100644 index 000000000000..43733ce7636f --- /dev/null +++ b/tests/source/remove_blank_lines.rs @@ -0,0 +1,44 @@ +fn main() { + + + + + let x = 1; + + + let y = 2; + + + println!("x + y = {}", x + y); + + + +} + + +fn foo() { + + #![attribute] + + let x = 1; + + // comment + + +} +// comment after item + + +// comment before item +fn bar() { + let x = 1; + // comment after statement + + + // comment before statement + let y = 2; + let z = 3; + + + println!("x + y + z = {}", x + y + z); +} diff --git a/tests/source/reorder-impl-items.rs b/tests/source/reorder-impl-items.rs new file mode 100644 index 000000000000..16efff55b066 --- /dev/null +++ b/tests/source/reorder-impl-items.rs @@ -0,0 +1,15 @@ +// rustfmt-reorder_impl_items: true + +// The ordering of the following impl items should be idempotent. +impl<'a> Command<'a> { + pub fn send_to(&self, w: &mut io::Write) -> io::Result<()> { + match self { + &Command::Data(ref c) => c.send_to(w), + &Command::Vrfy(ref c) => c.send_to(w), + } + } + + pub fn parse(arg: &[u8]) -> Result { + nom_to_result(command(arg)) + } +} diff --git a/tests/source/single-line-if-else.rs b/tests/source/single-line-if-else.rs new file mode 100644 index 000000000000..bcde390d1164 --- /dev/null +++ b/tests/source/single-line-if-else.rs @@ -0,0 +1,49 @@ + +// Format if-else expressions on a single line, when possible. + +fn main() { + let a = if 1 > 2 { + unreachable!() + } else { + 10 + }; + + let a = if x { 1 } else if y { 2 } else { 3 }; + + let b = if cond() { + 5 + } else { + // Brief comment. + 10 + }; + + let c = if cond() { + statement(); + + 5 + } else { + 10 + }; + + let d = if let Some(val) = turbo + { "cool" } else { + "beans" }; + + if cond() { statement(); } else { other_statement(); } + + if true { + do_something() + } + + let x = if veeeeeeeeery_loooooong_condition() { aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa } else { bbbbbbbbbb }; + + let x = if veeeeeeeeery_loooooong_condition() { aaaaaaaaaaaaaaaaaaaaaaaaa } else { + bbbbbbbbbb }; + + funk(if test() { + 1 + } else { + 2 + }, + arg2); +} diff --git a/tests/source/single-line-macro/v1.rs b/tests/source/single-line-macro/v1.rs new file mode 100644 index 000000000000..a3aa631ed4af --- /dev/null +++ b/tests/source/single-line-macro/v1.rs @@ -0,0 +1,10 @@ +// rustfmt-version: One + +// #2652 +// Preserve trailing comma inside macro, even if it looks an array. +macro_rules! bar { + ($m:ident) => { + $m!([a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z,]); + $m!([a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z]); + }; +} diff --git a/tests/source/single-line-macro/v2.rs b/tests/source/single-line-macro/v2.rs new file mode 100644 index 000000000000..51a665f75605 --- /dev/null +++ b/tests/source/single-line-macro/v2.rs @@ -0,0 +1,10 @@ +// rustfmt-version: Two + +// #2652 +// Preserve trailing comma inside macro, even if it looks an array. +macro_rules! bar { + ($m:ident) => { + $m!([a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z,]); + $m!([a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z]); + }; +} diff --git a/tests/source/skip_macro_invocations/all.rs b/tests/source/skip_macro_invocations/all.rs new file mode 100644 index 000000000000..d0437ee10fd1 --- /dev/null +++ b/tests/source/skip_macro_invocations/all.rs @@ -0,0 +1,11 @@ +// rustfmt-skip_macro_invocations: ["*"] + +// Should skip this invocation +items!( + const _: u8 = 0; +); + +// Should skip this invocation +renamed_items!( + const _: u8 = 0; +); diff --git a/tests/source/skip_macro_invocations/all_and_name.rs b/tests/source/skip_macro_invocations/all_and_name.rs new file mode 100644 index 000000000000..1f6722344fe3 --- /dev/null +++ b/tests/source/skip_macro_invocations/all_and_name.rs @@ -0,0 +1,11 @@ +// rustfmt-skip_macro_invocations: ["*","items"] + +// Should skip this invocation +items!( + const _: u8 = 0; +); + +// Should also skip this invocation, as the wildcard covers it +renamed_items!( + const _: u8 = 0; +); diff --git a/tests/source/skip_macro_invocations/empty.rs b/tests/source/skip_macro_invocations/empty.rs new file mode 100644 index 000000000000..f3dd89dc4db3 --- /dev/null +++ b/tests/source/skip_macro_invocations/empty.rs @@ -0,0 +1,11 @@ +// rustfmt-skip_macro_invocations: [] + +// Should not skip this invocation +items!( + const _: u8 = 0; +); + +// Should not skip this invocation +renamed_items!( + const _: u8 = 0; +); diff --git a/tests/source/skip_macro_invocations/name.rs b/tests/source/skip_macro_invocations/name.rs new file mode 100644 index 000000000000..7fa5d3a6f715 --- /dev/null +++ b/tests/source/skip_macro_invocations/name.rs @@ -0,0 +1,11 @@ +// rustfmt-skip_macro_invocations: ["items"] + +// Should skip this invocation +items!( + const _: u8 = 0; +); + +// Should not skip this invocation +renamed_items!( + const _: u8 = 0; +); diff --git a/tests/source/skip_macro_invocations/name_unknown.rs b/tests/source/skip_macro_invocations/name_unknown.rs new file mode 100644 index 000000000000..d56695325240 --- /dev/null +++ b/tests/source/skip_macro_invocations/name_unknown.rs @@ -0,0 +1,6 @@ +// rustfmt-skip_macro_invocations: ["unknown"] + +// Should not skip this invocation +items!( + const _: u8 = 0; +); diff --git a/tests/source/skip_macro_invocations/names.rs b/tests/source/skip_macro_invocations/names.rs new file mode 100644 index 000000000000..a920381a4552 --- /dev/null +++ b/tests/source/skip_macro_invocations/names.rs @@ -0,0 +1,16 @@ +// rustfmt-skip_macro_invocations: ["foo","bar"] + +// Should skip this invocation +foo!( + const _: u8 = 0; +); + +// Should skip this invocation +bar!( + const _: u8 = 0; +); + +// Should not skip this invocation +baz!( + const _: u8 = 0; +); diff --git a/tests/source/skip_macro_invocations/path_qualified_invocation_mismatch.rs b/tests/source/skip_macro_invocations/path_qualified_invocation_mismatch.rs new file mode 100644 index 000000000000..61296869a506 --- /dev/null +++ b/tests/source/skip_macro_invocations/path_qualified_invocation_mismatch.rs @@ -0,0 +1,6 @@ +// rustfmt-skip_macro_invocations: ["items"] + +// Should not skip this invocation +self::items!( + const _: u8 = 0; +); diff --git a/tests/source/skip_macro_invocations/path_qualified_match.rs b/tests/source/skip_macro_invocations/path_qualified_match.rs new file mode 100644 index 000000000000..9398918a9e11 --- /dev/null +++ b/tests/source/skip_macro_invocations/path_qualified_match.rs @@ -0,0 +1,6 @@ +// rustfmt-skip_macro_invocations: ["self::items"] + +// Should skip this invocation +self::items!( + const _: u8 = 0; +); diff --git a/tests/source/skip_macro_invocations/path_qualified_name_mismatch.rs b/tests/source/skip_macro_invocations/path_qualified_name_mismatch.rs new file mode 100644 index 000000000000..4e3eb542dbea --- /dev/null +++ b/tests/source/skip_macro_invocations/path_qualified_name_mismatch.rs @@ -0,0 +1,6 @@ +// rustfmt-skip_macro_invocations: ["self::items"] + +// Should not skip this invocation +items!( + const _: u8 = 0; +); diff --git a/tests/source/skip_macro_invocations/use_alias_examples.rs b/tests/source/skip_macro_invocations/use_alias_examples.rs new file mode 100644 index 000000000000..43cb8015de58 --- /dev/null +++ b/tests/source/skip_macro_invocations/use_alias_examples.rs @@ -0,0 +1,32 @@ +// rustfmt-skip_macro_invocations: ["aaa","ccc"] + +// These tests demonstrate a realistic use case with use aliases. +// The use statements should not impact functionality in any way. + +use crate::{aaa, bbb, ddd}; + +// No use alias, invocation in list +// Should skip this invocation +aaa!( + const _: u8 = 0; +); + +// Use alias, invocation in list +// Should skip this invocation +use crate::bbb as ccc; +ccc!( + const _: u8 = 0; +); + +// Use alias, invocation not in list +// Should not skip this invocation +use crate::ddd as eee; +eee!( + const _: u8 = 0; +); + +// No use alias, invocation not in list +// Should not skip this invocation +fff!( + const _: u8 = 0; +); diff --git a/tests/source/soft-wrapping.rs b/tests/source/soft-wrapping.rs new file mode 100644 index 000000000000..b0682d4db3a3 --- /dev/null +++ b/tests/source/soft-wrapping.rs @@ -0,0 +1,15 @@ +// rustfmt-wrap_comments: true +// rustfmt-max_width: 80 +// Soft wrapping for comments. + +// #535, soft wrapping for comments +// Compare the lowest `f32` of both inputs for greater than or equal. The +// lowest 32 bits of the result will be `0xffffffff` if `a.extract(0)` is +// ggreater than or equal `b.extract(0)`, or `0` otherwise. The upper 96 bits off +// the result are the upper 96 bits of `a`. + +/// Compares the lowest `f32` of both inputs for greater than or equal. The +/// lowest 32 bits of the result will be `0xffffffff` if `a.extract(0)` is +/// greater than or equal `b.extract(0)`, or `0` otherwise. The upper 96 bits off +/// the result are the upper 96 bits of `a`. +fn foo() {} diff --git a/tests/source/space-not-before-newline.rs b/tests/source/space-not-before-newline.rs new file mode 100644 index 000000000000..2a1e18569088 --- /dev/null +++ b/tests/source/space-not-before-newline.rs @@ -0,0 +1,8 @@ +struct Foo { + a: (), + // spaces ^^^ to be removed +} +enum Foo { + Bar, + // spaces ^^^ to be removed +} diff --git a/tests/source/spaces-around-ranges.rs b/tests/source/spaces-around-ranges.rs new file mode 100644 index 000000000000..1936b5e161f8 --- /dev/null +++ b/tests/source/spaces-around-ranges.rs @@ -0,0 +1,15 @@ +// rustfmt-spaces_around_ranges: true + +fn bar(v: &[u8]) {} + +fn foo() { + let a = vec![0; 20]; + for j in 0..=20 { + for i in 0..3 { + bar(a[i..j]); + bar(a[i..]); + bar(a[..j]); + bar(a[..=(j + 1)]); + } + } +} diff --git a/tests/source/statements.rs b/tests/source/statements.rs new file mode 100644 index 000000000000..c840b8ce105d --- /dev/null +++ b/tests/source/statements.rs @@ -0,0 +1,43 @@ +// FIXME(calebcartwright) - Hopefully one day we can +// elide these redundant semis like we do in other contexts. +fn redundant_item_semis() { + impl Foo { + fn get(&self) -> usize { + 5 + } + }; + + impl Bar { + fn get(&self) -> usize { + 5 + } + } /*asdfsf*/; + + + impl Baz { + fn get(&self) -> usize { + 5 + } + } /*asdfsf*/ + + // why would someone do this + ; + + + impl Qux { + fn get(&self) -> usize { + 5 + } + } + + // why + ; + + impl Lorem { + fn get(&self) -> usize { + 5 + } + } + // oh why + ; +} \ No newline at end of file diff --git a/tests/source/static.rs b/tests/source/static.rs new file mode 100644 index 000000000000..970786381cc4 --- /dev/null +++ b/tests/source/static.rs @@ -0,0 +1,23 @@ +const FILE_GENERIC_READ: DWORD = + STANDARD_RIGHTS_READ | FILE_READ_DATA | + FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE; + +static boolnames: &'static[&'static str] = &["bw", "am", "xsb", "xhp", "xenl", "eo", + "gn", "hc", "km", "hs", "in", "db", "da", "mir", "msgr", "os", "eslok", "xt", "hz", "ul", "xon", + "nxon", "mc5i", "chts", "nrrmc", "npc", "ndscr", "ccc", "bce", "hls", "xhpa", "crxm", "daisy", + "xvpa", "sam", "cpix", "lpix", "OTbs", "OTns", "OTnc", "OTMT", "OTNL", "OTpt", "OTxr"]; + +static mut name: SomeType = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa; + + pub static count : u8 = 10 ; + +pub const test: &Type = &val; + +impl Color { + pub const WHITE: u32 = 10; +} + +// #1391 +pub const XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX: NTSTATUS = 0 as usize; + +pub const XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX: Yyyyyyyyyyyyyyyyyyyyyyyyyyyy = 1; diff --git a/tests/source/string-lit-2.rs b/tests/source/string-lit-2.rs new file mode 100644 index 000000000000..6b95e25a052b --- /dev/null +++ b/tests/source/string-lit-2.rs @@ -0,0 +1,25 @@ +fn main() -> &'static str { + let too_many_lines = "Hello"; + let leave_me = "sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss\ + s + jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj"; +} + +fn issue_1237() { + let msg = "eedadn\n\ + drvtee\n\ + eandsr\n\ + raavrd\n\ + atevrs\n\ + tsrnev\n\ + sdttsa\n\ + rasrtv\n\ + nssdts\n\ + ntnada\n\ + svetve\n\ + tesnvt\n\ + vntsnd\n\ + vrdear\n\ + dvrsen\n\ + enarar"; +} diff --git a/tests/source/string-lit.rs b/tests/source/string-lit.rs new file mode 100644 index 000000000000..7719e76ffe75 --- /dev/null +++ b/tests/source/string-lit.rs @@ -0,0 +1,61 @@ +// rustfmt-format_strings: true +// Long string literals + +fn main() -> &'static str { + let str = "AAAAAAAAAAAAAAaAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAaAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaAa"; + let str = "AAAAAAAAAAAAAAaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaAa"; + let str = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + + let too_many_lines = "H\ + e\ + l\ + l\ + o"; + + // Make sure we don't break after an escape character. + let odd_length_name = "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"; + let even_length_name = "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"; + + let really_long_variable_name = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + + let raw_string = r#"Do +not +remove +formatting"#; + + filename.replace(" ", "\\" ); + + let xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx = + funktion("yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"); + + let unicode = "a̐éö̲\r\n"; + let unicode2 = "Löwe 老虎 Léopard"; + let unicode3 = "中华Việt Nam"; + let unicode4 = "☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃"; + + "stuffin'" +} + +fn issue682() { + let a = "hello \\ o/"; + let b = a.replace("\\ ", "\\"); +} + +fn issue716() { + println!("forall x. mult(e(), x) = x /\\ + forall x. mult(x, x) = e()"); +} + +fn issue_1282() { + { + match foo { + Permission::AndroidPermissionAccessLocationExtraCommands => { + "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" + } + } + } +} + +// #1987 +#[link_args = "-s NO_FILESYSTEM=1 -s NO_EXIT_RUNTIME=1 -s EXPORTED_RUNTIME_METHODS=[\"_malloc\"] -s NO_DYNAMIC_EXECUTION=1 -s ELIMINATE_DUPLICATE_FUNCTIONS=1 -s EVAL_CTORS=1"] +extern "C" {} diff --git a/tests/source/string_punctuation.rs b/tests/source/string_punctuation.rs new file mode 100644 index 000000000000..552c461ed348 --- /dev/null +++ b/tests/source/string_punctuation.rs @@ -0,0 +1,9 @@ +// rustfmt-format_strings: true + +fn main() { + println!("ThisIsAReallyLongStringWithNoSpaces.It_should_prefer_to_break_onpunctuation:Likethisssssssssssss"); + format!("{}__{}__{}ItShouldOnlyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyNoticeSemicolonsPeriodsColonsAndCommasAndResortToMid-CharBreaksAfterPunctuation{}{}",x,y,z,a,b); + println!("aaaaaaaaaaaaaaaaaaaaaaaaaaaaalhijalfhiigjapdighjapdigjapdighdapighapdighpaidhg;adopgihadoguaadbadgad,qeoihapethae8t0aet8haetadbjtaeg;ooeouthaoeutgadlgajduabgoiuadogabudogubaodugbadgadgadga;adoughaoeugbaouea"); + println!("sentuhaesnuthaesnutheasunteahusnaethuseantuihaesntdiastnidaetnuhaideuhsenathe。WeShouldSupportNonAsciiPunctuations§ensuhatheasunteahsuneathusneathuasnuhaesnuhaesnuaethusnaetuheasnuth"); + println!("ThisIsASampleOfCJKString.祇園精舍の鐘の声、諸行無常の響きあり。娑羅双樹の花の色、盛者必衰の理をあらはす。奢れる人も久しからず、ただ春の夜の夢のごとし。猛き者もつひにはほろびぬ、ひとへに風の前の塵に同じ。"); +} diff --git a/tests/source/struct-field-attributes.rs b/tests/source/struct-field-attributes.rs new file mode 100644 index 000000000000..76d6eda88538 --- /dev/null +++ b/tests/source/struct-field-attributes.rs @@ -0,0 +1,52 @@ +// #1535 +#![feature(struct_field_attributes)] + +struct Foo { + bar: u64, + + #[cfg(test)] + qux: u64, +} + +fn do_something() -> Foo { + Foo { + bar: 0, + + #[cfg(test)] + qux: 1, + } +} + +fn main() { + do_something(); +} + +// #1462 +struct Foo { + foo: usize, + #[cfg(feature="include-bar")] + bar: usize, +} + +fn new_foo() -> Foo { + Foo { + foo: 0, + #[cfg(feature="include-bar")] + bar: 0, + } +} + +// #2044 +pub enum State { + Closure(#[cfg_attr(feature = "serde_derive", serde(state_with = "::serialization::closure"))] GcPtr), +} + +struct Fields( + #[cfg_attr(feature = "serde_derive", serde(state_with = "::base::serialization::shared"))] Arc>, +); + +// #2309 +pub struct A { +#[doc="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"] +pub foos:Vec +} diff --git a/tests/source/struct_field_doc_comment.rs b/tests/source/struct_field_doc_comment.rs new file mode 100644 index 000000000000..191a62100459 --- /dev/null +++ b/tests/source/struct_field_doc_comment.rs @@ -0,0 +1,72 @@ +// #5215 +struct MyTuple( + /// Doc Comments + /* TODO note to add more to Doc Comments */ u32, + /// Doc Comments + // TODO note + u64, +); + +struct MyTuple( + #[cfg(unix)] // some comment + u64, + #[cfg(not(unix))] /*block comment */ + u32, +); + +struct MyTuple( + #[cfg(unix)] + // some comment + u64, + #[cfg(not(unix))] + /*block comment */ + u32, +); + +struct MyTuple( + #[cfg(unix)] // some comment + pub u64, + #[cfg(not(unix))] /*block comment */ + pub(crate) u32, +); + +struct MyTuple( + /// Doc Comments + /* TODO note to add more to Doc Comments */ + pub u32, + /// Doc Comments + // TODO note + pub(crate) u64, +); + +struct MyStruct { + #[cfg(unix)] // some comment + a: u64, + #[cfg(not(unix))] /*block comment */ + b: u32, +} + +struct MyStruct { + #[cfg(unix)] // some comment + pub a: u64, + #[cfg(not(unix))] /*block comment */ + pub(crate) b: u32, +} + +struct MyStruct { + /// Doc Comments + /* TODO note to add more to Doc Comments */ + a: u32, + /// Doc Comments + // TODO note + b: u64, +} + +struct MyStruct { + /// Doc Comments + /* TODO note to add more to Doc Comments */ + pub a: u32, + /// Doc Comments + // TODO note + pub(crate) b: u64, +} diff --git a/tests/source/struct_lits.rs b/tests/source/struct_lits.rs new file mode 100644 index 000000000000..c5aaf7ef8819 --- /dev/null +++ b/tests/source/struct_lits.rs @@ -0,0 +1,143 @@ +// rustfmt-normalize_comments: true +// rustfmt-wrap_comments: true +// Struct literal expressions. + +fn main() { + let x = Bar; + + // Comment + let y = Foo {a: x }; + + Foo { a: foo() /* comment*/, /* comment*/ b: bar(), ..something }; + + Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { a: f(), b: b(), }; + + Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { a: f(), b: b(), }; + + Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { + // Comment + a: foo(), // Comment + // Comment + b: bar(), // Comment + }; + + Foo { a:Bar, + b:f() }; + + Quux { x: if cond { bar(); }, y: baz() }; + + A { + // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Donec et mollis dolor. + first: item(), + // Praesent et diam eget libero egestas mattis sit amet vitae augue. + // Nam tincidunt congue enim, ut porta lorem lacinia consectetur. + second: Item + }; + + Some(Data::MethodCallData(MethodCallData { + span: sub_span.unwrap(), + scope: self.enclosing_scope(id), + ref_id: def_id, + decl_id: Some(decl_id), + })); + + Diagram { /* o This graph demonstrates how + * / \ significant whitespace is + * o o preserved. + * /|\ \ + * o o o o */ + graph: G, } +} + +fn matcher() { + TagTerminatedByteMatcher { + matcher: ByteMatcher { + pattern: b" { memb: T } + let foo = Foo:: { memb: 10 }; +} + +fn issue201() { + let s = S{a:0, .. b}; +} + +fn issue201_2() { + let s = S{a: S2{ .. c}, .. b}; +} + +fn issue278() { + let s = S { + a: 0, + // + b: 0, + }; + let s1 = S { + a: 0, + // foo + // + // bar + b: 0, + }; +} + +fn struct_exprs() { + Foo + { a : 1, b:f( 2)}; + Foo{a:1,b:f(2),..g(3)}; + LoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooongStruct { ..base }; + IntrinsicISizesContribution { content_intrinsic_sizes: IntrinsicISizes { minimum_inline_size: 0, }, }; +} + +fn issue123() { + Foo { a: b, c: d, e: f }; + + Foo { a: bb, c: dd, e: ff }; + + Foo { a: ddddddddddddddddddddd, b: cccccccccccccccccccccccccccccccccccccc }; +} + +fn issue491() { + Foo { + guard: None, + arm: 0, // Comment + }; + + Foo { + arm: 0, // Comment + }; + + Foo { a: aaaaaaaaaa, b: bbbbbbbb, c: cccccccccc, d: dddddddddd, /* a comment */ + e: eeeeeeeee }; +} + +fn issue698() { + Record { + ffffffffffffffffffffffffffields: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + }; + Record { + ffffffffffffffffffffffffffields: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + } +} + +fn issue835() { + MyStruct {}; + MyStruct { /* a comment */ }; + MyStruct { + // Another comment + }; + MyStruct {} +} + +fn field_init_shorthand() { + MyStruct { x, y, z }; + MyStruct { x, y, z, .. base }; + Foo { aaaaaaaaaa, bbbbbbbb, cccccccccc, dddddddddd, /* a comment */ + eeeeeeeee }; + Record { ffffffffffffffffffffffffffieldsaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa }; +} diff --git a/tests/source/struct_lits_multiline.rs b/tests/source/struct_lits_multiline.rs new file mode 100644 index 000000000000..256ba1bbda3b --- /dev/null +++ b/tests/source/struct_lits_multiline.rs @@ -0,0 +1,81 @@ +// rustfmt-normalize_comments: true +// rustfmt-wrap_comments: true +// rustfmt-struct_lit_single_line: false + +// Struct literal expressions. + +fn main() { + let x = Bar; + + // Comment + let y = Foo {a: x }; + + Foo { a: foo() /* comment*/, /* comment*/ b: bar(), ..something }; + + Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { a: foo(), b: bar(), }; + + Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { a: foo(), b: bar(), }; + + Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { + // Comment + a: foo(), // Comment + // Comment + b: bar(), // Comment + }; + + Foo { a:Bar, + b:foo() }; + + Quux { x: if cond { bar(); }, y: baz() }; + + A { + // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Donec et mollis dolor. + first: item(), + // Praesent et diam eget libero egestas mattis sit amet vitae augue. + // Nam tincidunt congue enim, ut porta lorem lacinia consectetur. + second: Item + }; + + Some(Data::MethodCallData(MethodCallData { + span: sub_span.unwrap(), + scope: self.enclosing_scope(id), + ref_id: def_id, + decl_id: Some(decl_id), + })); + + Diagram { /* o This graph demonstrates how + * / \ significant whitespace is + * o o preserved. + * /|\ \ + * o o o o */ + graph: G, } +} + +fn matcher() { + TagTerminatedByteMatcher { + matcher: ByteMatcher { + pattern: b" { memb: T } + let foo = Foo:: { memb: 10 }; +} + +fn issue201() { + let s = S{a:0, .. b}; +} + +fn issue201_2() { + let s = S{a: S2{ .. c}, .. b}; +} + +fn issue491() { + Foo { + guard: None, + arm: 0, // Comment + }; +} diff --git a/tests/source/struct_lits_visual.rs b/tests/source/struct_lits_visual.rs new file mode 100644 index 000000000000..e84652e9ea9a --- /dev/null +++ b/tests/source/struct_lits_visual.rs @@ -0,0 +1,46 @@ +// rustfmt-normalize_comments: true +// rustfmt-wrap_comments: true +// rustfmt-indent_style: Visual + +// Struct literal expressions. + +fn main() { + let x = Bar; + + // Comment + let y = Foo {a: x }; + + Foo { a: foo() /* comment*/, /* comment*/ b: bar(), ..something }; + + Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { a: f(), b: b(), }; + + Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { + // Comment + a: foo(), // Comment + // Comment + b: bar(), // Comment + }; + + Foo { a:Bar, + b:f() }; + + Quux { x: if cond { bar(); }, y: baz() }; + + Baz { x: yxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, z: zzzzz // test + }; + + A { + // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Donec et mollis dolor. + first: item(), + // Praesent et diam eget libero egestas mattis sit amet vitae augue. + // Nam tincidunt congue enim, ut porta lorem lacinia consectetur. + second: Item + }; + + Diagram { /* o This graph demonstrates how + * / \ significant whitespace is + * o o preserved. + * /|\ \ + * o o o o */ + graph: G, } +} diff --git a/tests/source/struct_lits_visual_multiline.rs b/tests/source/struct_lits_visual_multiline.rs new file mode 100644 index 000000000000..d2990f8da3dc --- /dev/null +++ b/tests/source/struct_lits_visual_multiline.rs @@ -0,0 +1,44 @@ +// rustfmt-normalize_comments: true +// rustfmt-wrap_comments: true +// rustfmt-indent_style: Visual +// rustfmt-struct_lit_single_line: false + +// Struct literal expressions. + +fn main() { + let x = Bar; + + // Comment + let y = Foo {a: x }; + + Foo { a: foo() /* comment*/, /* comment*/ b: bar(), ..something }; + + Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { a: foo(), b: bar(), }; + + Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { + // Comment + a: foo(), // Comment + // Comment + b: bar(), // Comment + }; + + Foo { a:Bar, + b:foo() }; + + Quux { x: if cond { bar(); }, y: baz() }; + + A { + // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Donec et mollis dolor. + first: item(), + // Praesent et diam eget libero egestas mattis sit amet vitae augue. + // Nam tincidunt congue enim, ut porta lorem lacinia consectetur. + second: Item + }; + + Diagram { /* o This graph demonstrates how + * / \ significant whitespace is + * o o preserved. + * /|\ \ + * o o o o */ + graph: G, } +} diff --git a/tests/source/struct_tuple_visual.rs b/tests/source/struct_tuple_visual.rs new file mode 100644 index 000000000000..f95f3fe4fd39 --- /dev/null +++ b/tests/source/struct_tuple_visual.rs @@ -0,0 +1,36 @@ +// rustfmt-normalize_comments: true +// rustfmt-wrap_comments: true +// rustfmt-indent_style: Visual +fn foo() { + Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo(f(), b()); + + Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo(// Comment + foo(), /* Comment */ + // Comment + bar() /* Comment */); + + Foo(Bar, f()); + + Quux(if cond { + bar(); + }, + baz()); + + Baz(xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, + zzzzz /* test */); + + A(// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit + // amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante + // hendrerit. Donec et mollis dolor. + item(), + // Praesent et diam eget libero egestas mattis sit amet vitae augue. + // Nam tincidunt congue enim, ut porta lorem lacinia consectetur. + Item); + + Diagram(// o This graph demonstrates how + // / \ significant whitespace is + // o o preserved. + // /|\ \ + // o o o o + G) +} diff --git a/tests/source/structs.rs b/tests/source/structs.rs new file mode 100644 index 000000000000..537151b276a7 --- /dev/null +++ b/tests/source/structs.rs @@ -0,0 +1,298 @@ +// rustfmt-normalize_comments: true +// rustfmt-wrap_comments: true + + /// A Doc comment +#[AnAttribute] +pub struct Foo { + #[rustfmt::skip] + f : SomeType, // Comment beside a field + f: SomeType, // Comment beside a field + // Comment on a field + #[AnAttribute] + g: SomeOtherType, + /// A doc comment on a field + h: AThirdType, + pub i: TypeForPublicField +} + +// Destructuring +fn foo() { + S { x: 5, + ..}; + Struct {..} = Struct { a: 1, b: 4 }; + Struct { a, .. } = Struct { a: 1, b: 2, c: 3}; + TupleStruct(a,.., b) = TupleStruct(1, 2); + TupleStruct( ..) = TupleStruct(3, 4); + TupleStruct(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, .., bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb) = TupleStruct(1, 2); +} + +// #1095 +struct S { + t: T, +} + +// #1029 +pub struct Foo { + #[doc(hidden)] + // This will NOT get deleted! + bar: String, // hi +} + +// #1029 +struct X { + // `x` is an important number. + #[allow(unused)] // TODO: use + x: u32, +} + +// #410 +#[allow(missing_docs)] +pub struct Writebatch { + #[allow(dead_code)] //only used for holding the internal pointer + writebatch: RawWritebatch, + marker: PhantomData, +} + +struct Bar; + +struct NewType(Type, OtherType); + +struct +NewInt (pub i32, SomeType /* inline comment */, T /* sup */ + + + ); + +struct Qux<'a, + N: Clone + 'a, + E: Clone + 'a, + G: Labeller<'a, N, E> + GraphWalk<'a, N, E>, + W: Write + Copy> +( + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, // Comment + BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB, + #[AnAttr] + // Comment + /// Testdoc + G, + pub W, +); + +struct Tuple(/*Comment 1*/ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, + /* Comment 2 */ BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB,); + +// With a where-clause and generics. +pub struct Foo<'a, Y: Baz> + where X: Whatever +{ + f: SomeType, // Comment beside a field +} + +struct Baz { + + a: A, // Comment A + b: B, // Comment B + c: C, // Comment C + +} + +struct Baz { + a: A, // Comment A + + b: B, // Comment B + + + + + c: C, // Comment C +} + +struct Baz { + + a: A, + + b: B, + c: C, + + + + + d: D + +} + +struct Baz +{ + // Comment A + a: A, + + // Comment B +b: B, + // Comment C + c: C,} + +// Will this be a one-liner? +struct Tuple( + A, //Comment + B +); + +pub struct State time::Timespec> { now: F } + +pub struct State ()> { now: F } + +pub struct State { now: F } + +struct Palette { /// A map of indices in the palette to a count of pixels in approximately that color + foo: i32} + +// Splitting a single line comment into a block previously had a misalignment +// when the field had attributes +struct FieldsWithAttributes { + // Pre Comment + #[rustfmt::skip] pub host:String, // Post comment BBBBBBBBBBBBBB BBBBBBBBBBBBBBBB BBBBBBBBBBBBBBBB BBBBBBBBBBBBBBBBB BBBBBBBBBBB + //Another pre comment + #[attr1] + #[attr2] pub id: usize // CCCCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCCC CCCCCCCCCCCCCC CCCCCCCCCCCC +} + +struct Deep { + deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeep: node::Handle>, + Type, + NodeType>, +} + +struct Foo(T); +struct Foo(T) where T: Copy, T: Eq; +struct Foo(TTTTTTTTTTTTTTTTT, UUUUUUUUUUUUUUUUUUUUUUUU, TTTTTTTTTTTTTTTTTTT, UUUUUUUUUUUUUUUUUUU); +struct Foo(TTTTTTTTTTTTTTTTTT, UUUUUUUUUUUUUUUUUUUUUUUU, TTTTTTTTTTTTTTTTTTT) where T: PartialEq; +struct Foo(TTTTTTTTTTTTTTTTT, UUUUUUUUUUUUUUUUUUUUUUUU, TTTTTTTTTTTTTTTTTTTTT) where T: PartialEq; +struct Foo(TTTTTTTTTTTTTTTTT, UUUUUUUUUUUUUUUUUUUUUUUU, TTTTTTTTTTTTTTTTTTT, UUUUUUUUUUUUUUUUUUU) where T: PartialEq; +struct Foo(TTTTTTTTTTTTTTTTT, // Foo + UUUUUUUUUUUUUUUUUUUUUUUU /* Bar */, + // Baz + TTTTTTTTTTTTTTTTTTT, + // Qux (FIXME #572 - doc comment) + UUUUUUUUUUUUUUUUUUU); + +mod m { + struct X where T: Sized { + a: T, + } +} + +struct Foo(TTTTTTTTTTTTTTTTTTT, + /// Qux + UUUUUUUUUUUUUUUUUUU); + +struct Issue677 { + pub ptr: *const libc::c_void, + pub trace: fn( obj: + *const libc::c_void, tracer : *mut JSTracer ), +} + +struct Foo {} +struct Foo { + } +struct Foo { + // comment + } +struct Foo { + // trailing space -> + + + } +struct Foo { /* comment */ } +struct Foo( /* comment */ ); + +struct LongStruct { + a: A, + the_quick_brown_fox_jumps_over_the_lazy_dog:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, +} + +struct Deep { + deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeep: node::Handle>, + Type, + NodeType>, +} + +struct Foo(String); + +// #1364 +fn foo() { + convex_shape.set_point(0, &Vector2f { x: 400.0, y: 100.0 }); + convex_shape.set_point(1, &Vector2f { x: 500.0, y: 70.0 }); + convex_shape.set_point(2, &Vector2f { x: 450.0, y: 100.0 }); + convex_shape.set_point(3, &Vector2f { x: 580.0, y: 150.0 }); +} + +// Vertical alignment +struct Foo { + aaaaa: u32, // a + + b: u32, // b + cc: u32, // cc + + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx: u32, // 1 + yy: u32, // comment2 + zzz: u32, // comment3 + + aaaaaa: u32, // comment4 + bb: u32, // comment5 + // separate + dd: u32, // comment7 + c: u32, // comment6 + + aaaaaaa: u32, /* multi + * line + * comment + */ + b: u32, // hi + + do_not_push_this_comment1: u32, // comment1 + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx: u32, // 2 + please_do_not_push_this_comment3: u32, // comment3 + + do_not_push_this_comment1: u32, // comment1 + // separate + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx: u32, // 2 + please_do_not_push_this_comment3: u32, // comment3 + + do_not_push_this_comment1: u32, // comment1 + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx: u32, // 2 + // separate + please_do_not_push_this_comment3: u32, // comment3 +} + +// structs with long identifier +struct Loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong {} +struct Looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong {} +struct Loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong {} +struct Loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong { x: i32 } + +// structs with visibility, do not duplicate visibility (#2110). +pub(in self) struct Foo{} +pub(super) struct Foo{} +pub(crate) struct Foo{} +pub(in self) struct Foo(); +pub(super) struct Foo(); +pub(crate) struct Foo(); + +// #2125 +pub struct ReadinessCheckRegistry(Mutex, Box ReadinessCheck + Sync + Send>>>); + +// #2144 unit struct with generics +struct MyBox; +struct MyBoxx where T: ?Sized, S: Clone; + +// #2208 +struct Test { + /// foo + #[serde(default)] + pub join: Vec, + #[serde(default)] pub tls: bool, +} + +// #2818 +struct Paren((i32)) where i32: Trait; +struct Parens((i32, i32)) where i32: Trait; diff --git a/tests/source/trailing-comma-never.rs b/tests/source/trailing-comma-never.rs new file mode 100644 index 000000000000..c74267cd1790 --- /dev/null +++ b/tests/source/trailing-comma-never.rs @@ -0,0 +1,45 @@ +// rustfmt-trailing_comma: Never + +enum X { + A, + B, +} + +enum Y { + A, + B +} + +enum TupX { + A(u32), + B(i32, u16), +} + +enum TupY { + A(u32), + B(i32, u16) +} + +enum StructX { + A { + s: u16, + }, + B { + u: u32, + i: i32, + }, +} + +enum StructY { + A { + s: u16, + }, + B { + u: u32, + i: i32, + } +} + +static XXX: [i8; 64] = [ +1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,1, 1, 1, +]; diff --git a/tests/source/trailing_commas.rs b/tests/source/trailing_commas.rs new file mode 100644 index 000000000000..3e5fcc8080ff --- /dev/null +++ b/tests/source/trailing_commas.rs @@ -0,0 +1,47 @@ +// rustfmt-match_block_trailing_comma: true +// rustfmt-trailing_comma: Always + +fn main() { + match foo { + x => {} + y => { + foo(); + } + _ => x + } +} + +fn f(x: T, y: S) -> T where T: P, S: Q +{ + x +} + +impl Trait for T where T: P +{ + fn f(x: T) -> T where T: Q + R + { + x + } +} + +struct Pair where T: P, S: P + Q { + a: T, + b: S +} + +struct TupPair (S, T) where T: P, S: P + Q; + +enum E where S: P, T: P { + A {a: T}, +} + +type Double where T: P, T: Q = Pair; + +extern "C" { + fn f(x: T, y: S) -> T where T: P, S: Q; +} + +trait Q where T: P, S: R +{ + fn f(self, x: T, y: S, z: U) -> Self where U: P, V: P; +} diff --git a/tests/source/trailing_comments/hard_tabs.rs b/tests/source/trailing_comments/hard_tabs.rs new file mode 100644 index 000000000000..88249aa5fb99 --- /dev/null +++ b/tests/source/trailing_comments/hard_tabs.rs @@ -0,0 +1,21 @@ +// rustfmt-version: Two +// rustfmt-wrap_comments: true +// rustfmt-hard_tabs: true + +impl Foo { + fn foo() { + bar(); // comment 1 + // comment 2 + // comment 3 + baz(); + } +} + +fn lorem_ipsum() { + let f = bar(); // Donec consequat mi. Quisque vitae dolor. Integer lobortis. Maecenas id nulla. Lorem. + // Id turpis. Nam posuere lectus vitae nibh. Etiam tortor orci, sagittis malesuada, rhoncus quis, hendrerit eget, libero. Quisque commodo nulla at nunc. Mauris consequat, enim vitae venenatis sollicitudin, dolor orci bibendum enim, a sagittis nulla nunc quis elit. Phasellus augue. Nunc suscipit, magna tincidunt lacinia faucibus, lacus tellus ornare purus, a pulvinar lacus orci eget nibh. Maecenas sed nibh non lacus tempor faucibus. In hac habitasse platea dictumst. Vivamus a orci at nulla tristique condimentum. Donec arcu quam, dictum accumsan, convallis accumsan, cursus sit amet, ipsum. In pharetra sagittis nunc. + let b = baz(); + + let normalized = self.ctfont.all_traits().normalized_weight(); // [-1.0, 1.0] + // TODO(emilio): It may make sense to make this range [.01, 10.0], to align with css-fonts-4's range of [1, 1000]. +} diff --git a/tests/source/trailing_comments/soft_tabs.rs b/tests/source/trailing_comments/soft_tabs.rs new file mode 100644 index 000000000000..7845f713b8a9 --- /dev/null +++ b/tests/source/trailing_comments/soft_tabs.rs @@ -0,0 +1,21 @@ +// rustfmt-version: Two +// rustfmt-wrap_comments: true + +pub const IFF_MULTICAST: ::c_int = 0x0000000800; // Supports multicast +// Multicast using broadcst. add. + +pub const SQ_CRETAB: u16 = 0x000e; // CREATE TABLE +pub const SQ_DRPTAB: u16 = 0x000f; // DROP TABLE +pub const SQ_CREIDX: u16 = 0x0010; // CREATE INDEX +//const SQ_DRPIDX: u16 = 0x0011; // DROP INDEX +//const SQ_GRANT: u16 = 0x0012; // GRANT +//const SQ_REVOKE: u16 = 0x0013; // REVOKE + +fn foo() { + let f = bar(); // Donec consequat mi. Quisque vitae dolor. Integer lobortis. Maecenas id nulla. Lorem. + // Id turpis. Nam posuere lectus vitae nibh. Etiam tortor orci, sagittis malesuada, rhoncus quis, hendrerit eget, libero. Quisque commodo nulla at nunc. Mauris consequat, enim vitae venenatis sollicitudin, dolor orci bibendum enim, a sagittis nulla nunc quis elit. Phasellus augue. Nunc suscipit, magna tincidunt lacinia faucibus, lacus tellus ornare purus, a pulvinar lacus orci eget nibh. Maecenas sed nibh non lacus tempor faucibus. In hac habitasse platea dictumst. Vivamus a orci at nulla tristique condimentum. Donec arcu quam, dictum accumsan, convallis accumsan, cursus sit amet, ipsum. In pharetra sagittis nunc. + let b = baz(); + + let normalized = self.ctfont.all_traits().normalized_weight(); // [-1.0, 1.0] + // TODO(emilio): It may make sense to make this range [.01, 10.0], to align with css-fonts-4's range of [1, 1000]. +} diff --git a/tests/source/trait.rs b/tests/source/trait.rs new file mode 100644 index 000000000000..b6db9e1590d4 --- /dev/null +++ b/tests/source/trait.rs @@ -0,0 +1,183 @@ +// Test traits + +trait Foo { + fn bar(x: i32 ) -> Baz< U> { Baz::new() + } + + fn baz(a: AAAAAAAAAAAAAAAAAAAAAA, +b: BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB) +-> RetType; + + fn foo(a: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, // Another comment +b: BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB) + -> RetType ; // Some comment + + fn baz(&mut self ) -> i32 ; + +fn increment(& mut self, x: i32 ); + + fn read(&mut self, x: BufReader /* Used to be MemReader */) + where R: Read; +} + +pub trait WriteMessage { + fn write_message (&mut self, &FrontendMessage) -> io::Result<()>; +} + +trait Runnable { + fn handler(self: & Runnable ); +} + +trait TraitWithExpr { + fn fn_with_expr(x: [i32; 1]); +} + +trait Test { + fn read_struct(&mut self, s_name: &str, len: usize, f: F) -> Result where F: FnOnce(&mut Self) -> Result; +} + +trait T<> {} + +trait Foo { type Bar: Baz; type Inner: Foo = Box< Foo >; } + +trait ConstCheck:Foo where T: Baz { + const J: i32; +} + +trait Tttttttttttttttttttttttttttttttttttttttttttttttttttttttttt + where T: Foo {} + +trait Ttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt where T: Foo {} + +trait FooBar : Tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt where J: Bar { fn test(); } + +trait WhereList where T: Foo, J: Bar {} + +trait X /* comment */ {} +trait Y // comment +{ +} + +// #2055 +pub trait Foo: +// A and C +A + C +// and B + + B +{} + +// #2158 +trait Foo { + type ItRev = > as UntypedTimeSeries>::IterRev; + type IteRev = > as UntypedTimeSeries>::IterRev; +} + +// #2331 +trait MyTrait { + fn foo() {} +} + +// Trait aliases +trait FooBar = + Foo + + Bar; +trait FooBar = + Foo + + Bar; +pub trait FooBar = + Foo + + Bar; +pub trait FooBar = + Foo + + Bar; +trait AAAAAAAAAAAAAAAAAA = BBBBBBBBBBBBBBBBBBB + CCCCCCCCCCCCCCCCCCCCCCCCCCCCC + DDDDDDDDDDDDDDDDDD; +pub trait AAAAAAAAAAAAAAAAAA = BBBBBBBBBBBBBBBBBBB + CCCCCCCCCCCCCCCCCCCCCCCCCCCCC + DDDDDDDDDDDDDDDDDD; +trait AAAAAAAAAAAAAAAAAAA = BBBBBBBBBBBBBBBBBBB + CCCCCCCCCCCCCCCCCCCCCCCCCCCCC + DDDDDDDDDDDDDDDDDD; +trait AAAAAAAAAAAAAAAAAA = BBBBBBBBBBBBBBBBBBB + CCCCCCCCCCCCCCCCCCCCCCCCCCCCC + DDDDDDDDDDDDDDDDDDD; +trait AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA = FooBar; +trait AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA = FooBar; +#[rustfmt::skip] +trait FooBar = Foo + + Bar; + +// #2637 +auto trait Example {} +pub auto trait PubExample {} +pub unsafe auto trait PubUnsafeExample {} + +// #3006 +trait Foo<'a> { + type Bar< 'a >; +} + +impl<'a> Foo<'a> for i32 { + type Bar< 'a > = i32; +} + +// #3092 +pub mod test { + pub trait ATraitWithALooongName {} + pub trait ATrait + :ATraitWithALooongName + ATraitWithALooongName + ATraitWithALooongName + ATraitWithALooongName +{ +} +} + +// Trait aliases with where clauses. +trait A = where for<'b> &'b Self: Send; + +trait B = where for<'b> &'b Self: Send + Clone + Copy + SomeTrait + AAAAAAAA + BBBBBBB + CCCCCCCCCC; +trait B = where for<'b> &'b Self: Send + Clone + Copy + SomeTrait + AAAAAAAA + BBBBBBB + CCCCCCCCCCC; +trait B = where + for<'b> &'b Self: +Send + Clone + Copy + SomeTrait + AAAAAAAA + BBBBBBB + CCCCCCCCCCCCCCCCCCCCCCC; +trait B = where + for<'b> &'b Self: +Send + Clone + Copy + SomeTrait + AAAAAAAA + BBBBBBB + CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC; + +trait B = where + for<'b> &'b Self: +Send + + Clone + + Copy + + SomeTrait + + AAAAAAAA + + BBBBBBB + + CCCCCCCCC + + DDDDDDD + + DDDDDDDD + + DDDDDDDDD + + EEEEEEE; + +trait A<'a, 'b, 'c> = Debug + Foo where for<'b> &'b Self: Send; + +trait B<'a, 'b, 'c> = Debug +Foo +where for<'b> &'b Self: +Send + + Clone + + Copy + + SomeTrait + + AAAAAAAA + + BBBBBBB + + CCCCCCCCC + + DDDDDDD; + +trait B<'a, 'b, 'c,T> = Debug<'a, T> where for<'b> &'b Self: +Send + + Clone + + Copy + + SomeTrait + + AAAAAAAA + + BBBBBBB + + CCCCCCCCC + + DDDDDDD + + DDDDDDDD + + DDDDDDDDD + + EEEEEEE; + +trait Visible { + pub const C: i32; + pub type T; + pub fn f(); + pub fn g() {} +} diff --git a/tests/source/try-conversion.rs b/tests/source/try-conversion.rs new file mode 100644 index 000000000000..ed83ee9e101c --- /dev/null +++ b/tests/source/try-conversion.rs @@ -0,0 +1,18 @@ +// rustfmt-use_try_shorthand: true + +fn main() { + let x = try!(some_expr()); + + let y = try!(a.very.loooooooooooooooooooooooooooooooooooooong().chain().inside().weeeeeeeeeeeeeee()).test().0.x; +} + +fn test() { + a? +} + +fn issue1291() { + try!(fs::create_dir_all(&gitfiledir).chain_err(|| { + format!("failed to create the {} submodule directory for the workarea", + name) + })); +} diff --git a/tests/source/try_block.rs b/tests/source/try_block.rs new file mode 100644 index 000000000000..2e8d61f7e66a --- /dev/null +++ b/tests/source/try_block.rs @@ -0,0 +1,30 @@ +// rustfmt-edition: 2018 + +fn main() -> Result<(), !> { + let _x: Option<_> = try { + 4 + }; + + try {} +} + +fn baz() -> Option { + if (1 == 1) { + return try { + 5 + }; + } + + // test + let x: Option<()> = try { + // try blocks are great + }; + + let y: Option = try { + 6 + }; // comment + + let x: Option = try { baz()?; baz()?; baz()?; 7 }; + + return None; +} diff --git a/tests/source/tuple.rs b/tests/source/tuple.rs new file mode 100644 index 000000000000..5189a7454f3a --- /dev/null +++ b/tests/source/tuple.rs @@ -0,0 +1,63 @@ +// Test tuple literals + +fn foo() { + let a = (a, a, a, a, a); + let aaaaaaaaaaaaaaaa = (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaa, aaaaaaaaaaaaaa); + let aaaaaaaaaaaaaaaaaaaaaa = (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + aaaaaaaaaaaaaaaaaaaaaaaaa, + aaaa); + let a = (a,); + + let b = (// This is a comment + b, // Comment + b /* Trailing comment */); + + // #1063 + foo(x.0 .0); +} + +fn a() { + ((aaaaaaaa, + aaaaaaaaaaaaa, + aaaaaaaaaaaaaaaaa, + aaaaaaaaaaaaaa, + aaaaaaaaaaaaaaaa, + aaaaaaaaaaaaaa),) +} + +fn b() { + ((bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb), + bbbbbbbbbbbbbbbbbb) +} + +fn issue550() { + self.visitor.visit_volume(self.level.sector_id(sector), (floor_y, + if is_sky_flat(ceil_tex) {from_wad_height(self.height_range.1)} else {ceil_y})); +} + +fn issue775() { + if indent { + let a = mk_object(&[("a".to_string(), Boolean(true)), + ("b".to_string(), + Array(vec![mk_object(&[("c".to_string(), + String("\x0c\r".to_string()))]), + mk_object(&[("d".to_string(), String("".to_string()))])]))]); + } +} + +fn issue1725() { + bench_antialiased_lines!(bench_draw_antialiased_line_segment_diagonal, (10, 10), (450, 450)); + bench_antialiased_lines!(bench_draw_antialiased_line_segment_shallow, (10, 10), (450, 80)); +} + +fn issue_4355() { + let _ = ((1,),).0.0; +} + +// https://github.com/rust-lang/rustfmt/issues/4410 +impl Drop for LockGuard { + fn drop(&mut self) { + LockMap::unlock(&self.0.0, &self.0.1); + } +} diff --git a/tests/source/tuple_v2.rs b/tests/source/tuple_v2.rs new file mode 100644 index 000000000000..9223033832b2 --- /dev/null +++ b/tests/source/tuple_v2.rs @@ -0,0 +1,5 @@ +// rustfmt-version: Two + +fn issue_4355() { + let _ = ((1,),).0 .0; +} diff --git a/tests/source/type.rs b/tests/source/type.rs new file mode 100644 index 000000000000..61ef73a3cab1 --- /dev/null +++ b/tests/source/type.rs @@ -0,0 +1,168 @@ +// rustfmt-normalize_comments: true +fn types() { + let x: [ Vec < _ > ] = []; + let y: * mut [ SomeType ; konst_funk() ] = expr(); + let z: (/*#digits*/ usize, /*exp*/ i16) = funk(); + let z: ( usize /*#digits*/ , i16 /*exp*/ ) = funk(); +} + +struct F { + f: extern "C" fn(x: u8, ... /* comment */), + g: extern "C" fn(x: u8,/* comment */ ...), + h: extern "C" fn(x: u8, ... ), + i: extern "C" fn(x: u8, /* comment 4*/ y: String, // comment 3 + z: Foo, /* comment */ .../* comment 2*/ ), +} + +fn issue_1006(def_id_to_string: for<'a, 'b> unsafe fn(TyCtxt<'b, 'tcx, 'tcx>, DefId) -> String) {} + +fn impl_trait_fn_1() -> impl Fn(i32) -> Option {} + +fn impl_trait_fn_2() -> impl Future {} + +fn issue_1234() { + do_parse!(name: take_while1!(is_token) >> (Header)) +} + +// #2510 +impl CombineTypes { + pub fn pop_callback( + &self, + query_id: Uuid, + ) -> Option< + ( + ProjectId, + Box () + Sync + Send>, + ), + > { + self.query_callbacks()(&query_id) + } +} + +// #2859 +pub fn do_something<'a, T: Trait1 + Trait2 + 'a>(&fooo: u32) -> impl Future< + Item = ( + impl Future + 'a, + impl Future + 'a, +impl Future + 'a, + ), + Error = SomeError, + > + + + 'a { +} + +pub fn do_something<'a, T: Trait1 + Trait2 + 'a>( &fooo: u32, +) -> impl Future< + Item = ( +impl Future + 'a, + impl Future + 'a, + impl Future + 'a, + ), + Error = SomeError, + > + + Future< + Item = ( + impl Future + 'a, +impl Future + 'a, + impl Future + 'a, + ), + Error = SomeError, + > + + Future< + Item = ( + impl Future + 'a, + impl Future + 'a, + impl Future + 'a, + ), + Error = SomeError, + > + + + 'a + 'b + + 'c { +} + +// #3051 +token![impl]; +token![ impl ]; + +// #3060 +macro_rules! foo { + ($foo_api: ty) => { + type Target = ( $foo_api ) + 'static; + } +} + +type Target = ( FooAPI ) + 'static; + +// #3137 +fn foo(t: T) +where + T: ( FnOnce() -> () ) + Clone, + U: ( FnOnce() -> () ) + 'static, +{ +} + +// #3117 +fn issue3117() { + { + { + { + { + { + { + { + { + let opt: &mut Option = + unsafe { &mut *self.future.get() }; + } + } + } + } + } + } + } + } +} + +// #3139 +fn issue3139() { + assert_eq!( + to_json_value(&None :: ).unwrap(), + json!( { "test": None :: } ) + ); +} + +// #3180 +fn foo(a: SomeLongComplexType, b: SomeOtherLongComplexType) -> Box> { +} + +type MyFn = fn(a: SomeLongComplexType, b: SomeOtherLongComplexType,) -> Box>; + +// Const bound + +trait T: ~ const Super {} + +const fn not_quite_const() -> i32 { ::CONST } + +struct S(std::marker::PhantomData); + +impl ~ const T {} + +fn apit(_: impl ~ const T) {} + +fn rpit() -> impl ~ const T { S } + +pub struct Foo(T); +impl Foo { + fn new(t: T) -> Self { + Self(t) + } +} + +// #4357 +type T = typeof( +1); +impl T for .. { +} diff --git a/tests/source/type_alias.rs b/tests/source/type_alias.rs new file mode 100644 index 000000000000..58c807f4029e --- /dev/null +++ b/tests/source/type_alias.rs @@ -0,0 +1,34 @@ +// rustfmt-normalize_comments: true + +type PrivateTest<'a, I> = (Box + 'a>, Box + 'a>); + +pub type PublicTest<'a, I, O> = Result, Box + 'a>, Box + 'a>>; + +pub type LongGenericListTest<'a, 'b, 'c, 'd, LONGPARAMETERNAME, LONGPARAMETERNAME, LONGPARAMETERNAME, A, B, C> = Option>; + +pub type Exactly100CharsTest<'a, 'b, 'c, 'd, LONGPARAMETERNAME, LONGPARAMETERNAME, A, B> = Vec; + +pub type Exactly101CharsTest<'a, 'b, 'c, 'd, LONGPARAMETERNAME, LONGPARAMETERNAME, A, B> = Vec; + +pub type Exactly100CharsToEqualTest<'a, 'b, 'c, 'd, LONGPARAMETERNAME, LONGPARAMETERNAME, A, B, C> = Vec; + +pub type GenericsFitButNotEqualTest<'a, 'b, 'c, 'd, LONGPARAMETERNAME, LONGPARAMETERNAME, A1, B, C> = Vec; + +pub type CommentTest< /* Lifetime */ 'a + , + // Type + T + > = (); + + +pub type WithWhereClause where T: Clone, LONGPARAMETERNAME: Clone + Eq + OtherTrait = Option; + +pub type Exactly100CharstoEqualWhereTest where T: Clone + Ord + Eq + SomeOtherTrait = Option; + +pub type Exactly101CharstoEqualWhereTest where T: Clone + Ord + Eq + SomeOtherTrait = Option; + +type RegisterPlugin = unsafe fn(pt: *const c_char, plugin: *mut c_void, data: *mut CallbackData); + +// #1683 +pub type Between = super::operators::Between, AsExpr>>; +pub type NotBetween = super::operators::NotBetween, AsExpr>>; diff --git a/tests/source/unicode.rs b/tests/source/unicode.rs new file mode 100644 index 000000000000..4c2119af5754 --- /dev/null +++ b/tests/source/unicode.rs @@ -0,0 +1,33 @@ +// rustfmt-wrap_comments: true + +fn foo() { + let s = "this line goes to 100: ͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶ"; + let s = 42; + + // a comment of length 80, with the starting sigil: ҘҘҘҘҘҘҘҘҘҘ ҘҘҘҘҘҘҘҘҘҘҘҘҘҘ + let s = 42; +} + +pub fn bar(config: &Config) { + let csv = RefCell::new(create_csv(config, "foo")); + { + let mut csv = csv.borrow_mut(); + for (i1, i2, i3) in iproduct!(0..2, 0..3, 0..3) { + csv.write_field(format!("γ[{}.{}.{}]", i1, i2, i3)) + .unwrap(); + csv.write_field(format!("d[{}.{}.{}]", i1, i2, i3)) + .unwrap(); + csv.write_field(format!("i[{}.{}.{}]", i1, i2, i3)) + .unwrap(); + } + csv.write_record(None::<&[u8]>).unwrap(); + } +} + +// The NotUnicode line is below 100 wrt chars but over it wrt String::len +fn baz() { + let our_error_b = result_b_from_func.or_else(|e| match e { + NotPresent => Err(e).chain_err(|| "env var wasn't provided"), + NotUnicode(_) => Err(e).chain_err(|| "env var was very very very bork文字化ã"), + }); +} diff --git a/tests/source/unions.rs b/tests/source/unions.rs new file mode 100644 index 000000000000..53630788fe5c --- /dev/null +++ b/tests/source/unions.rs @@ -0,0 +1,195 @@ +// rustfmt-normalize_comments: true +// rustfmt-wrap_comments: true + + /// A Doc comment +#[AnAttribute] +pub union Foo { + #[rustfmt::skip] + f : SomeType, // Comment beside a field + f: SomeType, // Comment beside a field + // Comment on a field + #[AnAttribute] + g: SomeOtherType, + /// A doc comment on a field + h: AThirdType, + pub i: TypeForPublicField +} + +// #1029 +pub union Foo { + #[doc(hidden)] + // This will NOT get deleted! + bar: String, // hi +} + +// #1029 +union X { + // `x` is an important number. + #[allow(unused)] // TODO: use + x: u32, +} + +// #410 +#[allow(missing_docs)] +pub union Writebatch { + #[allow(dead_code)] //only used for holding the internal pointer + writebatch: RawWritebatch, + marker: PhantomData, +} + +// With a where-clause and generics. +pub union Foo<'a, Y: Baz> + where X: Whatever +{ + f: SomeType, // Comment beside a field +} + +union Baz { + + a: A, // Comment A + b: B, // Comment B + c: C, // Comment C + +} + +union Baz { + a: A, // Comment A + + b: B, // Comment B + + + + + c: C, // Comment C +} + +union Baz { + + a: A, + + b: B, + c: C, + + + + + d: D + +} + +union Baz +{ + // Comment A + a: A, + + // Comment B +b: B, + // Comment C + c: C,} + +pub union State time::Timespec> { now: F } + +pub union State ()> { now: F } + +pub union State { now: F } + +union Palette { /// A map of indices in the palette to a count of pixels in approximately that color + foo: i32} + +// Splitting a single line comment into a block previously had a misalignment +// when the field had attributes +union FieldsWithAttributes { + // Pre Comment + #[rustfmt::skip] pub host:String, // Post comment BBBBBBBBBBBBBB BBBBBBBBBBBBBBBB BBBBBBBBBBBBBBBB BBBBBBBBBBBBBBBBB BBBBBBBBBBB + //Another pre comment + #[attr1] + #[attr2] pub id: usize // CCCCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCCC CCCCCCCCCCCCCC CCCCCCCCCCCC +} + +union Deep { + deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeep: node::Handle>, + Type, + NodeType>, +} + +mod m { + union X where T: Sized { + a: T, + } +} + +union Issue677 { + pub ptr: *const libc::c_void, + pub trace: fn( obj: + *const libc::c_void, tracer : *mut JSTracer ), +} + +union Foo {} +union Foo { + } +union Foo { + // comment + } +union Foo { + // trailing space -> + + + } +union Foo { /* comment */ } + +union LongUnion { + a: A, + the_quick_brown_fox_jumps_over_the_lazy_dog:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, +} + +union Deep { + deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeep: node::Handle>, + Type, + NodeType>, +} + +// #1364 +fn foo() { + convex_shape.set_point(0, &Vector2f { x: 400.0, y: 100.0 }); + convex_shape.set_point(1, &Vector2f { x: 500.0, y: 70.0 }); + convex_shape.set_point(2, &Vector2f { x: 450.0, y: 100.0 }); + convex_shape.set_point(3, &Vector2f { x: 580.0, y: 150.0 }); +} + +// Vertical alignment +union Foo { + aaaaa: u32, // a + + b: u32, // b + cc: u32, // cc + + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx: u32, // 1 + yy: u32, // comment2 + zzz: u32, // comment3 + + aaaaaa: u32, // comment4 + bb: u32, // comment5 + // separate + dd: u32, // comment7 + c: u32, // comment6 + + aaaaaaa: u32, /* multi + * line + * comment + */ + b: u32, // hi + + do_not_push_this_comment1: u32, // comment1 + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx: u32, // 2 + please_do_not_push_this_comment3: u32, // comment3 + + do_not_push_this_comment1: u32, // comment1 + // separate + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx: u32, // 2 + please_do_not_push_this_comment3: u32, // comment3 + + do_not_push_this_comment1: u32, // comment1 + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx: u32, // 2 + // separate + please_do_not_push_this_comment3: u32, // comment3 +} diff --git a/tests/source/unsafe-mod.rs b/tests/source/unsafe-mod.rs new file mode 100644 index 000000000000..9996b06271a5 --- /dev/null +++ b/tests/source/unsafe-mod.rs @@ -0,0 +1,7 @@ +// These are supported by rustc syntactically but not semantically. + +#[cfg(any())] +unsafe mod m { } + +#[cfg(any())] +unsafe extern "C++" { } diff --git a/tests/source/visibility.rs b/tests/source/visibility.rs new file mode 100644 index 000000000000..1c5919ccff97 --- /dev/null +++ b/tests/source/visibility.rs @@ -0,0 +1,8 @@ +// #2398 +pub mod outer_mod { + pub mod inner_mod { + pub ( in outer_mod ) fn outer_mod_visible_fn() {} + pub ( super ) fn super_mod_visible_fn() {} + pub ( self ) fn inner_mod_visible_fn() {} + } +} diff --git a/tests/source/visual-fn-type.rs b/tests/source/visual-fn-type.rs new file mode 100644 index 000000000000..67dad5fa4602 --- /dev/null +++ b/tests/source/visual-fn-type.rs @@ -0,0 +1,10 @@ +// rustfmt-indent_style: Visual +type CNodeSetAtts = unsafe extern "C" fn(node: *const RsvgNode, + node_impl: *const RsvgCNodeImpl, + handle: *const RsvgHandle, + pbag: *const PropertyBag) + ; +type CNodeDraw = unsafe extern "C" fn(node: *const RsvgNode, + node_impl: *const RsvgCNodeImpl, + draw_ctx: *const RsvgDrawingCtx, + dominate: i32); diff --git a/tests/source/where-clause-rfc.rs b/tests/source/where-clause-rfc.rs new file mode 100644 index 000000000000..219a9bddb11b --- /dev/null +++ b/tests/source/where-clause-rfc.rs @@ -0,0 +1,73 @@ +fn reflow_list_node_with_rule(node: &CompoundNode, rule: &Rule, args: &[Arg], shape: &Shape) where T: FOo, U: Bar { + let mut effects = HashMap::new(); +} + +fn reflow_list_node_with_rule(node: &CompoundNode, rule: &Rule, args: &[Arg], shape: &Shape) where T: FOo { + let mut effects = HashMap::new(); +} + +fn reflow_list_node_with_rule(node: &CompoundNode, rule: &Rule, args: &[Arg], shape: &Shape, shape: &Shape) where T: FOo, U: Bar { + let mut effects = HashMap::new(); +} + +fn reflow_list_node_with_rule(node: &CompoundNode, rule: &Rule, args: &[Arg], shape: &Shape, shape: &Shape) where T: FOo { + let mut effects = HashMap::new(); +} + +fn reflow_list_node_with_rule(node: &CompoundNode, rule: &Rule, args: &[Arg], shape: &Shape) -> Option where T: FOo, U: Bar { + let mut effects = HashMap::new(); +} + +fn reflow_list_node_with_rule(node: &CompoundNode, rule: &Rule, args: &[Arg], shape: &Shape) -> Option where T: FOo { + let mut effects = HashMap::new(); +} + +pub trait Test { + fn very_long_method_name(self, f: F) -> MyVeryLongReturnType where F: FnMut(Self::Item) -> bool; + + fn exactly_100_chars1(self, f: F) -> MyVeryLongReturnType where F: FnMut(Self::Item) -> bool; +} + +fn very_long_function_name(very_long_argument: F) -> MyVeryLongReturnType where F: FnMut(Self::Item) -> bool { } + +struct VeryLongTupleStructName(LongLongTypename, LongLongTypename, i32, i32) where A: LongTrait; + +struct Exactly100CharsToSemicolon + (LongLongTypename, i32, i32) + where A: LongTrait1234; + +struct AlwaysOnNextLine where A: LongTrait { + x: i32 +} + +pub trait SomeTrait + where + T: Something + Sync + Send + Display + Debug + Copy + Hash + Debug + Display + Write + Read + FromStr +{ +} + +// #2020 +impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { + fn elaborate_bounds(&mut self, bounds: &[ty::PolyTraitRef<'tcx>], mut mk_cand: F) + where F: for<'b> FnMut(&mut ProbeContext<'b, 'gcx, 'tcx>, ty::PolyTraitRef<'tcx>, ty::AssociatedItem), + { + // ... + } +} + +// #2497 +fn handle_update<'a, Tab, Conn, R, C>(executor: &Executor>>, change_set: &'a C) -> ExecutionResult +where &'a C: Identifiable + AsChangeset + HasTable, + <&'a C as AsChangeset>::Changeset: QueryFragment, + Tab: Table + HasTable
, + Tab::PrimaryKey: EqAll<<&'a C as Identifiable>::Id>, + Tab::FromClause: QueryFragment, + Tab: FindDsl<<&'a C as Identifiable>::Id>, + Find::Id>: IntoUpdateTarget
, + ::Id> as IntoUpdateTarget>::WhereClause: QueryFragment, + Tab::Query: FilterDsl<::Id>>::Output>, + Filter::Id>>::Output>: LimitDsl, + Limit::Id>>::Output>>: QueryDsl + BoxedDsl< 'a, Conn::Backend, Output = BoxedSelectStatement<'a, R::SqlType, Tab, Conn::Backend>>, + R: LoadingHandler + GraphQLType, { + unimplemented!() +} diff --git a/tests/source/where-clause.rs b/tests/source/where-clause.rs new file mode 100644 index 000000000000..2a9160825487 --- /dev/null +++ b/tests/source/where-clause.rs @@ -0,0 +1,58 @@ +// rustfmt-indent_style: Visual + +fn reflow_list_node_with_rule(node: &CompoundNode, rule: &Rule, args: &[Arg], shape: &Shape) where T: FOo, U: Bar { + let mut effects = HashMap::new(); +} + +fn reflow_list_node_with_rule(node: &CompoundNode, rule: &Rule, args: &[Arg], shape: &Shape) where T: FOo { + let mut effects = HashMap::new(); +} + +fn reflow_list_node_with_rule(node: &CompoundNode, rule: &Rule, args: &[Arg], shape: &Shape, shape: &Shape) where T: FOo, U: Bar { + let mut effects = HashMap::new(); +} + +fn reflow_list_node_with_rule(node: &CompoundNode, rule: &Rule, args: &[Arg], shape: &Shape, shape: &Shape) where T: FOo { + let mut effects = HashMap::new(); +} + +fn reflow_list_node_with_rule(node: &CompoundNode, rule: &Rule, args: &[Arg], shape: &Shape) -> Option where T: FOo, U: Bar { + let mut effects = HashMap::new(); +} + +fn reflow_list_node_with_rule(node: &CompoundNode, rule: &Rule, args: &[Arg], shape: &Shape) -> Option where T: FOo { + let mut effects = HashMap::new(); +} + +pub trait Test { + fn very_long_method_name(self, f: F) -> MyVeryLongReturnType where F: FnMut(Self::Item) -> bool; + + fn exactly_100_chars1(self, f: F) -> MyVeryLongReturnType where F: FnMut(Self::Item) -> bool; +} + +fn very_long_function_name(very_long_argument: F) -> MyVeryLongReturnType where F: FnMut(Self::Item) -> bool { } + +struct VeryLongTupleStructName(LongLongTypename, LongLongTypename, i32, i32) where A: LongTrait; + +struct Exactly100CharsToSemicolon + (LongLongTypename, i32, i32) + where A: LongTrait1234; + +struct AlwaysOnNextLine where A: LongTrait { + x: i32 +} + +pub trait SomeTrait + where + T: Something + Sync + Send + Display + Debug + Copy + Hash + Debug + Display + Write + Read + FromStr +{ +} + +// #2020 +impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { + fn elaborate_bounds(&mut self, bounds: &[ty::PolyTraitRef<'tcx>], mut mk_cand: F) + where F: for<'b> FnMut(&mut ProbeContext<'b, 'gcx, 'tcx>, ty::PolyTraitRef<'tcx>, ty::AssociatedItem), + { + // ... + } +} diff --git a/tests/source/width-heuristics.rs b/tests/source/width-heuristics.rs new file mode 100644 index 000000000000..a591218b4ccc --- /dev/null +++ b/tests/source/width-heuristics.rs @@ -0,0 +1,28 @@ +// rustfmt-max_width: 120 + +// elems on multiple lines for max_width 100, but same line for max_width 120 +fn foo(e: Enum) { + match e { + Enum::Var { + elem1, + elem2, + elem3, + } => { + return; + } + } +} + +// elems not on same line for either max_width 100 or 120 +fn bar(e: Enum) { + match e { + Enum::Var { + elem1, + elem2, + elem3, + elem4, + } => { + return; + } + } +} diff --git a/tests/source/wrap_comments_should_not_imply_format_doc_comments.rs b/tests/source/wrap_comments_should_not_imply_format_doc_comments.rs new file mode 100644 index 000000000000..56064e4a4ccc --- /dev/null +++ b/tests/source/wrap_comments_should_not_imply_format_doc_comments.rs @@ -0,0 +1,16 @@ +// rustfmt-wrap_comments: true + +/// Foo +/// +/// # Example +/// ``` +/// # #![cfg_attr(not(dox), feature(cfg_target_feature, target_feature, stdsimd))] +/// # #![cfg_attr(not(dox), no_std)] +/// fn foo() { } +/// ``` +/// +fn foo() {} + +/// A long comment for wrapping +/// This is a long long long long long long long long long long long long long long long long long long long long sentence. +fn bar() {} diff --git a/tests/target/5131_crate.rs b/tests/target/5131_crate.rs new file mode 100644 index 000000000000..557d66703554 --- /dev/null +++ b/tests/target/5131_crate.rs @@ -0,0 +1,9 @@ +// rustfmt-imports_granularity: Crate + +use foo::{ + a, b, b as b2, + b::{f, g, g as g2}, + c, + d::e, +}; +use qux::{h, h as h2, i}; diff --git a/tests/target/5131_module.rs b/tests/target/5131_module.rs new file mode 100644 index 000000000000..763024d6fa49 --- /dev/null +++ b/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/tests/target/5131_one.rs b/tests/target/5131_one.rs new file mode 100644 index 000000000000..a086dae5a422 --- /dev/null +++ b/tests/target/5131_one.rs @@ -0,0 +1,12 @@ +// rustfmt-imports_granularity: One + +pub use foo::{x, x as x2, y}; +use { + bar::{ + a, + b::{self, f, g}, + c, + d::{e, e as e2}, + }, + qux::{h, i}, +}; diff --git a/tests/target/alignment_2633/block_style.rs b/tests/target/alignment_2633/block_style.rs new file mode 100644 index 000000000000..f13e8a8760e7 --- /dev/null +++ b/tests/target/alignment_2633/block_style.rs @@ -0,0 +1,10 @@ +// rustfmt-struct_field_align_threshold: 50 + +fn func() { + Ok(ServerInformation { + name: unwrap_message_string(items.get(0)), + vendor: unwrap_message_string(items.get(1)), + version: unwrap_message_string(items.get(2)), + spec_version: unwrap_message_string(items.get(3)), + }); +} diff --git a/tests/target/alignment_2633/horizontal_tactic.rs b/tests/target/alignment_2633/horizontal_tactic.rs new file mode 100644 index 000000000000..a381945fd8b8 --- /dev/null +++ b/tests/target/alignment_2633/horizontal_tactic.rs @@ -0,0 +1,13 @@ +// rustfmt-struct_field_align_threshold: 5 + +#[derive(Fail, Debug, Clone)] +pub enum BuildError { + LineTooLong { length: usize, limit: usize }, + DisallowedByte { b: u8, pos: usize }, + ContainsNewLine { pos: usize }, +} + +enum Foo { + A { a: usize, bbbbb: () }, + B { a: (), bbbbb: () }, +} diff --git a/tests/target/alignment_2633/visual_style.rs b/tests/target/alignment_2633/visual_style.rs new file mode 100644 index 000000000000..7d21b599af4d --- /dev/null +++ b/tests/target/alignment_2633/visual_style.rs @@ -0,0 +1,9 @@ +// rustfmt-struct_field_align_threshold: 50 +// rustfmt-indent_style: Visual + +fn func() { + Ok(ServerInformation { name: unwrap_message_string(items.get(0)), + vendor: unwrap_message_string(items.get(1)), + version: unwrap_message_string(items.get(2)), + spec_version: unwrap_message_string(items.get(3)), }); +} diff --git a/tests/target/array_comment.rs b/tests/target/array_comment.rs new file mode 100644 index 000000000000..93e1f5f40345 --- /dev/null +++ b/tests/target/array_comment.rs @@ -0,0 +1,18 @@ +// Issue 2842 +// The comment should not make the last line shorter + +static XXX: [i8; 64] = [ + 1, // Comment + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +]; + +static XXX: [i8; 64] = [ + 1, // Comment + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +]; + +static XXX: [i8; 64] = [ + 1, // Comment + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, +]; diff --git a/tests/target/assignment.rs b/tests/target/assignment.rs new file mode 100644 index 000000000000..1a70d84813d0 --- /dev/null +++ b/tests/target/assignment.rs @@ -0,0 +1,39 @@ +// Test assignment + +fn main() { + let some_var: Type; + + let mut mutable; + + let variable = + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA::BBBBBBBBBBBBBBBBBBBBBB::CCCCCCCCCCCCCCCCCCCCCC::EEEEEE; + + variable = + LOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONG; + + let single_line_fit = DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD; + + single_line_fit = 5; + single_lit_fit >>= 10; + + // #2791 + let x = 2; +} + +fn break_meee() { + { + ( + block_start, + block_size, + margin_block_start, + margin_block_end, + ) = match (block_start, block_end, block_size) { + x => 1, + _ => 2, + }; + } +} + +// #2018 +pub const EXPLAIN_UNSIZED_TUPLE_COERCION: &'static str = + "Unsized tuple coercion is not stable enough for use and is subject to change"; diff --git a/tests/target/associated-items.rs b/tests/target/associated-items.rs new file mode 100644 index 000000000000..1b0a828d0291 --- /dev/null +++ b/tests/target/associated-items.rs @@ -0,0 +1,3 @@ +fn main() { + println!("{}", ::default()); +} diff --git a/tests/target/associated-types-bounds-wrapping.rs b/tests/target/associated-types-bounds-wrapping.rs new file mode 100644 index 000000000000..8aaeee3b16c7 --- /dev/null +++ b/tests/target/associated-types-bounds-wrapping.rs @@ -0,0 +1,6 @@ +// Test proper wrapping of long associated type bounds + +pub trait HttpService { + type WsService: 'static + + Service; +} diff --git a/tests/target/associated_type_bounds.rs b/tests/target/associated_type_bounds.rs new file mode 100644 index 000000000000..2dcbd65f86db --- /dev/null +++ b/tests/target/associated_type_bounds.rs @@ -0,0 +1,13 @@ +// See #3657 - https://github.com/rust-lang/rustfmt/issues/3657 + +#![feature(associated_type_bounds)] + +fn f>() {} + +fn g>() {} + +fn h>() {} + +fn i>() {} + +fn j>() {} diff --git a/tests/target/associated_type_defaults.rs b/tests/target/associated_type_defaults.rs new file mode 100644 index 000000000000..d0a08133725a --- /dev/null +++ b/tests/target/associated_type_defaults.rs @@ -0,0 +1,4 @@ +#![feature(associated_type_defaults)] +trait Foo { + type Bar = (); +} diff --git a/tests/target/async_block.rs b/tests/target/async_block.rs new file mode 100644 index 000000000000..137d849c9ee0 --- /dev/null +++ b/tests/target/async_block.rs @@ -0,0 +1,35 @@ +// rustfmt-edition: 2018 + +fn main() { + let x = async { Ok(()) }; +} + +fn baz() { + // test + let x = async { + // async blocks are great + Ok(()) + }; + + let y = async { Ok(()) }; // comment + + spawn(a, async move { + action(); + Ok(()) + }); + + spawn(a, async move || { + action(); + Ok(()) + }); + + spawn(a, static async || { + action(); + Ok(()) + }); + + spawn(a, static async move || { + action(); + Ok(()) + }); +} diff --git a/tests/target/async_closure.rs b/tests/target/async_closure.rs new file mode 100644 index 000000000000..9364e7dcc67d --- /dev/null +++ b/tests/target/async_closure.rs @@ -0,0 +1,22 @@ +// rustfmt-edition: 2018 + +fn main() { + let async_closure = async { + let x = 3; + x + }; + + let f = async /* comment */ { + let x = 3; + x + }; + + let g = async /* comment */ move { + let x = 3; + x + }; + + let f = |x| async { + println!("hello, world"); + }; +} diff --git a/tests/target/async_fn.rs b/tests/target/async_fn.rs new file mode 100644 index 000000000000..ac151dddb25e --- /dev/null +++ b/tests/target/async_fn.rs @@ -0,0 +1,24 @@ +// rustfmt-edition: 2018 + +async fn bar() -> Result<(), ()> { + Ok(()) +} + +pub async fn baz() -> Result<(), ()> { + Ok(()) +} + +async unsafe fn foo() { + async move { Ok(()) } +} + +async unsafe fn rust() { + async move { + // comment + Ok(()) + } +} + +async fn await_try() { + something.await?; +} diff --git a/tests/target/attrib-block-expr.rs b/tests/target/attrib-block-expr.rs new file mode 100644 index 000000000000..1e9557dc039e --- /dev/null +++ b/tests/target/attrib-block-expr.rs @@ -0,0 +1,58 @@ +fn issue_2073() { + let x = { + #![my_attr] + do_something() + }; + + let x = #[my_attr] + { + do_something() + }; + + let x = #[my_attr] + {}; + + { + #![just_an_attribute] + }; + + let z = #[attr1] + #[attr2] + { + body() + }; + + x = |y| { + #![inner] + }; + + x = |y| #[outer] + {}; + + x = |y| { + //! ynot + }; + + x = |y| #[outer] + unsafe {}; + + let x = unsafe { + #![my_attr] + do_something() + }; + + let x = #[my_attr] + unsafe { + do_something() + }; + + // This is a dumb but possible case + let x = #[my_attr] + unsafe {}; + + x = |y| #[outer] + #[outer2] + unsafe { + //! Comment + }; +} diff --git a/tests/target/attrib-extern-crate.rs b/tests/target/attrib-extern-crate.rs new file mode 100644 index 000000000000..ed64a0aeb071 --- /dev/null +++ b/tests/target/attrib-extern-crate.rs @@ -0,0 +1,17 @@ +// Attributes on extern crate. + +#[Attr1] +extern crate Bar; +#[Attr2] +#[Attr2] +extern crate Baz; +extern crate Foo; + +fn foo() { + #[Attr1] + extern crate Bar; + #[Attr2] + #[Attr2] + extern crate Baz; + extern crate Foo; +} diff --git a/tests/target/attrib.rs b/tests/target/attrib.rs new file mode 100644 index 000000000000..7e61f68d76a1 --- /dev/null +++ b/tests/target/attrib.rs @@ -0,0 +1,271 @@ +// rustfmt-wrap_comments: true +// Test attributes and doc comments are preserved. +#![doc( + html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", + html_favicon_url = "https://doc.rust-lang.org/favicon.ico", + html_root_url = "https://doc.rust-lang.org/nightly/", + html_playground_url = "https://play.rust-lang.org/", + test(attr(deny(warnings))) +)] + +//! Doc comment + +#![attribute] + +//! Crate doc comment + +// Comment + +// Comment on attribute +#![the(attribute)] + +// Another comment + +/// Blah blah blah. +/// Blah blah blah. +/// Blah blah blah. +/// Blah blah blah. + +/// Blah blah blah. +impl Bar { + /// Blah blah blooo. + /// Blah blah blooo. + /// Blah blah blooo. + /// Blah blah blooo. + #[an_attribute] + #[doc = "an attribute that shouldn't be normalized to a doc comment"] + fn foo(&mut self) -> isize {} + + /// Blah blah bing. + /// Blah blah bing. + /// Blah blah bing. + + /// Blah blah bing. + /// Blah blah bing. + /// Blah blah bing. + pub fn f2(self) { + (foo, bar) + } + + #[another_attribute] + fn f3(self) -> Dog {} + + /// Blah blah bing. + + #[attrib1] + /// Blah blah bing. + #[attrib2] + // Another comment that needs rewrite because it's tooooooooooooooooooooooooooooooo + // loooooooooooong. + /// Blah blah bing. + fn f4(self) -> Cat {} + + // We want spaces around `=` + #[cfg(feature = "nightly")] + fn f5(self) -> Monkey {} +} + +// #984 +struct Foo { + #[derive(Clone, PartialEq, Debug, Deserialize, Serialize)] + foo: usize, +} + +// #1668 + +/// Default path (*nix) +#[cfg(all( + unix, + not(target_os = "macos"), + not(target_os = "ios"), + not(target_os = "android") +))] +fn foo() { + #[cfg(target_os = "freertos")] + match port_id { + 'a' | 'A' => GpioPort { + port_address: GPIO_A, + }, + 'b' | 'B' => GpioPort { + port_address: GPIO_B, + }, + _ => panic!(), + } + + #[cfg_attr(not(target_os = "freertos"), allow(unused_variables))] + let x = 3; +} + +// #1777 +#[test] +#[should_panic(expected = "(")] +#[should_panic(expected = /* ( */ "(")] +#[should_panic(/* ((((( */expected /* ((((( */= /* ((((( */ "("/* ((((( */)] +#[should_panic( + /* (((((((( *//* + (((((((((()(((((((( */ + expected = "(" + // (((((((( +)] +fn foo() {} + +// #1799 +fn issue_1799() { + #[allow(unreachable_code)] // https://github.com/rust-lang/rust/issues/43336 + Some(Err(error)); + + #[allow(unreachable_code)] + // https://github.com/rust-lang/rust/issues/43336 + Some(Err(error)); +} + +// Formatting inner attributes +fn inner_attributes() { + #![this_is_an_inner_attribute(foo)] + + foo(); +} + +impl InnerAttributes() { + #![this_is_an_inner_attribute(foo)] + + fn foo() {} +} + +mod InnerAttributes { + #![this_is_an_inner_attribute(foo)] +} + +fn attributes_on_statements() { + // Local + #[attr(on(local))] + let x = 3; + + // Item + #[attr(on(item))] + use foo; + + // Expr + #[attr(on(expr))] + {} + + // Semi + #[attr(on(semi))] + foo(); + + // Mac + #[attr(on(mac))] + foo!(); +} + +// Large derives +#[derive( + Add, Sub, Mul, Div, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Debug, Hash, Serialize, Mul, +)] + +/// Foo bar baz + +#[derive( + Add, + Sub, + Mul, + Div, + Clone, + Copy, + Eq, + PartialEq, + Ord, + PartialOrd, + Debug, + Hash, + Serialize, + Deserialize, +)] +pub struct HP(pub u8); + +// Long `#[doc = "..."]` +struct A { + #[doc = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"] + b: i32, +} + +// #2647 +#[cfg( + feature = "this_line_is_101_characters_long_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +)] +pub fn foo() {} + +// path attrs +#[clippy::bar] +#[clippy::bar(a, b, c)] +pub fn foo() {} + +mod issue_2620 { + #[derive(Debug, StructOpt)] + #[structopt(about = "Display information about the character on FF Logs")] + pub struct Params { + #[structopt(help = "The server the character is on")] + server: String, + #[structopt(help = "The character's first name")] + first_name: String, + #[structopt(help = "The character's last name")] + last_name: String, + #[structopt( + short = "j", + long = "job", + help = "The job to look at", + parse(try_from_str) + )] + job: Option, + } +} + +// #2969 +#[cfg(not(all( + feature = "std", + any( + target_os = "linux", + target_os = "android", + target_os = "netbsd", + target_os = "dragonfly", + target_os = "haiku", + target_os = "emscripten", + target_os = "solaris", + target_os = "cloudabi", + target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "openbsd", + target_os = "redox", + target_os = "fuchsia", + windows, + all(target_arch = "wasm32", feature = "stdweb"), + all(target_arch = "wasm32", feature = "wasm-bindgen"), + ) +)))] +type Os = NoSource; + +// #3313 +fn stmt_expr_attributes() { + let foo; + #[must_use] + foo = false; +} + +// #3509 +fn issue3509() { + match MyEnum { + MyEnum::Option1 if cfg!(target_os = "windows") => + #[cfg(target_os = "windows")] + { + 1 + } + } + match MyEnum { + MyEnum::Option1 if cfg!(target_os = "windows") => + { + #[cfg(target_os = "windows")] + 1 + } + } +} diff --git a/tests/target/big-impl-block.rs b/tests/target/big-impl-block.rs new file mode 100644 index 000000000000..e3728caba3ff --- /dev/null +++ b/tests/target/big-impl-block.rs @@ -0,0 +1,82 @@ +// #1357 +impl<'a, Select, From, Distinct, Where, Order, Limit, Offset, Groupby, DB> InternalBoxedDsl<'a, DB> + for SelectStatement +where + DB: Backend, + Select: QueryFragment + SelectableExpression + 'a, + Distinct: QueryFragment + 'a, + Where: Into + 'a>>>, + Order: QueryFragment + 'a, + Limit: QueryFragment + 'a, + Offset: QueryFragment + 'a, +{ + type Output = BoxedSelectStatement<'a, Select::SqlTypeForSelect, From, DB>; + + fn internal_into_boxed(self) -> Self::Output { + BoxedSelectStatement::new( + Box::new(self.select), + self.from, + Box::new(self.distinct), + self.where_clause.into(), + Box::new(self.order), + Box::new(self.limit), + Box::new(self.offset), + ) + } +} + +// #1369 +impl Foo + for Bar +{ + fn foo() {} +} +impl Foo + for Bar +{ + fn foo() {} +} +impl + Foo + for Bar +{ + fn foo() {} +} +impl Foo + for Bar< + ExcessivelyLongGenericName, + ExcessivelyLongGenericName, + AnotherExcessivelyLongGenericName, + > +{ + fn foo() {} +} +impl Foo + for Bar< + ExcessivelyLongGenericName, + ExcessivelyLongGenericName, + AnotherExcessivelyLongGenericName, + > +{ + fn foo() {} +} +impl + Foo + for Bar< + ExcessivelyLongGenericName, + ExcessivelyLongGenericName, + AnotherExcessivelyLongGenericName, + > +{ + fn foo() {} +} + +// #1689 +impl SubSelectDirect +where + M: select::Selector, + S: event::Stream, + F: for<'t> FnMut(transform::Api<'t, Stream>>) -> transform::Api<'t, X>, + X: event::Stream, +{ +} diff --git a/tests/target/big-impl-visual.rs b/tests/target/big-impl-visual.rs new file mode 100644 index 000000000000..04b0a83fd97e --- /dev/null +++ b/tests/target/big-impl-visual.rs @@ -0,0 +1,65 @@ +// rustfmt-indent_style: Visual + +// #1357 +impl<'a, Select, From, Distinct, Where, Order, Limit, Offset, Groupby, DB> InternalBoxedDsl<'a, DB> + for SelectStatement + where DB: Backend, + Select: QueryFragment + SelectableExpression + 'a, + Distinct: QueryFragment + 'a, + Where: Into + 'a>>>, + Order: QueryFragment + 'a, + Limit: QueryFragment + 'a, + Offset: QueryFragment + 'a +{ + type Output = BoxedSelectStatement<'a, Select::SqlTypeForSelect, From, DB>; + + fn internal_into_boxed(self) -> Self::Output { + BoxedSelectStatement::new(Box::new(self.select), + self.from, + Box::new(self.distinct), + self.where_clause.into(), + Box::new(self.order), + Box::new(self.limit), + Box::new(self.offset)) + } +} + +// #1369 +impl Foo + for Bar +{ + fn foo() {} +} +impl Foo + for Bar +{ + fn foo() {} +} +impl + Foo + for Bar +{ + fn foo() {} +} +impl Foo + for Bar +{ + fn foo() {} +} +impl Foo + for Bar +{ + fn foo() {} +} +impl + Foo + for Bar +{ + fn foo() {} +} diff --git a/tests/target/binary-expr.rs b/tests/target/binary-expr.rs new file mode 100644 index 000000000000..93115b282caf --- /dev/null +++ b/tests/target/binary-expr.rs @@ -0,0 +1,16 @@ +// Binary expressions + +fn foo() { + // 100 + let x = aaaaaaaaaa || bbbbbbbbbb || cccccccccc || dddddddddd && eeeeeeeeee || ffffffffff || ggg; + // 101 + let x = + aaaaaaaaaa || bbbbbbbbbb || cccccccccc || dddddddddd && eeeeeeeeee || ffffffffff || gggg; + // 104 + let x = aaaaaaaaaa + || bbbbbbbbbb + || cccccccccc + || dddddddddd && eeeeeeeeee + || ffffffffff + || gggggggg; +} diff --git a/tests/target/binop-separator-back/bitwise.rs b/tests/target/binop-separator-back/bitwise.rs new file mode 100644 index 000000000000..ce32c05ef703 --- /dev/null +++ b/tests/target/binop-separator-back/bitwise.rs @@ -0,0 +1,18 @@ +// rustfmt-binop_separator: Back + +fn main() { + let value = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ^ + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ; + + let value = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ & + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ; + + let value = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ | + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ; + + let value = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ << + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ; + + let value = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ >> + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ; +} diff --git a/tests/target/binop-separator-back/comp.rs b/tests/target/binop-separator-back/comp.rs new file mode 100644 index 000000000000..efd837bcfe38 --- /dev/null +++ b/tests/target/binop-separator-back/comp.rs @@ -0,0 +1,33 @@ +// rustfmt-binop_separator: Back + +fn main() { + if abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ < + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + { + // + } + + if abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ <= + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + { + // + } + + if abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ > + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + { + // + } + + if abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ >= + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + { + // + } + + if abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ == + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + { + // + } +} diff --git a/tests/target/binop-separator-back/logic.rs b/tests/target/binop-separator-back/logic.rs new file mode 100644 index 000000000000..5f69fd5f55e4 --- /dev/null +++ b/tests/target/binop-separator-back/logic.rs @@ -0,0 +1,10 @@ +// rustfmt-binop_separator: Back + +fn main() { + if abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ && + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ || + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + { + // + } +} diff --git a/tests/target/binop-separator-back/math.rs b/tests/target/binop-separator-back/math.rs new file mode 100644 index 000000000000..7a3f27e733b2 --- /dev/null +++ b/tests/target/binop-separator-back/math.rs @@ -0,0 +1,23 @@ +// rustfmt-binop_separator: Back + +fn main() { + let value = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ; + + let value = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ * + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ - + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ / + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ * + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ * + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ / + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ / + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ; +} diff --git a/tests/target/binop-separator-back/patterns.rs b/tests/target/binop-separator-back/patterns.rs new file mode 100644 index 000000000000..2e59713526a0 --- /dev/null +++ b/tests/target/binop-separator-back/patterns.rs @@ -0,0 +1,11 @@ +// rustfmt-binop_separator: Back + +fn main() { + match val { + ThisIsA::ReallyLongPatternNameToHelpOverflowTheNextValueOntoTheNextLine | + ThisIsA::SecondValueSeparatedByAPipe | + ThisIsA::ThirdValueSeparatedByAPipe => { + // + } + } +} diff --git a/tests/target/binop-separator-back/range.rs b/tests/target/binop-separator-back/range.rs new file mode 100644 index 000000000000..19e5a81cd9cd --- /dev/null +++ b/tests/target/binop-separator-back/range.rs @@ -0,0 +1,9 @@ +// rustfmt-binop_separator: Back + +fn main() { + let value = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.. + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ; + + let value = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ..= + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ; +} diff --git a/tests/target/break-and-continue.rs b/tests/target/break-and-continue.rs new file mode 100644 index 000000000000..c01d8a078455 --- /dev/null +++ b/tests/target/break-and-continue.rs @@ -0,0 +1,23 @@ +// break and continue formatting + +#![feature(loop_break_value)] + +fn main() { + 'a: loop { + break 'a; + } + + let mut done = false; + 'b: while !done { + done = true; + continue 'b; + } + + let x = loop { + break 5; + }; + + let x = 'c: loop { + break 'c 5; + }; +} diff --git a/tests/target/catch.rs b/tests/target/catch.rs new file mode 100644 index 000000000000..ffe694f8e725 --- /dev/null +++ b/tests/target/catch.rs @@ -0,0 +1,22 @@ +// rustfmt-edition: 2018 +#![feature(try_blocks)] + +fn main() { + let x = try { foo()? }; + + let x = try /* Invisible comment */ { foo()? }; + + let x = try { unsafe { foo()? } }; + + let y = match (try { foo()? }) { + _ => (), + }; + + try { + foo()?; + }; + + try { + // Regular try block + }; +} diff --git a/tests/target/cfg_if/detect/arch/aarch64.rs b/tests/target/cfg_if/detect/arch/aarch64.rs new file mode 100644 index 000000000000..91c51ed89e6f --- /dev/null +++ b/tests/target/cfg_if/detect/arch/aarch64.rs @@ -0,0 +1,98 @@ +//! Aarch64 run-time features. + +/// Checks if `aarch64` feature is enabled. +#[macro_export] +#[unstable(feature = "stdsimd", issue = "27731")] +#[allow_internal_unstable(stdsimd_internal, stdsimd)] +macro_rules! is_aarch64_feature_detected { + ("neon") => { + // FIXME: this should be removed once we rename Aarch64 neon to asimd + cfg!(target_feature = "neon") || $crate::detect::check_for($crate::detect::Feature::asimd) + }; + ("asimd") => { + cfg!(target_feature = "neon") || $crate::detect::check_for($crate::detect::Feature::asimd) + }; + ("pmull") => { + cfg!(target_feature = "pmull") || $crate::detect::check_for($crate::detect::Feature::pmull) + }; + ("fp") => { + cfg!(target_feature = "fp") || $crate::detect::check_for($crate::detect::Feature::fp) + }; + ("fp16") => { + cfg!(target_feature = "fp16") || $crate::detect::check_for($crate::detect::Feature::fp16) + }; + ("sve") => { + cfg!(target_feature = "sve") || $crate::detect::check_for($crate::detect::Feature::sve) + }; + ("crc") => { + cfg!(target_feature = "crc") || $crate::detect::check_for($crate::detect::Feature::crc) + }; + ("crypto") => { + cfg!(target_feature = "crypto") + || $crate::detect::check_for($crate::detect::Feature::crypto) + }; + ("lse") => { + cfg!(target_feature = "lse") || $crate::detect::check_for($crate::detect::Feature::lse) + }; + ("rdm") => { + cfg!(target_feature = "rdm") || $crate::detect::check_for($crate::detect::Feature::rdm) + }; + ("rcpc") => { + cfg!(target_feature = "rcpc") || $crate::detect::check_for($crate::detect::Feature::rcpc) + }; + ("dotprod") => { + cfg!(target_feature = "dotprod") + || $crate::detect::check_for($crate::detect::Feature::dotprod) + }; + ("ras") => { + compile_error!("\"ras\" feature cannot be detected at run-time") + }; + ("v8.1a") => { + compile_error!("\"v8.1a\" feature cannot be detected at run-time") + }; + ("v8.2a") => { + compile_error!("\"v8.2a\" feature cannot be detected at run-time") + }; + ("v8.3a") => { + compile_error!("\"v8.3a\" feature cannot be detected at run-time") + }; + ($t:tt,) => { + is_aarch64_feature_detected!($t); + }; + ($t:tt) => { + compile_error!(concat!("unknown aarch64 target feature: ", $t)) + }; +} + +/// ARM Aarch64 CPU Feature enum. Each variant denotes a position in a bitset +/// for a particular feature. +/// +/// PLEASE: do not use this, it is an implementation detail subject to change. +#[doc(hidden)] +#[allow(non_camel_case_types)] +#[repr(u8)] +#[unstable(feature = "stdsimd_internal", issue = "0")] +pub enum Feature { + /// ARM Advanced SIMD (ASIMD) + asimd, + /// Polynomial Multiply + pmull, + /// Floating point support + fp, + /// Half-float support. + fp16, + /// Scalable Vector Extension (SVE) + sve, + /// CRC32 (Cyclic Redundancy Check) + crc, + /// Crypto: AES + PMULL + SHA1 + SHA2 + crypto, + /// Atomics (Large System Extension) + lse, + /// Rounding Double Multiply (ASIMDRDM) + rdm, + /// Release consistent Processor consistent (RcPc) + rcpc, + /// Vector Dot-Product (ASIMDDP) + dotprod, +} diff --git a/tests/target/cfg_if/detect/arch/arm.rs b/tests/target/cfg_if/detect/arch/arm.rs new file mode 100644 index 000000000000..90c61fed8acc --- /dev/null +++ b/tests/target/cfg_if/detect/arch/arm.rs @@ -0,0 +1,47 @@ +//! Run-time feature detection on ARM Aarch32. + +/// Checks if `arm` feature is enabled. +#[macro_export] +#[unstable(feature = "stdsimd", issue = "27731")] +#[allow_internal_unstable(stdsimd_internal, stdsimd)] +macro_rules! is_arm_feature_detected { + ("neon") => { + cfg!(target_feature = "neon") || $crate::detect::check_for($crate::detect::Feature::neon) + }; + ("pmull") => { + cfg!(target_feature = "pmull") || $crate::detect::check_for($crate::detect::Feature::pmull) + }; + ("v7") => { + compile_error!("\"v7\" feature cannot be detected at run-time") + }; + ("vfp2") => { + compile_error!("\"vfp2\" feature cannot be detected at run-time") + }; + ("vfp3") => { + compile_error!("\"vfp3\" feature cannot be detected at run-time") + }; + ("vfp4") => { + compile_error!("\"vfp4\" feature cannot be detected at run-time") + }; + ($t:tt,) => { + is_arm_feature_detected!($t); + }; + ($t:tt) => { + compile_error!(concat!("unknown arm target feature: ", $t)) + }; +} + +/// ARM CPU Feature enum. Each variant denotes a position in a bitset for a +/// particular feature. +/// +/// PLEASE: do not use this, it is an implementation detail subject to change. +#[doc(hidden)] +#[allow(non_camel_case_types)] +#[repr(u8)] +#[unstable(feature = "stdsimd_internal", issue = "0")] +pub enum Feature { + /// ARM Advanced SIMD (NEON) - Aarch32 + neon, + /// Polynomial Multiply + pmull, +} diff --git a/tests/target/cfg_if/detect/arch/mips.rs b/tests/target/cfg_if/detect/arch/mips.rs new file mode 100644 index 000000000000..2397a090602c --- /dev/null +++ b/tests/target/cfg_if/detect/arch/mips.rs @@ -0,0 +1,30 @@ +//! Run-time feature detection on MIPS. + +/// Checks if `mips` feature is enabled. +#[macro_export] +#[unstable(feature = "stdsimd", issue = "27731")] +#[allow_internal_unstable(stdsimd_internal, stdsimd)] +macro_rules! is_mips_feature_detected { + ("msa") => { + cfg!(target_feature = "msa") || $crate::detect::check_for($crate::detect::Feature::msa) + }; + ($t:tt,) => { + is_mips_feature_detected!($t); + }; + ($t:tt) => { + compile_error!(concat!("unknown mips target feature: ", $t)) + }; +} + +/// MIPS CPU Feature enum. Each variant denotes a position in a bitset for a +/// particular feature. +/// +/// PLEASE: do not use this, it is an implementation detail subject to change. +#[doc(hidden)] +#[allow(non_camel_case_types)] +#[repr(u8)] +#[unstable(feature = "stdsimd_internal", issue = "0")] +pub enum Feature { + /// MIPS SIMD Architecture (MSA) + msa, +} diff --git a/tests/target/cfg_if/detect/arch/mips64.rs b/tests/target/cfg_if/detect/arch/mips64.rs new file mode 100644 index 000000000000..d378defc5dfd --- /dev/null +++ b/tests/target/cfg_if/detect/arch/mips64.rs @@ -0,0 +1,30 @@ +//! Run-time feature detection on MIPS64. + +/// Checks if `mips64` feature is enabled. +#[macro_export] +#[unstable(feature = "stdsimd", issue = "27731")] +#[allow_internal_unstable(stdsimd_internal, stdsimd)] +macro_rules! is_mips64_feature_detected { + ("msa") => { + cfg!(target_feature = "msa") || $crate::detect::check_for($crate::detect::Feature::msa) + }; + ($t:tt,) => { + is_mips64_feature_detected!($t); + }; + ($t:tt) => { + compile_error!(concat!("unknown mips64 target feature: ", $t)) + }; +} + +/// MIPS64 CPU Feature enum. Each variant denotes a position in a bitset +/// for a particular feature. +/// +/// PLEASE: do not use this, it is an implementation detail subject to change. +#[doc(hidden)] +#[allow(non_camel_case_types)] +#[repr(u8)] +#[unstable(feature = "stdsimd_internal", issue = "0")] +pub enum Feature { + /// MIPS SIMD Architecture (MSA) + msa, +} diff --git a/tests/target/cfg_if/detect/arch/powerpc.rs b/tests/target/cfg_if/detect/arch/powerpc.rs new file mode 100644 index 000000000000..e7a9daac6887 --- /dev/null +++ b/tests/target/cfg_if/detect/arch/powerpc.rs @@ -0,0 +1,42 @@ +//! Run-time feature detection on PowerPC. + +/// Checks if `powerpc` feature is enabled. +#[macro_export] +#[unstable(feature = "stdsimd", issue = "27731")] +#[allow_internal_unstable(stdsimd_internal, stdsimd)] +macro_rules! is_powerpc_feature_detected { + ("altivec") => { + cfg!(target_feature = "altivec") + || $crate::detect::check_for($crate::detect::Feature::altivec) + }; + ("vsx") => { + cfg!(target_feature = "vsx") || $crate::detect::check_for($crate::detect::Feature::vsx) + }; + ("power8") => { + cfg!(target_feature = "power8") + || $crate::detect::check_for($crate::detect::Feature::power8) + }; + ($t:tt,) => { + is_powerpc_feature_detected!($t); + }; + ($t:tt) => { + compile_error!(concat!("unknown powerpc target feature: ", $t)) + }; +} + +/// PowerPC CPU Feature enum. Each variant denotes a position in a bitset +/// for a particular feature. +/// +/// PLEASE: do not use this, it is an implementation detail subject to change. +#[doc(hidden)] +#[allow(non_camel_case_types)] +#[repr(u8)] +#[unstable(feature = "stdsimd_internal", issue = "0")] +pub enum Feature { + /// Altivec + altivec, + /// VSX + vsx, + /// Power8 + power8, +} diff --git a/tests/target/cfg_if/detect/arch/powerpc64.rs b/tests/target/cfg_if/detect/arch/powerpc64.rs new file mode 100644 index 000000000000..c102202695ec --- /dev/null +++ b/tests/target/cfg_if/detect/arch/powerpc64.rs @@ -0,0 +1,42 @@ +//! Run-time feature detection on PowerPC64. + +/// Checks if `powerpc64` feature is enabled. +#[macro_export] +#[unstable(feature = "stdsimd", issue = "27731")] +#[allow_internal_unstable(stdsimd_internal, stdsimd)] +macro_rules! is_powerpc64_feature_detected { + ("altivec") => { + cfg!(target_feature = "altivec") + || $crate::detect::check_for($crate::detect::Feature::altivec) + }; + ("vsx") => { + cfg!(target_feature = "vsx") || $crate::detect::check_for($crate::detect::Feature::vsx) + }; + ("power8") => { + cfg!(target_feature = "power8") + || $crate::detect::check_for($crate::detect::Feature::power8) + }; + ($t:tt,) => { + is_powerpc64_feature_detected!($t); + }; + ($t:tt) => { + compile_error!(concat!("unknown powerpc64 target feature: ", $t)) + }; +} + +/// PowerPC64 CPU Feature enum. Each variant denotes a position in a bitset +/// for a particular feature. +/// +/// PLEASE: do not use this, it is an implementation detail subject to change. +#[doc(hidden)] +#[allow(non_camel_case_types)] +#[repr(u8)] +#[unstable(feature = "stdsimd_internal", issue = "0")] +pub enum Feature { + /// Altivec + altivec, + /// VSX + vsx, + /// Power8 + power8, +} diff --git a/tests/target/cfg_if/detect/arch/x86.rs b/tests/target/cfg_if/detect/arch/x86.rs new file mode 100644 index 000000000000..47210cae2aaa --- /dev/null +++ b/tests/target/cfg_if/detect/arch/x86.rs @@ -0,0 +1,333 @@ +//! This module implements minimal run-time feature detection for x86. +//! +//! The features are detected using the `detect_features` function below. +//! This function uses the CPUID instruction to read the feature flags from the +//! CPU and encodes them in a `usize` where each bit position represents +//! whether a feature is available (bit is set) or unavailable (bit is cleared). +//! +//! The enum `Feature` is used to map bit positions to feature names, and the +//! the `__crate::detect::check_for!` macro is used to map string literals (e.g., +//! "avx") to these bit positions (e.g., `Feature::avx`). +//! +//! The run-time feature detection is performed by the +//! `__crate::detect::check_for(Feature) -> bool` function. On its first call, +//! this functions queries the CPU for the available features and stores them +//! in a global `AtomicUsize` variable. The query is performed by just checking +//! whether the feature bit in this global variable is set or cleared. + +/// A macro to test at *runtime* whether a CPU feature is available on +/// x86/x86-64 platforms. +/// +/// This macro is provided in the standard library and will detect at runtime +/// whether the specified CPU feature is detected. This does **not** resolve at +/// compile time unless the specified feature is already enabled for the entire +/// crate. Runtime detection currently relies mostly on the `cpuid` instruction. +/// +/// This macro only takes one argument which is a string literal of the feature +/// being tested for. The feature names supported are the lowercase versions of +/// the ones defined by Intel in [their documentation][docs]. +/// +/// ## Supported arguments +/// +/// This macro supports the same names that `#[target_feature]` supports. Unlike +/// `#[target_feature]`, however, this macro does not support names separated +/// with a comma. Instead testing for multiple features must be done through +/// separate macro invocations for now. +/// +/// Supported arguments are: +/// +/// * `"aes"` +/// * `"pclmulqdq"` +/// * `"rdrand"` +/// * `"rdseed"` +/// * `"tsc"` +/// * `"mmx"` +/// * `"sse"` +/// * `"sse2"` +/// * `"sse3"` +/// * `"ssse3"` +/// * `"sse4.1"` +/// * `"sse4.2"` +/// * `"sse4a"` +/// * `"sha"` +/// * `"avx"` +/// * `"avx2"` +/// * `"avx512f"` +/// * `"avx512cd"` +/// * `"avx512er"` +/// * `"avx512pf"` +/// * `"avx512bw"` +/// * `"avx512dq"` +/// * `"avx512vl"` +/// * `"avx512ifma"` +/// * `"avx512vbmi"` +/// * `"avx512vpopcntdq"` +/// * `"f16c"` +/// * `"fma"` +/// * `"bmi1"` +/// * `"bmi2"` +/// * `"abm"` +/// * `"lzcnt"` +/// * `"tbm"` +/// * `"popcnt"` +/// * `"fxsr"` +/// * `"xsave"` +/// * `"xsaveopt"` +/// * `"xsaves"` +/// * `"xsavec"` +/// * `"adx"` +/// * `"rtm"` +/// +/// [docs]: https://software.intel.com/sites/landingpage/IntrinsicsGuide +#[macro_export] +#[stable(feature = "simd_x86", since = "1.27.0")] +#[allow_internal_unstable(stdsimd_internal, stdsimd)] +macro_rules! is_x86_feature_detected { + ("aes") => { + cfg!(target_feature = "aes") || $crate::detect::check_for($crate::detect::Feature::aes) + }; + ("pclmulqdq") => { + cfg!(target_feature = "pclmulqdq") + || $crate::detect::check_for($crate::detect::Feature::pclmulqdq) + }; + ("rdrand") => { + cfg!(target_feature = "rdrand") + || $crate::detect::check_for($crate::detect::Feature::rdrand) + }; + ("rdseed") => { + cfg!(target_feature = "rdseed") + || $crate::detect::check_for($crate::detect::Feature::rdseed) + }; + ("tsc") => { + cfg!(target_feature = "tsc") || $crate::detect::check_for($crate::detect::Feature::tsc) + }; + ("mmx") => { + cfg!(target_feature = "mmx") || $crate::detect::check_for($crate::detect::Feature::mmx) + }; + ("sse") => { + cfg!(target_feature = "sse") || $crate::detect::check_for($crate::detect::Feature::sse) + }; + ("sse2") => { + cfg!(target_feature = "sse2") || $crate::detect::check_for($crate::detect::Feature::sse2) + }; + ("sse3") => { + cfg!(target_feature = "sse3") || $crate::detect::check_for($crate::detect::Feature::sse3) + }; + ("ssse3") => { + cfg!(target_feature = "ssse3") || $crate::detect::check_for($crate::detect::Feature::ssse3) + }; + ("sse4.1") => { + cfg!(target_feature = "sse4.1") + || $crate::detect::check_for($crate::detect::Feature::sse4_1) + }; + ("sse4.2") => { + cfg!(target_feature = "sse4.2") + || $crate::detect::check_for($crate::detect::Feature::sse4_2) + }; + ("sse4a") => { + cfg!(target_feature = "sse4a") || $crate::detect::check_for($crate::detect::Feature::sse4a) + }; + ("sha") => { + cfg!(target_feature = "sha") || $crate::detect::check_for($crate::detect::Feature::sha) + }; + ("avx") => { + cfg!(target_feature = "avx") || $crate::detect::check_for($crate::detect::Feature::avx) + }; + ("avx2") => { + cfg!(target_feature = "avx2") || $crate::detect::check_for($crate::detect::Feature::avx2) + }; + ("avx512f") => { + cfg!(target_feature = "avx512f") + || $crate::detect::check_for($crate::detect::Feature::avx512f) + }; + ("avx512cd") => { + cfg!(target_feature = "avx512cd") + || $crate::detect::check_for($crate::detect::Feature::avx512cd) + }; + ("avx512er") => { + cfg!(target_feature = "avx512er") + || $crate::detect::check_for($crate::detect::Feature::avx512er) + }; + ("avx512pf") => { + cfg!(target_feature = "avx512pf") + || $crate::detect::check_for($crate::detect::Feature::avx512pf) + }; + ("avx512bw") => { + cfg!(target_feature = "avx512bw") + || $crate::detect::check_for($crate::detect::Feature::avx512bw) + }; + ("avx512dq") => { + cfg!(target_feature = "avx512dq") + || $crate::detect::check_for($crate::detect::Feature::avx512dq) + }; + ("avx512vl") => { + cfg!(target_Feature = "avx512vl") + || $crate::detect::check_for($crate::detect::Feature::avx512vl) + }; + ("avx512ifma") => { + cfg!(target_feature = "avx512ifma") + || $crate::detect::check_for($crate::detect::Feature::avx512_ifma) + }; + ("avx512vbmi") => { + cfg!(target_feature = "avx512vbmi") + || $crate::detect::check_for($crate::detect::Feature::avx512_vbmi) + }; + ("avx512vpopcntdq") => { + cfg!(target_feature = "avx512vpopcntdq") + || $crate::detect::check_for($crate::detect::Feature::avx512_vpopcntdq) + }; + ("f16c") => { + cfg!(target_feature = "f16c") || $crate::detect::check_for($crate::detect::Feature::f16c) + }; + ("fma") => { + cfg!(target_feature = "fma") || $crate::detect::check_for($crate::detect::Feature::fma) + }; + ("bmi1") => { + cfg!(target_feature = "bmi1") || $crate::detect::check_for($crate::detect::Feature::bmi) + }; + ("bmi2") => { + cfg!(target_feature = "bmi2") || $crate::detect::check_for($crate::detect::Feature::bmi2) + }; + ("abm") => { + cfg!(target_feature = "abm") || $crate::detect::check_for($crate::detect::Feature::abm) + }; + ("lzcnt") => { + cfg!(target_feature = "lzcnt") || $crate::detect::check_for($crate::detect::Feature::abm) + }; + ("tbm") => { + cfg!(target_feature = "tbm") || $crate::detect::check_for($crate::detect::Feature::tbm) + }; + ("popcnt") => { + cfg!(target_feature = "popcnt") + || $crate::detect::check_for($crate::detect::Feature::popcnt) + }; + ("fxsr") => { + cfg!(target_feature = "fxsr") || $crate::detect::check_for($crate::detect::Feature::fxsr) + }; + ("xsave") => { + cfg!(target_feature = "xsave") || $crate::detect::check_for($crate::detect::Feature::xsave) + }; + ("xsaveopt") => { + cfg!(target_feature = "xsaveopt") + || $crate::detect::check_for($crate::detect::Feature::xsaveopt) + }; + ("xsaves") => { + cfg!(target_feature = "xsaves") + || $crate::detect::check_for($crate::detect::Feature::xsaves) + }; + ("xsavec") => { + cfg!(target_feature = "xsavec") + || $crate::detect::check_for($crate::detect::Feature::xsavec) + }; + ("cmpxchg16b") => { + cfg!(target_feature = "cmpxchg16b") + || $crate::detect::check_for($crate::detect::Feature::cmpxchg16b) + }; + ("adx") => { + cfg!(target_feature = "adx") || $crate::detect::check_for($crate::detect::Feature::adx) + }; + ("rtm") => { + cfg!(target_feature = "rtm") || $crate::detect::check_for($crate::detect::Feature::rtm) + }; + ($t:tt,) => { + is_x86_feature_detected!($t); + }; + ($t:tt) => { + compile_error!(concat!("unknown target feature: ", $t)) + }; +} + +/// X86 CPU Feature enum. Each variant denotes a position in a bitset for a +/// particular feature. +/// +/// This is an unstable implementation detail subject to change. +#[allow(non_camel_case_types)] +#[repr(u8)] +#[doc(hidden)] +#[unstable(feature = "stdsimd_internal", issue = "0")] +pub enum Feature { + /// AES (Advanced Encryption Standard New Instructions AES-NI) + aes, + /// CLMUL (Carry-less Multiplication) + pclmulqdq, + /// RDRAND + rdrand, + /// RDSEED + rdseed, + /// TSC (Time Stamp Counter) + tsc, + /// MMX + mmx, + /// SSE (Streaming SIMD Extensions) + sse, + /// SSE2 (Streaming SIMD Extensions 2) + sse2, + /// SSE3 (Streaming SIMD Extensions 3) + sse3, + /// SSSE3 (Supplemental Streaming SIMD Extensions 3) + ssse3, + /// SSE4.1 (Streaming SIMD Extensions 4.1) + sse4_1, + /// SSE4.2 (Streaming SIMD Extensions 4.2) + sse4_2, + /// SSE4a (Streaming SIMD Extensions 4a) + sse4a, + /// SHA + sha, + /// AVX (Advanced Vector Extensions) + avx, + /// AVX2 (Advanced Vector Extensions 2) + avx2, + /// AVX-512 F (Foundation) + avx512f, + /// AVX-512 CD (Conflict Detection Instructions) + avx512cd, + /// AVX-512 ER (Exponential and Reciprocal Instructions) + avx512er, + /// AVX-512 PF (Prefetch Instructions) + avx512pf, + /// AVX-512 BW (Byte and Word Instructions) + avx512bw, + /// AVX-512 DQ (Doubleword and Quadword) + avx512dq, + /// AVX-512 VL (Vector Length Extensions) + avx512vl, + /// AVX-512 IFMA (Integer Fused Multiply Add) + avx512_ifma, + /// AVX-512 VBMI (Vector Byte Manipulation Instructions) + avx512_vbmi, + /// AVX-512 VPOPCNTDQ (Vector Population Count Doubleword and + /// Quadword) + avx512_vpopcntdq, + /// F16C (Conversions between IEEE-754 `binary16` and `binary32` formats) + f16c, + /// FMA (Fused Multiply Add) + fma, + /// BMI1 (Bit Manipulation Instructions 1) + bmi, + /// BMI1 (Bit Manipulation Instructions 2) + bmi2, + /// ABM (Advanced Bit Manipulation) on AMD / LZCNT (Leading Zero + /// Count) on Intel + abm, + /// TBM (Trailing Bit Manipulation) + tbm, + /// POPCNT (Population Count) + popcnt, + /// FXSR (Floating-point context fast save and restore) + fxsr, + /// XSAVE (Save Processor Extended States) + xsave, + /// XSAVEOPT (Save Processor Extended States Optimized) + xsaveopt, + /// XSAVES (Save Processor Extended States Supervisor) + xsaves, + /// XSAVEC (Save Processor Extended States Compacted) + xsavec, + /// CMPXCH16B, a 16-byte compare-and-swap instruction + cmpxchg16b, + /// ADX, Intel ADX (Multi-Precision Add-Carry Instruction Extensions) + adx, + /// RTM, Intel (Restricted Transactional Memory) + rtm, +} diff --git a/tests/target/cfg_if/detect/bit.rs b/tests/target/cfg_if/detect/bit.rs new file mode 100644 index 000000000000..578f0b16b742 --- /dev/null +++ b/tests/target/cfg_if/detect/bit.rs @@ -0,0 +1,9 @@ +//! Bit manipulation utilities. + +/// Tests the `bit` of `x`. +#[allow(dead_code)] +#[inline] +pub(crate) fn test(x: usize, bit: u32) -> bool { + debug_assert!(bit < 32, "bit index out-of-bounds"); + x & (1 << bit) != 0 +} diff --git a/tests/target/cfg_if/detect/cache.rs b/tests/target/cfg_if/detect/cache.rs new file mode 100644 index 000000000000..92bc4b58d167 --- /dev/null +++ b/tests/target/cfg_if/detect/cache.rs @@ -0,0 +1,164 @@ +//! Caches run-time feature detection so that it only needs to be computed +//! once. + +#![allow(dead_code)] // not used on all platforms + +use crate::sync::atomic::Ordering; + +#[cfg(target_pointer_width = "64")] +use crate::sync::atomic::AtomicU64; + +#[cfg(target_pointer_width = "32")] +use crate::sync::atomic::AtomicU32; + +/// Sets the `bit` of `x`. +#[inline] +const fn set_bit(x: u64, bit: u32) -> u64 { + x | 1 << bit +} + +/// Tests the `bit` of `x`. +#[inline] +const fn test_bit(x: u64, bit: u32) -> bool { + x & (1 << bit) != 0 +} + +/// Maximum number of features that can be cached. +const CACHE_CAPACITY: u32 = 63; + +/// This type is used to initialize the cache +#[derive(Copy, Clone)] +pub(crate) struct Initializer(u64); + +#[allow(clippy::use_self)] +impl Default for Initializer { + fn default() -> Self { + Initializer(0) + } +} + +impl Initializer { + /// Tests the `bit` of the cache. + #[allow(dead_code)] + #[inline] + pub(crate) fn test(self, bit: u32) -> bool { + // FIXME: this way of making sure that the cache is large enough is + // brittle. + debug_assert!( + bit < CACHE_CAPACITY, + "too many features, time to increase the cache size!" + ); + test_bit(self.0, bit) + } + + /// Sets the `bit` of the cache. + #[inline] + pub(crate) fn set(&mut self, bit: u32) { + // FIXME: this way of making sure that the cache is large enough is + // brittle. + debug_assert!( + bit < CACHE_CAPACITY, + "too many features, time to increase the cache size!" + ); + let v = self.0; + self.0 = set_bit(v, bit); + } +} + +/// This global variable is a cache of the features supported by the CPU. +static CACHE: Cache = Cache::uninitialized(); + +/// Feature cache with capacity for `CACHE_CAPACITY` features. +/// +/// Note: the last feature bit is used to represent an +/// uninitialized cache. +#[cfg(target_pointer_width = "64")] +struct Cache(AtomicU64); + +#[cfg(target_pointer_width = "64")] +#[allow(clippy::use_self)] +impl Cache { + /// Creates an uninitialized cache. + #[allow(clippy::declare_interior_mutable_const)] + const fn uninitialized() -> Self { + Cache(AtomicU64::new(u64::max_value())) + } + /// Is the cache uninitialized? + #[inline] + pub(crate) fn is_uninitialized(&self) -> bool { + self.0.load(Ordering::Relaxed) == u64::max_value() + } + + /// Is the `bit` in the cache set? + #[inline] + pub(crate) fn test(&self, bit: u32) -> bool { + test_bit(CACHE.0.load(Ordering::Relaxed), bit) + } + + /// Initializes the cache. + #[inline] + pub(crate) fn initialize(&self, value: Initializer) { + self.0.store(value.0, Ordering::Relaxed); + } +} + +/// Feature cache with capacity for `CACHE_CAPACITY` features. +/// +/// Note: the last feature bit is used to represent an +/// uninitialized cache. +#[cfg(target_pointer_width = "32")] +struct Cache(AtomicU32, AtomicU32); + +#[cfg(target_pointer_width = "32")] +impl Cache { + /// Creates an uninitialized cache. + const fn uninitialized() -> Self { + Cache( + AtomicU32::new(u32::max_value()), + AtomicU32::new(u32::max_value()), + ) + } + /// Is the cache uninitialized? + #[inline] + pub(crate) fn is_uninitialized(&self) -> bool { + self.1.load(Ordering::Relaxed) == u32::max_value() + } + + /// Is the `bit` in the cache set? + #[inline] + pub(crate) fn test(&self, bit: u32) -> bool { + if bit < 32 { + test_bit(CACHE.0.load(Ordering::Relaxed) as u64, bit) + } else { + test_bit(CACHE.1.load(Ordering::Relaxed) as u64, bit - 32) + } + } + + /// Initializes the cache. + #[inline] + pub(crate) fn initialize(&self, value: Initializer) { + let lo: u32 = value.0 as u32; + let hi: u32 = (value.0 >> 32) as u32; + self.0.store(lo, Ordering::Relaxed); + self.1.store(hi, Ordering::Relaxed); + } +} + +/// Tests the `bit` of the storage. If the storage has not been initialized, +/// initializes it with the result of `f()`. +/// +/// On its first invocation, it detects the CPU features and caches them in the +/// `CACHE` global variable as an `AtomicU64`. +/// +/// It uses the `Feature` variant to index into this variable as a bitset. If +/// the bit is set, the feature is enabled, and otherwise it is disabled. +#[inline] +pub(crate) fn test(bit: u32, f: F) -> bool +where + F: FnOnce() -> Initializer, +{ + if CACHE.is_uninitialized() { + CACHE.initialize(f()); + } + CACHE.test(bit) +} diff --git a/tests/target/cfg_if/detect/error_macros.rs b/tests/target/cfg_if/detect/error_macros.rs new file mode 100644 index 000000000000..6769757ed933 --- /dev/null +++ b/tests/target/cfg_if/detect/error_macros.rs @@ -0,0 +1,150 @@ +//! The `is_{target_arch}_feature_detected!` macro are only available on their +//! architecture. These macros provide a better error messages when the user +//! attempts to call them in a different architecture. + +/// Prevents compilation if `is_x86_feature_detected` is used somewhere +/// else than `x86` and `x86_64` targets. +#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] +#[macro_export] +#[unstable(feature = "stdsimd", issue = "27731")] +macro_rules! is_x86_feature_detected { + ($t: tt) => { + compile_error!( + r#" + is_x86_feature_detected can only be used on x86 and x86_64 targets. + You can prevent it from being used in other architectures by + guarding it behind a cfg(target_arch) as follows: + + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { + if is_x86_feature_detected(...) { ... } + } + "# + ) + }; +} + +/// Prevents compilation if `is_arm_feature_detected` is used somewhere else +/// than `ARM` targets. +#[cfg(not(target_arch = "arm"))] +#[macro_export] +#[unstable(feature = "stdsimd", issue = "27731")] +macro_rules! is_arm_feature_detected { + ($t:tt) => { + compile_error!( + r#" + is_arm_feature_detected can only be used on ARM targets. + You can prevent it from being used in other architectures by + guarding it behind a cfg(target_arch) as follows: + + #[cfg(target_arch = "arm")] { + if is_arm_feature_detected(...) { ... } + } + "# + ) + }; +} + +/// Prevents compilation if `is_aarch64_feature_detected` is used somewhere else +/// than `aarch64` targets. +#[cfg(not(target_arch = "aarch64"))] +#[macro_export] +#[unstable(feature = "stdsimd", issue = "27731")] +macro_rules! is_aarch64_feature_detected { + ($t: tt) => { + compile_error!( + r#" + is_aarch64_feature_detected can only be used on AArch64 targets. + You can prevent it from being used in other architectures by + guarding it behind a cfg(target_arch) as follows: + + #[cfg(target_arch = "aarch64")] { + if is_aarch64_feature_detected(...) { ... } + } + "# + ) + }; +} + +/// Prevents compilation if `is_powerpc_feature_detected` is used somewhere else +/// than `PowerPC` targets. +#[cfg(not(target_arch = "powerpc"))] +#[macro_export] +#[unstable(feature = "stdsimd", issue = "27731")] +macro_rules! is_powerpc_feature_detected { + ($t:tt) => { + compile_error!( + r#" +is_powerpc_feature_detected can only be used on PowerPC targets. +You can prevent it from being used in other architectures by +guarding it behind a cfg(target_arch) as follows: + + #[cfg(target_arch = "powerpc")] { + if is_powerpc_feature_detected(...) { ... } + } +"# + ) + }; +} + +/// Prevents compilation if `is_powerpc64_feature_detected` is used somewhere +/// else than `PowerPC64` targets. +#[cfg(not(target_arch = "powerpc64"))] +#[macro_export] +#[unstable(feature = "stdsimd", issue = "27731")] +macro_rules! is_powerpc64_feature_detected { + ($t:tt) => { + compile_error!( + r#" +is_powerpc64_feature_detected can only be used on PowerPC64 targets. +You can prevent it from being used in other architectures by +guarding it behind a cfg(target_arch) as follows: + + #[cfg(target_arch = "powerpc64")] { + if is_powerpc64_feature_detected(...) { ... } + } +"# + ) + }; +} + +/// Prevents compilation if `is_mips_feature_detected` is used somewhere else +/// than `MIPS` targets. +#[cfg(not(target_arch = "mips"))] +#[macro_export] +#[unstable(feature = "stdsimd", issue = "27731")] +macro_rules! is_mips_feature_detected { + ($t:tt) => { + compile_error!( + r#" + is_mips_feature_detected can only be used on MIPS targets. + You can prevent it from being used in other architectures by + guarding it behind a cfg(target_arch) as follows: + + #[cfg(target_arch = "mips")] { + if is_mips_feature_detected(...) { ... } + } + "# + ) + }; +} + +/// Prevents compilation if `is_mips64_feature_detected` is used somewhere else +/// than `MIPS64` targets. +#[cfg(not(target_arch = "mips64"))] +#[macro_export] +#[unstable(feature = "stdsimd", issue = "27731")] +macro_rules! is_mips64_feature_detected { + ($t:tt) => { + compile_error!( + r#" + is_mips64_feature_detected can only be used on MIPS64 targets. + You can prevent it from being used in other architectures by + guarding it behind a cfg(target_arch) as follows: + + #[cfg(target_arch = "mips64")] { + if is_mips64_feature_detected(...) { ... } + } + "# + ) + }; +} diff --git a/tests/target/cfg_if/detect/mod.rs b/tests/target/cfg_if/detect/mod.rs new file mode 100644 index 000000000000..f446e88eedc8 --- /dev/null +++ b/tests/target/cfg_if/detect/mod.rs @@ -0,0 +1,85 @@ +//! This module implements run-time feature detection. +//! +//! The `is_{arch}_feature_detected!("feature-name")` macros take the name of a +//! feature as a string-literal, and return a boolean indicating whether the +//! feature is enabled at run-time or not. +//! +//! These macros do two things: +//! * map the string-literal into an integer stored as a `Feature` enum, +//! * call a `os::check_for(x: Feature)` function that returns `true` if the +//! feature is enabled. +//! +//! The `Feature` enums are also implemented in the `arch/{target_arch}.rs` +//! modules. +//! +//! The `check_for` functions are, in general, Operating System dependent. Most +//! architectures do not allow user-space programs to query the feature bits +//! due to security concerns (x86 is the big exception). These functions are +//! implemented in the `os/{target_os}.rs` modules. + +#[macro_use] +mod error_macros; + +cfg_if! { + if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { + #[path = "arch/x86.rs"] + #[macro_use] + mod arch; + } else if #[cfg(target_arch = "arm")] { + #[path = "arch/arm.rs"] + #[macro_use] + mod arch; + } else if #[cfg(target_arch = "aarch64")] { + #[path = "arch/aarch64.rs"] + #[macro_use] + mod arch; + } else if #[cfg(target_arch = "powerpc")] { + #[path = "arch/powerpc.rs"] + #[macro_use] + mod arch; + } else if #[cfg(target_arch = "powerpc64")] { + #[path = "arch/powerpc64.rs"] + #[macro_use] + mod arch; + } else if #[cfg(target_arch = "mips")] { + #[path = "arch/mips.rs"] + #[macro_use] + mod arch; + } else if #[cfg(target_arch = "mips64")] { + #[path = "arch/mips64.rs"] + #[macro_use] + mod arch; + } else { + // Unimplemented architecture: + mod arch { + pub enum Feature { + Null + } + } + } +} +pub use self::arch::Feature; + +mod bit; +mod cache; + +cfg_if! { + if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { + // On x86/x86_64 no OS specific functionality is required. + #[path = "os/x86.rs"] + mod os; + } else if #[cfg(all(target_os = "linux", feature = "use_std"))] { + #[path = "os/linux/mod.rs"] + mod os; + } else if #[cfg(target_os = "freebsd")] { + #[cfg(target_arch = "aarch64")] + #[path = "os/aarch64.rs"] + mod aarch64; + #[path = "os/freebsd/mod.rs"] + mod os; + } else { + #[path = "os/other.rs"] + mod os; + } +} +pub use self::os::check_for; diff --git a/tests/target/cfg_if/detect/os/aarch64.rs b/tests/target/cfg_if/detect/os/aarch64.rs new file mode 100644 index 000000000000..9adc938a2643 --- /dev/null +++ b/tests/target/cfg_if/detect/os/aarch64.rs @@ -0,0 +1,88 @@ +//! Run-time feature detection for Aarch64 on any OS that emulates the mrs instruction. +//! +//! On FreeBSD >= 12.0, Linux >= 4.11 and other operating systems, it is possible to use +//! privileged system registers from userspace to check CPU feature support. +//! +//! AArch64 system registers ID_AA64ISAR0_EL1, ID_AA64PFR0_EL1, ID_AA64ISAR1_EL1 +//! have bits dedicated to features like AdvSIMD, CRC32, AES, atomics (LSE), etc. +//! Each part of the register indicates the level of support for a certain feature, e.g. +//! when ID_AA64ISAR0_EL1\[7:4\] is >= 1, AES is supported; when it's >= 2, PMULL is supported. +//! +//! For proper support of [SoCs where different cores have different capabilities](https://medium.com/@jadr2ddude/a-big-little-problem-a-tale-of-big-little-gone-wrong-e7778ce744bb), +//! the OS has to always report only the features supported by all cores, like [FreeBSD does](https://reviews.freebsd.org/D17137#393947). +//! +//! References: +//! +//! - [Zircon implementation](https://fuchsia.googlesource.com/zircon/+/master/kernel/arch/arm64/feature.cpp) +//! - [Linux documentation](https://www.kernel.org/doc/Documentation/arm64/cpu-feature-registers.txt) + +use crate::detect::{cache, Feature}; + +/// Try to read the features from the system registers. +/// +/// This will cause SIGILL if the current OS is not trapping the mrs instruction. +pub(crate) fn detect_features() -> cache::Initializer { + let mut value = cache::Initializer::default(); + + { + let mut enable_feature = |f, enable| { + if enable { + value.set(f as u32); + } + }; + + // ID_AA64ISAR0_EL1 - Instruction Set Attribute Register 0 + let aa64isar0: u64; + unsafe { + asm!("mrs $0, ID_AA64ISAR0_EL1" : "=r"(aa64isar0)); + } + + let aes = bits_shift(aa64isar0, 7, 4) >= 1; + let pmull = bits_shift(aa64isar0, 7, 4) >= 2; + let sha1 = bits_shift(aa64isar0, 11, 8) >= 1; + let sha2 = bits_shift(aa64isar0, 15, 12) >= 1; + enable_feature(Feature::pmull, pmull); + // Crypto is specified as AES + PMULL + SHA1 + SHA2 per LLVM/hosts.cpp + enable_feature(Feature::crypto, aes && pmull && sha1 && sha2); + enable_feature(Feature::lse, bits_shift(aa64isar0, 23, 20) >= 1); + enable_feature(Feature::crc, bits_shift(aa64isar0, 19, 16) >= 1); + + // ID_AA64PFR0_EL1 - Processor Feature Register 0 + let aa64pfr0: u64; + unsafe { + asm!("mrs $0, ID_AA64PFR0_EL1" : "=r"(aa64pfr0)); + } + + let fp = bits_shift(aa64pfr0, 19, 16) < 0xF; + let fphp = bits_shift(aa64pfr0, 19, 16) >= 1; + let asimd = bits_shift(aa64pfr0, 23, 20) < 0xF; + let asimdhp = bits_shift(aa64pfr0, 23, 20) >= 1; + enable_feature(Feature::fp, fp); + enable_feature(Feature::fp16, fphp); + // SIMD support requires float support - if half-floats are + // supported, it also requires half-float support: + enable_feature(Feature::asimd, fp && asimd && (!fphp | asimdhp)); + // SIMD extensions require SIMD support: + enable_feature(Feature::rdm, asimd && bits_shift(aa64isar0, 31, 28) >= 1); + enable_feature( + Feature::dotprod, + asimd && bits_shift(aa64isar0, 47, 44) >= 1, + ); + enable_feature(Feature::sve, asimd && bits_shift(aa64pfr0, 35, 32) >= 1); + + // ID_AA64ISAR1_EL1 - Instruction Set Attribute Register 1 + let aa64isar1: u64; + unsafe { + asm!("mrs $0, ID_AA64ISAR1_EL1" : "=r"(aa64isar1)); + } + + enable_feature(Feature::rcpc, bits_shift(aa64isar1, 23, 20) >= 1); + } + + value +} + +#[inline] +fn bits_shift(x: u64, high: usize, low: usize) -> u64 { + (x >> low) & ((1 << (high - low + 1)) - 1) +} diff --git a/tests/target/cfg_if/detect/os/freebsd/aarch64.rs b/tests/target/cfg_if/detect/os/freebsd/aarch64.rs new file mode 100644 index 000000000000..97fe40f80a46 --- /dev/null +++ b/tests/target/cfg_if/detect/os/freebsd/aarch64.rs @@ -0,0 +1,28 @@ +//! Run-time feature detection for Aarch64 on FreeBSD. + +use super::super::aarch64::detect_features; +use crate::detect::{cache, Feature}; + +/// Performs run-time feature detection. +#[inline] +pub fn check_for(x: Feature) -> bool { + cache::test(x as u32, detect_features) +} + +#[cfg(test)] +mod tests { + #[test] + fn dump() { + println!("asimd: {:?}", is_aarch64_feature_detected!("asimd")); + println!("pmull: {:?}", is_aarch64_feature_detected!("pmull")); + println!("fp: {:?}", is_aarch64_feature_detected!("fp")); + println!("fp16: {:?}", is_aarch64_feature_detected!("fp16")); + println!("sve: {:?}", is_aarch64_feature_detected!("sve")); + println!("crc: {:?}", is_aarch64_feature_detected!("crc")); + println!("crypto: {:?}", is_aarch64_feature_detected!("crypto")); + println!("lse: {:?}", is_aarch64_feature_detected!("lse")); + println!("rdm: {:?}", is_aarch64_feature_detected!("rdm")); + println!("rcpc: {:?}", is_aarch64_feature_detected!("rcpc")); + println!("dotprod: {:?}", is_aarch64_feature_detected!("dotprod")); + } +} diff --git a/tests/target/cfg_if/detect/os/freebsd/arm.rs b/tests/target/cfg_if/detect/os/freebsd/arm.rs new file mode 100644 index 000000000000..7aa040075e04 --- /dev/null +++ b/tests/target/cfg_if/detect/os/freebsd/arm.rs @@ -0,0 +1,27 @@ +//! Run-time feature detection for ARM on FreeBSD + +use super::auxvec; +use crate::detect::{cache, Feature}; + +/// Performs run-time feature detection. +#[inline] +pub fn check_for(x: Feature) -> bool { + cache::test(x as u32, detect_features) +} + +/// Try to read the features from the auxiliary vector +fn detect_features() -> cache::Initializer { + let mut value = cache::Initializer::default(); + let enable_feature = |value: &mut cache::Initializer, f, enable| { + if enable { + value.set(f as u32); + } + }; + + if let Ok(auxv) = auxvec::auxv() { + enable_feature(&mut value, Feature::neon, auxv.hwcap & 0x00001000 != 0); + enable_feature(&mut value, Feature::pmull, auxv.hwcap2 & 0x00000002 != 0); + return value; + } + value +} diff --git a/tests/target/cfg_if/detect/os/freebsd/auxvec.rs b/tests/target/cfg_if/detect/os/freebsd/auxvec.rs new file mode 100644 index 000000000000..c595ec459b5d --- /dev/null +++ b/tests/target/cfg_if/detect/os/freebsd/auxvec.rs @@ -0,0 +1,94 @@ +//! Parses ELF auxiliary vectors. +#![cfg_attr(any(target_arch = "arm", target_arch = "powerpc64"), allow(dead_code))] + +/// Key to access the CPU Hardware capabilities bitfield. +pub(crate) const AT_HWCAP: usize = 25; +/// Key to access the CPU Hardware capabilities 2 bitfield. +pub(crate) const AT_HWCAP2: usize = 26; + +/// Cache HWCAP bitfields of the ELF Auxiliary Vector. +/// +/// If an entry cannot be read all the bits in the bitfield are set to zero. +/// This should be interpreted as all the features being disabled. +#[derive(Debug, Copy, Clone)] +pub(crate) struct AuxVec { + pub hwcap: usize, + pub hwcap2: usize, +} + +/// ELF Auxiliary Vector +/// +/// The auxiliary vector is a memory region in a running ELF program's stack +/// composed of (key: usize, value: usize) pairs. +/// +/// The keys used in the aux vector are platform dependent. For FreeBSD, they are +/// defined in [sys/elf_common.h][elf_common_h]. The hardware capabilities of a given +/// CPU can be queried with the `AT_HWCAP` and `AT_HWCAP2` keys. +/// +/// Note that run-time feature detection is not invoked for features that can +/// be detected at compile-time. +/// +/// [elf_common.h]: https://svnweb.freebsd.org/base/release/12.0.0/sys/sys/elf_common.h?revision=341707 +pub(crate) fn auxv() -> Result { + if let Ok(hwcap) = archauxv(AT_HWCAP) { + if let Ok(hwcap2) = archauxv(AT_HWCAP2) { + if hwcap != 0 && hwcap2 != 0 { + return Ok(AuxVec { hwcap, hwcap2 }); + } + } + } + Err(()) +} + +/// Tries to read the `key` from the auxiliary vector. +fn archauxv(key: usize) -> Result { + use crate::mem; + + #[derive(Copy, Clone)] + #[repr(C)] + pub struct Elf_Auxinfo { + pub a_type: usize, + pub a_un: unnamed, + } + #[derive(Copy, Clone)] + #[repr(C)] + pub union unnamed { + pub a_val: libc::c_long, + pub a_ptr: *mut libc::c_void, + pub a_fcn: Option ()>, + } + + let mut auxv: [Elf_Auxinfo; 27] = [Elf_Auxinfo { + a_type: 0, + a_un: unnamed { a_val: 0 }, + }; 27]; + + let mut len: libc::c_uint = mem::size_of_val(&auxv) as libc::c_uint; + + unsafe { + let mut mib = [ + libc::CTL_KERN, + libc::KERN_PROC, + libc::KERN_PROC_AUXV, + libc::getpid(), + ]; + + let ret = libc::sysctl( + mib.as_mut_ptr(), + mib.len() as u32, + &mut auxv as *mut _ as *mut _, + &mut len as *mut _ as *mut _, + 0 as *mut libc::c_void, + 0, + ); + + if ret != -1 { + for i in 0..auxv.len() { + if auxv[i].a_type == key { + return Ok(auxv[i].a_un.a_val as usize); + } + } + } + } + return Ok(0); +} diff --git a/tests/target/cfg_if/detect/os/freebsd/mod.rs b/tests/target/cfg_if/detect/os/freebsd/mod.rs new file mode 100644 index 000000000000..1a5338a35559 --- /dev/null +++ b/tests/target/cfg_if/detect/os/freebsd/mod.rs @@ -0,0 +1,22 @@ +//! Run-time feature detection on FreeBSD + +mod auxvec; + +cfg_if! { + if #[cfg(target_arch = "aarch64")] { + mod aarch64; + pub use self::aarch64::check_for; + } else if #[cfg(target_arch = "arm")] { + mod arm; + pub use self::arm::check_for; + } else if #[cfg(target_arch = "powerpc64")] { + mod powerpc; + pub use self::powerpc::check_for; + } else { + use crate::arch::detect::Feature; + /// Performs run-time feature detection. + pub fn check_for(_x: Feature) -> bool { + false + } + } +} diff --git a/tests/target/cfg_if/detect/os/freebsd/powerpc.rs b/tests/target/cfg_if/detect/os/freebsd/powerpc.rs new file mode 100644 index 000000000000..203e5cd7f250 --- /dev/null +++ b/tests/target/cfg_if/detect/os/freebsd/powerpc.rs @@ -0,0 +1,27 @@ +//! Run-time feature detection for PowerPC on FreeBSD. + +use super::auxvec; +use crate::detect::{cache, Feature}; + +/// Performs run-time feature detection. +#[inline] +pub fn check_for(x: Feature) -> bool { + cache::test(x as u32, detect_features) +} + +fn detect_features() -> cache::Initializer { + let mut value = cache::Initializer::default(); + let enable_feature = |value: &mut cache::Initializer, f, enable| { + if enable { + value.set(f as u32); + } + }; + + if let Ok(auxv) = auxvec::auxv() { + enable_feature(&mut value, Feature::altivec, auxv.hwcap & 0x10000000 != 0); + enable_feature(&mut value, Feature::vsx, auxv.hwcap & 0x00000080 != 0); + enable_feature(&mut value, Feature::power8, auxv.hwcap2 & 0x80000000 != 0); + return value; + } + value +} diff --git a/tests/target/cfg_if/detect/os/linux/aarch64.rs b/tests/target/cfg_if/detect/os/linux/aarch64.rs new file mode 100644 index 000000000000..8d874f2280fc --- /dev/null +++ b/tests/target/cfg_if/detect/os/linux/aarch64.rs @@ -0,0 +1,160 @@ +//! Run-time feature detection for Aarch64 on Linux. + +use super::{auxvec, cpuinfo}; +use crate::detect::{bit, cache, Feature}; + +/// Performs run-time feature detection. +#[inline] +pub fn check_for(x: Feature) -> bool { + cache::test(x as u32, detect_features) +} + +/// Try to read the features from the auxiliary vector, and if that fails, try +/// to read them from /proc/cpuinfo. +fn detect_features() -> cache::Initializer { + if let Ok(auxv) = auxvec::auxv() { + let hwcap: AtHwcap = auxv.into(); + return hwcap.cache(); + } + if let Ok(c) = cpuinfo::CpuInfo::new() { + let hwcap: AtHwcap = c.into(); + return hwcap.cache(); + } + cache::Initializer::default() +} + +/// These values are part of the platform-specific [asm/hwcap.h][hwcap] . +/// +/// [hwcap]: https://github.com/torvalds/linux/blob/master/arch/arm64/include/uapi/asm/hwcap.h +struct AtHwcap { + fp: bool, // 0 + asimd: bool, // 1 + // evtstrm: bool, // 2 + aes: bool, // 3 + pmull: bool, // 4 + sha1: bool, // 5 + sha2: bool, // 6 + crc32: bool, // 7 + atomics: bool, // 8 + fphp: bool, // 9 + asimdhp: bool, // 10 + // cpuid: bool, // 11 + asimdrdm: bool, // 12 + // jscvt: bool, // 13 + // fcma: bool, // 14 + lrcpc: bool, // 15 + // dcpop: bool, // 16 + // sha3: bool, // 17 + // sm3: bool, // 18 + // sm4: bool, // 19 + asimddp: bool, // 20 + // sha512: bool, // 21 + sve: bool, // 22 +} + +impl From for AtHwcap { + /// Reads AtHwcap from the auxiliary vector. + fn from(auxv: auxvec::AuxVec) -> Self { + AtHwcap { + fp: bit::test(auxv.hwcap, 0), + asimd: bit::test(auxv.hwcap, 1), + // evtstrm: bit::test(auxv.hwcap, 2), + aes: bit::test(auxv.hwcap, 3), + pmull: bit::test(auxv.hwcap, 4), + sha1: bit::test(auxv.hwcap, 5), + sha2: bit::test(auxv.hwcap, 6), + crc32: bit::test(auxv.hwcap, 7), + atomics: bit::test(auxv.hwcap, 8), + fphp: bit::test(auxv.hwcap, 9), + asimdhp: bit::test(auxv.hwcap, 10), + // cpuid: bit::test(auxv.hwcap, 11), + asimdrdm: bit::test(auxv.hwcap, 12), + // jscvt: bit::test(auxv.hwcap, 13), + // fcma: bit::test(auxv.hwcap, 14), + lrcpc: bit::test(auxv.hwcap, 15), + // dcpop: bit::test(auxv.hwcap, 16), + // sha3: bit::test(auxv.hwcap, 17), + // sm3: bit::test(auxv.hwcap, 18), + // sm4: bit::test(auxv.hwcap, 19), + asimddp: bit::test(auxv.hwcap, 20), + // sha512: bit::test(auxv.hwcap, 21), + sve: bit::test(auxv.hwcap, 22), + } + } +} + +impl From for AtHwcap { + /// Reads AtHwcap from /proc/cpuinfo . + fn from(c: cpuinfo::CpuInfo) -> Self { + let f = &c.field("Features"); + AtHwcap { + // 64-bit names. FIXME: In 32-bit compatibility mode /proc/cpuinfo will + // map some of the 64-bit names to some 32-bit feature names. This does not + // cover that yet. + fp: f.has("fp"), + asimd: f.has("asimd"), + // evtstrm: f.has("evtstrm"), + aes: f.has("aes"), + pmull: f.has("pmull"), + sha1: f.has("sha1"), + sha2: f.has("sha2"), + crc32: f.has("crc32"), + atomics: f.has("atomics"), + fphp: f.has("fphp"), + asimdhp: f.has("asimdhp"), + // cpuid: f.has("cpuid"), + asimdrdm: f.has("asimdrdm"), + // jscvt: f.has("jscvt"), + // fcma: f.has("fcma"), + lrcpc: f.has("lrcpc"), + // dcpop: f.has("dcpop"), + // sha3: f.has("sha3"), + // sm3: f.has("sm3"), + // sm4: f.has("sm4"), + asimddp: f.has("asimddp"), + // sha512: f.has("sha512"), + sve: f.has("sve"), + } + } +} + +impl AtHwcap { + /// Initializes the cache from the feature -bits. + /// + /// The features are enabled approximately like in LLVM host feature detection: + /// https://github.com/llvm-mirror/llvm/blob/master/lib/Support/Host.cpp#L1273 + fn cache(self) -> cache::Initializer { + let mut value = cache::Initializer::default(); + { + let mut enable_feature = |f, enable| { + if enable { + value.set(f as u32); + } + }; + + enable_feature(Feature::fp, self.fp); + // Half-float support requires float support + enable_feature(Feature::fp16, self.fp && self.fphp); + enable_feature(Feature::pmull, self.pmull); + enable_feature(Feature::crc, self.crc32); + enable_feature(Feature::lse, self.atomics); + enable_feature(Feature::rcpc, self.lrcpc); + + // SIMD support requires float support - if half-floats are + // supported, it also requires half-float support: + let asimd = self.fp && self.asimd && (!self.fphp | self.asimdhp); + enable_feature(Feature::asimd, asimd); + // SIMD extensions require SIMD support: + enable_feature(Feature::rdm, self.asimdrdm && asimd); + enable_feature(Feature::dotprod, self.asimddp && asimd); + enable_feature(Feature::sve, self.sve && asimd); + + // Crypto is specified as AES + PMULL + SHA1 + SHA2 per LLVM/hosts.cpp + enable_feature( + Feature::crypto, + self.aes && self.pmull && self.sha1 && self.sha2, + ); + } + value + } +} diff --git a/tests/target/cfg_if/detect/os/linux/arm.rs b/tests/target/cfg_if/detect/os/linux/arm.rs new file mode 100644 index 000000000000..9c89500cc100 --- /dev/null +++ b/tests/target/cfg_if/detect/os/linux/arm.rs @@ -0,0 +1,52 @@ +//! Run-time feature detection for ARM on Linux. + +use super::{auxvec, cpuinfo}; +use crate::detect::{bit, cache, Feature}; + +/// Performs run-time feature detection. +#[inline] +pub fn check_for(x: Feature) -> bool { + cache::test(x as u32, detect_features) +} + +/// Try to read the features from the auxiliary vector, and if that fails, try +/// to read them from /proc/cpuinfo. +fn detect_features() -> cache::Initializer { + let mut value = cache::Initializer::default(); + let enable_feature = |value: &mut cache::Initializer, f, enable| { + if enable { + value.set(f as u32); + } + }; + + // The values are part of the platform-specific [asm/hwcap.h][hwcap] + // + // [hwcap]: https://github.com/torvalds/linux/blob/master/arch/arm64/include/uapi/asm/hwcap.h + if let Ok(auxv) = auxvec::auxv() { + enable_feature(&mut value, Feature::neon, bit::test(auxv.hwcap, 12)); + enable_feature(&mut value, Feature::pmull, bit::test(auxv.hwcap2, 1)); + return value; + } + + if let Ok(c) = cpuinfo::CpuInfo::new() { + enable_feature( + &mut value, + Feature::neon, + c.field("Features").has("neon") && !has_broken_neon(&c), + ); + enable_feature(&mut value, Feature::pmull, c.field("Features").has("pmull")); + return value; + } + value +} + +/// Is the CPU known to have a broken NEON unit? +/// +/// See https://crbug.com/341598. +fn has_broken_neon(cpuinfo: &cpuinfo::CpuInfo) -> bool { + cpuinfo.field("CPU implementer") == "0x51" + && cpuinfo.field("CPU architecture") == "7" + && cpuinfo.field("CPU variant") == "0x1" + && cpuinfo.field("CPU part") == "0x04d" + && cpuinfo.field("CPU revision") == "0" +} diff --git a/tests/target/cfg_if/detect/os/linux/auxvec.rs b/tests/target/cfg_if/detect/os/linux/auxvec.rs new file mode 100644 index 000000000000..6ebae67fbf87 --- /dev/null +++ b/tests/target/cfg_if/detect/os/linux/auxvec.rs @@ -0,0 +1,304 @@ +//! Parses ELF auxiliary vectors. +#![cfg_attr(not(target_arch = "aarch64"), allow(dead_code))] + +#[cfg(feature = "std_detect_file_io")] +use crate::{fs::File, io::Read}; + +/// Key to access the CPU Hardware capabilities bitfield. +pub(crate) const AT_HWCAP: usize = 16; +/// Key to access the CPU Hardware capabilities 2 bitfield. +#[cfg(any(target_arch = "arm", target_arch = "powerpc64"))] +pub(crate) const AT_HWCAP2: usize = 26; + +/// Cache HWCAP bitfields of the ELF Auxiliary Vector. +/// +/// If an entry cannot be read all the bits in the bitfield are set to zero. +/// This should be interpreted as all the features being disabled. +#[derive(Debug, Copy, Clone)] +pub(crate) struct AuxVec { + pub hwcap: usize, + #[cfg(any(target_arch = "arm", target_arch = "powerpc64"))] + pub hwcap2: usize, +} + +/// ELF Auxiliary Vector +/// +/// The auxiliary vector is a memory region in a running ELF program's stack +/// composed of (key: usize, value: usize) pairs. +/// +/// The keys used in the aux vector are platform dependent. For Linux, they are +/// defined in [linux/auxvec.h][auxvec_h]. The hardware capabilities of a given +/// CPU can be queried with the `AT_HWCAP` and `AT_HWCAP2` keys. +/// +/// There is no perfect way of reading the auxiliary vector. +/// +/// - If the `std_detect_dlsym_getauxval` cargo feature is enabled, this will use +/// `getauxval` if its linked to the binary, and otherwise proceed to a fallback implementation. +/// When `std_detect_dlsym_getauxval` is disabled, this will assume that `getauxval` is +/// linked to the binary - if that is not the case the behavior is undefined. +/// - Otherwise, if the `std_detect_file_io` cargo feature is enabled, it will +/// try to read `/proc/self/auxv`. +/// - If that fails, this function returns an error. +/// +/// Note that run-time feature detection is not invoked for features that can +/// be detected at compile-time. Also note that if this function returns an +/// error, cpuinfo still can (and will) be used to try to perform run-time +/// feature detecton on some platforms. +/// +/// For more information about when `getauxval` is available check the great +/// [`auxv` crate documentation][auxv_docs]. +/// +/// [auxvec_h]: https://github.com/torvalds/linux/blob/master/include/uapi/linux/auxvec.h +/// [auxv_docs]: https://docs.rs/auxv/0.3.3/auxv/ +pub(crate) fn auxv() -> Result { + #[cfg(feature = "std_detect_dlsym_getauxval")] + { + // Try to call a dynamically-linked getauxval function. + if let Ok(hwcap) = getauxval(AT_HWCAP) { + // Targets with only AT_HWCAP: + #[cfg(any(target_arch = "aarch64", target_arch = "mips", target_arch = "mips64"))] + { + if hwcap != 0 { + return Ok(AuxVec { hwcap }); + } + } + + // Targets with AT_HWCAP and AT_HWCAP2: + #[cfg(any(target_arch = "arm", target_arch = "powerpc64"))] + { + if let Ok(hwcap2) = getauxval(AT_HWCAP2) { + if hwcap != 0 && hwcap2 != 0 { + return Ok(AuxVec { hwcap, hwcap2 }); + } + } + } + drop(hwcap); + } + #[cfg(feature = "std_detect_file_io")] + { + // If calling getauxval fails, try to read the auxiliary vector from + // its file: + auxv_from_file("/proc/self/auxv") + } + #[cfg(not(feature = "std_detect_file_io"))] + { + Err(()) + } + } + + #[cfg(not(feature = "std_detect_dlsym_getauxval"))] + { + let hwcap = unsafe { ffi_getauxval(AT_HWCAP) }; + + // Targets with only AT_HWCAP: + #[cfg(any(target_arch = "aarch64", target_arch = "mips", target_arch = "mips64"))] + { + if hwcap != 0 { + return Ok(AuxVec { hwcap }); + } + } + + // Targets with AT_HWCAP and AT_HWCAP2: + #[cfg(any(target_arch = "arm", target_arch = "powerpc64"))] + { + let hwcap2 = unsafe { ffi_getauxval(AT_HWCAP2) }; + if hwcap != 0 && hwcap2 != 0 { + return Ok(AuxVec { hwcap, hwcap2 }); + } + } + } +} + +/// Tries to read the `key` from the auxiliary vector by calling the +/// dynamically-linked `getauxval` function. If the function is not linked, +/// this function return `Err`. +#[cfg(feature = "std_detect_dlsym_getauxval")] +fn getauxval(key: usize) -> Result { + use libc; + pub type F = unsafe extern "C" fn(usize) -> usize; + unsafe { + let ptr = libc::dlsym(libc::RTLD_DEFAULT, "getauxval\0".as_ptr() as *const _); + if ptr.is_null() { + return Err(()); + } + + let ffi_getauxval: F = mem::transmute(ptr); + Ok(ffi_getauxval(key)) + } +} + +/// Tries to read the auxiliary vector from the `file`. If this fails, this +/// function returns `Err`. +#[cfg(feature = "std_detect_file_io")] +fn auxv_from_file(file: &str) -> Result { + let mut file = File::open(file).map_err(|_| ())?; + + // See . + // + // The auxiliary vector contains at most 32 (key,value) fields: from + // `AT_EXECFN = 31` to `AT_NULL = 0`. That is, a buffer of + // 2*32 `usize` elements is enough to read the whole vector. + let mut buf = [0_usize; 64]; + { + let raw: &mut [u8; 64 * mem::size_of::()] = unsafe { mem::transmute(&mut buf) }; + file.read(raw).map_err(|_| ())?; + } + auxv_from_buf(&buf) +} + +/// Tries to interpret the `buffer` as an auxiliary vector. If that fails, this +/// function returns `Err`. +#[cfg(feature = "std_detect_file_io")] +fn auxv_from_buf(buf: &[usize; 64]) -> Result { + // Targets with only AT_HWCAP: + #[cfg(any(target_arch = "aarch64", target_arch = "mips", target_arch = "mips64"))] + { + for el in buf.chunks(2) { + match el[0] { + AT_HWCAP => return Ok(AuxVec { hwcap: el[1] }), + _ => (), + } + } + } + // Targets with AT_HWCAP and AT_HWCAP2: + #[cfg(any(target_arch = "arm", target_arch = "powerpc64"))] + { + let mut hwcap = None; + let mut hwcap2 = None; + for el in buf.chunks(2) { + match el[0] { + AT_HWCAP => hwcap = Some(el[1]), + AT_HWCAP2 => hwcap2 = Some(el[1]), + _ => (), + } + } + + if let (Some(hwcap), Some(hwcap2)) = (hwcap, hwcap2) { + return Ok(AuxVec { hwcap, hwcap2 }); + } + } + drop(buf); + Err(()) +} + +#[cfg(test)] +mod tests { + extern crate auxv as auxv_crate; + use super::*; + + // Reads the Auxiliary Vector key from /proc/self/auxv + // using the auxv crate. + #[cfg(feature = "std_detect_file_io")] + fn auxv_crate_getprocfs(key: usize) -> Option { + use self::auxv_crate::procfs::search_procfs_auxv; + use self::auxv_crate::AuxvType; + let k = key as AuxvType; + match search_procfs_auxv(&[k]) { + Ok(v) => Some(v[&k] as usize), + Err(_) => None, + } + } + + // Reads the Auxiliary Vector key from getauxval() + // using the auxv crate. + #[cfg(not(any(target_arch = "mips", target_arch = "mips64")))] + fn auxv_crate_getauxval(key: usize) -> Option { + use self::auxv_crate::getauxval::Getauxval; + use self::auxv_crate::AuxvType; + let q = auxv_crate::getauxval::NativeGetauxval {}; + match q.getauxval(key as AuxvType) { + Ok(v) => Some(v as usize), + Err(_) => None, + } + } + + // FIXME: on mips/mips64 getauxval returns 0, and /proc/self/auxv + // does not always contain the AT_HWCAP key under qemu. + #[cfg(not(any(target_arch = "mips", target_arch = "mips64", target_arch = "powerpc")))] + #[test] + fn auxv_crate() { + let v = auxv(); + if let Some(hwcap) = auxv_crate_getauxval(AT_HWCAP) { + let rt_hwcap = v.expect("failed to find hwcap key").hwcap; + assert_eq!(rt_hwcap, hwcap); + } + + // Targets with AT_HWCAP and AT_HWCAP2: + #[cfg(any(target_arch = "arm", target_arch = "powerpc64"))] + { + if let Some(hwcap2) = auxv_crate_getauxval(AT_HWCAP2) { + let rt_hwcap2 = v.expect("failed to find hwcap2 key").hwcap2; + assert_eq!(rt_hwcap2, hwcap2); + } + } + } + + #[test] + fn auxv_dump() { + if let Ok(auxvec) = auxv() { + println!("{:?}", auxvec); + } else { + println!("both getauxval() and reading /proc/self/auxv failed!"); + } + } + + #[cfg(feature = "std_detect_file_io")] + cfg_if! { + if #[cfg(target_arch = "arm")] { + #[test] + fn linux_rpi3() { + let file = concat!(env!("CARGO_MANIFEST_DIR"), "/src/detect/test_data/linux-rpi3.auxv"); + println!("file: {}", file); + let v = auxv_from_file(file).unwrap(); + assert_eq!(v.hwcap, 4174038); + assert_eq!(v.hwcap2, 16); + } + + #[test] + #[should_panic] + fn linux_macos_vb() { + let file = concat!(env!("CARGO_MANIFEST_DIR"), "/src/detect/test_data/macos-virtualbox-linux-x86-4850HQ.auxv"); + println!("file: {}", file); + let v = auxv_from_file(file).unwrap(); + // this file is incomplete (contains hwcap but not hwcap2), we + // want to fall back to /proc/cpuinfo in this case, so + // reading should fail. assert_eq!(v.hwcap, 126614527); + // assert_eq!(v.hwcap2, 0); + } + } else if #[cfg(target_arch = "aarch64")] { + #[test] + fn linux_x64() { + let file = concat!(env!("CARGO_MANIFEST_DIR"), "/src/detect/test_data/linux-x64-i7-6850k.auxv"); + println!("file: {}", file); + let v = auxv_from_file(file).unwrap(); + assert_eq!(v.hwcap, 3219913727); + } + } + } + + #[test] + #[cfg(feature = "std_detect_file_io")] + fn auxv_dump_procfs() { + if let Ok(auxvec) = auxv_from_file("/proc/self/auxv") { + println!("{:?}", auxvec); + } else { + println!("reading /proc/self/auxv failed!"); + } + } + + #[test] + fn auxv_crate_procfs() { + let v = auxv(); + if let Some(hwcap) = auxv_crate_getprocfs(AT_HWCAP) { + assert_eq!(v.unwrap().hwcap, hwcap); + } + + // Targets with AT_HWCAP and AT_HWCAP2: + #[cfg(any(target_arch = "arm", target_arch = "powerpc64"))] + { + if let Some(hwcap2) = auxv_crate_getprocfs(AT_HWCAP2) { + assert_eq!(v.unwrap().hwcap2, hwcap2); + } + } + } +} diff --git a/tests/target/cfg_if/detect/os/linux/cpuinfo.rs b/tests/target/cfg_if/detect/os/linux/cpuinfo.rs new file mode 100644 index 000000000000..f76c48a4b16b --- /dev/null +++ b/tests/target/cfg_if/detect/os/linux/cpuinfo.rs @@ -0,0 +1,300 @@ +//! Parses /proc/cpuinfo +#![cfg_attr(not(target_arch = "arm"), allow(dead_code))] + +extern crate std; +use self::std::{fs::File, io, io::Read, prelude::v1::*}; + +/// cpuinfo +pub(crate) struct CpuInfo { + raw: String, +} + +impl CpuInfo { + /// Reads /proc/cpuinfo into CpuInfo. + pub(crate) fn new() -> Result { + let mut file = File::open("/proc/cpuinfo")?; + let mut cpui = Self { raw: String::new() }; + file.read_to_string(&mut cpui.raw)?; + Ok(cpui) + } + /// Returns the value of the cpuinfo `field`. + pub(crate) fn field(&self, field: &str) -> CpuInfoField { + for l in self.raw.lines() { + if l.trim().starts_with(field) { + return CpuInfoField::new(l.split(": ").nth(1)); + } + } + CpuInfoField(None) + } + + /// Returns the `raw` contents of `/proc/cpuinfo` + #[cfg(test)] + fn raw(&self) -> &String { + &self.raw + } + + #[cfg(test)] + fn from_str(other: &str) -> Result { + Ok(Self { + raw: String::from(other), + }) + } +} + +/// Field of cpuinfo +#[derive(Debug)] +pub(crate) struct CpuInfoField<'a>(Option<&'a str>); + +impl<'a> PartialEq<&'a str> for CpuInfoField<'a> { + fn eq(&self, other: &&'a str) -> bool { + match self.0 { + None => other.is_empty(), + Some(f) => f == other.trim(), + } + } +} + +impl<'a> CpuInfoField<'a> { + pub(crate) fn new<'b>(v: Option<&'b str>) -> CpuInfoField<'b> { + match v { + None => CpuInfoField::<'b>(None), + Some(f) => CpuInfoField::<'b>(Some(f.trim())), + } + } + /// Does the field exist? + #[cfg(test)] + pub(crate) fn exists(&self) -> bool { + self.0.is_some() + } + /// Does the field contain `other`? + pub(crate) fn has(&self, other: &str) -> bool { + match self.0 { + None => other.is_empty(), + Some(f) => { + let other = other.trim(); + for v in f.split(' ') { + if v == other { + return true; + } + } + false + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn raw_dump() { + let cpuinfo = CpuInfo::new().unwrap(); + if cpuinfo.field("vendor_id") == "GenuineIntel" { + assert!(cpuinfo.field("flags").exists()); + assert!(!cpuinfo.field("vendor33_id").exists()); + assert!(cpuinfo.field("flags").has("sse")); + assert!(!cpuinfo.field("flags").has("avx314")); + } + println!("{}", cpuinfo.raw()); + } + + const CORE_DUO_T6500: &str = r"processor : 0 +vendor_id : GenuineIntel +cpu family : 6 +model : 23 +model name : Intel(R) Core(TM)2 Duo CPU T6500 @ 2.10GHz +stepping : 10 +microcode : 0xa0b +cpu MHz : 1600.000 +cache size : 2048 KB +physical id : 0 +siblings : 2 +core id : 0 +cpu cores : 2 +apicid : 0 +initial apicid : 0 +fdiv_bug : no +hlt_bug : no +f00f_bug : no +coma_bug : no +fpu : yes +fpu_exception : yes +cpuid level : 13 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe nx lm constant_tsc arch_perfmon pebs bts aperfmperf pni dtes64 monitor ds_cpl est tm2 ssse3 cx16 xtpr pdcm sse4_1 xsave lahf_lm dtherm +bogomips : 4190.43 +clflush size : 64 +cache_alignment : 64 +address sizes : 36 bits physical, 48 bits virtual +power management: +"; + + #[test] + fn core_duo_t6500() { + let cpuinfo = CpuInfo::from_str(CORE_DUO_T6500).unwrap(); + assert_eq!(cpuinfo.field("vendor_id"), "GenuineIntel"); + assert_eq!(cpuinfo.field("cpu family"), "6"); + assert_eq!(cpuinfo.field("model"), "23"); + assert_eq!( + cpuinfo.field("model name"), + "Intel(R) Core(TM)2 Duo CPU T6500 @ 2.10GHz" + ); + assert_eq!( + cpuinfo.field("flags"), + "fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe nx lm constant_tsc arch_perfmon pebs bts aperfmperf pni dtes64 monitor ds_cpl est tm2 ssse3 cx16 xtpr pdcm sse4_1 xsave lahf_lm dtherm" + ); + assert!(cpuinfo.field("flags").has("fpu")); + assert!(cpuinfo.field("flags").has("dtherm")); + assert!(cpuinfo.field("flags").has("sse2")); + assert!(!cpuinfo.field("flags").has("avx")); + } + + const ARM_CORTEX_A53: &str = r"Processor : AArch64 Processor rev 3 (aarch64) + processor : 0 + processor : 1 + processor : 2 + processor : 3 + processor : 4 + processor : 5 + processor : 6 + processor : 7 + Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 + CPU implementer : 0x41 + CPU architecture: AArch64 + CPU variant : 0x0 + CPU part : 0xd03 + CPU revision : 3 + + Hardware : HiKey Development Board + "; + + #[test] + fn arm_cortex_a53() { + let cpuinfo = CpuInfo::from_str(ARM_CORTEX_A53).unwrap(); + assert_eq!( + cpuinfo.field("Processor"), + "AArch64 Processor rev 3 (aarch64)" + ); + assert_eq!( + cpuinfo.field("Features"), + "fp asimd evtstrm aes pmull sha1 sha2 crc32" + ); + assert!(cpuinfo.field("Features").has("pmull")); + assert!(!cpuinfo.field("Features").has("neon")); + assert!(cpuinfo.field("Features").has("asimd")); + } + + const ARM_CORTEX_A57: &str = r"Processor : Cortex A57 Processor rev 1 (aarch64) +processor : 0 +processor : 1 +processor : 2 +processor : 3 +Features : fp asimd aes pmull sha1 sha2 crc32 wp half thumb fastmult vfp edsp neon vfpv3 tlsi vfpv4 idiva idivt +CPU implementer : 0x41 +CPU architecture: 8 +CPU variant : 0x1 +CPU part : 0xd07 +CPU revision : 1"; + + #[test] + fn arm_cortex_a57() { + let cpuinfo = CpuInfo::from_str(ARM_CORTEX_A57).unwrap(); + assert_eq!( + cpuinfo.field("Processor"), + "Cortex A57 Processor rev 1 (aarch64)" + ); + assert_eq!( + cpuinfo.field("Features"), + "fp asimd aes pmull sha1 sha2 crc32 wp half thumb fastmult vfp edsp neon vfpv3 tlsi vfpv4 idiva idivt" + ); + assert!(cpuinfo.field("Features").has("pmull")); + assert!(cpuinfo.field("Features").has("neon")); + assert!(cpuinfo.field("Features").has("asimd")); + } + + const POWER8E_POWERKVM: &str = r"processor : 0 +cpu : POWER8E (raw), altivec supported +clock : 3425.000000MHz +revision : 2.1 (pvr 004b 0201) + +processor : 1 +cpu : POWER8E (raw), altivec supported +clock : 3425.000000MHz +revision : 2.1 (pvr 004b 0201) + +processor : 2 +cpu : POWER8E (raw), altivec supported +clock : 3425.000000MHz +revision : 2.1 (pvr 004b 0201) + +processor : 3 +cpu : POWER8E (raw), altivec supported +clock : 3425.000000MHz +revision : 2.1 (pvr 004b 0201) + +timebase : 512000000 +platform : pSeries +model : IBM pSeries (emulated by qemu) +machine : CHRP IBM pSeries (emulated by qemu)"; + + #[test] + fn power8_powerkvm() { + let cpuinfo = CpuInfo::from_str(POWER8E_POWERKVM).unwrap(); + assert_eq!(cpuinfo.field("cpu"), "POWER8E (raw), altivec supported"); + + assert!(cpuinfo.field("cpu").has("altivec")); + } + + const POWER5P: &str = r"processor : 0 +cpu : POWER5+ (gs) +clock : 1900.098000MHz +revision : 2.1 (pvr 003b 0201) + +processor : 1 +cpu : POWER5+ (gs) +clock : 1900.098000MHz +revision : 2.1 (pvr 003b 0201) + +processor : 2 +cpu : POWER5+ (gs) +clock : 1900.098000MHz +revision : 2.1 (pvr 003b 0201) + +processor : 3 +cpu : POWER5+ (gs) +clock : 1900.098000MHz +revision : 2.1 (pvr 003b 0201) + +processor : 4 +cpu : POWER5+ (gs) +clock : 1900.098000MHz +revision : 2.1 (pvr 003b 0201) + +processor : 5 +cpu : POWER5+ (gs) +clock : 1900.098000MHz +revision : 2.1 (pvr 003b 0201) + +processor : 6 +cpu : POWER5+ (gs) +clock : 1900.098000MHz +revision : 2.1 (pvr 003b 0201) + +processor : 7 +cpu : POWER5+ (gs) +clock : 1900.098000MHz +revision : 2.1 (pvr 003b 0201) + +timebase : 237331000 +platform : pSeries +machine : CHRP IBM,9133-55A"; + + #[test] + fn power5p() { + let cpuinfo = CpuInfo::from_str(POWER5P).unwrap(); + assert_eq!(cpuinfo.field("cpu"), "POWER5+ (gs)"); + + assert!(!cpuinfo.field("cpu").has("altivec")); + } +} diff --git a/tests/target/cfg_if/detect/os/linux/mips.rs b/tests/target/cfg_if/detect/os/linux/mips.rs new file mode 100644 index 000000000000..46a47fb7b78e --- /dev/null +++ b/tests/target/cfg_if/detect/os/linux/mips.rs @@ -0,0 +1,31 @@ +//! Run-time feature detection for MIPS on Linux. + +use super::auxvec; +use crate::detect::{bit, cache, Feature}; + +/// Performs run-time feature detection. +#[inline] +pub fn check_for(x: Feature) -> bool { + cache::test(x as u32, detect_features) +} + +/// Try to read the features from the auxiliary vector, and if that fails, try +/// to read them from `/proc/cpuinfo`. +fn detect_features() -> cache::Initializer { + let mut value = cache::Initializer::default(); + let enable_feature = |value: &mut cache::Initializer, f, enable| { + if enable { + value.set(f as u32); + } + }; + + // The values are part of the platform-specific [asm/hwcap.h][hwcap] + // + // [hwcap]: https://github.com/torvalds/linux/blob/master/arch/arm64/include/uapi/asm/hwcap.h + if let Ok(auxv) = auxvec::auxv() { + enable_feature(&mut value, Feature::msa, bit::test(auxv.hwcap, 1)); + return value; + } + // TODO: fall back via `cpuinfo`. + value +} diff --git a/tests/target/cfg_if/detect/os/linux/mod.rs b/tests/target/cfg_if/detect/os/linux/mod.rs new file mode 100644 index 000000000000..e02d5e6dcda7 --- /dev/null +++ b/tests/target/cfg_if/detect/os/linux/mod.rs @@ -0,0 +1,28 @@ +//! Run-time feature detection on Linux + +mod auxvec; + +#[cfg(feature = "std_detect_file_io")] +mod cpuinfo; + +cfg_if! { + if #[cfg(target_arch = "aarch64")] { + mod aarch64; + pub use self::aarch64::check_for; + } else if #[cfg(target_arch = "arm")] { + mod arm; + pub use self::arm::check_for; + } else if #[cfg(any(target_arch = "mips", target_arch = "mips64"))] { + mod mips; + pub use self::mips::check_for; + } else if #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] { + mod powerpc; + pub use self::powerpc::check_for; + } else { + use crate::detect::Feature; + /// Performs run-time feature detection. + pub fn check_for(_x: Feature) -> bool { + false + } + } +} diff --git a/tests/target/cfg_if/detect/os/linux/powerpc.rs b/tests/target/cfg_if/detect/os/linux/powerpc.rs new file mode 100644 index 000000000000..dc19bc8eda53 --- /dev/null +++ b/tests/target/cfg_if/detect/os/linux/powerpc.rs @@ -0,0 +1,41 @@ +//! Run-time feature detection for PowerPC on Linux. + +use super::{auxvec, cpuinfo}; +use crate::detect::{cache, Feature}; + +/// Performs run-time feature detection. +#[inline] +pub fn check_for(x: Feature) -> bool { + cache::test(x as u32, detect_features) +} + +/// Try to read the features from the auxiliary vector, and if that fails, try +/// to read them from /proc/cpuinfo. +fn detect_features() -> cache::Initializer { + let mut value = cache::Initializer::default(); + let enable_feature = |value: &mut cache::Initializer, f, enable| { + if enable { + value.set(f as u32); + } + }; + + // The values are part of the platform-specific [asm/cputable.h][cputable] + // + // [cputable]: https://github.com/torvalds/linux/blob/master/arch/powerpc/include/uapi/asm/cputable.h + if let Ok(auxv) = auxvec::auxv() { + // note: the PowerPC values are the mask to do the test (instead of the + // index of the bit to test like in ARM and Aarch64) + enable_feature(&mut value, Feature::altivec, auxv.hwcap & 0x10000000 != 0); + enable_feature(&mut value, Feature::vsx, auxv.hwcap & 0x00000080 != 0); + enable_feature(&mut value, Feature::power8, auxv.hwcap2 & 0x80000000 != 0); + return value; + } + + // PowerPC's /proc/cpuinfo lacks a proper Feature field, + // but `altivec` support is indicated in the `cpu` field. + if let Ok(c) = cpuinfo::CpuInfo::new() { + enable_feature(&mut value, Feature::altivec, c.field("cpu").has("altivec")); + return value; + } + value +} diff --git a/tests/target/cfg_if/detect/os/other.rs b/tests/target/cfg_if/detect/os/other.rs new file mode 100644 index 000000000000..23e399ea7907 --- /dev/null +++ b/tests/target/cfg_if/detect/os/other.rs @@ -0,0 +1,9 @@ +//! Other operating systems + +use crate::detect::Feature; + +/// Performs run-time feature detection. +#[inline] +pub fn check_for(_x: Feature) -> bool { + false +} diff --git a/tests/target/cfg_if/detect/os/x86.rs b/tests/target/cfg_if/detect/os/x86.rs new file mode 100644 index 000000000000..2e228aa37452 --- /dev/null +++ b/tests/target/cfg_if/detect/os/x86.rs @@ -0,0 +1,367 @@ +//! x86 run-time feature detection is OS independent. + +#[cfg(target_arch = "x86")] +use crate::arch::x86::*; +#[cfg(target_arch = "x86_64")] +use crate::arch::x86_64::*; + +use crate::mem; + +use crate::detect::{bit, cache, Feature}; + +/// Performs run-time feature detection. +#[inline] +pub fn check_for(x: Feature) -> bool { + cache::test(x as u32, detect_features) +} + +/// Run-time feature detection on x86 works by using the CPUID instruction. +/// +/// The [CPUID Wikipedia page][wiki_cpuid] contains +/// all the information about which flags to set to query which values, and in +/// which registers these are reported. +/// +/// The definitive references are: +/// - [Intel 64 and IA-32 Architectures Software Developer's Manual Volume 2: +/// Instruction Set Reference, A-Z][intel64_ref]. +/// - [AMD64 Architecture Programmer's Manual, Volume 3: General-Purpose and +/// System Instructions][amd64_ref]. +/// +/// [wiki_cpuid]: https://en.wikipedia.org/wiki/CPUID +/// [intel64_ref]: http://www.intel.de/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf +/// [amd64_ref]: http://support.amd.com/TechDocs/24594.pdf +#[allow(clippy::similar_names)] +fn detect_features() -> cache::Initializer { + let mut value = cache::Initializer::default(); + + // If the x86 CPU does not support the CPUID instruction then it is too + // old to support any of the currently-detectable features. + if !has_cpuid() { + return value; + } + + // Calling `__cpuid`/`__cpuid_count` from here on is safe because the CPU + // has `cpuid` support. + + // 0. EAX = 0: Basic Information: + // - EAX returns the "Highest Function Parameter", that is, the maximum + // leaf value for subsequent calls of `cpuinfo` in range [0, + // 0x8000_0000]. - The vendor ID is stored in 12 u8 ascii chars, + // returned in EBX, EDX, and ECX (in that order): + let (max_basic_leaf, vendor_id) = unsafe { + let CpuidResult { + eax: max_basic_leaf, + ebx, + ecx, + edx, + } = __cpuid(0); + let vendor_id: [[u8; 4]; 3] = [ + mem::transmute(ebx), + mem::transmute(edx), + mem::transmute(ecx), + ]; + let vendor_id: [u8; 12] = mem::transmute(vendor_id); + (max_basic_leaf, vendor_id) + }; + + if max_basic_leaf < 1 { + // Earlier Intel 486, CPUID not implemented + return value; + } + + // EAX = 1, ECX = 0: Queries "Processor Info and Feature Bits"; + // Contains information about most x86 features. + let CpuidResult { + ecx: proc_info_ecx, + edx: proc_info_edx, + .. + } = unsafe { __cpuid(0x0000_0001_u32) }; + + // EAX = 7, ECX = 0: Queries "Extended Features"; + // Contains information about bmi,bmi2, and avx2 support. + let (extended_features_ebx, extended_features_ecx) = if max_basic_leaf >= 7 { + let CpuidResult { ebx, ecx, .. } = unsafe { __cpuid(0x0000_0007_u32) }; + (ebx, ecx) + } else { + (0, 0) // CPUID does not support "Extended Features" + }; + + // EAX = 0x8000_0000, ECX = 0: Get Highest Extended Function Supported + // - EAX returns the max leaf value for extended information, that is, + // `cpuid` calls in range [0x8000_0000; u32::MAX]: + let CpuidResult { + eax: extended_max_basic_leaf, + .. + } = unsafe { __cpuid(0x8000_0000_u32) }; + + // EAX = 0x8000_0001, ECX=0: Queries "Extended Processor Info and Feature + // Bits" + let extended_proc_info_ecx = if extended_max_basic_leaf >= 1 { + let CpuidResult { ecx, .. } = unsafe { __cpuid(0x8000_0001_u32) }; + ecx + } else { + 0 + }; + + { + // borrows value till the end of this scope: + let mut enable = |r, rb, f| { + if bit::test(r as usize, rb) { + value.set(f as u32); + } + }; + + enable(proc_info_ecx, 0, Feature::sse3); + enable(proc_info_ecx, 1, Feature::pclmulqdq); + enable(proc_info_ecx, 9, Feature::ssse3); + enable(proc_info_ecx, 13, Feature::cmpxchg16b); + enable(proc_info_ecx, 19, Feature::sse4_1); + enable(proc_info_ecx, 20, Feature::sse4_2); + enable(proc_info_ecx, 23, Feature::popcnt); + enable(proc_info_ecx, 25, Feature::aes); + enable(proc_info_ecx, 29, Feature::f16c); + enable(proc_info_ecx, 30, Feature::rdrand); + enable(extended_features_ebx, 18, Feature::rdseed); + enable(extended_features_ebx, 19, Feature::adx); + enable(extended_features_ebx, 11, Feature::rtm); + enable(proc_info_edx, 4, Feature::tsc); + enable(proc_info_edx, 23, Feature::mmx); + enable(proc_info_edx, 24, Feature::fxsr); + enable(proc_info_edx, 25, Feature::sse); + enable(proc_info_edx, 26, Feature::sse2); + enable(extended_features_ebx, 29, Feature::sha); + + enable(extended_features_ebx, 3, Feature::bmi); + enable(extended_features_ebx, 8, Feature::bmi2); + + // `XSAVE` and `AVX` support: + let cpu_xsave = bit::test(proc_info_ecx as usize, 26); + if cpu_xsave { + // 0. Here the CPU supports `XSAVE`. + + // 1. Detect `OSXSAVE`, that is, whether the OS is AVX enabled and + // supports saving the state of the AVX/AVX2 vector registers on + // context-switches, see: + // + // - [intel: is avx enabled?][is_avx_enabled], + // - [mozilla: sse.cpp][mozilla_sse_cpp]. + // + // [is_avx_enabled]: https://software.intel.com/en-us/blogs/2011/04/14/is-avx-enabled + // [mozilla_sse_cpp]: https://hg.mozilla.org/mozilla-central/file/64bab5cbb9b6/mozglue/build/SSE.cpp#l190 + let cpu_osxsave = bit::test(proc_info_ecx as usize, 27); + + if cpu_osxsave { + // 2. The OS must have signaled the CPU that it supports saving and + // restoring the: + // + // * SSE -> `XCR0.SSE[1]` + // * AVX -> `XCR0.AVX[2]` + // * AVX-512 -> `XCR0.AVX-512[7:5]`. + // + // by setting the corresponding bits of `XCR0` to `1`. + // + // This is safe because the CPU supports `xsave` + // and the OS has set `osxsave`. + let xcr0 = unsafe { _xgetbv(0) }; + // Test `XCR0.SSE[1]` and `XCR0.AVX[2]` with the mask `0b110 == 6`: + let os_avx_support = xcr0 & 6 == 6; + // Test `XCR0.AVX-512[7:5]` with the mask `0b1110_0000 == 224`: + let os_avx512_support = xcr0 & 224 == 224; + + // Only if the OS and the CPU support saving/restoring the AVX + // registers we enable `xsave` support: + if os_avx_support { + // See "13.3 ENABLING THE XSAVE FEATURE SET AND XSAVE-ENABLED + // FEATURES" in the "Intel® 64 and IA-32 Architectures Software + // Developer’s Manual, Volume 1: Basic Architecture": + // + // "Software enables the XSAVE feature set by setting + // CR4.OSXSAVE[bit 18] to 1 (e.g., with the MOV to CR4 + // instruction). If this bit is 0, execution of any of XGETBV, + // XRSTOR, XRSTORS, XSAVE, XSAVEC, XSAVEOPT, XSAVES, and XSETBV + // causes an invalid-opcode exception (#UD)" + // + enable(proc_info_ecx, 26, Feature::xsave); + + // For `xsaveopt`, `xsavec`, and `xsaves` we need to query: + // Processor Extended State Enumeration Sub-leaf (EAX = 0DH, + // ECX = 1): + if max_basic_leaf >= 0xd { + let CpuidResult { + eax: proc_extended_state1_eax, + .. + } = unsafe { __cpuid_count(0xd_u32, 1) }; + enable(proc_extended_state1_eax, 0, Feature::xsaveopt); + enable(proc_extended_state1_eax, 1, Feature::xsavec); + enable(proc_extended_state1_eax, 3, Feature::xsaves); + } + + // FMA (uses 256-bit wide registers): + enable(proc_info_ecx, 12, Feature::fma); + + // And AVX/AVX2: + enable(proc_info_ecx, 28, Feature::avx); + enable(extended_features_ebx, 5, Feature::avx2); + + // For AVX-512 the OS also needs to support saving/restoring + // the extended state, only then we enable AVX-512 support: + if os_avx512_support { + enable(extended_features_ebx, 16, Feature::avx512f); + enable(extended_features_ebx, 17, Feature::avx512dq); + enable(extended_features_ebx, 21, Feature::avx512_ifma); + enable(extended_features_ebx, 26, Feature::avx512pf); + enable(extended_features_ebx, 27, Feature::avx512er); + enable(extended_features_ebx, 28, Feature::avx512cd); + enable(extended_features_ebx, 30, Feature::avx512bw); + enable(extended_features_ebx, 31, Feature::avx512vl); + enable(extended_features_ecx, 1, Feature::avx512_vbmi); + enable(extended_features_ecx, 14, Feature::avx512_vpopcntdq); + } + } + } + } + + // This detects ABM on AMD CPUs and LZCNT on Intel CPUs. + // On intel CPUs with popcnt, lzcnt implements the + // "missing part" of ABM, so we map both to the same + // internal feature. + // + // The `is_x86_feature_detected!("lzcnt")` macro then + // internally maps to Feature::abm. + enable(extended_proc_info_ecx, 5, Feature::abm); + // As Hygon Dhyana originates from AMD technology and shares most of the architecture with + // AMD's family 17h, but with different CPU Vendor ID("HygonGenuine")/Family series + // number(Family 18h). + // + // For CPUID feature bits, Hygon Dhyana(family 18h) share the same definition with AMD + // family 17h. + // + // Related AMD CPUID specification is https://www.amd.com/system/files/TechDocs/25481.pdf. + // Related Hygon kernel patch can be found on + // http://lkml.kernel.org/r/5ce86123a7b9dad925ac583d88d2f921040e859b.1538583282.git.puwen@hygon.cn + if vendor_id == *b"AuthenticAMD" || vendor_id == *b"HygonGenuine" { + // These features are available on AMD arch CPUs: + enable(extended_proc_info_ecx, 6, Feature::sse4a); + enable(extended_proc_info_ecx, 21, Feature::tbm); + } + } + + value +} + +#[cfg(test)] +mod tests { + extern crate cupid; + + #[test] + fn dump() { + println!("aes: {:?}", is_x86_feature_detected!("aes")); + println!("pclmulqdq: {:?}", is_x86_feature_detected!("pclmulqdq")); + println!("rdrand: {:?}", is_x86_feature_detected!("rdrand")); + println!("rdseed: {:?}", is_x86_feature_detected!("rdseed")); + println!("tsc: {:?}", is_x86_feature_detected!("tsc")); + println!("sse: {:?}", is_x86_feature_detected!("sse")); + println!("sse2: {:?}", is_x86_feature_detected!("sse2")); + println!("sse3: {:?}", is_x86_feature_detected!("sse3")); + println!("ssse3: {:?}", is_x86_feature_detected!("ssse3")); + println!("sse4.1: {:?}", is_x86_feature_detected!("sse4.1")); + println!("sse4.2: {:?}", is_x86_feature_detected!("sse4.2")); + println!("sse4a: {:?}", is_x86_feature_detected!("sse4a")); + println!("sha: {:?}", is_x86_feature_detected!("sha")); + println!("avx: {:?}", is_x86_feature_detected!("avx")); + println!("avx2: {:?}", is_x86_feature_detected!("avx2")); + println!("avx512f {:?}", is_x86_feature_detected!("avx512f")); + println!("avx512cd {:?}", is_x86_feature_detected!("avx512cd")); + println!("avx512er {:?}", is_x86_feature_detected!("avx512er")); + println!("avx512pf {:?}", is_x86_feature_detected!("avx512pf")); + println!("avx512bw {:?}", is_x86_feature_detected!("avx512bw")); + println!("avx512dq {:?}", is_x86_feature_detected!("avx512dq")); + println!("avx512vl {:?}", is_x86_feature_detected!("avx512vl")); + println!("avx512_ifma {:?}", is_x86_feature_detected!("avx512ifma")); + println!("avx512_vbmi {:?}", is_x86_feature_detected!("avx512vbmi")); + println!( + "avx512_vpopcntdq {:?}", + is_x86_feature_detected!("avx512vpopcntdq") + ); + println!("fma: {:?}", is_x86_feature_detected!("fma")); + println!("abm: {:?}", is_x86_feature_detected!("abm")); + println!("bmi: {:?}", is_x86_feature_detected!("bmi1")); + println!("bmi2: {:?}", is_x86_feature_detected!("bmi2")); + println!("tbm: {:?}", is_x86_feature_detected!("tbm")); + println!("popcnt: {:?}", is_x86_feature_detected!("popcnt")); + println!("lzcnt: {:?}", is_x86_feature_detected!("lzcnt")); + println!("fxsr: {:?}", is_x86_feature_detected!("fxsr")); + println!("xsave: {:?}", is_x86_feature_detected!("xsave")); + println!("xsaveopt: {:?}", is_x86_feature_detected!("xsaveopt")); + println!("xsaves: {:?}", is_x86_feature_detected!("xsaves")); + println!("xsavec: {:?}", is_x86_feature_detected!("xsavec")); + println!("cmpxchg16b: {:?}", is_x86_feature_detected!("cmpxchg16b")); + println!("adx: {:?}", is_x86_feature_detected!("adx")); + println!("rtm: {:?}", is_x86_feature_detected!("rtm")); + } + + #[test] + fn compare_with_cupid() { + let information = cupid::master().unwrap(); + assert_eq!(is_x86_feature_detected!("aes"), information.aesni()); + assert_eq!( + is_x86_feature_detected!("pclmulqdq"), + information.pclmulqdq() + ); + assert_eq!(is_x86_feature_detected!("rdrand"), information.rdrand()); + assert_eq!(is_x86_feature_detected!("rdseed"), information.rdseed()); + assert_eq!(is_x86_feature_detected!("tsc"), information.tsc()); + assert_eq!(is_x86_feature_detected!("sse"), information.sse()); + assert_eq!(is_x86_feature_detected!("sse2"), information.sse2()); + assert_eq!(is_x86_feature_detected!("sse3"), information.sse3()); + assert_eq!(is_x86_feature_detected!("ssse3"), information.ssse3()); + assert_eq!(is_x86_feature_detected!("sse4.1"), information.sse4_1()); + assert_eq!(is_x86_feature_detected!("sse4.2"), information.sse4_2()); + assert_eq!(is_x86_feature_detected!("sse4a"), information.sse4a()); + assert_eq!(is_x86_feature_detected!("sha"), information.sha()); + assert_eq!(is_x86_feature_detected!("avx"), information.avx()); + assert_eq!(is_x86_feature_detected!("avx2"), information.avx2()); + assert_eq!(is_x86_feature_detected!("avx512f"), information.avx512f()); + assert_eq!(is_x86_feature_detected!("avx512cd"), information.avx512cd()); + assert_eq!(is_x86_feature_detected!("avx512er"), information.avx512er()); + assert_eq!(is_x86_feature_detected!("avx512pf"), information.avx512pf()); + assert_eq!(is_x86_feature_detected!("avx512bw"), information.avx512bw()); + assert_eq!(is_x86_feature_detected!("avx512dq"), information.avx512dq()); + assert_eq!(is_x86_feature_detected!("avx512vl"), information.avx512vl()); + assert_eq!( + is_x86_feature_detected!("avx512ifma"), + information.avx512_ifma() + ); + assert_eq!( + is_x86_feature_detected!("avx512vbmi"), + information.avx512_vbmi() + ); + assert_eq!( + is_x86_feature_detected!("avx512vpopcntdq"), + information.avx512_vpopcntdq() + ); + assert_eq!(is_x86_feature_detected!("fma"), information.fma()); + assert_eq!(is_x86_feature_detected!("bmi1"), information.bmi1()); + assert_eq!(is_x86_feature_detected!("bmi2"), information.bmi2()); + assert_eq!(is_x86_feature_detected!("popcnt"), information.popcnt()); + assert_eq!(is_x86_feature_detected!("abm"), information.lzcnt()); + assert_eq!(is_x86_feature_detected!("tbm"), information.tbm()); + assert_eq!(is_x86_feature_detected!("lzcnt"), information.lzcnt()); + assert_eq!(is_x86_feature_detected!("xsave"), information.xsave()); + assert_eq!(is_x86_feature_detected!("xsaveopt"), information.xsaveopt()); + assert_eq!( + is_x86_feature_detected!("xsavec"), + information.xsavec_and_xrstor() + ); + assert_eq!( + is_x86_feature_detected!("xsaves"), + information.xsaves_xrstors_and_ia32_xss() + ); + assert_eq!( + is_x86_feature_detected!("cmpxchg16b"), + information.cmpxchg16b(), + ); + assert_eq!(is_x86_feature_detected!("adx"), information.adx(),); + assert_eq!(is_x86_feature_detected!("rtm"), information.rtm(),); + } +} diff --git a/tests/target/cfg_if/lib.rs b/tests/target/cfg_if/lib.rs new file mode 100644 index 000000000000..8b3bb304f1c8 --- /dev/null +++ b/tests/target/cfg_if/lib.rs @@ -0,0 +1,49 @@ +//! Run-time feature detection for the Rust standard library. +//! +//! To detect whether a feature is enabled in the system running the binary +//! use one of the appropriate macro for the target: +//! +//! * `x86` and `x86_64`: [`is_x86_feature_detected`] +//! * `arm`: [`is_arm_feature_detected`] +//! * `aarch64`: [`is_aarch64_feature_detected`] +//! * `mips`: [`is_mips_feature_detected`] +//! * `mips64`: [`is_mips64_feature_detected`] +//! * `powerpc`: [`is_powerpc_feature_detected`] +//! * `powerpc64`: [`is_powerpc64_feature_detected`] + +#![unstable(feature = "stdsimd", issue = "27731")] +#![feature(const_fn, staged_api, stdsimd, doc_cfg, allow_internal_unstable)] +#![allow(clippy::shadow_reuse)] +#![deny(clippy::missing_inline_in_public_items)] +#![cfg_attr(target_os = "linux", feature(linkage))] +#![cfg_attr(all(target_os = "freebsd", target_arch = "aarch64"), feature(asm))] +#![cfg_attr(stdsimd_strict, deny(warnings))] +#![cfg_attr(test, allow(unused_imports))] +#![no_std] + +#[macro_use] +extern crate cfg_if; + +cfg_if! { + if #[cfg(feature = "std_detect_file_io")] { + #[cfg_attr(test, macro_use(println))] + extern crate std; + + #[allow(unused_imports)] + use std::{arch, fs, io, mem, sync}; + } else { + #[cfg(test)] + #[macro_use(println)] + extern crate std; + + #[allow(unused_imports)] + use core::{arch, mem, sync}; + } +} + +#[cfg(feature = "std_detect_dlsym_getauxval")] +extern crate libc; + +#[doc(hidden)] +#[unstable(feature = "stdsimd", issue = "27731")] +pub mod detect; diff --git a/tests/target/cfg_if/mod.rs b/tests/target/cfg_if/mod.rs new file mode 100644 index 000000000000..b630e7ff383a --- /dev/null +++ b/tests/target/cfg_if/mod.rs @@ -0,0 +1,5 @@ +//! `std_detect` + +#[doc(hidden)] // unstable implementation detail +#[unstable(feature = "stdsimd", issue = "27731")] +pub mod detect; diff --git a/tests/target/cfg_mod/bar.rs b/tests/target/cfg_mod/bar.rs new file mode 100644 index 000000000000..20dc5b4a080c --- /dev/null +++ b/tests/target/cfg_mod/bar.rs @@ -0,0 +1,3 @@ +fn bar() -> &str { + "bar" +} diff --git a/tests/target/cfg_mod/dir/dir1/dir2/wasm32.rs b/tests/target/cfg_mod/dir/dir1/dir2/wasm32.rs new file mode 100644 index 000000000000..ac437e4225ab --- /dev/null +++ b/tests/target/cfg_mod/dir/dir1/dir2/wasm32.rs @@ -0,0 +1,3 @@ +fn wasm32() -> &str { + "wasm32" +} diff --git a/tests/target/cfg_mod/dir/dir1/dir3/wasm32.rs b/tests/target/cfg_mod/dir/dir1/dir3/wasm32.rs new file mode 100644 index 000000000000..ac437e4225ab --- /dev/null +++ b/tests/target/cfg_mod/dir/dir1/dir3/wasm32.rs @@ -0,0 +1,3 @@ +fn wasm32() -> &str { + "wasm32" +} diff --git a/tests/target/cfg_mod/foo.rs b/tests/target/cfg_mod/foo.rs new file mode 100644 index 000000000000..053c8e6f3ed0 --- /dev/null +++ b/tests/target/cfg_mod/foo.rs @@ -0,0 +1,3 @@ +fn foo() -> &str { + "foo" +} diff --git a/tests/target/cfg_mod/mod.rs b/tests/target/cfg_mod/mod.rs new file mode 100644 index 000000000000..45ba86f11b3f --- /dev/null +++ b/tests/target/cfg_mod/mod.rs @@ -0,0 +1,10 @@ +#[cfg_attr(feature = "foo", path = "foo.rs")] +#[cfg_attr(not(feture = "foo"), path = "bar.rs")] +mod sub_mod; + +#[cfg_attr(target_arch = "wasm32", path = "dir/dir1/dir2/wasm32.rs")] +#[cfg_attr(not(target_arch = "wasm32"), path = "dir/dir1/dir3/wasm32.rs")] +mod wasm32; + +#[some_attr(path = "somewhere.rs")] +mod other; diff --git a/tests/target/cfg_mod/other.rs b/tests/target/cfg_mod/other.rs new file mode 100644 index 000000000000..5929b8dcf531 --- /dev/null +++ b/tests/target/cfg_mod/other.rs @@ -0,0 +1,3 @@ +fn other() -> &str { + "other" +} diff --git a/tests/target/cfg_mod/wasm32.rs b/tests/target/cfg_mod/wasm32.rs new file mode 100644 index 000000000000..ac437e4225ab --- /dev/null +++ b/tests/target/cfg_mod/wasm32.rs @@ -0,0 +1,3 @@ +fn wasm32() -> &str { + "wasm32" +} diff --git a/tests/target/chains-visual.rs b/tests/target/chains-visual.rs new file mode 100644 index 000000000000..76ef99a4b59f --- /dev/null +++ b/tests/target/chains-visual.rs @@ -0,0 +1,158 @@ +// rustfmt-indent_style: Visual +// Test chain formatting. + +fn main() { + // Don't put chains on a single line if it wasn't so in source. + let a = b.c.d.1.foo(|x| x + 1); + + bbbbbbbbbbbbbbbbbbb.ccccccccccccccccccccccccccccccccccccc + .ddddddddddddddddddddddddddd(); + + bbbbbbbbbbbbbbbbbbb.ccccccccccccccccccccccccccccccccccccc + .ddddddddddddddddddddddddddd + .eeeeeeee(); + + // Test case where first chain element isn't a path, but is shorter than + // the size of a tab. + x().y(|| match cond() { + true => (), + false => (), + }); + + loong_func().quux(move || if true { 1 } else { 2 }); + + some_fuuuuuuuuunction().method_call_a(aaaaa, bbbbb, |c| { + let x = c; + x + }); + + some_fuuuuuuuuunction().method_call_a(aaaaa, bbbbb, |c| { + let x = c; + x + }) + .method_call_b(aaaaa, bbbbb, |c| { + let x = c; + x + }); + + fffffffffffffffffffffffffffffffffff(a, { + SCRIPT_TASK_ROOT.with(|root| { + *root.borrow_mut() = Some(&script_task); + }); + }); + + let suuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuum = + xxxxxxx.map(|x| x + 5) + .map(|x| x / 2) + .fold(0, |acc, x| acc + x); + + aaaaaaaaaaaaaaaa.map(|x| { + x += 1; + x + }) + .filter(some_mod::some_filter) +} + +fn floaters() { + let z = Foo { field1: val1, + field2: val2 }; + + let x = Foo { field1: val1, + field2: val2 }.method_call() + .method_call(); + + let y = if cond { val1 } else { val2 }.method_call(); + + { + match x { + PushParam => { + // params are 1-indexed + stack.push(mparams[match cur.to_digit(10) { + Some(d) => d as usize - 1, + None => return Err("bad param number".to_owned()), + }].clone()); + } + } + } + + if cond { + some(); + } else { + none(); + }.bar() + .baz(); + + Foo { x: val }.baz(|| { + force(); + multiline(); + }) + .quux(); + + Foo { y: i_am_multi_line, + z: ok }.baz(|| { + force(); + multiline(); + }) + .quux(); + + a + match x { + true => "yay!", + false => "boo!", + }.bar() +} + +fn is_replaced_content() -> bool { + constellat.send(ConstellationMsg::ViewportConstrained(self.id, constraints)) + .unwrap(); +} + +fn issue587() { + a.b::<()>(c); + + std::mem::transmute(dl.symbol::<()>("init").unwrap()) +} + +fn issue_1389() { + let names = String::from_utf8(names)?.split('|') + .map(str::to_owned) + .collect(); +} + +fn issue1217() -> Result { + let random_chars: String = OsRng::new()?.gen_ascii_chars() + .take(self.bit_length) + .collect(); + + Ok(Mnemonic::new(&random_chars)) +} + +fn issue1236(options: Vec) -> Result> { + let process = Command::new("dmenu").stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn() + .chain_err(|| "failed to spawn dmenu")?; +} + +fn issue1434() { + for _ in 0..100 { + let prototype_id = + PrototypeIdData::from_reader::<_, B>(&mut self.file_cursor).chain_err(|| { + format!("could not read prototype ID at offset {:#010x}", + current_offset) + })?; + } +} + +fn issue2264() { + { + something.function() + .map(|| { + if let a_very_very_very_very_very_very_very_very_long_variable = + compute_this_variable() + { + println!("Hello"); + } + }) + .collect(); + } +} diff --git a/tests/target/chains.rs b/tests/target/chains.rs new file mode 100644 index 000000000000..292da2981954 --- /dev/null +++ b/tests/target/chains.rs @@ -0,0 +1,306 @@ +// rustfmt-use_small_heuristics: Off +// Test chain formatting. + +fn main() { + let a = b.c.d.1.foo(|x| x + 1); + + bbbbbbbbbbbbbbbbbbb.ccccccccccccccccccccccccccccccccccccc.ddddddddddddddddddddddddddd(); + + bbbbbbbbbbbbbbbbbbb + .ccccccccccccccccccccccccccccccccccccc + .ddddddddddddddddddddddddddd + .eeeeeeee(); + + let f = fooooooooooooooooooooooooooooooooooooooooooooooooooo + .baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaar; + + // Test case where first chain element isn't a path, but is shorter than + // the size of a tab. + x().y(|| match cond() { + true => (), + false => (), + }); + + loong_func().quux(move || { + if true { + 1 + } else { + 2 + } + }); + + some_fuuuuuuuuunction().method_call_a(aaaaa, bbbbb, |c| { + let x = c; + x + }); + + some_fuuuuuuuuunction() + .method_call_a(aaaaa, bbbbb, |c| { + let x = c; + x + }) + .method_call_b(aaaaa, bbbbb, |c| { + let x = c; + x + }); + + fffffffffffffffffffffffffffffffffff(a, { + SCRIPT_TASK_ROOT.with(|root| { + *root.borrow_mut() = Some(&script_task); + }); + }); + + let suuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuum = + xxxxxxx.map(|x| x + 5).map(|x| x / 2).fold(0, |acc, x| acc + x); + + body.fold(Body::new(), |mut body, chunk| { + body.extend(chunk); + Ok(body) + }) + .and_then(move |body| { + let req = Request::from_parts(parts, body); + f(req).map_err(|_| io::Error::new(io::ErrorKind::Other, "")) + }); + + aaaaaaaaaaaaaaaa + .map(|x| { + x += 1; + x + }) + .filter(some_mod::some_filter) +} + +fn floaters() { + let z = Foo { + field1: val1, + field2: val2, + }; + + let x = Foo { + field1: val1, + field2: val2, + } + .method_call() + .method_call(); + + let y = if cond { + val1 + } else { + val2 + } + .method_call(); + + { + match x { + PushParam => { + // params are 1-indexed + stack.push( + mparams[match cur.to_digit(10) { + Some(d) => d as usize - 1, + None => return Err("bad param number".to_owned()), + }] + .clone(), + ); + } + } + } + + if cond { + some(); + } else { + none(); + } + .bar() + .baz(); + + Foo { + x: val, + } + .baz(|| { + force(); + multiline(); + }) + .quux(); + + Foo { + y: i_am_multi_line, + z: ok, + } + .baz(|| { + force(); + multiline(); + }) + .quux(); + + a + match x { + true => "yay!", + false => "boo!", + } + .bar() +} + +fn is_replaced_content() -> bool { + constellat.send(ConstellationMsg::ViewportConstrained(self.id, constraints)).unwrap(); +} + +fn issue587() { + a.b::<()>(c); + + std::mem::transmute(dl.symbol::<()>("init").unwrap()) +} + +fn try_shorthand() { + let x = expr?; + let y = expr.kaas()?.test(); + let loooooooooooooooooooooooooooooooooooooooooong = + does_this?.look?.good?.should_we_break?.after_the_first_question_mark?; + let yyyy = expr?.another?.another?.another?.another?.another?.another?.another?.another?.test(); + let zzzz = expr?.another?.another?.another?.another?; + let aaa = x??????????????????????????????????????????????????????????????????????????; + + let y = a + .very + .loooooooooooooooooooooooooooooooooooooong() + .chain() + .inside() + .weeeeeeeeeeeeeee()? + .test() + .0 + .x; + + parameterized(f, substs, def_id, Ns::Value, &[], |tcx| tcx.lookup_item_type(def_id).generics)?; + fooooooooooooooooooooooooooo()? + .bar()? + .baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaz()?; +} + +fn issue_1004() { + match *self { + ty::ImplOrTraitItem::MethodTraitItem(ref i) => write!(f, "{:?}", i), + ty::ImplOrTraitItem::ConstTraitItem(ref i) => write!(f, "{:?}", i), + ty::ImplOrTraitItem::TypeTraitItem(ref i) => write!(f, "{:?}", i), + }?; + + ty::tls::with(|tcx| { + let tap = ty::Binder(TraitAndProjections(principal, projections)); + in_binder(f, tcx, &ty::Binder(""), Some(tap)) + })?; +} + +fn issue1392() { + test_method( + r#" + if foo { + a(); + } + else { + b(); + } + "# + .trim(), + ); +} + +// #2067 +impl Settings { + fn save(&self) -> Result<()> { + let mut file = File::create(&settings_path) + .chain_err(|| ErrorKind::WriteError(settings_path.clone()))?; + } +} + +fn issue2126() { + { + { + { + { + { + let x = self + .span_from(sub_span.expect("No span found for struct arant variant")); + self.sspanpan_from_span( + sub_span.expect("No span found for struct variant"), + ); + let x = self.spanpan_from_span( + sub_span.expect("No span found for struct variant"), + )?; + } + } + } + } + } +} + +// #2200 +impl Foo { + pub fn from_ast(diagnostic: &::errors::Handler, attrs: &[ast::Attribute]) -> Attributes { + let other_attrs = attrs + .iter() + .filter_map(|attr| { + attr.with_desugared_doc(|attr| { + if attr.check_name("doc") { + if let Some(mi) = attr.meta() { + if let Some(value) = mi.value_str() { + doc_strings.push(DocFragment::Include( + line, attr.span, filename, contents, + )); + } + } + } + }) + }) + .collect(); + } +} + +// #2415 +// Avoid orphan in chain +fn issue2415() { + let base_url = (|| { + // stuff + + Ok((|| { + // stuff + Some(value.to_string()) + })() + .ok_or("")?) + })() + .unwrap_or_else(|_: Box<::std::error::Error>| String::from("")); +} + +impl issue_2786 { + fn thing(&self) { + foo(|a| { + println!("a"); + println!("b"); + }) + .bar(|c| { + println!("a"); + println!("b"); + }) + .baz(|c| { + println!("a"); + println!("b"); + }) + } +} + +fn issue_2773() { + let bar = Some(0); + bar.or_else(|| { + // do stuff + None + }) + .or_else(|| { + // do other stuff + None + }) + .and_then(|val| { + // do this stuff + None + }); +} + +fn issue_3034() { + disallowed_headers.iter().any(|header| *header == name) + || disallowed_header_prefixes.iter().any(|prefix| name.starts_with(prefix)) +} diff --git a/tests/target/chains_with_comment.rs b/tests/target/chains_with_comment.rs new file mode 100644 index 000000000000..522d70713bcd --- /dev/null +++ b/tests/target/chains_with_comment.rs @@ -0,0 +1,137 @@ +// Chains with comment. + +fn main() { + let x = y // comment + .z; + + foo // foo + // comment after parent + .x + .y + // comment 1 + .bar() // comment after bar() + // comment 2 + .foobar + // comment after + // comment 3 + .baz(x, y, z); + + self.rev_dep_graph + .iter() + // Remove nodes that are not dirty + .filter(|&(unit, _)| dirties.contains(&unit)) + // Retain only dirty dependencies of the ones that are dirty + .map(|(k, deps)| { + ( + k.clone(), + deps.iter() + .cloned() + .filter(|d| dirties.contains(&d)) + .collect(), + ) + }); + + let y = expr /* comment */ + .kaas()? + // comment + .test(); + let loooooooooooooooooooooooooooooooooooooooooong = does_this? + .look? + .good? + .should_we_break? + .after_the_first_question_mark?; + let zzzz = expr? // comment after parent + // comment 0 + .another??? // comment 1 + .another???? // comment 2 + .another? // comment 3 + .another?; + + let y = a + .very + .loooooooooooooooooooooooooooooooooooooong() /* comment */ + .chain() + .inside() /* comment */ + .weeeeeeeeeeeeeee()? + .test() + .0 + .x; + + parameterized(f, substs, def_id, Ns::Value, &[], |tcx| { + tcx.lookup_item_type(def_id).generics + })?; + fooooooooooooooooooooooooooo()? + .bar()? + .baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaz()?; + + // #2559 + App::new("cargo-cache") + .version(crate_version!()) + .bin_name("cargo") + .about("Manage cargo cache") + .author("matthiaskrgr") + .subcommand( + SubCommand::with_name("cache") + .version(crate_version!()) + .bin_name("cargo-cache") + .about("Manage cargo cache") + .author("matthiaskrgr") + .arg(&list_dirs) + .arg(&remove_dir) + .arg(&gc_repos) + .arg(&info) + .arg(&keep_duplicate_crates) + .arg(&dry_run) + .arg(&auto_clean) + .arg(&auto_clean_expensive), + ) // subcommand + .arg(&list_dirs); +} + +// #2177 +impl Foo { + fn dirty_rev_dep_graph( + &self, + dirties: &HashSet, + ) -> HashMap> { + let dirties = self.transitive_dirty_units(dirties); + trace!("transitive_dirty_units: {:?}", dirties); + + self.rev_dep_graph + .iter() + // Remove nodes that are not dirty + .filter(|&(unit, _)| dirties.contains(&unit)) + // Retain only dirty dependencies of the ones that are dirty + .map(|(k, deps)| { + ( + k.clone(), + deps.iter() + .cloned() + .filter(|d| dirties.contains(&d)) + .collect(), + ) + }) + } +} + +// #2907 +fn foo() { + let x = foo + .bar??? // comment + .baz; + let x = foo + .bar??? + // comment + .baz; + let x = foo + .bar??? // comment + // comment + .baz; + let x = foo + .bar??????????????? // comment + // comment + // comment + // comment + // comment + .baz; +} diff --git a/tests/target/closure-block-inside-macro.rs b/tests/target/closure-block-inside-macro.rs new file mode 100644 index 000000000000..b3ddfb51263b --- /dev/null +++ b/tests/target/closure-block-inside-macro.rs @@ -0,0 +1,9 @@ +// #1547 +fuzz_target!(|data: &[u8]| if let Some(first) = data.first() { + let index = *first as usize; + if index >= ENCODINGS.len() { + return; + } + let encoding = ENCODINGS[index]; + dispatch_test(encoding, &data[1..]); +}); diff --git a/tests/target/closure.rs b/tests/target/closure.rs new file mode 100644 index 000000000000..e8b4ff7a96bb --- /dev/null +++ b/tests/target/closure.rs @@ -0,0 +1,256 @@ +// rustfmt-normalize_comments: true +// Closures + +fn main() { + let square = (|i: i32| i * i); + + let commented = |// first + a, // argument + // second + b: WithType, // argument + // ignored + _| { + ( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, + ) + }; + + let block_body = move |xxxxxxxxxxxxxxxxxxxxxxxxxxxxx, + ref yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy| { + xxxxxxxxxxxxxxxxxxxxxxxxxxxxx + yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy + }; + + let loooooooooooooong_name = |field| { + // format comments. + if field.node.attrs.len() > 0 { + field.node.attrs[0].span.lo() + } else { + field.span.lo() + } + }; + + let unblock_me = |trivial| closure(); + + let empty = |arg| {}; + + let simple = |arg| { + // comment formatting + foo(arg) + }; + + let test = || { + do_something(); + do_something_else(); + }; + + let arg_test = + |big_argument_name, test123| looooooooooooooooooong_function_naaaaaaaaaaaaaaaaame(); + + let arg_test = + |big_argument_name, test123| looooooooooooooooooong_function_naaaaaaaaaaaaaaaaame(); + + let simple_closure = move || -> () {}; + + let closure = |input: Ty| -> Option { foo() }; + + let closure_with_return_type = + |aaaaaaaaaaaaaaaaaaaaaaarg1, aaaaaaaaaaaaaaaaaaaaaaarg2| -> Strong { "sup".to_owned() }; + + |arg1, arg2, _, _, arg3, arg4| { + let temp = arg4 + arg3; + arg2 * arg1 - temp + }; + + let block_body_with_comment = args.iter().map(|a| { + // Emitting only dep-info is possible only for final crate type, as + // as others may emit required metadata for dependent crate types + if a.starts_with("--emit") && is_final_crate_type && !self.workspace_mode { + "--emit=dep-info" + } else { + a + } + }); + + for<> || -> () {}; + for<> || -> () {}; + for<> || -> () {}; + + for<'a, 'b, 'c> |_: &'a (), _: &'b (), _: &'c ()| -> () {}; +} + +fn issue311() { + let func = |x| println!("{}", x); + + (func)(0.0); +} + +fn issue863() { + let closure = |x| match x { + 0 => true, + _ => false, + } == true; +} + +fn issue934() { + let hash: &Fn(&&Block) -> u64 = &|block| -> u64 { + let mut h = SpanlessHash::new(cx); + h.hash_block(block); + h.finish() + }; + + let hash: &Fn(&&Block) -> u64 = &|block| -> u64 { + let mut h = SpanlessHash::new(cx); + h.hash_block(block); + h.finish(); + }; +} + +impl<'a, 'tcx: 'a> SpanlessEq<'a, 'tcx> { + pub fn eq_expr(&self, left: &Expr, right: &Expr) -> bool { + match (&left.node, &right.node) { + (&ExprBinary(l_op, ref ll, ref lr), &ExprBinary(r_op, ref rl, ref rr)) => { + l_op.node == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) + || swap_binop(l_op.node, ll, lr).map_or(false, |(l_op, ll, lr)| { + l_op == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) + }) + } + } + } +} + +fn foo() { + lifetimes_iter___map(|lasdfasfd| { + let hi = if l.bounds.is_empty() { + l.lifetime.span.hi() + }; + }); +} + +fn issue1405() { + open_raw_fd(fd, b'r').and_then(|file| { + Capture::new_raw(None, |_, err| unsafe { raw::pcap_fopen_offline(file, err) }) + }); +} + +fn issue1466() { + let vertex_buffer = frame.scope(|ctx| { + let buffer = ctx.create_host_visible_buffer::>(&vertices); + ctx.create_device_local_buffer(buffer) + }); +} + +fn issue470() { + { + { + { + let explicit_arg_decls = + explicit_arguments + .into_iter() + .enumerate() + .map(|(index, (ty, pattern))| { + let lvalue = Lvalue::Arg(index as u32); + block = this.pattern( + block, + argument_extent, + hair::PatternRef::Hair(pattern), + &lvalue, + ); + ArgDecl { ty: ty } + }); + } + } + } +} + +// #1509 +impl Foo { + pub fn bar(&self) { + Some(SomeType { + push_closure_out_to_100_chars: iter(otherwise_it_works_ok.into_iter().map(|f| Ok(f))), + }) + } +} + +fn issue1329() { + aaaaaaaaaaaaaaaa + .map(|x| { + x += 1; + x + }) + .filter +} + +fn issue325() { + let f = + || unsafe { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }; +} + +fn issue1697() { + Test.func_a( + A_VERY_LONG_CONST_VARIABLE_NAME, + move |arg1, arg2, arg3, arg4| arg1 + arg2 + arg3 + arg4, + ) +} + +fn issue1694() { + foooooo( + |_referencefffffffff: _, _target_reference: _, _oid: _, _target_oid: _| { + format!("refs/pull/{}/merge", pr_id) + }, + ) +} + +fn issue1713() { + rayon::join( + || recurse(left, is_less, pred, limit), + || recurse(right, is_less, Some(pivot), limit), + ); + + rayon::join( + 1, + || recurse(left, is_less, pred, limit), + 2, + || recurse(right, is_less, Some(pivot), limit), + ); +} + +fn issue2063() { + |ctx: Ctx<(String, String)>| -> io::Result { + Ok(Response::new().with_body(ctx.params.0)) + } +} + +fn issue1524() { + let f = |x| x; + let f = |x| x; + let f = |x| x; + let f = |x| x; + let f = |x| x; +} + +fn issue2171() { + foo(|| unsafe { + if PERIPHERALS { + loop {} + } else { + PERIPHERALS = true; + } + }) +} + +fn issue2207() { + a.map(|_| { + unsafe { a_very_very_very_very_very_very_very_long_function_name_or_anything_else() } + .to_string() + }) +} + +fn issue2262() { + result + .init(&mut result.slave.borrow_mut(), &mut (result.strategy)()) + .map_err(|factory| Error { + factory, + slave: None, + })?; +} diff --git a/tests/target/comment-inside-const.rs b/tests/target/comment-inside-const.rs new file mode 100644 index 000000000000..f847f2c69def --- /dev/null +++ b/tests/target/comment-inside-const.rs @@ -0,0 +1,9 @@ +fn issue982() { + const SOME_CONSTANT: u32 = + // Explanation why SOME_CONSTANT needs FLAG_A to be set. + FLAG_A | + // Explanation why SOME_CONSTANT needs FLAG_B to be set. + FLAG_B | + // Explanation why SOME_CONSTANT needs FLAG_C to be set. + FLAG_C; +} diff --git a/tests/target/comment-not-disappear.rs b/tests/target/comment-not-disappear.rs new file mode 100644 index 000000000000..b1fa0ff6fe11 --- /dev/null +++ b/tests/target/comment-not-disappear.rs @@ -0,0 +1,38 @@ +// All the comments here should not disappear. + +fn a() { + match x { + X | + // A comment + Y => {} + }; +} + +fn b() { + match x { + X => + // A comment + { + y + } + } +} + +fn c() { + a() /* ... */; +} + +fn foo() -> Vec { + (0..11) + .map(|x| + // This comment disappears. + if x % 2 == 0 { x } else { x * 2 }) + .collect() +} + +fn calc_page_len(prefix_len: usize, sofar: usize) -> usize { + 2 // page type and flags + + 1 // stored depth + + 2 // stored count + + prefix_len + sofar // sum of size of all the actual items +} diff --git a/tests/target/comment.rs b/tests/target/comment.rs new file mode 100644 index 000000000000..b987c8a44f3d --- /dev/null +++ b/tests/target/comment.rs @@ -0,0 +1,93 @@ +// rustfmt-normalize_comments: true +// rustfmt-wrap_comments: true + +//! Doc comment +fn test() { + //! Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam + //! lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam + + // comment + // comment2 + + code(); // leave this comment alone! + // ok? + + // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a + // diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam + // viverra nec consectetur ante hendrerit. Donec et mollis dolor. + // Praesent et diam eget libero egestas mattis sit amet vitae augue. Nam + // tincidunt congue enim, ut porta lorem lacinia consectetur. Donec ut + // libero sed arcu vehicula ultricies a non tortor. Lorem ipsum dolor sit + // amet, consectetur adipiscing elit. Aenean ut gravida lorem. Ut turpis + // felis, pulvinar a semper sed, adipiscing id dolor. + + // Very looooooooooooooooooooooooooooooooooooooooooooooooooooooooong comment + // that should be split + + // println!("{:?}", rewrite_comment(subslice, + // false, + // comment_width, + // self.block_indent, + // self.config) + // .unwrap()); + + funk(); // dontchangeme + // or me + + // #1388 + const EXCEPTION_PATHS: &'static [&'static str] = &[ + // std crates + "src/libstd/sys/", // Platform-specific code for std lives here. + "src/bootstrap", + ]; +} + +/// test123 +fn doc_comment() {} + +fn chains() { + foo.bar(|| { + let x = 10; + // comment + x + }) +} + +fn issue_1086() { + // +} + +// random comment + +fn main() { // Test +} + +// #1643 +fn some_fn() // some comment +{ +} + +fn some_fn1() +// some comment +{ +} + +fn some_fn2() // some comment +{ +} + +fn some_fn3() // some comment some comment some comment some comment some comment some comment so +{ +} + +fn some_fn4() +// some comment some comment some comment some comment some comment some comment some comment +{ +} + +// #1603 +pub enum Foo { + A, // `/** **/` + B, // `/*!` + C, +} diff --git a/tests/target/comment2.rs b/tests/target/comment2.rs new file mode 100644 index 000000000000..04f84a15c957 --- /dev/null +++ b/tests/target/comment2.rs @@ -0,0 +1,5 @@ +// rustfmt-wrap_comments: true + +/// This is a long line that angers rustfmt. Rustfmt shall deal with it swiftly +/// and justly. +pub mod foo {} diff --git a/tests/target/comment3.rs b/tests/target/comment3.rs new file mode 100644 index 000000000000..3a810590d7b3 --- /dev/null +++ b/tests/target/comment3.rs @@ -0,0 +1,6 @@ +// rustfmt-wrap_comments: true + +//! This is a long line that angers rustfmt. Rustfmt shall deal with it swiftly +//! and justly. + +pub mod foo {} diff --git a/tests/target/comment4.rs b/tests/target/comment4.rs new file mode 100644 index 000000000000..e2ef7de978f9 --- /dev/null +++ b/tests/target/comment4.rs @@ -0,0 +1,51 @@ +#![allow(dead_code)] // bar + +//! Doc comment +fn test() { + // comment + // comment2 + + code(); /* leave this comment alone! + * ok? */ + + /* Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a + * diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam + * viverra nec consectetur ante hendrerit. Donec et mollis dolor. + * Praesent et diam eget libero egestas mattis sit amet vitae augue. Nam + * tincidunt congue enim, ut porta lorem lacinia consectetur. Donec ut + * libero sed arcu vehicula ultricies a non tortor. Lorem ipsum dolor sit + * amet, consectetur adipiscing elit. Aenean ut gravida lorem. Ut turpis + * felis, pulvinar a semper sed, adipiscing id dolor. */ + + // Very loooooooooooooooooooooooooooooooooooooooooooooooooooooooong comment that should be split + + // println!("{:?}", rewrite_comment(subslice, + // false, + // comment_width, + // self.block_indent, + // self.config) + // .unwrap()); + + funk(); //dontchangeme + // or me +} + +/// test123 +fn doc_comment() {} + +/* +Regression test for issue #956 + +(some very important text) +*/ + +/* +fn debug_function() { + println!("hello"); +} +// */ + +#[link_section=".vectors"] +#[no_mangle] // Test this attribute is preserved. +#[cfg_attr(rustfmt, rustfmt::skip)] +pub static ISSUE_1284: [i32; 16] = []; diff --git a/tests/target/comment5.rs b/tests/target/comment5.rs new file mode 100644 index 000000000000..82d171e6f6a6 --- /dev/null +++ b/tests/target/comment5.rs @@ -0,0 +1,16 @@ +// rustfmt-normalize_comments: true +// rustfmt-wrap_comments: true + +//@ special comment +//@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec adiam lectus. +//@ Sed sit amet ipsum mauris. Maecenas congue ligula ac quam +//@ +//@ foo +fn test() {} + +//@@@ another special comment +//@@@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec adiam +//@@@ lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam +//@@@ +//@@@ foo +fn bar() {} diff --git a/tests/target/comment6.rs b/tests/target/comment6.rs new file mode 100644 index 000000000000..565fee632f57 --- /dev/null +++ b/tests/target/comment6.rs @@ -0,0 +1,14 @@ +// rustfmt-wrap_comments: true + +// Pendant la nuit du 9 mars 1860, les nuages, se confondant avec la mer, +// limitaient à quelques brasses la portée de la vue. Sur cette mer démontée, +// dont les lames déferlaient en projetant des lueurs livides, un léger bâtiment +// fuyait presque à sec de toile. + +pub mod foo {} + +// ゆく河の流れは絶えずして、しかももとの水にあらず。淀みに浮かぶうたかたは、 +// かつ消えかつ結びて、久しくとどまりたるためしなし。世の中にある人とすみかと、 +// またかくのごとし。 + +pub mod bar {} diff --git a/tests/target/comment_crlf_newline.rs b/tests/target/comment_crlf_newline.rs new file mode 100644 index 000000000000..aab9e94d9e6f --- /dev/null +++ b/tests/target/comment_crlf_newline.rs @@ -0,0 +1,4 @@ +// rustfmt-normalize_comments: true +// Block comments followed by CRLF newlines should not an extra newline at the end + +// Something else diff --git a/tests/target/comments-fn.rs b/tests/target/comments-fn.rs new file mode 100644 index 000000000000..1f43bd93bb0c --- /dev/null +++ b/tests/target/comments-fn.rs @@ -0,0 +1,38 @@ +// Test comments on functions are preserved. + +// Comment on foo. +fn foo( + a: aaaaaaaaaaaaa, // A comment + b: bbbbbbbbbbbbb, // a second comment + c: ccccccccccccc, + // Newline comment + d: ddddddddddddd, + // A multi line comment + // between args. + e: eeeeeeeeeeeee, /* comment before paren */ +) -> bar +where + F: Foo, // COmment after where-clause + G: Goo, // final comment +{ +} + +fn bar() {} + +fn baz() -> Baz /* Comment after return type */ {} + +fn some_fn() +where + T: Eq, // some comment +{ +} + +fn issue458(a: &str, f: F) +// comment1 +where + // comment2 + F: FnOnce(&str) -> bool, +{ + f(a); + () +} diff --git a/tests/target/comments-in-lists/format-doc-comments.rs b/tests/target/comments-in-lists/format-doc-comments.rs new file mode 100644 index 000000000000..be4b7a8c42e3 --- /dev/null +++ b/tests/target/comments-in-lists/format-doc-comments.rs @@ -0,0 +1,94 @@ +// rustfmt-format_code_in_doc_comments: true + +// https://github.com/rust-lang/rustfmt/issues/4420 +enum Minimal { + Example, + //[thisisremoved thatsleft + // canbeanything +} + +struct Minimal2 { + Example: usize, + //[thisisremoved thatsleft + // canbeanything +} + +pub enum E { + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + Variant1, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + Variant2, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +} + +pub enum E2 { + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +} + +pub struct S { + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + some_field: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + last_field: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +} + +pub struct S2 { + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +} + +fn foo( + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + a: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + b: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +) -> usize { + 5 +} + +fn foo2(// Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +) -> usize { + 5 +} + +fn main() { + let v = vec![ + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + 1, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + 2, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + ]; + + let v2: Vec = vec![ + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + ]; + + match a { + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + b => c, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + d => e, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + } +} diff --git a/tests/target/comments-in-lists/wrap-comments-false.rs b/tests/target/comments-in-lists/wrap-comments-false.rs new file mode 100644 index 000000000000..db4da6223721 --- /dev/null +++ b/tests/target/comments-in-lists/wrap-comments-false.rs @@ -0,0 +1,83 @@ +// rustfmt-normalize_comments: true + +// https://github.com/rust-lang/rustfmt/issues/4909 +pub enum E { + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + Variant1, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + Variant2, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +} + +pub enum E2 { + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +} + +pub struct S { + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + some_field: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + last_field: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +} + +pub struct S2 { + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +} + +fn foo( + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + a: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + b: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +) -> usize { + 5 +} + +fn foo2(// Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +) -> usize { + 5 +} + +fn main() { + let v = vec![ + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + 1, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + 2, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + ]; + + let v2: Vec = vec![ + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + ]; + + // https://github.com/rust-lang/rustfmt/issues/4430 + match a { + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + b => c, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + d => e, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + } +} diff --git a/tests/target/comments-in-lists/wrap-comments-not-normalized.rs b/tests/target/comments-in-lists/wrap-comments-not-normalized.rs new file mode 100644 index 000000000000..9b9147eb1247 --- /dev/null +++ b/tests/target/comments-in-lists/wrap-comments-not-normalized.rs @@ -0,0 +1,142 @@ +// rustfmt-wrap_comments: true + +// https://github.com/rust-lang/rustfmt/issues/4909 +pub enum E { + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + Variant1, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + Variant2, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +} + +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 +} + +pub enum E3 { + // Expand as needed, numbers should be ascending according to the stage through the inclusion + // pipeline, or according to the descriptions + Variant1, + // Expand as needed, numbers should be ascending according to the stage through the inclusion + // pipeline, or according to the descriptions + Variant2, + // Expand as needed, numbers should be ascending according to the stage through the inclusion + // pipeline, or according to the descriptions +} + +pub struct S { + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + some_field: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + last_field: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +} + +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 +} + +pub struct S3 { + // Expand as needed, numbers should be ascending according to the stage through the inclusion + // pipeline, or according to the descriptions + some_field: usize, + // Expand as needed, numbers should be ascending according to the stage through the inclusion + // pipeline, or according to the descriptions + last_field: usize, + // Expand as needed, numbers should be ascending according to the stage through the inclusion + // pipeline, or according to the descriptions +} + +fn foo( + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + a: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + b: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +) -> usize { + 5 +} + +fn foo2(// Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +) -> usize { + 5 +} + +fn foo3( + // Expand as needed, numbers should be ascending according to the stage through the inclusion + // pipeline, or according to the descriptions + a: usize, + // Expand as needed, numbers should be ascending according to the stage through the inclusion + // pipeline, or according to the descriptions + b: usize, + // Expand as needed, numbers should be ascending according to the stage through the inclusion + // pipeline, or according to the descriptions +) -> usize { + 5 +} + +fn main() { + let v = vec![ + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + 1, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + 2, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + ]; + + let v2: Vec = vec![ + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + ]; + + let v3 = vec![ + // Expand as needed, numbers should be ascending according to the stage through the + // inclusion pipeline, or according to the descriptions + 1, + // Expand as needed, numbers should be ascending according to the stage through the + // inclusion pipeline, or according to the descriptions + 2, + // Expand as needed, numbers should be ascending according to the stage through the + // inclusion pipeline, or according to the descriptions + ]; + + // https://github.com/rust-lang/rustfmt/issues/4430 + match a { + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + b => c, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + d => e, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + } + + match a { + // Expand as needed, numbers should be ascending according to the stage through the + // inclusion pipeline, or according to the descriptions + b => c, + // Expand as needed, numbers should be ascending according to the stage through the + // inclusion pipeline, or according to the descriptions + d => e, + // Expand as needed, numbers should be ascending according to the stage through the + // inclusion pipeline, or according to the descriptions + } +} diff --git a/tests/target/comments-in-lists/wrap-comments-true.rs b/tests/target/comments-in-lists/wrap-comments-true.rs new file mode 100644 index 000000000000..c1531d22a4a7 --- /dev/null +++ b/tests/target/comments-in-lists/wrap-comments-true.rs @@ -0,0 +1,143 @@ +// rustfmt-normalize_comments: true +// rustfmt-wrap_comments: true + +// https://github.com/rust-lang/rustfmt/issues/4909 +pub enum E { + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + Variant1, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + Variant2, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +} + +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 +} + +pub enum E3 { + // Expand as needed, numbers should be ascending according to the stage through the inclusion + // pipeline, or according to the descriptions + Variant1, + // Expand as needed, numbers should be ascending according to the stage through the inclusion + // pipeline, or according to the descriptions + Variant2, + // Expand as needed, numbers should be ascending according to the stage through the inclusion + // pipeline, or according to the descriptions +} + +pub struct S { + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + some_field: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + last_field: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +} + +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 +} + +pub struct S3 { + // Expand as needed, numbers should be ascending according to the stage through the inclusion + // pipeline, or according to the descriptions + some_field: usize, + // Expand as needed, numbers should be ascending according to the stage through the inclusion + // pipeline, or according to the descriptions + last_field: usize, + // Expand as needed, numbers should be ascending according to the stage through the inclusion + // pipeline, or according to the descriptions +} + +fn foo( + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + a: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + b: usize, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +) -> usize { + 5 +} + +fn foo2(// Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions +) -> usize { + 5 +} + +fn foo3( + // Expand as needed, numbers should be ascending according to the stage through the inclusion + // pipeline, or according to the descriptions + a: usize, + // Expand as needed, numbers should be ascending according to the stage through the inclusion + // pipeline, or according to the descriptions + b: usize, + // Expand as needed, numbers should be ascending according to the stage through the inclusion + // pipeline, or according to the descriptions +) -> usize { + 5 +} + +fn main() { + let v = vec![ + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + 1, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + 2, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + ]; + + let v2: Vec = vec![ + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + ]; + + let v3 = vec![ + // Expand as needed, numbers should be ascending according to the stage through the + // inclusion pipeline, or according to the descriptions + 1, + // Expand as needed, numbers should be ascending according to the stage through the + // inclusion pipeline, or according to the descriptions + 2, + // Expand as needed, numbers should be ascending according to the stage through the + // inclusion pipeline, or according to the descriptions + ]; + + // https://github.com/rust-lang/rustfmt/issues/4430 + match a { + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + b => c, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + d => e, + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions + } + + match a { + // Expand as needed, numbers should be ascending according to the stage through the + // inclusion pipeline, or according to the descriptions + b => c, + // Expand as needed, numbers should be ascending according to the stage through the + // inclusion pipeline, or according to the descriptions + d => e, + // Expand as needed, numbers should be ascending according to the stage through the + // inclusion pipeline, or according to the descriptions + } +} diff --git a/tests/target/comments_unicode.rs b/tests/target/comments_unicode.rs new file mode 100644 index 000000000000..3e1b6b0a28fe --- /dev/null +++ b/tests/target/comments_unicode.rs @@ -0,0 +1,140 @@ +impl Default for WhitespaceCharacters { + fn default() -> Self { + Self { + space: '·', // U+00B7 + nbsp: '⍽', // U+237D + tab: '→', // U+2192 + newline: '⏎', // U+23CE + } + } +} + +const RAINBOWS: &[&str] = &[ + "rаinЬοѡ", // hue: 0 + "raіnЬοw", // hue: 2 + "rаіɴЬow", // hue: 2 + "raіɴЬoѡ", // hue: 8 + "ʀainЬow", // hue: 8 + "ʀaіɴboѡ", // hue: 8 + "ʀаіnbοw", // hue: 11 + "rainЬoѡ", // hue: 14 + "raіɴbow", // hue: 14 + "rаiɴЬow", // hue: 20 + "raіnЬow", // hue: 26 + "ʀaiɴbοw", // hue: 32 + "raіɴboѡ", // hue: 35 + "rаiɴbow", // hue: 35 + "rаіnbοw", // hue: 38 + "rаinЬow", // hue: 47 + "ʀaіnboѡ", // hue: 47 + "ʀaіnЬoѡ", // hue: 47 + "ʀаіɴbοw", // hue: 53 + "ʀaіnЬοѡ", // hue: 57 + "raiɴЬoѡ", // hue: 68 + "ʀainbοѡ", // hue: 68 + "ʀаinboѡ", // hue: 68 + "ʀаiɴbοw", // hue: 68 + "ʀаіnbow", // hue: 68 + "rаіnЬοѡ", // hue: 69 + "ʀainЬοw", // hue: 71 + "raiɴbow", // hue: 73 + "raіnЬoѡ", // hue: 74 + "rаіɴbοw", // hue: 77 + "raіnЬοѡ", // hue: 81 + "raiɴЬow", // hue: 83 + "ʀainbοw", // hue: 83 + "ʀаinbow", // hue: 83 + "ʀаiɴbοѡ", // hue: 83 + "ʀаіnboѡ", // hue: 83 + "ʀаіɴЬοѡ", // hue: 84 + "rainЬow", // hue: 85 + "ʀаiɴЬοw", // hue: 86 + "ʀаіnbοѡ", // hue: 89 + "ʀаіnЬοw", // hue: 92 + "rаiɴbοw", // hue: 95 + "ʀаіɴbοѡ", // hue: 98 + "ʀаiɴЬοѡ", // hue: 99 + "raіnbοw", // hue: 101 + "ʀаіɴЬοw", // hue: 101 + "ʀaiɴboѡ", // hue: 104 + "ʀаinbοѡ", // hue: 104 + "rаiɴbοѡ", // hue: 107 + "ʀаinЬοw", // hue: 107 + "rаiɴЬοw", // hue: 110 + "rаіnboѡ", // hue: 110 + "rаіnbοѡ", // hue: 113 + "ʀainЬοѡ", // hue: 114 + "rаіnЬοw", // hue: 116 + "ʀaіɴЬow", // hue: 116 + "rаinbοw", // hue: 122 + "ʀаіɴboѡ", // hue: 125 + "rаinbοѡ", // hue: 131 + "rainbow", // hue: 134 + "rаinЬοw", // hue: 134 + "ʀаiɴboѡ", // hue: 140 + "rainЬοѡ", // hue: 141 + "raіɴЬow", // hue: 143 + "ʀainЬoѡ", // hue: 143 + "ʀaіɴbow", // hue: 143 + "ʀainbow", // hue: 148 + "rаіɴboѡ", // hue: 149 + "ʀainboѡ", // hue: 155 + "ʀaіnbow", // hue: 155 + "ʀaіnЬow", // hue: 155 + "raiɴbοw", // hue: 158 + "ʀаiɴЬoѡ", // hue: 158 + "rainbοw", // hue: 160 + "rаinbow", // hue: 160 + "ʀaіɴbοѡ", // hue: 164 + "ʀаiɴbow", // hue: 164 + "ʀаіnЬoѡ", // hue: 164 + "ʀaiɴЬοѡ", // hue: 165 + "rаiɴboѡ", // hue: 167 + "ʀaіɴЬοw", // hue: 167 + "ʀaіɴЬοѡ", // hue: 171 + "raіnboѡ", // hue: 173 + "ʀаіɴЬoѡ", // hue: 173 + "rаіɴbοѡ", // hue: 176 + "ʀаinЬow", // hue: 176 + "rаiɴЬοѡ", // hue: 177 + "rаіɴЬοw", // hue: 179 + "ʀаinЬoѡ", // hue: 179 + "ʀаіɴbow", // hue: 179 + "rаiɴЬoѡ", // hue: 182 + "raіɴbοѡ", // hue: 188 + "rаіnЬoѡ", // hue: 188 + "raiɴЬοѡ", // hue: 189 + "raіɴЬοw", // hue: 191 + "ʀaіɴbοw", // hue: 191 + "ʀаіnЬow", // hue: 191 + "rainbοѡ", // hue: 194 + "rаinboѡ", // hue: 194 + "rаіnbow", // hue: 194 + "rainЬοw", // hue: 197 + "rаinЬoѡ", // hue: 206 + "rаіɴbow", // hue: 206 + "rаіɴЬοѡ", // hue: 210 + "ʀaiɴЬow", // hue: 212 + "raіɴbοw", // hue: 218 + "rаіnЬow", // hue: 218 + "ʀaiɴbοѡ", // hue: 221 + "ʀaiɴЬοw", // hue: 224 + "ʀaіnbοѡ", // hue: 227 + "raiɴboѡ", // hue: 230 + "ʀaіnbοw", // hue: 230 + "ʀaіnЬοw", // hue: 230 + "ʀаinЬοѡ", // hue: 231 + "rainboѡ", // hue: 232 + "raіnbow", // hue: 232 + "ʀаіɴЬow", // hue: 233 + "ʀaіɴЬoѡ", // hue: 239 + "ʀаіnЬοѡ", // hue: 246 + "raiɴbοѡ", // hue: 248 + "ʀаiɴЬow", // hue: 248 + "raіɴЬοѡ", // hue: 249 + "raiɴЬοw", // hue: 251 + "rаіɴЬoѡ", // hue: 251 + "ʀaiɴbow", // hue: 251 + "ʀаinbοw", // hue: 251 + "raіnbοѡ", // hue: 254 +]; diff --git a/tests/target/configs/blank_lines_lower_bound/1.rs b/tests/target/configs/blank_lines_lower_bound/1.rs new file mode 100644 index 000000000000..9706699dc7a3 --- /dev/null +++ b/tests/target/configs/blank_lines_lower_bound/1.rs @@ -0,0 +1,16 @@ +// rustfmt-blank_lines_lower_bound: 1 + +fn foo() {} + +fn bar() {} + +// comment +fn foobar() {} + +fn foo1() {} + +fn bar1() {} + +// comment + +fn foobar1() {} diff --git a/tests/target/configs/brace_style/fn_always_next_line.rs b/tests/target/configs/brace_style/fn_always_next_line.rs new file mode 100644 index 000000000000..2755a264689f --- /dev/null +++ b/tests/target/configs/brace_style/fn_always_next_line.rs @@ -0,0 +1,19 @@ +// rustfmt-brace_style: AlwaysNextLine +// Function brace style + +fn lorem() +{ + // body +} + +fn lorem(ipsum: usize) +{ + // body +} + +fn lorem(ipsum: T) +where + T: Add + Sub + Mul + Div, +{ + // body +} diff --git a/tests/target/configs/brace_style/fn_prefer_same_line.rs b/tests/target/configs/brace_style/fn_prefer_same_line.rs new file mode 100644 index 000000000000..23f98b6dd7a6 --- /dev/null +++ b/tests/target/configs/brace_style/fn_prefer_same_line.rs @@ -0,0 +1,16 @@ +// rustfmt-brace_style: PreferSameLine +// Function brace style + +fn lorem() { + // body +} + +fn lorem(ipsum: usize) { + // body +} + +fn lorem(ipsum: T) +where + T: Add + Sub + Mul + Div, { + // body +} diff --git a/tests/target/configs/brace_style/fn_same_line_where.rs b/tests/target/configs/brace_style/fn_same_line_where.rs new file mode 100644 index 000000000000..2afe59943a30 --- /dev/null +++ b/tests/target/configs/brace_style/fn_same_line_where.rs @@ -0,0 +1,17 @@ +// rustfmt-brace_style: SameLineWhere +// Function brace style + +fn lorem() { + // body +} + +fn lorem(ipsum: usize) { + // body +} + +fn lorem(ipsum: T) +where + T: Add + Sub + Mul + Div, +{ + // body +} diff --git a/tests/target/configs/brace_style/item_always_next_line.rs b/tests/target/configs/brace_style/item_always_next_line.rs new file mode 100644 index 000000000000..c13018630be0 --- /dev/null +++ b/tests/target/configs/brace_style/item_always_next_line.rs @@ -0,0 +1,25 @@ +// rustfmt-brace_style: AlwaysNextLine +// Item brace style + +enum Foo {} + +struct Bar {} + +struct Lorem +{ + ipsum: bool, +} + +struct Dolor +where + T: Eq, +{ + sit: T, +} + +#[cfg(test)] +mod tests +{ + #[test] + fn it_works() {} +} diff --git a/tests/target/configs/brace_style/item_prefer_same_line.rs b/tests/target/configs/brace_style/item_prefer_same_line.rs new file mode 100644 index 000000000000..5143d7517c75 --- /dev/null +++ b/tests/target/configs/brace_style/item_prefer_same_line.rs @@ -0,0 +1,18 @@ +// rustfmt-brace_style: PreferSameLine +// Item brace style + +struct Lorem { + ipsum: bool, +} + +struct Dolor +where + T: Eq, { + sit: T, +} + +#[cfg(test)] +mod tests { + #[test] + fn it_works() {} +} diff --git a/tests/target/configs/brace_style/item_same_line_where.rs b/tests/target/configs/brace_style/item_same_line_where.rs new file mode 100644 index 000000000000..8a3b28526547 --- /dev/null +++ b/tests/target/configs/brace_style/item_same_line_where.rs @@ -0,0 +1,19 @@ +// rustfmt-brace_style: SameLineWhere +// Item brace style + +struct Lorem { + ipsum: bool, +} + +struct Dolor +where + T: Eq, +{ + sit: T, +} + +#[cfg(test)] +mod tests { + #[test] + fn it_works() {} +} diff --git a/tests/target/configs/chain_width/always.rs b/tests/target/configs/chain_width/always.rs new file mode 100644 index 000000000000..b16d25251f62 --- /dev/null +++ b/tests/target/configs/chain_width/always.rs @@ -0,0 +1,29 @@ +// rustfmt-chain_width: 1 +// setting an unachievable chain_width to always get chains +// on separate lines + +struct Fluent {} + +impl Fluent { + fn blorp(&self) -> &Self { + self + } +} + +fn main() { + let test = Fluent {}; + + // should be left alone + test.blorp(); + + // should be wrapped + test.blorp() + .blorp(); + test.blorp() + .blorp() + .blorp(); + test.blorp() + .blorp() + .blorp() + .blorp(); +} diff --git a/tests/target/configs/chain_width/small.rs b/tests/target/configs/chain_width/small.rs new file mode 100644 index 000000000000..2f2f72777f8b --- /dev/null +++ b/tests/target/configs/chain_width/small.rs @@ -0,0 +1,32 @@ +// rustfmt-chain_width: 40 + +struct Fluent {} + +impl Fluent { + fn blorp(&self) -> &Self { + self + } +} + +fn main() { + let test = Fluent {}; + + // should not be wrapped + test.blorp(); + test.blorp().blorp(); + test.blorp().blorp().blorp(); + test.blorp().blorp().blorp().blorp(); + + // should be wrapped + test.blorp() + .blorp() + .blorp() + .blorp() + .blorp(); + test.blorp() + .blorp() + .blorp() + .blorp() + .blorp() + .blorp(); +} diff --git a/tests/target/configs/chain_width/tiny.rs b/tests/target/configs/chain_width/tiny.rs new file mode 100644 index 000000000000..960d245f8a12 --- /dev/null +++ b/tests/target/configs/chain_width/tiny.rs @@ -0,0 +1,26 @@ +// rustfmt-chain_width: 20 + +struct Fluent {} + +impl Fluent { + fn blorp(&self) -> &Self { + self + } +} + +fn main() { + let test = Fluent {}; + + // should not be wrapped + test.blorp(); + test.blorp().blorp(); + + // should be wrapped + test.blorp() + .blorp() + .blorp(); + test.blorp() + .blorp() + .blorp() + .blorp(); +} diff --git a/tests/target/configs/combine_control_expr/false.rs b/tests/target/configs/combine_control_expr/false.rs new file mode 100644 index 000000000000..0ab820249374 --- /dev/null +++ b/tests/target/configs/combine_control_expr/false.rs @@ -0,0 +1,128 @@ +// rustfmt-indent_style: Block +// rustfmt-combine_control_expr: false + +// Combining openings and closings. See rust-lang/fmt-rfcs#61. + +fn main() { + // Call + foo(bar( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, + )); + + // Mac + foo(foo!( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, + )); + + // MethodCall + foo(x.foo::( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, + )); + + // Block + foo!({ + foo(); + bar(); + }); + + // Closure + foo(|x| { + let y = x + 1; + y + }); + + // Match + foo(match opt { + Some(x) => x, + None => y, + }); + + // Struct + foo(Bar { + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, + }); + + // If + foo!( + if x { + foo(); + } else { + bar(); + } + ); + + // IfLet + foo!( + if let Some(..) = x { + foo(); + } else { + bar(); + } + ); + + // While + foo!( + while x { + foo(); + bar(); + } + ); + + // WhileLet + foo!( + while let Some(..) = x { + foo(); + bar(); + } + ); + + // ForLoop + foo!( + for x in y { + foo(); + bar(); + } + ); + + // Loop + foo!( + loop { + foo(); + bar(); + } + ); + + // Tuple + foo(( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, + )); + + // AddrOf + foo(&bar( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, + )); + + // Unary + foo(!bar( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, + )); + + // Try + foo(bar( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, + )?); + + // Cast + foo(Bar { + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, + } as i64); +} diff --git a/tests/target/configs/combine_control_expr/true.rs b/tests/target/configs/combine_control_expr/true.rs new file mode 100644 index 000000000000..aa41e021fb7a --- /dev/null +++ b/tests/target/configs/combine_control_expr/true.rs @@ -0,0 +1,116 @@ +// rustfmt-indent_style: Block +// rustfmt-combine_control_expr: true + +// Combining openings and closings. See rust-lang/fmt-rfcs#61. + +fn main() { + // Call + foo(bar( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, + )); + + // Mac + foo(foo!( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, + )); + + // MethodCall + foo(x.foo::( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, + )); + + // Block + foo!({ + foo(); + bar(); + }); + + // Closure + foo(|x| { + let y = x + 1; + y + }); + + // Match + foo(match opt { + Some(x) => x, + None => y, + }); + + // Struct + foo(Bar { + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, + }); + + // If + foo!(if x { + foo(); + } else { + bar(); + }); + + // IfLet + foo!(if let Some(..) = x { + foo(); + } else { + bar(); + }); + + // While + foo!(while x { + foo(); + bar(); + }); + + // WhileLet + foo!(while let Some(..) = x { + foo(); + bar(); + }); + + // ForLoop + foo!(for x in y { + foo(); + bar(); + }); + + // Loop + foo!(loop { + foo(); + bar(); + }); + + // Tuple + foo(( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, + )); + + // AddrOf + foo(&bar( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, + )); + + // Unary + foo(!bar( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, + )); + + // Try + foo(bar( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, + )?); + + // Cast + foo(Bar { + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, + } as i64); +} diff --git a/tests/target/configs/comment_width/above.rs b/tests/target/configs/comment_width/above.rs new file mode 100644 index 000000000000..ddfecda65c1a --- /dev/null +++ b/tests/target/configs/comment_width/above.rs @@ -0,0 +1,8 @@ +// rustfmt-comment_width: 40 +// rustfmt-wrap_comments: true +// Comment width + +fn main() { + // Lorem ipsum dolor sit amet, + // consectetur adipiscing elit. +} diff --git a/tests/target/configs/comment_width/below.rs b/tests/target/configs/comment_width/below.rs new file mode 100644 index 000000000000..abbc5930c4ce --- /dev/null +++ b/tests/target/configs/comment_width/below.rs @@ -0,0 +1,7 @@ +// rustfmt-comment_width: 80 +// rustfmt-wrap_comments: true +// Comment width + +fn main() { + // Lorem ipsum dolor sit amet, consectetur adipiscing elit. +} diff --git a/tests/target/configs/comment_width/ignore.rs b/tests/target/configs/comment_width/ignore.rs new file mode 100644 index 000000000000..c86e71c28981 --- /dev/null +++ b/tests/target/configs/comment_width/ignore.rs @@ -0,0 +1,7 @@ +// rustfmt-comment_width: 40 +// rustfmt-wrap_comments: false +// Comment width + +fn main() { + // Lorem ipsum dolor sit amet, consectetur adipiscing elit. +} diff --git a/tests/target/configs/condense_wildcard_suffixes/false.rs b/tests/target/configs/condense_wildcard_suffixes/false.rs new file mode 100644 index 000000000000..3b967f35a8e8 --- /dev/null +++ b/tests/target/configs/condense_wildcard_suffixes/false.rs @@ -0,0 +1,6 @@ +// rustfmt-condense_wildcard_suffixes: false +// Condense wildcard suffixes + +fn main() { + let (lorem, ipsum, _, _) = (1, 2, 3, 4); +} diff --git a/tests/target/configs/condense_wildcard_suffixes/true.rs b/tests/target/configs/condense_wildcard_suffixes/true.rs new file mode 100644 index 000000000000..4f880abe80e7 --- /dev/null +++ b/tests/target/configs/condense_wildcard_suffixes/true.rs @@ -0,0 +1,6 @@ +// rustfmt-condense_wildcard_suffixes: true +// Condense wildcard suffixes + +fn main() { + let (lorem, ipsum, ..) = (1, 2, 3, 4); +} diff --git a/tests/target/configs/control_brace_style/always_next_line.rs b/tests/target/configs/control_brace_style/always_next_line.rs new file mode 100644 index 000000000000..7dc06f207fe3 --- /dev/null +++ b/tests/target/configs/control_brace_style/always_next_line.rs @@ -0,0 +1,18 @@ +// rustfmt-control_brace_style: AlwaysNextLine +// Control brace style + +fn main() { + if lorem + { + println!("ipsum!"); + } + else + { + println!("dolor!"); + } + match magi + { + Homura => "Akemi", + Madoka => "Kaname", + } +} diff --git a/tests/target/configs/control_brace_style/always_same_line.rs b/tests/target/configs/control_brace_style/always_same_line.rs new file mode 100644 index 000000000000..993b6b681fed --- /dev/null +++ b/tests/target/configs/control_brace_style/always_same_line.rs @@ -0,0 +1,14 @@ +// rustfmt-control_brace_style: AlwaysSameLine +// Control brace style + +fn main() { + if lorem { + println!("ipsum!"); + } else { + println!("dolor!"); + } + match magi { + Homura => "Akemi", + Madoka => "Kaname", + } +} diff --git a/tests/target/configs/control_brace_style/closing_next_line.rs b/tests/target/configs/control_brace_style/closing_next_line.rs new file mode 100644 index 000000000000..013852ee79ad --- /dev/null +++ b/tests/target/configs/control_brace_style/closing_next_line.rs @@ -0,0 +1,15 @@ +// rustfmt-control_brace_style: ClosingNextLine +// Control brace style + +fn main() { + if lorem { + println!("ipsum!"); + } + else { + println!("dolor!"); + } + match magi { + Homura => "Akemi", + Madoka => "Kaname", + } +} diff --git a/tests/target/configs/disable_all_formatting/false.rs b/tests/target/configs/disable_all_formatting/false.rs new file mode 100644 index 000000000000..1a0477ddb39e --- /dev/null +++ b/tests/target/configs/disable_all_formatting/false.rs @@ -0,0 +1,10 @@ +// rustfmt-disable_all_formatting: false +// Disable all formatting + +fn main() { + if lorem { + println!("ipsum!"); + } else { + println!("dolor!"); + } +} diff --git a/tests/target/configs/disable_all_formatting/true.rs b/tests/target/configs/disable_all_formatting/true.rs new file mode 100644 index 000000000000..736ccf569423 --- /dev/null +++ b/tests/target/configs/disable_all_formatting/true.rs @@ -0,0 +1,6 @@ +// rustfmt-disable_all_formatting: true +// Disable all formatting + +fn main() { + if lorem{println!("ipsum!");}else{println!("dolor!");} +} diff --git a/tests/target/configs/doc_comment_code_block_width/100.rs b/tests/target/configs/doc_comment_code_block_width/100.rs new file mode 100644 index 000000000000..c010a28aab61 --- /dev/null +++ b/tests/target/configs/doc_comment_code_block_width/100.rs @@ -0,0 +1,16 @@ +// rustfmt-format_code_in_doc_comments: true +// rustfmt-doc_comment_code_block_width: 100 + +/// ```rust +/// impl Test { +/// pub const fn from_bytes(v: &[u8]) -> Result { +/// Self::from_bytes_manual_slice(v, 0, v.len()) +/// } +/// } +/// ``` + +impl Test { + pub const fn from_bytes(v: &[u8]) -> Result { + Self::from_bytes_manual_slice(v, 0, v.len()) + } +} diff --git a/tests/target/configs/doc_comment_code_block_width/100_greater_max_width.rs b/tests/target/configs/doc_comment_code_block_width/100_greater_max_width.rs new file mode 100644 index 000000000000..6bcb99b915ff --- /dev/null +++ b/tests/target/configs/doc_comment_code_block_width/100_greater_max_width.rs @@ -0,0 +1,29 @@ +// rustfmt-max_width: 50 +// rustfmt-format_code_in_doc_comments: true +// rustfmt-doc_comment_code_block_width: 100 + +/// ```rust +/// impl Test { +/// pub const fn from_bytes( +/// v: &[u8], +/// ) -> Result { +/// Self::from_bytes_manual_slice( +/// v, +/// 0, +/// v.len(), +/// ) +/// } +/// } +/// ``` + +impl Test { + pub const fn from_bytes( + v: &[u8], + ) -> Result { + Self::from_bytes_manual_slice( + v, + 0, + v.len(), + ) + } +} diff --git a/tests/target/configs/doc_comment_code_block_width/50.rs b/tests/target/configs/doc_comment_code_block_width/50.rs new file mode 100644 index 000000000000..e8ab6f28bdc5 --- /dev/null +++ b/tests/target/configs/doc_comment_code_block_width/50.rs @@ -0,0 +1,22 @@ +// rustfmt-format_code_in_doc_comments: true +// rustfmt-doc_comment_code_block_width: 50 + +/// ```rust +/// impl Test { +/// pub const fn from_bytes( +/// v: &[u8], +/// ) -> Result { +/// Self::from_bytes_manual_slice( +/// v, +/// 0, +/// v.len(), +/// ) +/// } +/// } +/// ``` + +impl Test { + pub const fn from_bytes(v: &[u8]) -> Result { + Self::from_bytes_manual_slice(v, 0, v.len()) + } +} diff --git a/tests/target/configs/empty_item_single_line/false.rs b/tests/target/configs/empty_item_single_line/false.rs new file mode 100644 index 000000000000..174fe330a8da --- /dev/null +++ b/tests/target/configs/empty_item_single_line/false.rs @@ -0,0 +1,14 @@ +// rustfmt-empty_item_single_line: false +// Empty impl on single line + +impl Lorem { +} + +impl Ipsum { +} + +fn lorem() { +} + +fn lorem() { +} diff --git a/tests/target/configs/empty_item_single_line/true.rs b/tests/target/configs/empty_item_single_line/true.rs new file mode 100644 index 000000000000..0755485fea95 --- /dev/null +++ b/tests/target/configs/empty_item_single_line/true.rs @@ -0,0 +1,10 @@ +// rustfmt-empty_item_single_line: true +// Empty impl on single line + +impl Lorem {} + +impl Ipsum {} + +fn lorem() {} + +fn lorem() {} diff --git a/tests/target/configs/enum_discrim_align_threshold/40.rs b/tests/target/configs/enum_discrim_align_threshold/40.rs new file mode 100644 index 000000000000..3ed66039c9dd --- /dev/null +++ b/tests/target/configs/enum_discrim_align_threshold/40.rs @@ -0,0 +1,34 @@ +// rustfmt-enum_discrim_align_threshold: 40 + +enum Standard { + A = 1, + Bcdef = 2, +} + +enum NoDiscrims { + ThisIsAFairlyLongEnumVariantWithoutDiscrimLongerThan40, + A = 1, + ThisIsAnotherFairlyLongEnumVariantWithoutDiscrimLongerThan40, + Bcdef = 2, +} + +enum TooLong { + ThisOneHasDiscrimAaaaaaaaaaaaaaaaaaaaaaChar40 = 10, + A = 1, + Bcdef = 2, +} + +enum Borderline { + ThisOneHasDiscrimAaaaaaaaaaaaaaaaaaaaaa = 10, + A = 1, + Bcdef = 2, +} + +// Live specimen from #1686 +enum LongWithSmallDiff { + SceneColorimetryEstimates = 0x73636F65, + SceneAppearanceEstimates = 0x73617065, + FocalPlaneColorimetryEstimates = 0x66706365, + ReflectionHardcopyOriginalColorimetry = 0x72686F63, + ReflectionPrintOutputColorimetry = 0x72706F63, +} diff --git a/tests/target/configs/error_on_line_overflow/false.rs b/tests/target/configs/error_on_line_overflow/false.rs new file mode 100644 index 000000000000..fa70ae78352f --- /dev/null +++ b/tests/target/configs/error_on_line_overflow/false.rs @@ -0,0 +1,6 @@ +// rustfmt-error_on_line_overflow: false +// Error on line overflow + +fn main() { + let lorem_ipsum_dolor_sit_amet_consectetur_adipiscing_elit_lorem_ipsum_dolor_sit_amet_consectetur_adipiscing_elit; +} diff --git a/tests/target/configs/error_on_unformatted/false.rs b/tests/target/configs/error_on_unformatted/false.rs new file mode 100644 index 000000000000..6a78374e2a26 --- /dev/null +++ b/tests/target/configs/error_on_unformatted/false.rs @@ -0,0 +1,12 @@ +// rustfmt-error_on_unformatted: false +// Error on line overflow comment or string literals. + +// aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +fn main() { + // aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + + let x = " "; + let a = " + +"; +} diff --git a/tests/target/configs/fn_params_layout/compressed.rs b/tests/target/configs/fn_params_layout/compressed.rs new file mode 100644 index 000000000000..ff32f0f1d586 --- /dev/null +++ b/tests/target/configs/fn_params_layout/compressed.rs @@ -0,0 +1,22 @@ +// rustfmt-fn_params_layout: Compressed +// Function arguments density + +trait Lorem { + fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet); + + fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet) { + // body + } + + fn lorem( + ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: onsectetur, + adipiscing: Adipiscing, elit: Elit, + ); + + fn lorem( + ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: onsectetur, + adipiscing: Adipiscing, elit: Elit, + ) { + // body + } +} diff --git a/tests/target/configs/fn_params_layout/tall.rs b/tests/target/configs/fn_params_layout/tall.rs new file mode 100644 index 000000000000..25a86799af0d --- /dev/null +++ b/tests/target/configs/fn_params_layout/tall.rs @@ -0,0 +1,32 @@ +// rustfmt-fn_params_layout: Tall +// Function arguments density + +trait Lorem { + fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet); + + fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet) { + // body + } + + fn lorem( + ipsum: Ipsum, + dolor: Dolor, + sit: Sit, + amet: Amet, + consectetur: onsectetur, + adipiscing: Adipiscing, + elit: Elit, + ); + + fn lorem( + ipsum: Ipsum, + dolor: Dolor, + sit: Sit, + amet: Amet, + consectetur: onsectetur, + adipiscing: Adipiscing, + elit: Elit, + ) { + // body + } +} diff --git a/tests/target/configs/fn_params_layout/vertical.rs b/tests/target/configs/fn_params_layout/vertical.rs new file mode 100644 index 000000000000..7a0e42415f3b --- /dev/null +++ b/tests/target/configs/fn_params_layout/vertical.rs @@ -0,0 +1,42 @@ +// rustfmt-fn_params_layout: Vertical +// Function arguments density + +trait Lorem { + fn lorem( + ipsum: Ipsum, + dolor: Dolor, + sit: Sit, + amet: Amet, + ); + + fn lorem( + ipsum: Ipsum, + dolor: Dolor, + sit: Sit, + amet: Amet, + ) { + // body + } + + fn lorem( + ipsum: Ipsum, + dolor: Dolor, + sit: Sit, + amet: Amet, + consectetur: onsectetur, + adipiscing: Adipiscing, + elit: Elit, + ); + + fn lorem( + ipsum: Ipsum, + dolor: Dolor, + sit: Sit, + amet: Amet, + consectetur: onsectetur, + adipiscing: Adipiscing, + elit: Elit, + ) { + // body + } +} diff --git a/tests/target/configs/fn_single_line/false.rs b/tests/target/configs/fn_single_line/false.rs new file mode 100644 index 000000000000..3d092f0c0bf5 --- /dev/null +++ b/tests/target/configs/fn_single_line/false.rs @@ -0,0 +1,11 @@ +// rustfmt-fn_single_line: false +// Single-expression function on single line + +fn lorem() -> usize { + 42 +} + +fn lorem() -> usize { + let ipsum = 42; + ipsum +} diff --git a/tests/target/configs/fn_single_line/true.rs b/tests/target/configs/fn_single_line/true.rs new file mode 100644 index 000000000000..10d94e02f16d --- /dev/null +++ b/tests/target/configs/fn_single_line/true.rs @@ -0,0 +1,9 @@ +// rustfmt-fn_single_line: true +// Single-expression function on single line + +fn lorem() -> usize { 42 } + +fn lorem() -> usize { + let ipsum = 42; + ipsum +} diff --git a/tests/target/configs/force_explicit_abi/false.rs b/tests/target/configs/force_explicit_abi/false.rs new file mode 100644 index 000000000000..3c48f8e0c78d --- /dev/null +++ b/tests/target/configs/force_explicit_abi/false.rs @@ -0,0 +1,6 @@ +// rustfmt-force_explicit_abi: false +// Force explicit abi + +extern { + pub static lorem: c_int; +} diff --git a/tests/target/configs/force_explicit_abi/true.rs b/tests/target/configs/force_explicit_abi/true.rs new file mode 100644 index 000000000000..90f5a8c4ecb9 --- /dev/null +++ b/tests/target/configs/force_explicit_abi/true.rs @@ -0,0 +1,6 @@ +// rustfmt-force_explicit_abi: true +// Force explicit abi + +extern "C" { + pub static lorem: c_int; +} diff --git a/tests/target/configs/force_multiline_block/false.rs b/tests/target/configs/force_multiline_block/false.rs new file mode 100644 index 000000000000..7cb4cac1d691 --- /dev/null +++ b/tests/target/configs/force_multiline_block/false.rs @@ -0,0 +1,20 @@ +// rustfmt-force_multiline_blocks: false +// Option forces multiline match arm and closure bodies to be wrapped in a block + +fn main() { + match lorem { + Lorem::Ipsum => { + if ipsum { + println!("dolor"); + } + } + Lorem::Dolor => println!("amet"), + } +} + +fn main() { + result.and_then(|maybe_value| match maybe_value { + None => Err("oops"), + Some(value) => Ok(1), + }); +} diff --git a/tests/target/configs/force_multiline_block/true.rs b/tests/target/configs/force_multiline_block/true.rs new file mode 100644 index 000000000000..aec50afe5a84 --- /dev/null +++ b/tests/target/configs/force_multiline_block/true.rs @@ -0,0 +1,22 @@ +// rustfmt-force_multiline_blocks: true +// Option forces multiline match arm and closure bodies to be wrapped in a block + +fn main() { + match lorem { + Lorem::Ipsum => { + if ipsum { + println!("dolor"); + } + } + Lorem::Dolor => println!("amet"), + } +} + +fn main() { + result.and_then(|maybe_value| { + match maybe_value { + None => Err("oops"), + Some(value) => Ok(1), + } + }); +} diff --git a/tests/target/configs/format_generated_files/false.rs b/tests/target/configs/format_generated_files/false.rs new file mode 100644 index 000000000000..dec1e00d117b --- /dev/null +++ b/tests/target/configs/format_generated_files/false.rs @@ -0,0 +1,8 @@ +// @generated +// rustfmt-format_generated_files: false + +fn main() +{ + println!("hello, world") + ; +} diff --git a/tests/target/configs/format_generated_files/true.rs b/tests/target/configs/format_generated_files/true.rs new file mode 100644 index 000000000000..5fea7e8b3413 --- /dev/null +++ b/tests/target/configs/format_generated_files/true.rs @@ -0,0 +1,6 @@ +// @generated +// rustfmt-format_generated_files: true + +fn main() { + println!("hello, world"); +} diff --git a/tests/target/configs/format_macro_bodies/false.rs b/tests/target/configs/format_macro_bodies/false.rs new file mode 100644 index 000000000000..ec871b25bf7c --- /dev/null +++ b/tests/target/configs/format_macro_bodies/false.rs @@ -0,0 +1,6 @@ +// rustfmt-format_macro_bodies: false + +macro_rules! foo { + ($a: ident : $b: ty) => { $a(42): $b; }; + ($a: ident $b: ident $c: ident) => { $a=$b+$c; }; +} diff --git a/tests/target/configs/format_macro_bodies/true.rs b/tests/target/configs/format_macro_bodies/true.rs new file mode 100644 index 000000000000..17ac1498c932 --- /dev/null +++ b/tests/target/configs/format_macro_bodies/true.rs @@ -0,0 +1,6 @@ +// rustfmt-format_macro_bodies: true + +macro_rules! foo { + ($a: ident : $b: ty) => { $a(42): $b; }; + ($a: ident $b: ident $c: ident) => { $a=$b+$c; }; +} diff --git a/tests/target/configs/format_macro_matchers/false.rs b/tests/target/configs/format_macro_matchers/false.rs new file mode 100644 index 000000000000..01ecac9879d6 --- /dev/null +++ b/tests/target/configs/format_macro_matchers/false.rs @@ -0,0 +1,6 @@ +// rustfmt-format_macro_matchers: false + +macro_rules! foo { + ($a: ident : $b: ty) => { $a(42): $b; }; + ($a: ident $b: ident $c: ident) => { $a=$b+$c; }; +} diff --git a/tests/target/configs/format_macro_matchers/true.rs b/tests/target/configs/format_macro_matchers/true.rs new file mode 100644 index 000000000000..fa0442e228ac --- /dev/null +++ b/tests/target/configs/format_macro_matchers/true.rs @@ -0,0 +1,6 @@ +// rustfmt-format_macro_matchers: true + +macro_rules! foo { + ($a: ident : $b: ty) => { $a(42): $b; }; + ($a: ident $b: ident $c: ident) => { $a=$b+$c; }; +} diff --git a/tests/target/configs/format_strings/false.rs b/tests/target/configs/format_strings/false.rs new file mode 100644 index 000000000000..ecca0d7d1fca --- /dev/null +++ b/tests/target/configs/format_strings/false.rs @@ -0,0 +1,8 @@ +// rustfmt-format_strings: false +// rustfmt-max_width: 50 +// rustfmt-error_on_line_overflow: false +// Force format strings + +fn main() { + let lorem = "ipsum dolor sit amet consectetur adipiscing elit lorem ipsum dolor sit"; +} diff --git a/tests/target/configs/format_strings/true.rs b/tests/target/configs/format_strings/true.rs new file mode 100644 index 000000000000..fdd5ab2c97d1 --- /dev/null +++ b/tests/target/configs/format_strings/true.rs @@ -0,0 +1,9 @@ +// rustfmt-format_strings: true +// rustfmt-max_width: 50 +// Force format strings + +fn main() { + let lorem = "ipsum dolor sit amet \ + consectetur adipiscing elit \ + lorem ipsum dolor sit"; +} diff --git a/tests/target/configs/group_imports/One-merge_imports.rs b/tests/target/configs/group_imports/One-merge_imports.rs new file mode 100644 index 000000000000..52e0e1c5ac21 --- /dev/null +++ b/tests/target/configs/group_imports/One-merge_imports.rs @@ -0,0 +1,14 @@ +// rustfmt-group_imports: One +// rustfmt-imports_granularity: Crate +use super::{ + schema::{Context, Payload}, + update::convert_publish_payload, +}; +use crate::models::Event; +use alloc::{alloc::Layout, vec::Vec}; +use broker::database::PooledConnection; +use chrono::Utc; +use core::f32; +use juniper::{FieldError, FieldResult}; +use std::sync::Arc; +use uuid::Uuid; diff --git a/tests/target/configs/group_imports/One-nested.rs b/tests/target/configs/group_imports/One-nested.rs new file mode 100644 index 000000000000..5b648548260f --- /dev/null +++ b/tests/target/configs/group_imports/One-nested.rs @@ -0,0 +1,6 @@ +// rustfmt-group_imports: One +mod test { + use crate::foo::bar; + use crate::foo::bar2; + use std::path; +} diff --git a/tests/target/configs/group_imports/One-no_reorder.rs b/tests/target/configs/group_imports/One-no_reorder.rs new file mode 100644 index 000000000000..015e841d0148 --- /dev/null +++ b/tests/target/configs/group_imports/One-no_reorder.rs @@ -0,0 +1,12 @@ +// rustfmt-group_imports: One +// rustfmt-reorder_imports: false +use chrono::Utc; +use super::update::convert_publish_payload; +use juniper::{FieldError, FieldResult}; +use uuid::Uuid; +use alloc::alloc::Layout; +use std::sync::Arc; +use broker::database::PooledConnection; +use super::schema::{Context, Payload}; +use core::f32; +use crate::models::Event; diff --git a/tests/target/configs/group_imports/One.rs b/tests/target/configs/group_imports/One.rs new file mode 100644 index 000000000000..3094c7ae1157 --- /dev/null +++ b/tests/target/configs/group_imports/One.rs @@ -0,0 +1,11 @@ +// rustfmt-group_imports: One +use super::schema::{Context, Payload}; +use super::update::convert_publish_payload; +use crate::models::Event; +use alloc::alloc::Layout; +use broker::database::PooledConnection; +use chrono::Utc; +use core::f32; +use juniper::{FieldError, FieldResult}; +use std::sync::Arc; +use uuid::Uuid; diff --git a/tests/target/configs/group_imports/StdExternalCrate-merge_imports.rs b/tests/target/configs/group_imports/StdExternalCrate-merge_imports.rs new file mode 100644 index 000000000000..5e4064dd8119 --- /dev/null +++ b/tests/target/configs/group_imports/StdExternalCrate-merge_imports.rs @@ -0,0 +1,16 @@ +// rustfmt-group_imports: StdExternalCrate +// rustfmt-imports_granularity: Crate +use alloc::{alloc::Layout, vec::Vec}; +use core::f32; +use std::sync::Arc; + +use broker::database::PooledConnection; +use chrono::Utc; +use juniper::{FieldError, FieldResult}; +use uuid::Uuid; + +use super::{ + schema::{Context, Payload}, + update::convert_publish_payload, +}; +use crate::models::Event; diff --git a/tests/target/configs/group_imports/StdExternalCrate-nested.rs b/tests/target/configs/group_imports/StdExternalCrate-nested.rs new file mode 100644 index 000000000000..daf23375c0e4 --- /dev/null +++ b/tests/target/configs/group_imports/StdExternalCrate-nested.rs @@ -0,0 +1,7 @@ +// rustfmt-group_imports: StdExternalCrate +mod test { + use std::path; + + use crate::foo::bar; + use crate::foo::bar2; +} diff --git a/tests/target/configs/group_imports/StdExternalCrate-no_reorder.rs b/tests/target/configs/group_imports/StdExternalCrate-no_reorder.rs new file mode 100644 index 000000000000..76d3d6ccb954 --- /dev/null +++ b/tests/target/configs/group_imports/StdExternalCrate-no_reorder.rs @@ -0,0 +1,15 @@ +// rustfmt-group_imports: StdExternalCrate +// rustfmt-reorder_imports: false + +use alloc::alloc::Layout; +use std::sync::Arc; +use core::f32; + +use chrono::Utc; +use juniper::{FieldError, FieldResult}; +use uuid::Uuid; +use broker::database::PooledConnection; + +use super::update::convert_publish_payload; +use super::schema::{Context, Payload}; +use crate::models::Event; diff --git a/tests/target/configs/group_imports/StdExternalCrate-non_consecutive.rs b/tests/target/configs/group_imports/StdExternalCrate-non_consecutive.rs new file mode 100644 index 000000000000..ecc8ede02cc6 --- /dev/null +++ b/tests/target/configs/group_imports/StdExternalCrate-non_consecutive.rs @@ -0,0 +1,18 @@ +// rustfmt-group_imports: StdExternalCrate +use alloc::alloc::Layout; + +use chrono::Utc; +use juniper::{FieldError, FieldResult}; +use uuid::Uuid; + +use super::update::convert_publish_payload; + +extern crate uuid; + +use core::f32; +use std::sync::Arc; + +use broker::database::PooledConnection; + +use super::schema::{Context, Payload}; +use crate::models::Event; diff --git a/tests/target/configs/group_imports/StdExternalCrate.rs b/tests/target/configs/group_imports/StdExternalCrate.rs new file mode 100644 index 000000000000..080257968982 --- /dev/null +++ b/tests/target/configs/group_imports/StdExternalCrate.rs @@ -0,0 +1,13 @@ +// rustfmt-group_imports: StdExternalCrate +use alloc::alloc::Layout; +use core::f32; +use std::sync::Arc; + +use broker::database::PooledConnection; +use chrono::Utc; +use juniper::{FieldError, FieldResult}; +use uuid::Uuid; + +use super::schema::{Context, Payload}; +use super::update::convert_publish_payload; +use crate::models::Event; diff --git a/tests/target/configs/hard_tabs/false.rs b/tests/target/configs/hard_tabs/false.rs new file mode 100644 index 000000000000..ccfb53d8c865 --- /dev/null +++ b/tests/target/configs/hard_tabs/false.rs @@ -0,0 +1,6 @@ +// rustfmt-hard_tabs: false +// Hard tabs + +fn lorem() -> usize { + 42 // spaces before 42 +} diff --git a/tests/target/configs/hard_tabs/true.rs b/tests/target/configs/hard_tabs/true.rs new file mode 100644 index 000000000000..3ed4e4f20aa4 --- /dev/null +++ b/tests/target/configs/hard_tabs/true.rs @@ -0,0 +1,6 @@ +// rustfmt-hard_tabs: true +// Hard tabs + +fn lorem() -> usize { + 42 // spaces before 42 +} diff --git a/tests/target/configs/imports_indent/block.rs b/tests/target/configs/imports_indent/block.rs new file mode 100644 index 000000000000..84c3b26bd6e1 --- /dev/null +++ b/tests/target/configs/imports_indent/block.rs @@ -0,0 +1,7 @@ +// rustfmt-imports_indent: Block + +use lists::{ + definitive_tactic, itemize_list, shape_for_tactic, struct_lit_formatting, struct_lit_shape, + struct_lit_tactic, write_list, DefinitiveListTactic, ListFormatting, ListItem, ListTactic, + SeparatorTactic, +}; diff --git a/tests/target/configs/imports_layout/horizontal_vertical.rs b/tests/target/configs/imports_layout/horizontal_vertical.rs new file mode 100644 index 000000000000..4a63556d45bc --- /dev/null +++ b/tests/target/configs/imports_layout/horizontal_vertical.rs @@ -0,0 +1,18 @@ +// rustfmt-imports_indent: Block +// rustfmt-imports_layout: HorizontalVertical + +use comment::{contains_comment, recover_comment_removed, rewrite_comment, FindUncommented}; +use lists::{ + definitive_tactic, + itemize_list, + shape_for_tactic, + struct_lit_formatting, + struct_lit_shape, + struct_lit_tactic, + write_list, + DefinitiveListTactic, + ListFormatting, + ListItem, + ListTactic, + SeparatorTactic, +}; diff --git a/tests/target/configs/imports_layout/merge_mixed.rs b/tests/target/configs/imports_layout/merge_mixed.rs new file mode 100644 index 000000000000..bc0da92fffba --- /dev/null +++ b/tests/target/configs/imports_layout/merge_mixed.rs @@ -0,0 +1,5 @@ +// rustfmt-imports_indent: Block +// rustfmt-imports_granularity: Crate +// rustfmt-imports_layout: Mixed + +use std::{fmt, io, str, str::FromStr}; diff --git a/tests/target/configs/imports_layout/mixed.rs b/tests/target/configs/imports_layout/mixed.rs new file mode 100644 index 000000000000..5d3349a01bba --- /dev/null +++ b/tests/target/configs/imports_layout/mixed.rs @@ -0,0 +1,9 @@ +// rustfmt-imports_indent: Block +// rustfmt-imports_layout: Mixed + +use comment::{contains_comment, recover_comment_removed, rewrite_comment, FindUncommented}; +use lists::{ + definitive_tactic, itemize_list, shape_for_tactic, struct_lit_formatting, struct_lit_shape, + struct_lit_tactic, write_list, DefinitiveListTactic, ListFormatting, ListItem, ListTactic, + SeparatorTactic, +}; diff --git a/tests/target/configs/indent_style/block_args.rs b/tests/target/configs/indent_style/block_args.rs new file mode 100644 index 000000000000..80f4e133356c --- /dev/null +++ b/tests/target/configs/indent_style/block_args.rs @@ -0,0 +1,47 @@ +// rustfmt-indent_style: Block +// Function arguments layout + +fn lorem() {} + +fn lorem(ipsum: usize) {} + +fn lorem( + ipsum: usize, + dolor: usize, + sit: usize, + amet: usize, + consectetur: usize, + adipiscing: usize, + elit: usize, +) { + // body +} + +// #1441 +extern "system" { + pub fn GetConsoleHistoryInfo( + console_history_info: *mut ConsoleHistoryInfo, + ) -> Boooooooooooooool; +} + +// rustfmt should not add trailing comma for variadic function. See #1623. +extern "C" { + pub fn variadic_fn( + first_parameter: FirstParameterType, + second_parameter: SecondParameterType, + ... + ); +} + +// #1652 +fn deconstruct( + foo: Bar, +) -> ( + SocketAddr, + Header, + Method, + RequestUri, + HttpVersion, + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, +) { +} diff --git a/tests/target/configs/indent_style/block_array.rs b/tests/target/configs/indent_style/block_array.rs new file mode 100644 index 000000000000..5d458248c0b9 --- /dev/null +++ b/tests/target/configs/indent_style/block_array.rs @@ -0,0 +1,14 @@ +// rustfmt-indent_style: Block +// Array layout + +fn main() { + let lorem = vec![ + "ipsum", + "dolor", + "sit", + "amet", + "consectetur", + "adipiscing", + "elit", + ]; +} diff --git a/tests/target/configs/indent_style/block_call.rs b/tests/target/configs/indent_style/block_call.rs new file mode 100644 index 000000000000..19c44dc019a2 --- /dev/null +++ b/tests/target/configs/indent_style/block_call.rs @@ -0,0 +1,151 @@ +// rustfmt-indent_style: Block +// Function call style + +fn main() { + lorem( + "lorem", + "ipsum", + "dolor", + "sit", + "amet", + "consectetur", + "adipiscing", + "elit", + ); + // #1501 + let hyper = Arc::new(Client::with_connector( + HttpsConnector::new(TlsClient::new()), + )); + + // chain + let x = yooooooooooooo + .fooooooooooooooo + .baaaaaaaaaaaaar(hello, world); + + // #1380 + { + { + let creds = self + .client + .client_credentials(&self.config.auth.oauth2.id, &self.config.auth.oauth2.secret)?; + } + } + + // nesting macro and function call + try!(foo( + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + )); + try!(foo(try!( + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + ))); +} + +// #1521 +impl Foo { + fn map_pixel_to_coords(&self, point: &Vector2i, view: &View) -> Vector2f { + unsafe { + Vector2f::from_raw(ffi::sfRenderTexture_mapPixelToCoords( + self.render_texture, + point.raw(), + view.raw(), + )) + } + } +} + +fn issue1420() { + given( + r#" + # Getting started + ... + "#, + ) + .running(waltz) +} + +// #1563 +fn query(conn: &Connection) -> Result<()> { + conn.query_row( + r#" + SELECT title, date + FROM posts, + WHERE DATE(date) = $1 + "#, + &[], + |row| Post { + title: row.get(0), + date: row.get(1), + }, + )?; + + Ok(()) +} + +// #1449 +fn future_rayon_wait_1_thread() { + // run with only 1 worker thread; this would deadlock if we couldn't make progress + let mut result = None; + ThreadPool::new(Configuration::new().num_threads(1)) + .unwrap() + .install(|| { + scope(|s| { + use std::sync::mpsc::channel; + let (tx, rx) = channel(); + let a = s.spawn_future(lazy(move || Ok::(rx.recv().unwrap()))); + // ^^^^ FIXME: why is this needed? + let b = s.spawn_future(a.map(|v| v + 1)); + let c = s.spawn_future(b.map(|v| v + 1)); + s.spawn(move |_| tx.send(20).unwrap()); + result = Some(c.rayon_wait().unwrap()); + }); + }); + assert_eq!(result, Some(22)); +} + +// #1494 +impl Cursor { + fn foo() { + self.cur_type() + .num_template_args() + .or_else(|| { + let n: c_int = unsafe { clang_Cursor_getNumTemplateArguments(self.x) }; + + if n >= 0 { + Some(n as u32) + } else { + debug_assert_eq!(n, -1); + None + } + }) + .or_else(|| { + let canonical = self.canonical(); + if canonical != *self { + canonical.num_template_args() + } else { + None + } + }); + } +} + +fn issue1581() { + bootstrap.checks.register("PERSISTED_LOCATIONS", move || { + if locations2.0.inner_mut.lock().poisoned { + Check::new( + State::Error, + "Persisted location storage is poisoned due to a write failure", + ) + } else { + Check::new(State::Healthy, "Persisted location storage is healthy") + } + }); +} + +fn issue1651() { + { + let type_list: Vec<_> = + try_opt!(types.iter().map(|ty| ty.rewrite(context, shape)).collect()); + } +} diff --git a/tests/target/configs/indent_style/block_chain.rs b/tests/target/configs/indent_style/block_chain.rs new file mode 100644 index 000000000000..23340a4aba2f --- /dev/null +++ b/tests/target/configs/indent_style/block_chain.rs @@ -0,0 +1,12 @@ +// rustfmt-indent_style: Block +// Chain indent + +fn main() { + let lorem = ipsum + .dolor() + .sit() + .amet() + .consectetur() + .adipiscing() + .elite(); +} diff --git a/tests/target/configs/indent_style/block_generic.rs b/tests/target/configs/indent_style/block_generic.rs new file mode 100644 index 000000000000..c4fcaaf65d1c --- /dev/null +++ b/tests/target/configs/indent_style/block_generic.rs @@ -0,0 +1,22 @@ +// rustfmt-indent_style: Block +// Generics indent + +fn lorem< + Ipsum: Eq = usize, + Dolor: Eq = usize, + Sit: Eq = usize, + Amet: Eq = usize, + Adipiscing: Eq = usize, + Consectetur: Eq = usize, + Elit: Eq = usize, +>( + ipsum: Ipsum, + dolor: Dolor, + sit: Sit, + amet: Amet, + adipiscing: Adipiscing, + consectetur: Consectetur, + elit: Elit, +) -> T { + // body +} diff --git a/tests/target/configs/indent_style/block_struct_lit.rs b/tests/target/configs/indent_style/block_struct_lit.rs new file mode 100644 index 000000000000..656b562266fd --- /dev/null +++ b/tests/target/configs/indent_style/block_struct_lit.rs @@ -0,0 +1,9 @@ +// rustfmt-indent_style: Block +// Struct literal-style + +fn main() { + let lorem = Lorem { + ipsum: dolor, + sit: amet, + }; +} diff --git a/tests/target/configs/indent_style/block_tab_spaces_call.rs b/tests/target/configs/indent_style/block_tab_spaces_call.rs new file mode 100644 index 000000000000..5531e61ddc8c --- /dev/null +++ b/tests/target/configs/indent_style/block_tab_spaces_call.rs @@ -0,0 +1,14 @@ +// rustfmt-indent_style: Block +// rustfmt-max_width: 80 +// rustfmt-tab_spaces: 2 + +// #1427 +fn main() { + exceptaions::config(move || { + ( + NmiConfig {}, + HardFaultConfig {}, + SysTickConfig { gpio_sbsrr }, + ) + }); +} diff --git a/tests/target/configs/indent_style/block_trailing_comma_call/one.rs b/tests/target/configs/indent_style/block_trailing_comma_call/one.rs new file mode 100644 index 000000000000..6b9489bef550 --- /dev/null +++ b/tests/target/configs/indent_style/block_trailing_comma_call/one.rs @@ -0,0 +1,12 @@ +// rustfmt-version: One +// rustfmt-error_on_line_overflow: false +// rustfmt-indent_style: Block + +// rustfmt should not add trailing comma when rewriting macro. See #1528. +fn a() { + panic!("this is a long string that goes past the maximum line length causing rustfmt to insert a comma here:"); + foo( + a, + oooptoptoptoptptooptoptoptoptptooptoptoptoptptoptoptoptoptpt(), + ); +} diff --git a/tests/target/configs/indent_style/block_trailing_comma_call/two.rs b/tests/target/configs/indent_style/block_trailing_comma_call/two.rs new file mode 100644 index 000000000000..4f4292e5f485 --- /dev/null +++ b/tests/target/configs/indent_style/block_trailing_comma_call/two.rs @@ -0,0 +1,14 @@ +// rustfmt-version: Two +// rustfmt-error_on_line_overflow: false +// rustfmt-indent_style: Block + +// rustfmt should not add trailing comma when rewriting macro. See #1528. +fn a() { + panic!( + "this is a long string that goes past the maximum line length causing rustfmt to insert a comma here:" + ); + foo( + a, + oooptoptoptoptptooptoptoptoptptooptoptoptoptptoptoptoptoptpt(), + ); +} diff --git a/tests/target/configs/indent_style/block_where_pred.rs b/tests/target/configs/indent_style/block_where_pred.rs new file mode 100644 index 000000000000..ad7e0b8f3633 --- /dev/null +++ b/tests/target/configs/indent_style/block_where_pred.rs @@ -0,0 +1,12 @@ +// rustfmt-indent_style: Block +// Where predicate indent + +fn lorem() -> T +where + Ipsum: Eq, + Dolor: Eq, + Sit: Eq, + Amet: Eq, +{ + // body +} diff --git a/tests/target/configs/indent_style/default.rs b/tests/target/configs/indent_style/default.rs new file mode 100644 index 000000000000..a8f0902b34ea --- /dev/null +++ b/tests/target/configs/indent_style/default.rs @@ -0,0 +1,11 @@ +// rustfmt-indent_style: Visual +// Where style + +fn lorem() -> T + where Ipsum: Eq, + Dolor: Eq, + Sit: Eq, + Amet: Eq +{ + // body +} diff --git a/tests/target/configs/indent_style/rfc_control.rs b/tests/target/configs/indent_style/rfc_control.rs new file mode 100644 index 000000000000..6619d8b26303 --- /dev/null +++ b/tests/target/configs/indent_style/rfc_control.rs @@ -0,0 +1,39 @@ +// rustfmt-indent_style: Block + +// #1618 +fn main() { + loop { + if foo { + if ((right_paddle_speed < 0.) && (right_paddle.position().y - paddle_size.y / 2. > 5.)) + || ((right_paddle_speed > 0.) + && (right_paddle.position().y + paddle_size.y / 2. < game_height as f32 - 5.)) + { + foo + } + if ai_timer.elapsed_time().as_microseconds() > ai_time.as_microseconds() { + if ball.position().y + ball_radius > right_paddle.position().y + paddle_size.y / 2. + { + foo + } + } + } + } +} + +fn issue1656() { + { + { + match rewrite { + Some(ref body_str) + if (!body_str.contains('\n') && body_str.len() <= arm_shape.width) + || !context.config.match_arm_blocks() + || (extend && first_line_width(body_str) <= arm_shape.width) + || is_block => + { + return None; + } + _ => {} + } + } + } +} diff --git a/tests/target/configs/indent_style/rfc_where.rs b/tests/target/configs/indent_style/rfc_where.rs new file mode 100644 index 000000000000..a7b9a4f02b3d --- /dev/null +++ b/tests/target/configs/indent_style/rfc_where.rs @@ -0,0 +1,12 @@ +// rustfmt-indent_style: Block +// Where style + +fn lorem() -> T +where + Ipsum: Eq, + Dolor: Eq, + Sit: Eq, + Amet: Eq, +{ + // body +} diff --git a/tests/target/configs/indent_style/visual_args.rs b/tests/target/configs/indent_style/visual_args.rs new file mode 100644 index 000000000000..04c2eaee3cf2 --- /dev/null +++ b/tests/target/configs/indent_style/visual_args.rs @@ -0,0 +1,40 @@ +// rustfmt-indent_style: Visual +// Function arguments layout + +fn lorem() {} + +fn lorem(ipsum: usize) {} + +fn lorem(ipsum: usize, + dolor: usize, + sit: usize, + amet: usize, + consectetur: usize, + adipiscing: usize, + elit: usize) { + // body +} + +// #1922 +extern "C" { + pub fn LAPACKE_csytrs_rook_work(matrix_layout: c_int, + uplo: c_char, + n: lapack_int, + nrhs: lapack_int, + a: *const lapack_complex_float, + lda: lapack_int, + ipiv: *const lapack_int, + b: *mut lapack_complex_float, + ldb: lapack_int) + -> lapack_int; + + pub fn LAPACKE_csytrs_rook_work(matrix_layout: c_int, + uplo: c_char, + n: lapack_int, + nrhs: lapack_int, + lda: lapack_int, + ipiv: *const lapack_int, + b: *mut lapack_complex_float, + ldb: lapack_int) + -> lapack_int; +} diff --git a/tests/target/configs/indent_style/visual_array.rs b/tests/target/configs/indent_style/visual_array.rs new file mode 100644 index 000000000000..1da6ff237387 --- /dev/null +++ b/tests/target/configs/indent_style/visual_array.rs @@ -0,0 +1,12 @@ +// rustfmt-indent_style: Visual +// Array layout + +fn main() { + let lorem = vec!["ipsum", + "dolor", + "sit", + "amet", + "consectetur", + "adipiscing", + "elit"]; +} diff --git a/tests/target/configs/indent_style/visual_call.rs b/tests/target/configs/indent_style/visual_call.rs new file mode 100644 index 000000000000..5454c44ef965 --- /dev/null +++ b/tests/target/configs/indent_style/visual_call.rs @@ -0,0 +1,13 @@ +// rustfmt-indent_style: Visual +// Function call style + +fn main() { + lorem("lorem", + "ipsum", + "dolor", + "sit", + "amet", + "consectetur", + "adipiscing", + "elit"); +} diff --git a/tests/target/configs/indent_style/visual_chain.rs b/tests/target/configs/indent_style/visual_chain.rs new file mode 100644 index 000000000000..569f3d8b81d7 --- /dev/null +++ b/tests/target/configs/indent_style/visual_chain.rs @@ -0,0 +1,11 @@ +// rustfmt-indent_style: Visual +// Chain indent + +fn main() { + let lorem = ipsum.dolor() + .sit() + .amet() + .consectetur() + .adipiscing() + .elite(); +} diff --git a/tests/target/configs/indent_style/visual_generics.rs b/tests/target/configs/indent_style/visual_generics.rs new file mode 100644 index 000000000000..491075a146d1 --- /dev/null +++ b/tests/target/configs/indent_style/visual_generics.rs @@ -0,0 +1,20 @@ +// rustfmt-indent_style: Visual +// Generics indent + +fn lorem( + ipsum: Ipsum, + dolor: Dolor, + sit: Sit, + amet: Amet, + adipiscing: Adipiscing, + consectetur: Consectetur, + elit: Elit) + -> T { + // body +} diff --git a/tests/target/configs/indent_style/visual_struct_lit.rs b/tests/target/configs/indent_style/visual_struct_lit.rs new file mode 100644 index 000000000000..ec49021d330e --- /dev/null +++ b/tests/target/configs/indent_style/visual_struct_lit.rs @@ -0,0 +1,7 @@ +// rustfmt-indent_style: Visual +// Struct literal-style + +fn main() { + let lorem = Lorem { ipsum: dolor, + sit: amet }; +} diff --git a/tests/target/configs/indent_style/visual_trailing_comma.rs b/tests/target/configs/indent_style/visual_trailing_comma.rs new file mode 100644 index 000000000000..9738d397dbf6 --- /dev/null +++ b/tests/target/configs/indent_style/visual_trailing_comma.rs @@ -0,0 +1,7 @@ +// rustfmt-error_on_line_overflow: false +// rustfmt-indent_style: Visual + +// rustfmt should not add trailing comma when rewriting macro. See #1528. +fn a() { + panic!("this is a long string that goes past the maximum line length causing rustfmt to insert a comma here:"); +} diff --git a/tests/target/configs/indent_style/visual_where_pred.rs b/tests/target/configs/indent_style/visual_where_pred.rs new file mode 100644 index 000000000000..45799dcd5fcd --- /dev/null +++ b/tests/target/configs/indent_style/visual_where_pred.rs @@ -0,0 +1,11 @@ +// rustfmt-indent_style: Visual +// Where predicate indent + +fn lorem() -> T + where Ipsum: Eq, + Dolor: Eq, + Sit: Eq, + Amet: Eq +{ + // body +} diff --git a/tests/target/configs/match_arm_blocks/false.rs b/tests/target/configs/match_arm_blocks/false.rs new file mode 100644 index 000000000000..7a9834168c75 --- /dev/null +++ b/tests/target/configs/match_arm_blocks/false.rs @@ -0,0 +1,12 @@ +// rustfmt-match_arm_blocks: false +// Wrap match-arms + +fn main() { + match lorem { + true => + foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo(x), + false => { + println!("{}", sit) + } + } +} diff --git a/tests/target/configs/match_arm_blocks/true.rs b/tests/target/configs/match_arm_blocks/true.rs new file mode 100644 index 000000000000..eb9e34059c7e --- /dev/null +++ b/tests/target/configs/match_arm_blocks/true.rs @@ -0,0 +1,13 @@ +// rustfmt-match_arm_blocks: true +// Wrap match-arms + +fn main() { + match lorem { + true => { + foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo(x) + } + false => { + println!("{}", sit) + } + } +} diff --git a/tests/target/configs/match_arm_leading_pipes/always.rs b/tests/target/configs/match_arm_leading_pipes/always.rs new file mode 100644 index 000000000000..f2af81eac3ca --- /dev/null +++ b/tests/target/configs/match_arm_leading_pipes/always.rs @@ -0,0 +1,27 @@ +// rustfmt-match_arm_leading_pipes: Always + +fn foo() { + match foo { + | "foo" | "bar" => {} + | "baz" + | "something relatively long" + | "something really really really realllllllllllllly long" => println!("x"), + | "qux" => println!("y"), + | _ => {} + } +} + +fn issue_3973() { + match foo { + | "foo" | "bar" => {} + | _ => {} + } +} + +fn bar() { + match baz { + | "qux" => {} + | "foo" | "bar" => {} + | _ => {} + } +} diff --git a/tests/target/configs/match_arm_leading_pipes/never.rs b/tests/target/configs/match_arm_leading_pipes/never.rs new file mode 100644 index 000000000000..345014e4b4e8 --- /dev/null +++ b/tests/target/configs/match_arm_leading_pipes/never.rs @@ -0,0 +1,27 @@ +// rustfmt-match_arm_leading_pipes: Never + +fn foo() { + match foo { + "foo" | "bar" => {} + "baz" + | "something relatively long" + | "something really really really realllllllllllllly long" => println!("x"), + "qux" => println!("y"), + _ => {} + } +} + +fn issue_3973() { + match foo { + "foo" | "bar" => {} + _ => {} + } +} + +fn bar() { + match baz { + "qux" => {} + "foo" | "bar" => {} + _ => {} + } +} diff --git a/tests/target/configs/match_arm_leading_pipes/preserve.rs b/tests/target/configs/match_arm_leading_pipes/preserve.rs new file mode 100644 index 000000000000..4775575842ab --- /dev/null +++ b/tests/target/configs/match_arm_leading_pipes/preserve.rs @@ -0,0 +1,35 @@ +// rustfmt-match_arm_leading_pipes: Preserve + +fn foo() { + match foo { + | "foo" | "bar" => {} + | "baz" + | "something relatively long" + | "something really really really realllllllllllllly long" => println!("x"), + | "qux" => println!("y"), + _ => {} + } +} + +fn issue_3973() { + match foo { + | "foo" | "bar" => {} + _ => {} + } +} + +fn bar() { + match baz { + "qux" => {} + "foo" | "bar" => {} + _ => {} + } +} + +fn f(x: NonAscii) -> bool { + match x { + // foo + | Éfgh => true, + _ => false, + } +} diff --git a/tests/target/configs/match_block_trailing_comma/false.rs b/tests/target/configs/match_block_trailing_comma/false.rs new file mode 100644 index 000000000000..70e02955fb01 --- /dev/null +++ b/tests/target/configs/match_block_trailing_comma/false.rs @@ -0,0 +1,11 @@ +// rustfmt-match_block_trailing_comma: false +// Match block trailing comma + +fn main() { + match lorem { + Lorem::Ipsum => { + println!("ipsum"); + } + Lorem::Dolor => println!("dolor"), + } +} diff --git a/tests/target/configs/match_block_trailing_comma/true.rs b/tests/target/configs/match_block_trailing_comma/true.rs new file mode 100644 index 000000000000..b78b046dc1c9 --- /dev/null +++ b/tests/target/configs/match_block_trailing_comma/true.rs @@ -0,0 +1,11 @@ +// rustfmt-match_block_trailing_comma: true +// Match block trailing comma + +fn main() { + match lorem { + Lorem::Ipsum => { + println!("ipsum"); + }, + Lorem::Dolor => println!("dolor"), + } +} diff --git a/tests/target/configs/merge_derives/true.rs b/tests/target/configs/merge_derives/true.rs new file mode 100644 index 000000000000..4d0148b1c6e2 --- /dev/null +++ b/tests/target/configs/merge_derives/true.rs @@ -0,0 +1,40 @@ +// rustfmt-merge_derives: true +// Merge multiple derives to a single one. + +#[bar] +#[derive(Eq, PartialEq)] +#[foo] +#[derive(Debug)] +#[foobar] +#[derive(Copy, Clone)] +pub enum Foo {} + +#[derive(Eq, PartialEq, Debug)] +#[foobar] +#[derive(Copy, Clone)] +pub enum Bar {} + +#[derive(Eq, PartialEq, Debug, Copy, Clone)] +pub enum FooBar {} + +mod foo { + #[bar] + #[derive(Eq, PartialEq)] + #[foo] + #[derive(Debug)] + #[foobar] + #[derive(Copy, Clone)] + pub enum Foo {} +} + +mod bar { + #[derive(Eq, PartialEq, Debug)] + #[foobar] + #[derive(Copy, Clone)] + pub enum Bar {} +} + +mod foobar { + #[derive(Eq, PartialEq, Debug, Copy, Clone)] + pub enum FooBar {} +} diff --git a/tests/target/configs/normalize_comments/false.rs b/tests/target/configs/normalize_comments/false.rs new file mode 100644 index 000000000000..488962ed9362 --- /dev/null +++ b/tests/target/configs/normalize_comments/false.rs @@ -0,0 +1,13 @@ +// rustfmt-normalize_comments: false +// Normalize comments + +// Lorem ipsum: +fn dolor() -> usize {} + +/* sit amet: */ +fn adipiscing() -> usize {} + +// #652 +//////////////////////////////////////////////////////////////////////////////// +// Basic slice extension methods +//////////////////////////////////////////////////////////////////////////////// diff --git a/tests/target/configs/normalize_comments/true.rs b/tests/target/configs/normalize_comments/true.rs new file mode 100644 index 000000000000..0bdbe08ab4fd --- /dev/null +++ b/tests/target/configs/normalize_comments/true.rs @@ -0,0 +1,13 @@ +// rustfmt-normalize_comments: true +// Normalize comments + +// Lorem ipsum: +fn dolor() -> usize {} + +// sit amet: +fn adipiscing() -> usize {} + +// #652 +//////////////////////////////////////////////////////////////////////////////// +// Basic slice extension methods +//////////////////////////////////////////////////////////////////////////////// diff --git a/tests/target/configs/normalize_doc_attributes/false.rs b/tests/target/configs/normalize_doc_attributes/false.rs new file mode 100644 index 000000000000..f8eb64273c3d --- /dev/null +++ b/tests/target/configs/normalize_doc_attributes/false.rs @@ -0,0 +1,13 @@ +// rustfmt-normalize_doc_attributes: false +// Normalize doc attributes + +#![doc = " Example documentation"] + +#[doc = " Example item documentation"] +pub enum Foo {} + +#[doc = " Lots of space"] +pub enum Bar {} + +#[doc = "no leading space"] +pub mod FooBar {} diff --git a/tests/target/configs/normalize_doc_attributes/true.rs b/tests/target/configs/normalize_doc_attributes/true.rs new file mode 100644 index 000000000000..fadab985bea0 --- /dev/null +++ b/tests/target/configs/normalize_doc_attributes/true.rs @@ -0,0 +1,13 @@ +// rustfmt-normalize_doc_attributes: true +// Normalize doc attributes + +//! Example documentation + +/// Example item documentation +pub enum Foo {} + +/// Lots of space +pub enum Bar {} + +///no leading space +pub mod FooBar {} diff --git a/tests/target/configs/remove_nested_parens/remove_nested_parens.rs b/tests/target/configs/remove_nested_parens/remove_nested_parens.rs new file mode 100644 index 000000000000..d896042c33ce --- /dev/null +++ b/tests/target/configs/remove_nested_parens/remove_nested_parens.rs @@ -0,0 +1,5 @@ +// rustfmt-remove_nested_parens: true + +fn main() { + (foo()); +} diff --git a/tests/target/configs/reorder_impl_items/false.rs b/tests/target/configs/reorder_impl_items/false.rs new file mode 100644 index 000000000000..beb99f0fb8e6 --- /dev/null +++ b/tests/target/configs/reorder_impl_items/false.rs @@ -0,0 +1,11 @@ +// rustfmt-reorder_impl_items: false + +struct Dummy; + +impl Iterator for Dummy { + fn next(&mut self) -> Option { + None + } + + type Item = i32; +} diff --git a/tests/target/configs/reorder_impl_items/true.rs b/tests/target/configs/reorder_impl_items/true.rs new file mode 100644 index 000000000000..f2294412a991 --- /dev/null +++ b/tests/target/configs/reorder_impl_items/true.rs @@ -0,0 +1,11 @@ +// rustfmt-reorder_impl_items: true + +struct Dummy; + +impl Iterator for Dummy { + type Item = i32; + + fn next(&mut self) -> Option { + None + } +} diff --git a/tests/target/configs/reorder_imports/false.rs b/tests/target/configs/reorder_imports/false.rs new file mode 100644 index 000000000000..4b85684dc013 --- /dev/null +++ b/tests/target/configs/reorder_imports/false.rs @@ -0,0 +1,7 @@ +// rustfmt-reorder_imports: false +// Reorder imports + +use lorem; +use ipsum; +use dolor; +use sit; diff --git a/tests/target/configs/reorder_imports/true.rs b/tests/target/configs/reorder_imports/true.rs new file mode 100644 index 000000000000..e4ff7295fd0b --- /dev/null +++ b/tests/target/configs/reorder_imports/true.rs @@ -0,0 +1,19 @@ +// rustfmt-reorder_imports: true +// Reorder imports + +use dolor; +use ipsum; +use lorem; +use sit; + +fn foo() { + use A; + use B; + use C; + + bar(); + + use D; + use E; + use F; +} diff --git a/tests/target/configs/reorder_modules/dolor/mod.rs b/tests/target/configs/reorder_modules/dolor/mod.rs new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/tests/target/configs/reorder_modules/dolor/mod.rs @@ -0,0 +1 @@ + diff --git a/tests/target/configs/reorder_modules/false.rs b/tests/target/configs/reorder_modules/false.rs new file mode 100644 index 000000000000..56b1aa03ed79 --- /dev/null +++ b/tests/target/configs/reorder_modules/false.rs @@ -0,0 +1,7 @@ +// rustfmt-reorder_modules: false +// Reorder modules + +mod lorem; +mod ipsum; +mod dolor; +mod sit; diff --git a/tests/target/configs/reorder_modules/ipsum/mod.rs b/tests/target/configs/reorder_modules/ipsum/mod.rs new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/tests/target/configs/reorder_modules/ipsum/mod.rs @@ -0,0 +1 @@ + diff --git a/tests/target/configs/reorder_modules/lorem/mod.rs b/tests/target/configs/reorder_modules/lorem/mod.rs new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/tests/target/configs/reorder_modules/lorem/mod.rs @@ -0,0 +1 @@ + diff --git a/tests/target/configs/reorder_modules/sit/mod.rs b/tests/target/configs/reorder_modules/sit/mod.rs new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/tests/target/configs/reorder_modules/sit/mod.rs @@ -0,0 +1 @@ + diff --git a/tests/target/configs/reorder_modules/true.rs b/tests/target/configs/reorder_modules/true.rs new file mode 100644 index 000000000000..18361e88b5fc --- /dev/null +++ b/tests/target/configs/reorder_modules/true.rs @@ -0,0 +1,7 @@ +// rustfmt-reorder_modules: true +// Reorder modules + +mod dolor; +mod ipsum; +mod lorem; +mod sit; diff --git a/tests/target/configs/short_array_element_width_threshold/10.rs b/tests/target/configs/short_array_element_width_threshold/10.rs new file mode 100644 index 000000000000..78c4adba1c1f --- /dev/null +++ b/tests/target/configs/short_array_element_width_threshold/10.rs @@ -0,0 +1,11 @@ +// rustfmt-short_array_element_width_threshold: 10 + +fn main() { + pub const FORMAT_TEST: [u64; 5] = [ + 0x0000000000000000, + 0xaaaaaaaaaaaaaaaa, + 0xbbbbbbbbbbbbbbbb, + 0xcccccccccccccccc, + 0xdddddddddddddddd, + ]; +} diff --git a/tests/target/configs/short_array_element_width_threshold/20.rs b/tests/target/configs/short_array_element_width_threshold/20.rs new file mode 100644 index 000000000000..6084690652f0 --- /dev/null +++ b/tests/target/configs/short_array_element_width_threshold/20.rs @@ -0,0 +1,8 @@ +// rustfmt-short_array_element_width_threshold: 20 + +fn main() { + pub const FORMAT_TEST: [u64; 5] = [ + 0x0000000000000000, 0xaaaaaaaaaaaaaaaa, 0xbbbbbbbbbbbbbbbb, 0xcccccccccccccccc, + 0xdddddddddddddddd, + ]; +} diff --git a/tests/target/configs/short_array_element_width_threshold/greater_than_max_width.rs b/tests/target/configs/short_array_element_width_threshold/greater_than_max_width.rs new file mode 100644 index 000000000000..710b6fe7c4ba --- /dev/null +++ b/tests/target/configs/short_array_element_width_threshold/greater_than_max_width.rs @@ -0,0 +1,12 @@ +// rustfmt-max_width: 20 +// rustfmt-short_array_element_width_threshold: 30 + +fn main() { + pub const FORMAT_TEST: [u64; 5] = [ + 0x0000000000000000, + 0xaaaaaaaaaaaaaaaa, + 0xbbbbbbbbbbbbbbbb, + 0xcccccccccccccccc, + 0xdddddddddddddddd, + ]; +} diff --git a/tests/target/configs/single_line_let_else_max_width/100.rs b/tests/target/configs/single_line_let_else_max_width/100.rs new file mode 100644 index 000000000000..0409124a5b08 --- /dev/null +++ b/tests/target/configs/single_line_let_else_max_width/100.rs @@ -0,0 +1,60 @@ +// rustfmt-single_line_let_else_max_width: 100 + +fn main() { + let Some(a) = opt else {}; + + let Some(b) = opt else { return }; + + let Some(c) = opt else { return }; + + let Some(c) = opt else { + // a comment should always force the block to be multi-lined + return; + }; + + let Some(c) = opt else { + /* a comment should always force the block to be multi-lined */ + return; + }; + + let Some(d) = some_very_very_very_very_long_name else { return }; + + let Expr::Slice(ast::ExprSlice { + lower, + upper, + step, + range: _, + }) = slice.as_ref() + else { + return; + }; + + let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true)? else { + return Ok(None); + }; + + let Some(doc_attr) = variant + .attrs + .iter() + .find(|attr| attr.path().is_ident("doc")) + else { + return Err(Error::new(variant.span(), r#"expected a doc comment"#)); + }; + + let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true) else { + return Ok(None); + }; + + let Stmt::Expr( + Expr::Call(ExprCall { + args: some_args, .. + }), + _, + ) = last_stmt + else { + return Err(Error::new( + last_stmt.span(), + "expected last expression to be `Some(match (..) { .. })`", + )); + }; +} diff --git a/tests/target/configs/single_line_let_else_max_width/50.rs b/tests/target/configs/single_line_let_else_max_width/50.rs new file mode 100644 index 000000000000..6afc2b6f2b06 --- /dev/null +++ b/tests/target/configs/single_line_let_else_max_width/50.rs @@ -0,0 +1,62 @@ +// rustfmt-single_line_let_else_max_width: 50 + +fn main() { + let Some(a) = opt else {}; + + let Some(b) = opt else { return }; + + let Some(c) = opt else { return }; + + let Some(c) = opt else { + // a comment should always force the block to be multi-lined + return; + }; + + let Some(c) = opt else { + /* a comment should always force the block to be multi-lined */ + return; + }; + + let Some(d) = some_very_very_very_very_long_name else { + return; + }; + + let Expr::Slice(ast::ExprSlice { + lower, + upper, + step, + range: _, + }) = slice.as_ref() + else { + return; + }; + + let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true)? else { + return Ok(None); + }; + + let Some(doc_attr) = variant + .attrs + .iter() + .find(|attr| attr.path().is_ident("doc")) + else { + return Err(Error::new(variant.span(), r#"expected a doc comment"#)); + }; + + let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true) else { + return Ok(None); + }; + + let Stmt::Expr( + Expr::Call(ExprCall { + args: some_args, .. + }), + _, + ) = last_stmt + else { + return Err(Error::new( + last_stmt.span(), + "expected last expression to be `Some(match (..) { .. })`", + )); + }; +} diff --git a/tests/target/configs/single_line_let_else_max_width/zero.rs b/tests/target/configs/single_line_let_else_max_width/zero.rs new file mode 100644 index 000000000000..b5fd0b9edaf9 --- /dev/null +++ b/tests/target/configs/single_line_let_else_max_width/zero.rs @@ -0,0 +1,66 @@ +// rustfmt-single_line_let_else_max_width: 0 + +fn main() { + let Some(a) = opt else {}; + + let Some(b) = opt else { + return; + }; + + let Some(c) = opt else { + return; + }; + + let Some(c) = opt else { + // a comment should always force the block to be multi-lined + return; + }; + + let Some(c) = opt else { + /* a comment should always force the block to be multi-lined */ + return; + }; + + let Some(d) = some_very_very_very_very_long_name else { + return; + }; + + let Expr::Slice(ast::ExprSlice { + lower, + upper, + step, + range: _, + }) = slice.as_ref() + else { + return; + }; + + let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true)? else { + return Ok(None); + }; + + let Some(doc_attr) = variant + .attrs + .iter() + .find(|attr| attr.path().is_ident("doc")) + else { + return Err(Error::new(variant.span(), r#"expected a doc comment"#)); + }; + + let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true) else { + return Ok(None); + }; + + let Stmt::Expr( + Expr::Call(ExprCall { + args: some_args, .. + }), + _, + ) = last_stmt + else { + return Err(Error::new( + last_stmt.span(), + "expected last expression to be `Some(match (..) { .. })`", + )); + }; +} diff --git a/tests/target/configs/skip_children/foo/mod.rs b/tests/target/configs/skip_children/foo/mod.rs new file mode 100644 index 000000000000..d7ff6cdb8290 --- /dev/null +++ b/tests/target/configs/skip_children/foo/mod.rs @@ -0,0 +1,3 @@ +fn skip_formatting_this() { + println ! ( "Skip this" ) ; +} diff --git a/tests/target/configs/skip_children/true.rs b/tests/target/configs/skip_children/true.rs new file mode 100644 index 000000000000..33fd782b4fa2 --- /dev/null +++ b/tests/target/configs/skip_children/true.rs @@ -0,0 +1,4 @@ +// rustfmt-skip_children: true + +mod foo; +mod void; diff --git a/tests/target/configs/space_before_colon/true.rs b/tests/target/configs/space_before_colon/true.rs new file mode 100644 index 000000000000..e2895b5d77b1 --- /dev/null +++ b/tests/target/configs/space_before_colon/true.rs @@ -0,0 +1,11 @@ +// rustfmt-space_before_colon: true +// Space before colon + +fn lorem(t : T) { + let ipsum : Dolor = sit; +} + +const LOREM : Lorem = Lorem { + ipsum : dolor, + sit : amet, +}; diff --git a/tests/target/configs/spaces_around_ranges/false.rs b/tests/target/configs/spaces_around_ranges/false.rs new file mode 100644 index 000000000000..72b1be4804c6 --- /dev/null +++ b/tests/target/configs/spaces_around_ranges/false.rs @@ -0,0 +1,34 @@ +// rustfmt-spaces_around_ranges: false +// Spaces around ranges + +fn main() { + let lorem = 0..10; + let ipsum = 0..=10; + + match lorem { + 1..5 => foo(), + _ => bar, + } + + match lorem { + 1..=5 => foo(), + _ => bar, + } + + match lorem { + 1...5 => foo(), + _ => bar, + } +} + +fn half_open() { + match [5..4, 99..105, 43..44] { + [_, 99.., _] => {} + [_, ..105, _] => {} + _ => {} + }; + + if let ..=5 = 0 {} + if let ..5 = 0 {} + if let 5.. = 0 {} +} diff --git a/tests/target/configs/spaces_around_ranges/true.rs b/tests/target/configs/spaces_around_ranges/true.rs new file mode 100644 index 000000000000..c56fdbb02b68 --- /dev/null +++ b/tests/target/configs/spaces_around_ranges/true.rs @@ -0,0 +1,34 @@ +// rustfmt-spaces_around_ranges: true +// Spaces around ranges + +fn main() { + let lorem = 0 .. 10; + let ipsum = 0 ..= 10; + + match lorem { + 1 .. 5 => foo(), + _ => bar, + } + + match lorem { + 1 ..= 5 => foo(), + _ => bar, + } + + match lorem { + 1 ... 5 => foo(), + _ => bar, + } +} + +fn half_open() { + match [5 .. 4, 99 .. 105, 43 .. 44] { + [_, 99 .., _] => {} + [_, .. 105, _] => {} + _ => {} + }; + + if let ..= 5 = 0 {} + if let .. 5 = 0 {} + if let 5 .. = 0 {} +} diff --git a/tests/target/configs/struct_field_align_threshold/20.rs b/tests/target/configs/struct_field_align_threshold/20.rs new file mode 100644 index 000000000000..12a523e9d83d --- /dev/null +++ b/tests/target/configs/struct_field_align_threshold/20.rs @@ -0,0 +1,471 @@ +// rustfmt-struct_field_align_threshold: 20 +// rustfmt-normalize_comments: true +// rustfmt-wrap_comments: true +// rustfmt-error_on_line_overflow: false + +struct Foo { + x: u32, + yy: u32, // comment + zzz: u32, +} + +pub struct Bar { + x: u32, + yy: u32, + zzz: u32, + + xxxxxxx: u32, +} + +fn main() { + let foo = Foo { + x: 0, + yy: 1, + zzz: 2, + }; + + let bar = Bar { + x: 0, + yy: 1, + zzz: 2, + + xxxxxxx: 3, + }; +} + +/// A Doc comment +#[AnAttribute] +pub struct Foo { + #[rustfmt::skip] + f : SomeType, // Comment beside a field + f: SomeType, // Comment beside a field + // Comment on a field + #[AnAttribute] + g: SomeOtherType, + /// A doc comment on a field + h: AThirdType, + pub i: TypeForPublicField, +} + +// #1029 +pub struct Foo { + #[doc(hidden)] + // This will NOT get deleted! + bar: String, // hi +} + +// #1029 +struct X { + // `x` is an important number. + #[allow(unused)] // TODO: use + x: u32, +} + +// #410 +#[allow(missing_docs)] +pub struct Writebatch { + #[allow(dead_code)] // only used for holding the internal pointer + writebatch: RawWritebatch, + marker: PhantomData, +} + +struct Bar; + +struct NewType(Type, OtherType); + +struct NewInt( + pub i32, + SomeType, // inline comment + T, // sup +); + +struct Qux< + 'a, + N: Clone + 'a, + E: Clone + 'a, + G: Labeller<'a, N, E> + GraphWalk<'a, N, E>, + W: Write + Copy, +>( + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, // Comment + BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB, + #[AnAttr] + // Comment + /// Testdoc + G, + pub W, +); + +struct Tuple( + // Comment 1 + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, + // Comment 2 + BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB, +); + +// With a where-clause and generics. +pub struct Foo<'a, Y: Baz> +where + X: Whatever, +{ + f: SomeType, // Comment beside a field +} + +struct Baz { + a: A, // Comment A + b: B, // Comment B + c: C, // Comment C +} + +struct Baz { + a: A, // Comment A + + b: B, // Comment B + + c: C, // Comment C +} + +struct Baz { + a: A, + + b: B, + c: C, + + d: D, +} + +struct Baz { + // Comment A + a: A, + + // Comment B + b: B, + // Comment C + c: C, +} + +// Will this be a one-liner? +struct Tuple( + A, // Comment + B, +); + +pub struct State time::Timespec> { + now: F, +} + +pub struct State ()> { + now: F, +} + +pub struct State { + now: F, +} + +struct Palette { + /// A map of indices in the palette to a count of pixels in approximately + /// that color + foo: i32, +} + +// Splitting a single line comment into a block previously had a misalignment +// when the field had attributes +struct FieldsWithAttributes { + // Pre Comment + #[rustfmt::skip] pub host:String, /* Post comment BBBBBBBBBBBBBB BBBBBBBBBBBBBBBB + * BBBBBBBBBBBBBBBB BBBBBBBBBBBBBBBBB BBBBBBBBBBB */ + // Another pre comment + #[attr1] + #[attr2] + pub id: usize, /* CCCCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCC + * CCCCCCCCCCCCCCCCCC CCCCCCCCCCCCCC CCCCCCCCCCCC */ +} + +struct Deep { + deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeep: + node::Handle>, Type, NodeType>, +} + +struct Foo(T); +struct Foo(T) +where + T: Copy, + T: Eq; +struct Foo( + TTTTTTTTTTTTTTTTT, + UUUUUUUUUUUUUUUUUUUUUUUU, + TTTTTTTTTTTTTTTTTTT, + UUUUUUUUUUUUUUUUUUU, +); +struct Foo( + TTTTTTTTTTTTTTTTTT, + UUUUUUUUUUUUUUUUUUUUUUUU, + TTTTTTTTTTTTTTTTTTT, +) +where + T: PartialEq; +struct Foo( + TTTTTTTTTTTTTTTTT, + UUUUUUUUUUUUUUUUUUUUUUUU, + TTTTTTTTTTTTTTTTTTTTT, +) +where + T: PartialEq; +struct Foo( + TTTTTTTTTTTTTTTTT, + UUUUUUUUUUUUUUUUUUUUUUUU, + TTTTTTTTTTTTTTTTTTT, + UUUUUUUUUUUUUUUUUUU, +) +where + T: PartialEq; +struct Foo( + TTTTTTTTTTTTTTTTT, // Foo + UUUUUUUUUUUUUUUUUUUUUUUU, // Bar + // Baz + TTTTTTTTTTTTTTTTTTT, + // Qux (FIXME #572 - doc comment) + UUUUUUUUUUUUUUUUUUU, +); + +mod m { + struct X + where + T: Sized, + { + a: T, + } +} + +struct Foo( + TTTTTTTTTTTTTTTTTTT, + /// Qux + UUUUUUUUUUUUUUUUUUU, +); + +struct Issue677 { + pub ptr: *const libc::c_void, + pub trace: fn(obj: *const libc::c_void, tracer: *mut JSTracer), +} + +struct Foo {} +struct Foo {} +struct Foo { + // comment +} +struct Foo { + // trailing space -> +} +struct Foo { + // comment +} +struct Foo( + // comment +); + +struct LongStruct { + a: A, + the_quick_brown_fox_jumps_over_the_lazy_dog: + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, +} + +struct Deep { + deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeep: + node::Handle>, Type, NodeType>, +} + +struct Foo(String); + +// #1364 +fn foo() { + convex_shape.set_point(0, &Vector2f { x: 400.0, y: 100.0 }); + convex_shape.set_point(1, &Vector2f { x: 500.0, y: 70.0 }); + convex_shape.set_point(2, &Vector2f { x: 450.0, y: 100.0 }); + convex_shape.set_point(3, &Vector2f { x: 580.0, y: 150.0 }); +} + +fn main() { + let x = Bar; + + // Comment + let y = Foo { a: x }; + + Foo { + a: foo(), // comment + // comment + b: bar(), + ..something + }; + + Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { a: f(), b: b() }; + + Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { + a: f(), + b: b(), + }; + + Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { + // Comment + a: foo(), // Comment + // Comment + b: bar(), // Comment + }; + + Foo { a: Bar, b: f() }; + + Quux { + x: if cond { + bar(); + }, + y: baz(), + }; + + A { + // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit + // amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante + // hendrerit. Donec et mollis dolor. + first: item(), + // Praesent et diam eget libero egestas mattis sit amet vitae augue. + // Nam tincidunt congue enim, ut porta lorem lacinia consectetur. + second: Item, + }; + + Some(Data::MethodCallData(MethodCallData { + span: sub_span.unwrap(), + scope: self.enclosing_scope(id), + ref_id: def_id, + decl_id: Some(decl_id), + })); + + Diagram { + // o This graph demonstrates how + // / \ significant whitespace is + // o o preserved. + // /|\ \ + // o o o o + graph: G, + } +} + +fn matcher() { + TagTerminatedByteMatcher { + matcher: ByteMatcher { + pattern: b" { + memb: T, + } + let foo = Foo:: { memb: 10 }; +} + +fn issue201() { + let s = S { a: 0, ..b }; +} + +fn issue201_2() { + let s = S { a: S2 { ..c }, ..b }; +} + +fn issue278() { + let s = S { + a: 0, + // + b: 0, + }; + let s1 = S { + a: 0, + // foo + // + // bar + b: 0, + }; +} + +fn struct_exprs() { + Foo { a: 1, b: f(2) }; + Foo { + a: 1, + b: f(2), + ..g(3) + }; + LoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooongStruct { + ..base + }; + IntrinsicISizesContribution { + content_intrinsic_sizes: IntrinsicISizes { + minimum_inline_size: 0, + }, + }; +} + +fn issue123() { + Foo { a: b, c: d, e: f }; + + Foo { + a: bb, + c: dd, + e: ff, + }; + + Foo { + a: ddddddddddddddddddddd, + b: cccccccccccccccccccccccccccccccccccccc, + }; +} + +fn issue491() { + Foo { + guard: None, + arm: 0, // Comment + }; + + Foo { + arm: 0, // Comment + }; + + Foo { + a: aaaaaaaaaa, + b: bbbbbbbb, + c: cccccccccc, + d: dddddddddd, // a comment + e: eeeeeeeee, + }; +} + +fn issue698() { + Record { + ffffffffffffffffffffffffffields: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + }; + Record { + ffffffffffffffffffffffffffields: + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + } +} + +fn issue835() { + MyStruct {}; + MyStruct { /* a comment */ }; + MyStruct { + // Another comment + }; + MyStruct {} +} + +fn field_init_shorthand() { + MyStruct { x, y, z }; + MyStruct { x, y, z, ..base }; + Foo { + aaaaaaaaaa, + bbbbbbbb, + cccccccccc, + dddddddddd, // a comment + eeeeeeeee, + }; + Record { + ffffffffffffffffffffffffffieldsaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + }; +} diff --git a/tests/target/configs/struct_lit_single_line/false.rs b/tests/target/configs/struct_lit_single_line/false.rs new file mode 100644 index 000000000000..e2732b5a7c9b --- /dev/null +++ b/tests/target/configs/struct_lit_single_line/false.rs @@ -0,0 +1,9 @@ +// rustfmt-struct_lit_single_line: false +// Struct literal multiline-style + +fn main() { + let lorem = Lorem { + ipsum: dolor, + sit: amet, + }; +} diff --git a/tests/target/configs/tab_spaces/2.rs b/tests/target/configs/tab_spaces/2.rs new file mode 100644 index 000000000000..85961706ea72 --- /dev/null +++ b/tests/target/configs/tab_spaces/2.rs @@ -0,0 +1,14 @@ +// rustfmt-tab_spaces: 2 +// rustfmt-max_width: 30 +// rustfmt-indent_style: Block +// Tab spaces + +fn lorem() { + let ipsum = dolor(); + let sit = vec![ + "amet", + "consectetur", + "adipiscing", + "elit.", + ]; +} diff --git a/tests/target/configs/tab_spaces/4.rs b/tests/target/configs/tab_spaces/4.rs new file mode 100644 index 000000000000..524a55121e4a --- /dev/null +++ b/tests/target/configs/tab_spaces/4.rs @@ -0,0 +1,14 @@ +// rustfmt-tab_spaces: 4 +// rustfmt-max_width: 30 +// rustfmt-indent_style: Block +// Tab spaces + +fn lorem() { + let ipsum = dolor(); + let sit = vec![ + "amet", + "consectetur", + "adipiscing", + "elit.", + ]; +} diff --git a/tests/target/configs/trailing_comma/always.rs b/tests/target/configs/trailing_comma/always.rs new file mode 100644 index 000000000000..951dc680912e --- /dev/null +++ b/tests/target/configs/trailing_comma/always.rs @@ -0,0 +1,14 @@ +// rustfmt-trailing_comma: Always +// Trailing comma + +fn main() { + let Lorem { ipsum, dolor, sit, } = amet; + let Lorem { + ipsum, + dolor, + sit, + amet, + consectetur, + adipiscing, + } = elit; +} diff --git a/tests/target/configs/trailing_comma/never.rs b/tests/target/configs/trailing_comma/never.rs new file mode 100644 index 000000000000..ae0e50f96d18 --- /dev/null +++ b/tests/target/configs/trailing_comma/never.rs @@ -0,0 +1,35 @@ +// rustfmt-trailing_comma: Never +// Trailing comma + +fn main() { + let Lorem { ipsum, dolor, sit } = amet; + let Lorem { + ipsum, + dolor, + sit, + amet, + consectetur, + adipiscing + } = elit; + + // #1544 + if let VrMsg::ClientReply { + request_num: reply_req_num, + value, + .. + } = msg + { + let _ = safe_assert_eq!(reply_req_num, request_num, op); + return Ok((request_num, op, value)); + } + + // #1710 + pub struct FileInput { + input: StringInput, + file_name: OsString + } + match len { + Some(len) => Ok(new(self.input, self.pos + len)), + None => Err(self) + } +} diff --git a/tests/target/configs/trailing_comma/vertical.rs b/tests/target/configs/trailing_comma/vertical.rs new file mode 100644 index 000000000000..7283cde8d060 --- /dev/null +++ b/tests/target/configs/trailing_comma/vertical.rs @@ -0,0 +1,14 @@ +// rustfmt-trailing_comma: Vertical +// Trailing comma + +fn main() { + let Lorem { ipsum, dolor, sit } = amet; + let Lorem { + ipsum, + dolor, + sit, + amet, + consectetur, + adipiscing, + } = elit; +} diff --git a/tests/target/configs/trailing_semicolon/false.rs b/tests/target/configs/trailing_semicolon/false.rs new file mode 100644 index 000000000000..9fa746e9c0ff --- /dev/null +++ b/tests/target/configs/trailing_semicolon/false.rs @@ -0,0 +1,27 @@ +// rustfmt-trailing_semicolon: false + +#![feature(loop_break_value)] + +fn main() { + 'a: loop { + break 'a + } + + let mut done = false; + 'b: while !done { + done = true; + continue 'b + } + + let x = loop { + break 5 + }; + + let x = 'c: loop { + break 'c 5 + }; +} + +fn foo() -> usize { + return 0 +} diff --git a/tests/target/configs/trailing_semicolon/true.rs b/tests/target/configs/trailing_semicolon/true.rs new file mode 100644 index 000000000000..61b6843d677c --- /dev/null +++ b/tests/target/configs/trailing_semicolon/true.rs @@ -0,0 +1,27 @@ +// rustfmt-trailing_semicolon: true + +#![feature(loop_break_value)] + +fn main() { + 'a: loop { + break 'a; + } + + let mut done = false; + 'b: while !done { + done = true; + continue 'b; + } + + let x = loop { + break 5; + }; + + let x = 'c: loop { + break 'c 5; + }; +} + +fn foo() -> usize { + return 0; +} diff --git a/tests/target/configs/type_punctuation_density/compressed.rs b/tests/target/configs/type_punctuation_density/compressed.rs new file mode 100644 index 000000000000..6571e448e446 --- /dev/null +++ b/tests/target/configs/type_punctuation_density/compressed.rs @@ -0,0 +1,41 @@ +// rustfmt-type_punctuation_density: Compressed +// Type punctuation density + +fn lorem() { + // body +} + +struct Foo +where + U: Eq+Clone, { + // body +} + +trait Foo<'a, T=usize> +where + T: 'a+Eq+Clone, +{ + type Bar: Eq+Clone; +} + +trait Foo: Eq+Clone { + // body +} + +impl Foo<'a> for Bar +where + for<'a> T: 'a+Eq+Clone, +{ + // body +} + +fn foo<'a, 'b, 'c>() +where + 'a: 'b+'c, +{ + // body +} + +fn Foo+Foo>() { + let i = 6; +} diff --git a/tests/target/configs/type_punctuation_density/wide.rs b/tests/target/configs/type_punctuation_density/wide.rs new file mode 100644 index 000000000000..01546c7b0a51 --- /dev/null +++ b/tests/target/configs/type_punctuation_density/wide.rs @@ -0,0 +1,41 @@ +// rustfmt-type_punctuation_density: Wide +// Type punctuation density + +fn lorem() { + // body +} + +struct Foo +where + U: Eq + Clone, { + // body +} + +trait Foo<'a, T = usize> +where + T: 'a + Eq + Clone, +{ + type Bar: Eq + Clone; +} + +trait Foo: Eq + Clone { + // body +} + +impl Foo<'a> for Bar +where + for<'a> T: 'a + Eq + Clone, +{ + // body +} + +fn foo<'a, 'b, 'c>() +where + 'a: 'b + 'c, +{ + // body +} + +fn Foo + Foo>() { + let i = 6; +} diff --git a/tests/target/configs/use_field_init_shorthand/false.rs b/tests/target/configs/use_field_init_shorthand/false.rs new file mode 100644 index 000000000000..743304468867 --- /dev/null +++ b/tests/target/configs/use_field_init_shorthand/false.rs @@ -0,0 +1,15 @@ +// rustfmt-use_field_init_shorthand: false +// Use field initialization shorthand if possible. + +fn main() { + let a = Foo { x: x, y: y, z: z }; + + let b = Bar { + x: x, + y: y, + #[attr] + z: z, + #[rustfmt::skip] + skipped: skipped, + }; +} diff --git a/tests/target/configs/use_field_init_shorthand/true.rs b/tests/target/configs/use_field_init_shorthand/true.rs new file mode 100644 index 000000000000..8b80e81534b7 --- /dev/null +++ b/tests/target/configs/use_field_init_shorthand/true.rs @@ -0,0 +1,15 @@ +// rustfmt-use_field_init_shorthand: true +// Use field initialization shorthand if possible. + +fn main() { + let a = Foo { x, y, z }; + + let b = Bar { + x, + y, + #[attr] + z, + #[rustfmt::skip] + skipped: skipped, + }; +} diff --git a/tests/target/configs/use_small_heuristics/default.rs b/tests/target/configs/use_small_heuristics/default.rs new file mode 100644 index 000000000000..ad40739233e7 --- /dev/null +++ b/tests/target/configs/use_small_heuristics/default.rs @@ -0,0 +1,38 @@ +// rustfmt-use_small_heuristics: Default + +enum Lorem { + Ipsum, + Dolor(bool), + Sit { amet: Consectetur, adipiscing: Elit }, +} + +fn main() { + lorem( + "lorem", + "ipsum", + "dolor", + "sit", + "amet", + "consectetur", + "adipiscing", + ); + + let lorem = Lorem { + ipsum: dolor, + sit: amet, + }; + + let lorem = if ipsum { dolor } else { sit }; +} + +fn format_let_else() { + let Some(a) = opt else {}; + + let Some(b) = opt else { return }; + + let Some(c) = opt else { return }; + + let Some(d) = some_very_very_very_very_long_name else { + return; + }; +} diff --git a/tests/target/configs/use_small_heuristics/max.rs b/tests/target/configs/use_small_heuristics/max.rs new file mode 100644 index 000000000000..fe57f853d9dd --- /dev/null +++ b/tests/target/configs/use_small_heuristics/max.rs @@ -0,0 +1,25 @@ +// rustfmt-use_small_heuristics: Max + +enum Lorem { + Ipsum, + Dolor(bool), + Sit { amet: Consectetur, adipiscing: Elit }, +} + +fn main() { + lorem("lorem", "ipsum", "dolor", "sit", "amet", "consectetur", "adipiscing"); + + let lorem = Lorem { ipsum: dolor, sit: amet }; + + let lorem = if ipsum { dolor } else { sit }; +} + +fn format_let_else() { + let Some(a) = opt else {}; + + let Some(b) = opt else { return }; + + let Some(c) = opt else { return }; + + let Some(d) = some_very_very_very_very_long_name else { return }; +} diff --git a/tests/target/configs/use_small_heuristics/off.rs b/tests/target/configs/use_small_heuristics/off.rs new file mode 100644 index 000000000000..b0b4e4ee49fe --- /dev/null +++ b/tests/target/configs/use_small_heuristics/off.rs @@ -0,0 +1,41 @@ +// rustfmt-use_small_heuristics: Off + +enum Lorem { + Ipsum, + Dolor(bool), + Sit { + amet: Consectetur, + adipiscing: Elit, + }, +} + +fn main() { + lorem("lorem", "ipsum", "dolor", "sit", "amet", "consectetur", "adipiscing"); + + let lorem = Lorem { + ipsum: dolor, + sit: amet, + }; + + let lorem = if ipsum { + dolor + } else { + sit + }; +} + +fn format_let_else() { + let Some(a) = opt else {}; + + let Some(b) = opt else { + return; + }; + + let Some(c) = opt else { + return; + }; + + let Some(d) = some_very_very_very_very_long_name else { + return; + }; +} diff --git a/tests/target/configs/use_try_shorthand/false.rs b/tests/target/configs/use_try_shorthand/false.rs new file mode 100644 index 000000000000..de7f8b4a5e24 --- /dev/null +++ b/tests/target/configs/use_try_shorthand/false.rs @@ -0,0 +1,6 @@ +// rustfmt-use_try_shorthand: false +// Use try! shorthand + +fn main() { + let lorem = try!(ipsum.map(|dolor| dolor.sit())); +} diff --git a/tests/target/configs/use_try_shorthand/true.rs b/tests/target/configs/use_try_shorthand/true.rs new file mode 100644 index 000000000000..d3aa035792c2 --- /dev/null +++ b/tests/target/configs/use_try_shorthand/true.rs @@ -0,0 +1,6 @@ +// rustfmt-use_try_shorthand: true +// Use try! shorthand + +fn main() { + let lorem = ipsum.map(|dolor| dolor.sit())?; +} diff --git a/tests/target/configs/where_single_line/true-with-brace-style.rs b/tests/target/configs/where_single_line/true-with-brace-style.rs new file mode 100644 index 000000000000..ec7f79b689ca --- /dev/null +++ b/tests/target/configs/where_single_line/true-with-brace-style.rs @@ -0,0 +1,22 @@ +// rustfmt-brace_style: SameLineWhere +// rustfmt-where_single_line: true + +fn lorem_multi_line_clauseless( + a: Aaaaaaaaaaaaaaa, + b: Bbbbbbbbbbbbbbbb, + c: Ccccccccccccccccc, + d: Ddddddddddddddddddddddddd, + e: Eeeeeeeeeeeeeeeeeee, +) -> T { + // body +} + +fn lorem_multi_line_clauseless( + a: Aaaaaaaaaaaaaaa, + b: Bbbbbbbbbbbbbbbb, + c: Ccccccccccccccccc, + d: Ddddddddddddddddddddddddd, + e: Eeeeeeeeeeeeeeeeeee, +) { + // body +} diff --git a/tests/target/configs/where_single_line/true.rs b/tests/target/configs/where_single_line/true.rs new file mode 100644 index 000000000000..7f816459e3c6 --- /dev/null +++ b/tests/target/configs/where_single_line/true.rs @@ -0,0 +1,30 @@ +// rustfmt-where_single_line: true +// Where style + +fn lorem_two_items() -> T +where + Ipsum: Eq, + Lorem: Eq, +{ + // body +} + +fn lorem_multi_line( + a: Aaaaaaaaaaaaaaa, + b: Bbbbbbbbbbbbbbbb, + c: Ccccccccccccccccc, + d: Ddddddddddddddddddddddddd, + e: Eeeeeeeeeeeeeeeeeee, +) -> T +where + Ipsum: Eq, +{ + // body +} + +fn lorem() -> T +where Ipsum: Eq { + // body +} + +unsafe impl Sync for Foo where (): Send {} diff --git a/tests/target/configs/wrap_comments/false.rs b/tests/target/configs/wrap_comments/false.rs new file mode 100644 index 000000000000..48ecd88accbf --- /dev/null +++ b/tests/target/configs/wrap_comments/false.rs @@ -0,0 +1,8 @@ +// rustfmt-wrap_comments: false +// rustfmt-max_width: 50 +// rustfmt-error_on_line_overflow: false +// Wrap comments + +fn main() { + // 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. +} diff --git a/tests/target/configs/wrap_comments/true.rs b/tests/target/configs/wrap_comments/true.rs new file mode 100644 index 000000000000..4096fd4d89d2 --- /dev/null +++ b/tests/target/configs/wrap_comments/true.rs @@ -0,0 +1,20 @@ +// rustfmt-wrap_comments: true +// rustfmt-max_width: 50 +// Wrap comments + +fn main() { + // 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. +} + +fn code_block() { + // ```rust + // let x = 3; + // + // println!("x = {}", x); + // ``` +} diff --git a/tests/target/const_generics.rs b/tests/target/const_generics.rs new file mode 100644 index 000000000000..b30b7b58c12b --- /dev/null +++ b/tests/target/const_generics.rs @@ -0,0 +1,37 @@ +struct Message { + field2: Vec<"MessageEntity">, + field3: Vec<1>, + field4: Vec<2, 3>, +} + +struct RectangularArray { + array: [[T; WIDTH]; HEIGHT], +} + +fn main() { + const X: usize = 7; + let x: RectangularArray; + let y: RectangularArray; +} + +fn foo() { + const Y: usize = X * 2; + static Z: (usize, usize) = (X, X); + + struct Foo([i32; X]); +} + +type Foo = [i32; N + 1]; + +pub trait Foo: Bar<{ Baz::COUNT }> { + const ASD: usize; +} + +// #4263 +fn const_generics_on_params< + // AAAA + const BBBB: usize, + /* CCCC */ + const DDDD: usize, +>() { +} diff --git a/tests/target/control-brace-style-always-next-line.rs b/tests/target/control-brace-style-always-next-line.rs new file mode 100644 index 000000000000..054a3075ca0c --- /dev/null +++ b/tests/target/control-brace-style-always-next-line.rs @@ -0,0 +1,50 @@ +// rustfmt-control_brace_style: AlwaysNextLine + +fn main() { + loop + { + (); + (); + } + + 'label: loop + // loop comment + { + (); + } + + cond = true; + while cond + { + (); + } + + 'while_label: while cond + { + // while comment + (); + } + + for obj in iter + { + for sub_obj in obj + { + 'nested_while_label: while cond + { + (); + } + } + } + + match some_var + { + // match comment + pattern0 => val0, + pattern1 => val1, + pattern2 | pattern3 => + { + do_stuff(); + val2 + } + }; +} diff --git a/tests/target/control-brace-style-always-same-line.rs b/tests/target/control-brace-style-always-same-line.rs new file mode 100644 index 000000000000..cf3f82dfcf10 --- /dev/null +++ b/tests/target/control-brace-style-always-same-line.rs @@ -0,0 +1,40 @@ +fn main() { + loop { + (); + (); + } + + 'label: loop + // loop comment + { + (); + } + + cond = true; + while cond { + (); + } + + 'while_label: while cond { + // while comment + (); + } + + for obj in iter { + for sub_obj in obj { + 'nested_while_label: while cond { + (); + } + } + } + + match some_var { + // match comment + pattern0 => val0, + pattern1 => val1, + pattern2 | pattern3 => { + do_stuff(); + val2 + } + }; +} diff --git a/tests/target/doc-attrib.rs b/tests/target/doc-attrib.rs new file mode 100644 index 000000000000..36527b7cd425 --- /dev/null +++ b/tests/target/doc-attrib.rs @@ -0,0 +1,131 @@ +// rustfmt-wrap_comments: true +// rustfmt-normalize_doc_attributes: true + +// Only doc = "" attributes should be normalized +//! Example doc attribute comment +//! Example doc attribute comment with 10 leading spaces +#![doc( + html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", + html_favicon_url = "https://doc.rust-lang.org/favicon.ico", + html_root_url = "https://doc.rust-lang.org/nightly/", + html_playground_url = "https://play.rust-lang.org/", + test(attr(deny(warnings))) +)] + +// Long `#[doc = "..."]` +struct A { + /// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + b: i32, +} + +/// The `nodes` and `edges` method each return instantiations of `Cow<[T]>` to +/// leave implementers the freedom to create entirely new vectors or to pass +/// back slices into internally owned vectors. +struct B { + b: i32, +} + +/// Level 1 comment +mod tests { + /// Level 2 comment + impl A { + /// Level 3 comment + fn f() { + /// Level 4 comment + fn g() {} + } + } +} + +struct C { + /// item doc attrib comment + // regular item comment + b: i32, + + // regular item comment + /// item doc attrib comment + c: i32, +} + +// non-regression test for regular attributes, from #2647 +#[cfg( + feature = "this_line_is_101_characters_long_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +)] +pub fn foo() {} + +// path attrs +#[clippy::bar] +#[clippy::bar(a, b, c)] +pub fn foo() {} + +mod issue_2620 { + #[derive(Debug, StructOpt)] + #[structopt(about = "Display information about the character on FF Logs")] + pub struct Params { + #[structopt(help = "The server the character is on")] + server: String, + #[structopt(help = "The character's first name")] + first_name: String, + #[structopt(help = "The character's last name")] + last_name: String, + #[structopt( + short = "j", + long = "job", + help = "The job to look at", + parse(try_from_str) + )] + job: Option, + } +} + +// non-regression test for regular attributes, from #2969 +#[cfg(not(all( + feature = "std", + any( + target_os = "linux", + target_os = "android", + target_os = "netbsd", + target_os = "dragonfly", + target_os = "haiku", + target_os = "emscripten", + target_os = "solaris", + target_os = "cloudabi", + target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "openbsd", + target_os = "redox", + target_os = "fuchsia", + windows, + all(target_arch = "wasm32", feature = "stdweb"), + all(target_arch = "wasm32", feature = "wasm-bindgen"), + ) +)))] +type Os = NoSource; + +// use cases from bindgen needing precise control over leading spaces +///
+#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct ContradictAccessors { + ///no leading spaces here + pub mBothAccessors: ::std::os::raw::c_int, + ///
+ pub mNoAccessors: ::std::os::raw::c_int, + ///
+ pub mUnsafeAccessors: ::std::os::raw::c_int, + ///
+ pub mImmutableAccessor: ::std::os::raw::c_int, +} + +/// \brief MPI structure +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct mbedtls_mpi { + ///< integer sign + pub s: ::std::os::raw::c_int, + ///< total # of limbs + pub n: ::std::os::raw::c_ulong, + ///< pointer to limbs + pub p: *mut mbedtls_mpi_uint, +} diff --git a/tests/target/doc-comment-with-example.rs b/tests/target/doc-comment-with-example.rs new file mode 100644 index 000000000000..c5a4e779ea22 --- /dev/null +++ b/tests/target/doc-comment-with-example.rs @@ -0,0 +1,11 @@ +// rustfmt-format_code_in_doc_comments: true + +/// Foo +/// +/// # Example +/// ``` +/// # #![cfg_attr(not(dox), feature(cfg_target_feature, target_feature, stdsimd))] +/// # #![cfg_attr(not(dox), no_std)] +/// fn foo() {} +/// ``` +fn foo() {} diff --git a/tests/target/doc-of-generic-item.rs b/tests/target/doc-of-generic-item.rs new file mode 100644 index 000000000000..80886e74f83e --- /dev/null +++ b/tests/target/doc-of-generic-item.rs @@ -0,0 +1,32 @@ +// 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, +>; + +// 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 + // Non-doc post-comment of 'a + 'a, + // Non-doc pre-comment of T + /// doc of T + // Non-doc post-comment of T + T, + // Non-doc pre-comment of N + /// doc of N + // Non-doc post-comment of N + const N: item, +>; diff --git a/tests/target/doc.rs b/tests/target/doc.rs new file mode 100644 index 000000000000..0f9e2d21c1fc --- /dev/null +++ b/tests/target/doc.rs @@ -0,0 +1,5 @@ +// rustfmt-normalize_comments: true +// Part of multiple.rs + +// sadfsdfa +// sdffsdfasdf diff --git a/tests/target/dyn_trait.rs b/tests/target/dyn_trait.rs new file mode 100644 index 000000000000..b6e2810a57cb --- /dev/null +++ b/tests/target/dyn_trait.rs @@ -0,0 +1,27 @@ +#![feature(dyn_trait)] + +fn main() { + // #2506 + // checks rustfmt doesn't remove dyn + trait MyTrait { + fn method(&self) -> u64; + } + fn f1(a: Box) {} + + // checks if line wrap works correctly + trait Very_______________________Long__________________Name_______________________________Trait + { + fn method(&self) -> u64; + } + + fn f2( + a: Box< + dyn Very_______________________Long__________________Name____________________Trait + + 'static, + >, + ) { + } + + // #2582 + let _: &dyn (::std::any::Any) = &msg; +} diff --git a/tests/target/else-if-brace-style-always-next-line.rs b/tests/target/else-if-brace-style-always-next-line.rs new file mode 100644 index 000000000000..31e12cfa0d02 --- /dev/null +++ b/tests/target/else-if-brace-style-always-next-line.rs @@ -0,0 +1,53 @@ +// rustfmt-control_brace_style: AlwaysNextLine + +fn main() { + if false + { + (); + (); + } + + if false + // lone if comment + { + (); + (); + } + + let a = if 0 > 1 { unreachable!() } else { 0x0 }; + + if true + { + (); + } + else if false + { + (); + (); + } + else + { + (); + (); + (); + } + + if true + // else-if-chain if comment + { + (); + } + else if false + // else-if-chain else-if comment + { + (); + (); + } + else + // else-if-chain else comment + { + (); + (); + (); + } +} diff --git a/tests/target/else-if-brace-style-always-same-line.rs b/tests/target/else-if-brace-style-always-same-line.rs new file mode 100644 index 000000000000..07b71fd790dc --- /dev/null +++ b/tests/target/else-if-brace-style-always-same-line.rs @@ -0,0 +1,43 @@ +fn main() { + if false { + (); + (); + } + + if false + // lone if comment + { + (); + (); + } + + let a = if 0 > 1 { unreachable!() } else { 0x0 }; + + if true { + (); + } else if false { + (); + (); + } else { + (); + (); + (); + } + + if true + // else-if-chain if comment + { + (); + } else if false + // else-if-chain else-if comment + { + (); + (); + } else + // else-if-chain else comment + { + (); + (); + (); + } +} diff --git a/tests/target/else-if-brace-style-closing-next-line.rs b/tests/target/else-if-brace-style-closing-next-line.rs new file mode 100644 index 000000000000..c99807dc06db --- /dev/null +++ b/tests/target/else-if-brace-style-closing-next-line.rs @@ -0,0 +1,49 @@ +// rustfmt-control_brace_style: ClosingNextLine + +fn main() { + if false { + (); + (); + } + + if false + // lone if comment + { + (); + (); + } + + let a = if 0 > 1 { unreachable!() } else { 0x0 }; + + if true { + (); + } + else if false { + (); + (); + } + else { + (); + (); + (); + } + + if true + // else-if-chain if comment + { + (); + } + else if false + // else-if-chain else-if comment + { + (); + (); + } + else + // else-if-chain else comment + { + (); + (); + (); + } +} diff --git a/tests/target/empty-item-single-line-false.rs b/tests/target/empty-item-single-line-false.rs new file mode 100644 index 000000000000..bf7f70e7c432 --- /dev/null +++ b/tests/target/empty-item-single-line-false.rs @@ -0,0 +1,41 @@ +// rustfmt-brace_style: AlwaysNextLine +// rustfmt-empty_item_single_line: false + +fn function() +{ +} + +struct Struct {} + +enum Enum {} + +trait Trait +{ +} + +impl Trait for T +{ +} + +trait Trait2 +where + T: Copy + Display + Write + Read + FromStr, +{ +} + +trait Trait3 +where + T: Something + + SomethingElse + + Sync + + Send + + Display + + Debug + + Copy + + Hash + + Debug + + Display + + Write + + Read, +{ +} diff --git a/tests/target/empty-tuple-no-conversion-to-unit-struct.rs b/tests/target/empty-tuple-no-conversion-to-unit-struct.rs new file mode 100644 index 000000000000..0b9a15e8a926 --- /dev/null +++ b/tests/target/empty-tuple-no-conversion-to-unit-struct.rs @@ -0,0 +1,12 @@ +enum TestEnum { + Arm1(), + Arm2, +} + +fn foo() { + let test = TestEnum::Arm1; + match test { + TestEnum::Arm1() => {} + TestEnum::Arm2 => {} + } +} diff --git a/tests/target/empty_file.rs b/tests/target/empty_file.rs new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/tests/target/empty_file.rs @@ -0,0 +1 @@ + diff --git a/tests/target/enum.rs b/tests/target/enum.rs new file mode 100644 index 000000000000..70fc8ab376cc --- /dev/null +++ b/tests/target/enum.rs @@ -0,0 +1,289 @@ +// rustfmt-wrap_comments: true +// Enums test + +#[atrr] +pub enum Test { + A, + B(u32, A /* comment */, SomeType), + /// Doc comment + C, +} + +pub enum Foo<'a, Y: Baz> +where + X: Whatever, +{ + A, +} + +enum EmtpyWithComment { + // Some comment +} + +// C-style enum +enum Bar { + A = 1, + #[someAttr(test)] + B = 2, // comment + C, +} + +enum LongVariants { + First( + LOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONG, // comment + VARIANT, + ), + // This is the second variant + Second, +} + +enum StructLikeVariants { + Normal(u32, String), + StructLike { + x: i32, // Test comment + // Pre-comment + #[Attr50] + y: SomeType, // Another Comment + }, + SL { + a: A, + }, +} + +enum X { + CreateWebGLPaintTask( + Size2D, + GLContextAttributes, + IpcSender, usize), String>>, + ), // This is a post comment +} + +pub enum EnumWithAttributes { + //This is a pre comment + // AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + TupleVar(usize, usize, usize), /* AAAA AAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAA + * AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA */ + // Pre Comment + #[rustfmt::skip] + SkippedItem(String,String,), // Post-comment + #[another_attr] + #[attr2] + ItemStruct { + x: usize, + y: usize, + }, /* Comment AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA */ + // And another + ForcedPreflight, /* AAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + * AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA */ +} + +pub enum SingleTuple { + // Pre Comment AAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + // AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + Match(usize, usize, String), /* Post-comment AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA */ +} + +pub enum SingleStruct { + Match { name: String, loc: usize }, // Post-comment +} + +pub enum GenericEnum +where + I: Iterator, +{ + // Pre Comment + Left { list: I, root: T }, // Post-comment + Right { list: I, root: T }, // Post Comment +} + +enum EmtpyWithComment { + // Some comment +} + +enum TestFormatFails { + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, +} + +fn nested_enum_test() { + if true { + enum TestEnum { + One( + usize, + usize, + usize, + usize, + usize, + usize, + usize, + usize, + usize, + usize, + usize, + usize, + usize, + usize, + usize, + usize, + ), /* AAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAA + * AAAAAAAAAAAAAAAAAAAAAA */ + Two, /* AAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + * AAAAAAAAAAAAAAAAAA */ + } + enum TestNestedFormatFail { + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, + } + } +} + +pub struct EmtpyWithComment { + // FIXME: Implement this struct +} + +// #1115 +pub enum Bencoding<'i> { + Str(&'i [u8]), + Int(i64), + List(Vec>), + /// A bencoded dict value. The first element the slice of bytes in the + /// source that the dict is composed of. The second is the dict, decoded + /// into an ordered map. + // TODO make Dict "structlike" AKA name the two values. + Dict(&'i [u8], BTreeMap<&'i [u8], Bencoding<'i>>), +} + +// #1261 +pub enum CoreResourceMsg { + SetCookieForUrl( + ServoUrl, + #[serde( + deserialize_with = "::hyper_serde::deserialize", + serialize_with = "::hyper_serde::serialize" + )] + Cookie, + CookieSource, + ), +} + +enum Loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong +{} +enum Looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong +{} +enum Loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong +{} +enum Loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong +{ + Foo, +} + +// #1046 +pub enum Entry<'a, K: 'a, V: 'a> { + Vacant(#[stable(feature = "rust1", since = "1.0.0")] VacantEntry<'a, K, V>), + Occupied(#[stable(feature = "rust1", since = "1.0.0")] OccupiedEntry<'a, K, V>), +} + +// #2081 +pub enum ForegroundColor { + CYAN = + (winapi::FOREGROUND_INTENSITY | winapi::FOREGROUND_GREEN | winapi::FOREGROUND_BLUE) as u16, +} + +// #2098 +pub enum E<'a> { + V( as Iterator>::Item), +} + +// #1809 +enum State { + TryRecv { + pos: usize, + lap: u8, + closed_count: usize, + }, + Subscribe { + pos: usize, + }, + IsReady { + pos: usize, + ready: bool, + }, + Unsubscribe { + pos: usize, + lap: u8, + id_woken: usize, + }, + FinalTryRecv { + pos: usize, + id_woken: usize, + }, + TimedOut, + Disconnected, +} + +// #2190 +#[derive(Debug, Fail)] +enum AnError { + #[fail( + display = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + )] + UnexpectedSingleToken { token: syn::Token }, +} + +// #2193 +enum WidthOf101 { + #[fail(display = ".....................................................")] + Io(::std::io::Error), + #[fail(display = ".....................................................")] + Ioo(::std::io::Error), + Xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx(::std::io::Error), + Xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx( + ::std::io::Error, + ), +} + +// #2389 +pub enum QlError { + #[fail(display = "Parsing error: {}", 0)] + LexError(parser::lexer::LexError), + #[fail(display = "Parsing error: {:?}", 0)] + ParseError(parser::ParseError), + #[fail(display = "Validation error: {:?}", 0)] + ValidationError(Vec), + #[fail(display = "Execution error: {}", 0)] + ExecutionError(String), + // (from, to) + #[fail(display = "Translation error: from {} to {}", 0, 1)] + TranslationError(String, String), + // (kind, input, expected) + #[fail( + display = "aaaaaaaaaaaaCould not find {}: Found: {}, expected: {:?}", + 0, 1, 2 + )] + ResolveError(&'static str, String, Option), +} + +// #2594 +enum Foo {} +enum Bar {} + +// #3562 +enum PublishedFileVisibility { + Public = + sys::ERemoteStoragePublishedFileVisibility_k_ERemoteStoragePublishedFileVisibilityPublic, + FriendsOnly = sys::ERemoteStoragePublishedFileVisibility_k_ERemoteStoragePublishedFileVisibilityFriendsOnly, + Private = + sys::ERemoteStoragePublishedFileVisibility_k_ERemoteStoragePublishedFileVisibilityPrivate, +} + +// #3771 +//#![feature(arbitrary_enum_discriminant)] +#[repr(u32)] +pub enum E { + A { + a: u32, + } = 0x100, + B { + field1: u32, + field2: u8, + field3: m::M, + } = 0x300, // comment +} diff --git a/tests/target/existential_type.rs b/tests/target/existential_type.rs new file mode 100644 index 000000000000..ffc206875b59 --- /dev/null +++ b/tests/target/existential_type.rs @@ -0,0 +1,23 @@ +// Opaque type. + +#![feature(type_alias_impl_trait)] + +pub type Adder +where + T: Clone, + F: Copy, += impl Fn(T) -> T; + +pub type Adderrr = impl Fn(T) -> T; + +impl Foo for Bar { + type E = impl Trait; +} + +pub type Adder_without_impl +where + T: Clone, + F: Copy, += Fn(T) -> T; + +pub type Adderrr_without_impl = Fn(T) -> T; diff --git a/tests/target/expr-block.rs b/tests/target/expr-block.rs new file mode 100644 index 000000000000..c57700650873 --- /dev/null +++ b/tests/target/expr-block.rs @@ -0,0 +1,305 @@ +// Test expressions with block formatting. + +fn arrays() { + []; + let empty = []; + + let foo = [a_long_name, a_very_lng_name, a_long_name]; + + let foo = [ + a_long_name, + a_very_lng_name, + a_long_name, + a_very_lng_name, + a_long_name, + a_very_lng_name, + a_long_name, + a_very_lng_name, + ]; + + vec![ + a_long_name, + a_very_lng_name, + a_long_name, + a_very_lng_name, + a_long_name, + a_very_lng_name, + a_very_lng_name, + ]; + + [ + a_long_name, + a_very_lng_name, + a_long_name, + a_very_lng_name, + a_long_name, + a_very_lng_name, + a_very_lng_name, + ] +} + +fn arrays() { + let x = [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 7, 8, 9, 0, 1, 2, 3, 4, 5, + 6, 7, 8, 9, 0, + ]; + + let y = [/* comment */ 1, 2 /* post comment */, 3]; + + let xy = [ + strukt { + test123: value_one_two_three_four, + turbo: coolio(), + }, + /* comment */ 1, + ]; + + let a = WeightedChoice::new(&mut [ + Weighted { weight: x, item: 0 }, + Weighted { weight: 1, item: 1 }, + Weighted { weight: x, item: 2 }, + Weighted { weight: 1, item: 3 }, + ]); + + let z = [ + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, + yyyyyyyyyyyyyyyyyyyyyyyyyyy, + zzzzzzzzzzzzzzzzz, + q, + ]; + + [1 + 3, 4, 5, 6, 7, 7, fncall::>(3 - 1)] +} + +fn function_calls() { + let items = itemize_list( + context.source_map, + args.iter(), + ")", + |item| item.span.lo(), + |item| item.span.hi(), + |item| { + item.rewrite( + context, + Shape { + width: remaining_width, + ..nested_shape + }, + ) + }, + span.lo(), + span.hi(), + ); + + itemize_list( + context.source_map, + args.iter(), + ")", + |item| item.span.lo(), + |item| item.span.hi(), + |item| { + item.rewrite( + context, + Shape { + width: remaining_width, + ..nested_shape + }, + ) + }, + span.lo(), + span.hi(), + ) +} + +fn macros() { + baz!( + do_not, add, trailing, commas, inside, of, function, like, macros, even, if_they, are, long + ); + + baz!(one_item_macro_which_is_also_loooooooooooooooooooooooooooooooooooooooooooooooong); + + let _ = match option { + None => baz!( + function, + like, + macro_as, + expression, + which, + is, + loooooooooooooooong + ), + Some(p) => baz!(one_item_macro_as_expression_which_is_also_loooooooooooooooong), + }; +} + +fn issue_1450() { + if selfstate + .compare_exchandsfasdsdfgsdgsdfgsdfgsdfgsdfgsdfgfsfdsage_weak( + STATE_PARKED, + STATE_UNPARKED, + Release, + Relaxed, + Release, + Relaxed, + ) + .is_ok() + { + return; + } +} + +fn foo() { + if real_total <= limit + && !pre_line_comments + && !items.into_iter().any(|item| item.as_ref().is_multiline()) + { + DefinitiveListTactic::Horizontal + } +} + +fn combine_block() { + foo(Bar { + x: value, + y: value2, + }); + + foo((Bar { + x: value, + y: value2, + },)); + + foo(( + 1, + 2, + 3, + Bar { + x: value, + y: value2, + }, + )); + + foo((1, 2, 3, |x| { + let y = x + 1; + let z = y + 1; + z + })); + + let opt = Some(Struct( + long_argument_one, + long_argument_two, + long_argggggggg, + )); + + do_thing(|param| { + action(); + foo(param) + }); + + do_thing(x, |param| { + action(); + foo(param) + }); + + do_thing( + x, + (1, 2, 3, |param| { + action(); + foo(param) + }), + ); + + Ok(some_function( + lllllllllong_argument_one, + lllllllllong_argument_two, + lllllllllllllllllllllllllllllong_argument_three, + )); + + foo( + thing, + bar( + param2, + pparam1param1param1param1param1param1param1param1param1param1aram1, + param3, + ), + ); + + foo.map_or(|| { + Ok(SomeStruct { + f1: 0, + f2: 0, + f3: 0, + }) + }); + + match opt { + Some(x) => somefunc(anotherfunc( + long_argument_one, + long_argument_two, + long_argument_three, + )), + Some(x) => |x| { + let y = x + 1; + let z = y + 1; + z + }, + Some(x) => (1, 2, |x| { + let y = x + 1; + let z = y + 1; + z + }), + Some(x) => SomeStruct { + f1: long_argument_one, + f2: long_argument_two, + f3: long_argument_three, + }, + None => Ok(SomeStruct { + f1: long_argument_one, + f2: long_argument_two, + f3: long_argument_three, + }), + }; + + match x { + y => func(xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx), + _ => func( + x, + yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy, + zzz, + ), + } +} + +fn issue_1862() { + foo( + /* bar = */ None, + something_something, + /* baz = */ None, + /* This comment waaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaay too long to be kept on the same line */ + None, + /* com */ + this_last_arg_is_tooooooooooooooooooooooooooooooooo_long_to_be_kept_with_the_pre_comment, + ) +} + +fn issue_3025() { + foo( + // This describes the argument below. + /* bar = */ None, + // This describes the argument below. + something_something, + // This describes the argument below. */ + None, + // This describes the argument below. + /* This comment waaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaay too long to be kept on the same line */ + None, + // This describes the argument below. + /* com */ + this_last_arg_is_tooooooooooooooooooooooooooooooooo_long_to_be_kept_with_the_pre_comment, + ) +} + +fn issue_1878() { + let channel: &str = seq + .next_element()? + .ok_or_else(|| de::Error::invalid_length(2, &self))?; +} diff --git a/tests/target/expr-overflow-delimited.rs b/tests/target/expr-overflow-delimited.rs new file mode 100644 index 000000000000..b00e81fcd5a5 --- /dev/null +++ b/tests/target/expr-overflow-delimited.rs @@ -0,0 +1,120 @@ +// rustfmt-overflow_delimited_expr: true + +fn combine_blocklike() { + do_thing(|param| { + action(); + foo(param) + }); + + do_thing(x, |param| { + action(); + foo(param) + }); + + do_thing( + x, + // I'll be discussing the `action` with your para(m)legal counsel + |param| { + action(); + foo(param) + }, + ); + + do_thing(Bar { + x: value, + y: value2, + }); + + do_thing(x, Bar { + x: value, + y: value2, + }); + + do_thing( + x, + // Let me tell you about that one time at the `Bar` + Bar { + x: value, + y: value2, + }, + ); + + do_thing(&[ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ]); + + do_thing(x, &[ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ]); + + do_thing( + x, + // Just admit it; my list is longer than can be folded on to one line + &[ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ], + ); + + do_thing(vec![ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ]); + + do_thing(x, vec![ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ]); + + do_thing( + x, + // Just admit it; my list is longer than can be folded on to one line + vec![ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ], + ); + + do_thing( + x, + (1, 2, 3, |param| { + action(); + foo(param) + }), + ); +} + +fn combine_struct_sample() { + let identity = verify(&ctx, VerifyLogin { + type_: LoginType::Username, + username: args.username.clone(), + password: Some(args.password.clone()), + domain: None, + })?; +} + +fn combine_macro_sample() { + rocket::ignite() + .mount("/", routes![ + http::auth::login, + http::auth::logout, + http::cors::options, + http::action::dance, + http::action::sleep, + ]) + .launch(); +} diff --git a/tests/target/expr.rs b/tests/target/expr.rs new file mode 100644 index 000000000000..187a1dc976a4 --- /dev/null +++ b/tests/target/expr.rs @@ -0,0 +1,657 @@ +// rustfmt-normalize_comments: true +// rustfmt-wrap_comments: true +// Test expressions + +fn foo() -> bool { + let referenced = &5; + + let very_long_variable_name = (a + first + simple + test); + let very_long_variable_name = + (a + first + simple + test + AAAAAAAAAAAAA + BBBBBBBBBBBBBBBBB + b + c); + + let is_internalxxxx = + self.source_map.span_to_filename(s) == self.source_map.span_to_filename(m.inner); + + let some_val = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa * bbbb + / (bbbbbb - function_call(x, *very_long_pointer, y)) + + 1000; + + some_ridiculously_loooooooooooooooooooooong_function( + 10000 * 30000000000 + 40000 / 1002200000000 - 50000 * sqrt(-1), + trivial_value, + ); + (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + + a + + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + + aaaaa); + + { + for _ in 0..10 {} + } + + { + { + { + {} + } + } + } + + if 1 + 2 > 0 { + let result = 5; + result + } else { + 4 + }; + + if let Some(x) = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa { + // Nothing + } + + if let Some(x) = + (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) + {} + + if let ( + some_very_large, + tuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuple, + ) = 1 + 2 + 3 + {} + + if let ( + some_very_large, + tuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuple, + ) = 1111 + 2222 + {} + + if let ( + some_very_large, + tuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuple, + ) = 1 + 2 + 3 + {} + + if let ast::ItemKind::Trait(_, unsafety, ref generics, ref type_param_bounds, ref trait_items) = + item.node + { + // nothing + } + + let test = if true { 5 } else { 3 }; + + if cond() { + something(); + } else if different_cond() { + something_else(); + } else { + // Check subformatting + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + } + + // #2884 + let _ = [0; { + struct Foo; + impl Foo { + const fn get(&self) -> usize { + 5 + } + }; + Foo.get() + }]; +} + +fn bar() { + let range = + (111111111 + 333333333333333333 + 1111 + 400000000000000000)..(2222 + 2333333333333333); + + let another_range = 5..some_func(a, b /* comment */); + + for _ in 1.. { + call_forever(); + } + + syntactically_correct( + loop { + sup('?'); + }, + if cond { 0 } else { 1 }, + ); + + let third = ..10; + let infi_range = ..; + let foo = 1..; + let bar = 5; + let nonsense = (10..0)..(0..10); + + loop { + if true { + break; + } + } + + let x = ( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa && aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + a, + ); +} + +fn baz() { + unsafe /* {}{}{}{{{{}} */ { + let foo = 1u32; + } + + unsafe /* very looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong + * comment */ { + } + + unsafe /* So this is a very long comment. + * Multi-line, too. + * Will it still format correctly? */ { + } + + unsafe { + // Regular unsafe block + } + + unsafe { foo() } + + unsafe { + foo(); + } + + // #2289 + let identifier_0 = unsafe { this_is_58_chars_long_and_line_is_93_chars_long_xxxxxxxxxx }; + let identifier_1 = unsafe { this_is_59_chars_long_and_line_is_94_chars_long_xxxxxxxxxxx }; + let identifier_2 = unsafe { this_is_65_chars_long_and_line_is_100_chars_long_xxxxxxxxxxxxxxxx }; + let identifier_3 = + unsafe { this_is_66_chars_long_and_line_is_101_chars_long_xxxxxxxxxxxxxxxxx }; +} + +// Test some empty blocks. +fn qux() { + {} + // FIXME this one could be done better. + { /* a block with a comment */ } + {} + { + // A block with a comment. + } +} + +fn issue184(source: &str) { + for c in source.chars() { + if index < 'a' { + continue; + } + } +} + +fn arrays() { + let x = [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 7, 8, 9, 0, 1, 2, 3, 4, 5, + 6, 7, 8, 9, 0, + ]; + + let y = [/* comment */ 1, 2 /* post comment */, 3]; + + let xy = [ + strukt { + test123: value_one_two_three_four, + turbo: coolio(), + }, + // comment + 1, + ]; + + let a = WeightedChoice::new(&mut [ + Weighted { + weightweight: x, + item: 0, + }, + Weighted { + weightweight: 1, + item: 1, + }, + Weighted { + weightweight: x, + item: 2, + }, + Weighted { + weightweight: 1, + item: 3, + }, + ]); + + let z = [ + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, + yyyyyyyyyyyyyyyyyyyyyyyyyyy, + zzzzzzzzzzzzzzzzzz, + q, + ]; + + [1 + 3, 4, 5, 6, 7, 7, fncall::>(3 - 1)] +} + +fn returns() { + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + && return; + + return aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa; +} + +fn addrof() { + &mut (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb); + &(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb); + + // raw reference operator + &raw const a; + &raw mut b; +} + +fn casts() { + fn unpack(packed: u32) -> [u16; 2] { + [(packed >> 16) as u16, (packed >> 0) as u16] + } + + let some_trait_xxx = xxxxxxxxxxx + xxxxxxxxxxxxx as SomeTraitXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + let slightly_longer_trait = + yyyyyyyyy + yyyyyyyyyyy as SomeTraitYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY; +} + +fn indices() { + let x = (aaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + cccccccccccccccc) + [x + y + z]; + let y = (aaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + cccccccccccccccc) + [xxxxx + yyyyy + zzzzz]; + let z = xxxxxxxxxx + .x() + .y() + .zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz()[aaaaa]; + let z = xxxxxxxxxx + .x() + .y() + .zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz() + [aaaaa]; +} + +fn repeats() { + let x = [aaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + cccccccccccccccc; + x + y + z]; + let y = [aaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + cccccccccccccccc; + xxxxx + yyyyy + zzzzz]; +} + +fn blocks() { + if 1 + 1 == 2 { + println!("yay arithmetix!"); + }; +} + +fn issue767() { + if false { + if false { + } else { + // A let binding here seems necessary to trigger it. + let _ = (); + } + } else if let false = false { + } +} + +fn ranges() { + let x = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa..bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb; + let y = + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa..=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb; + let z = ..=x; + + // #1766 + let x = [0. ..10.0]; + let x = [0. ..=10.0]; + + a..=b + + // the expr below won't compile because inclusive ranges need a defined end + // let a = 0 ..= ; +} + +fn if_else() { + let exact = diff / (if size == 0 { 1 } else { size }); + + let cx = tp1.x + any * radius * if anticlockwise { 1.0 } else { -1.0 }; +} + +fn complex_if_else() { + if let Some(x) = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx { + } else if let Some(x) = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx { + ha(); + } else if xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + xxxxxxxx { + yo(); + } else if let Some(x) = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + { + ha(); + } else if xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + xxxxxxxxx + { + yo(); + } +} + +fn issue1106() { + { + if let hir::ItemEnum(ref enum_def, ref generics) = + self.ast_map.expect_item(enum_node_id).node + {} + } + + for entry in WalkDir::new(path) + .into_iter() + .filter_entry(|entry| exclusions.filter_entry(entry)) + {} +} + +fn issue1570() { + a_very_long_function_name({ some_func(1, { 1 }) }) +} + +fn issue1714() { + v = &mut { v }[mid..]; + let (left, right) = { v }.split_at_mut(mid); +} + +// Multi-lined index should be put on the next line if it fits in one line. +fn issue1749() { + { + { + { + if self.shape[(r as f32 + self.x_offset) as usize] + [(c as f32 + self.y_offset) as usize] + != 0 + { + // hello + } + } + } + } +} + +// #1172 +fn newlines_between_list_like_expr() { + foo( + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, + yyyyyyyyyyyyyyyyyyyyyyyyyyyyyy, + zzzzzzzzzzzzzzzzzzzzzzzzzzzzzz, + ); + + vec![ + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, + yyyyyyyyyyyyyyyyyyyyyyyyyyyyyy, + zzzzzzzzzzzzzzzzzzzzzzzzzzzzzz, + ]; + + match x { + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + | yyyyyyyyyyyyyyyyyyyyyyyyyyyyyy + | zzzzzzzzzzzzzzzzzzzzzzzzzzzzzz => foo(a, b, c), + _ => bar(), + }; +} + +fn issue2178() { + Ok(result + .iter() + .map(|item| ls_util::rls_to_location(item)) + .collect()) +} + +// #2493 +impl Foo { + fn bar(&self) { + { + let x = match () { + () => { + let i; + i == self + .install_config + .storage + .experimental_compressed_block_size as usize + } + }; + } + } +} + +fn dots() { + .. .. ..; // (.. (.. (..))) + ..= ..= ..; + (..).. ..; // ((..) .. (..)) +} + +// #2676 +// A function call with a large single argument. +fn foo() { + let my_var = Mutex::new( + RpcClientType::connect(server_iddd).chain_err(|| "Unable to create RPC client")?, + ); +} + +// #2704 +// Method call with prefix and suffix. +fn issue2704() { + // We should not combine the callee with a multi-lined method call. + let requires = requires.set( + &requires0 + .concat(&requires1) + .concat(&requires2) + .distinct_total(), + ); + let requires = requires.set( + requires0 + .concat(&requires1) + .concat(&requires2) + .distinct_total() as u32, + ); + let requires = requires.set( + requires0 + .concat(&requires1) + .concat(&requires2) + .distinct_total()?, + ); + let requires = requires.set( + !requires0 + .concat(&requires1) + .concat(&requires2) + .distinct_total(), + ); + // We should combine a small callee with an argument. + bar(vec![22] + .into_iter() + .map(|x| x * 2) + .filter(|_| true) + .collect()); + // But we should not combine a long callee with an argument. + barrrr( + vec![22] + .into_iter() + .map(|x| x * 2) + .filter(|_| true) + .collect(), + ); +} + +// #2782 +fn issue2782() { + { + let f = { + let f = { + { + match f { + F(f, _) => loop { + let f = { + match f { + F(f, _) => match f { + F(f, _) => loop { + let f = { + let f = { + match f { + '-' => F(f, ()), + } + }; + }; + }, + }, + } + }; + }, + } + } + }; + }; + } +} + +fn issue_2802() { + function_to_fill_this_line(some_arg, some_arg, some_arg) + * a_very_specific_length(specific_length_arg) + * very_specific_length(Foo { + a: some_much_much_longer_value, + }) + * some_value +} + +fn issue_3003() { + let mut path: PathBuf = [ + env!("CARGO_MANIFEST_DIR"), + "tests", + "support", + "dejavu-fonts-ttf-2.37", + "ttf", + ] + .iter() + .collect(); +} + +fn issue3226() { + { + { + { + return Err( + ErrorKind::ManagementInterfaceError("Server exited unexpectedly").into(), + ); + } + } + } + { + { + { + break Err( + ErrorKind::ManagementInterfaceError("Server exited unexpectedlyy").into(), + ); + } + } + } +} + +// #3457 +fn issue3457() { + { + { + { + { + { + { + { + { + { + { + { + { + { + { + { + { + { + { + { + { + { + { + { + { + println!("Test"); + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } +} + +// #3498 +static REPRO: &[usize] = &[ + #[cfg(feature = "zero")] + 0, +]; + +fn overflow_with_attr() { + foo( + #[cfg(feature = "zero")] + 0, + ); + foobar( + #[cfg(feature = "zero")] + 0, + ); + foobar( + x, + y, + #[cfg(feature = "zero")] + {}, + ); +} + +// https://github.com/rust-lang/rustfmt/issues/3765 +fn foo() { + async { + // Do + // some + // work + } + .await; + + async { + // Do + // some + // work + } + .await; +} + +fn underscore() { + _ = 1; + _; + [_, a, _] = [1, 2, 3]; + (a, _) = (8, 9); + TupleStruct(_, a) = TupleStruct(2, 2); + + let _: usize = foo(_, _); +} diff --git a/tests/target/extern.rs b/tests/target/extern.rs new file mode 100644 index 000000000000..d1741360cfd6 --- /dev/null +++ b/tests/target/extern.rs @@ -0,0 +1,97 @@ +// rustfmt-normalize_comments: true + +extern crate foo; +extern crate foo as bar; + +extern crate chrono; +extern crate dotenv; +extern crate futures; + +extern crate bar; +extern crate foo; + +// #2315 +extern crate proc_macro; +extern crate proc_macro2; + +// #3128 +extern crate serde; // 1.0.78 +extern crate serde_derive; // 1.0.78 +extern crate serde_json; // 1.0.27 + +extern "C" { + fn c_func(x: *mut *mut libc::c_void); + + fn c_func( + x: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX, + y: YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY, + ); + + #[test123] + fn foo() -> uint64_t; + + pub fn bar(); +} + +extern "C" { + fn DMR_GetDevice( + pHDev: *mut HDEV, + searchMode: DeviceSearchMode, + pSearchString: *const c_char, + devNr: c_uint, + wildcard: c_char, + ) -> TDMR_ERROR; + + fn quux() -> (); // Post comment + + pub type Foo; + + type Bar; +} + +extern "Rust" { + static ext: u32; + // Some comment. + pub static mut var: SomeType; +} + +extern "C" { + fn syscall( + number: libc::c_long, // comment 1 + // comm 2 + ... // sup? + ) -> libc::c_long; + + fn foo(x: *const c_char, ...) -> libc::c_long; +} + +extern "C" { + pub fn freopen( + filename: *const c_char, + mode: *const c_char, + mode2: *const c_char, + mode3: *const c_char, + file: *mut FILE, + ) -> *mut FILE; + + const fn foo() -> *mut Bar; + unsafe fn foo() -> *mut Bar; + + pub(super) const fn foo() -> *mut Bar; + pub(crate) unsafe fn foo() -> *mut Bar; +} + +extern "C" {} + +macro_rules! x { + ($tt:tt) => {}; +} + +extern "macros" { + x!(ident); + x!(#); + x![ident]; + x![#]; + x! {ident} + x! {#} +} diff --git a/tests/target/extern_not_explicit.rs b/tests/target/extern_not_explicit.rs new file mode 100644 index 000000000000..b55b64d05b34 --- /dev/null +++ b/tests/target/extern_not_explicit.rs @@ -0,0 +1,18 @@ +// rustfmt-force_explicit_abi: false + +extern { + fn some_fn() -> (); +} + +extern fn sup() {} + +type funky_func = extern fn( + unsafe extern "rust-call" fn( + *const JSJitInfo, + *mut JSContext, + HandleObject, + *mut libc::c_void, + u32, + *mut JSVal, + ) -> u8, +); diff --git a/tests/target/file-lines-1.rs b/tests/target/file-lines-1.rs new file mode 100644 index 000000000000..13820ec293fa --- /dev/null +++ b/tests/target/file-lines-1.rs @@ -0,0 +1,30 @@ +// rustfmt-file_lines: [{"file":"tests/source/file-lines-1.rs","range":[4,8]}] + +fn floaters() { + let x = Foo { + field1: val1, + field2: val2, + } + .method_call() + .method_call(); + + let y = if cond { + val1 + } else { + val2 + } + .method_call(); + + { + match x { + PushParam => { + // comment + stack.push(mparams[match cur.to_digit(10) { + Some(d) => d as usize - 1, + None => return Err("bad param number".to_owned()), + }] + .clone()); + } + } + } +} diff --git a/tests/target/file-lines-2.rs b/tests/target/file-lines-2.rs new file mode 100644 index 000000000000..bc25698c2ed0 --- /dev/null +++ b/tests/target/file-lines-2.rs @@ -0,0 +1,24 @@ +// rustfmt-file_lines: [{"file":"tests/source/file-lines-2.rs","range":[10,15]}] + +fn floaters() { + let x = Foo { + field1: val1, + field2: val2, + } + .method_call().method_call(); + + let y = if cond { val1 } else { val2 }.method_call(); + + { + match x { + PushParam => { + // comment + stack.push(mparams[match cur.to_digit(10) { + Some(d) => d as usize - 1, + None => return Err("bad param number".to_owned()), + }] + .clone()); + } + } + } +} diff --git a/tests/target/file-lines-3.rs b/tests/target/file-lines-3.rs new file mode 100644 index 000000000000..77d6fb263558 --- /dev/null +++ b/tests/target/file-lines-3.rs @@ -0,0 +1,25 @@ +// rustfmt-file_lines: [{"file":"tests/source/file-lines-3.rs","range":[4,8]},{"file":"tests/source/file-lines-3.rs","range":[10,15]}] + +fn floaters() { + let x = Foo { + field1: val1, + field2: val2, + } + .method_call() + .method_call(); + + let y = if cond { val1 } else { val2 }.method_call(); + + { + match x { + PushParam => { + // comment + stack.push(mparams[match cur.to_digit(10) { + Some(d) => d as usize - 1, + None => return Err("bad param number".to_owned()), + }] + .clone()); + } + } + } +} diff --git a/tests/target/file-lines-4.rs b/tests/target/file-lines-4.rs new file mode 100644 index 000000000000..83928bf6fecf --- /dev/null +++ b/tests/target/file-lines-4.rs @@ -0,0 +1,30 @@ +// rustfmt-file_lines: [] +// (Test that nothing is formatted if an empty array is specified.) + +fn floaters() { + let x = Foo { + field1: val1, + field2: val2, + } + .method_call().method_call(); + + let y = if cond { + val1 + } else { + val2 + } + .method_call(); + // aaaaaaaaaaaaa + { + match x { + PushParam => { + // comment + stack.push(mparams[match cur.to_digit(10) { + Some(d) => d as usize - 1, + None => return Err("bad param number".to_owned()), + }] + .clone()); + } + } + } +} diff --git a/tests/target/file-lines-5.rs b/tests/target/file-lines-5.rs new file mode 100644 index 000000000000..3966dc06303c --- /dev/null +++ b/tests/target/file-lines-5.rs @@ -0,0 +1,17 @@ +// rustfmt-file_lines: [{"file":"tests/source/file-lines-5.rs","range":[3,5]}] + +struct A { + t: i64, +} + +mod foo { + fn bar() { + // test + let i = 12; + // test + } + // aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + fn baz() { + let j = 15; + } +} diff --git a/tests/target/file-lines-6.rs b/tests/target/file-lines-6.rs new file mode 100644 index 000000000000..8a092df869a6 --- /dev/null +++ b/tests/target/file-lines-6.rs @@ -0,0 +1,18 @@ +// rustfmt-file_lines: [{"file":"tests/source/file-lines-6.rs","range":[9,10]}] + +struct A { + t: i64, +} + +mod foo { + fn bar() { + // test + let i = 12; + // test + } + + fn baz() { +/// + let j = 15; + } +} diff --git a/tests/target/file-lines-7.rs b/tests/target/file-lines-7.rs new file mode 100644 index 000000000000..62d913d8823e --- /dev/null +++ b/tests/target/file-lines-7.rs @@ -0,0 +1,21 @@ +// rustfmt-file_lines: [{"file":"tests/source/file-lines-7.rs","range":[8,15]}] + +struct A { + t: i64, +} + +mod foo { + fn bar() { + // test + let i = 12; + // test + } + + fn baz() { + + + + /// + let j = 15; + } +} diff --git a/tests/target/file-lines-item.rs b/tests/target/file-lines-item.rs new file mode 100644 index 000000000000..8d39eb609589 --- /dev/null +++ b/tests/target/file-lines-item.rs @@ -0,0 +1,21 @@ +// rustfmt-file_lines: [{"file":"tests/source/file-lines-item.rs","range":[6,8]}] + +use foo::{c, b, a}; +use bar; + +fn foo() { + bar(); +} + +impl Drop for Context { + fn drop(&mut self) { + } +} + +impl Bar for Baz { + fn foo() { + bar( + baz, // Who knows? + ) + } +} diff --git a/tests/target/fn-args-with-last-line-comment.rs b/tests/target/fn-args-with-last-line-comment.rs new file mode 100644 index 000000000000..27e0e09653ee --- /dev/null +++ b/tests/target/fn-args-with-last-line-comment.rs @@ -0,0 +1,24 @@ +// #1587 +pub trait X { + fn a(&self) -> &'static str; + fn bcd( + &self, + c: &str, // comment on this arg + d: u16, // comment on this arg + e: &Vec, // comment on this arg + ) -> Box; +} + +// #1595 +fn foo( + arg1: LongTypeName, + arg2: LongTypeName, + arg3: LongTypeName, + arg4: LongTypeName, + arg5: LongTypeName, + arg6: LongTypeName, + arg7: LongTypeName, + //arg8: LongTypeName, +) { + // do stuff +} diff --git a/tests/target/fn-custom-2.rs b/tests/target/fn-custom-2.rs new file mode 100644 index 000000000000..0e723396c686 --- /dev/null +++ b/tests/target/fn-custom-2.rs @@ -0,0 +1,77 @@ +// Test different indents. + +fn foo( + a: Aaaaaaaaaaaaaaa, + b: Bbbbbbbbbbbbbbbb, + c: Ccccccccccccccccc, + d: Ddddddddddddddddddddddddd, + e: Eeeeeeeeeeeeeeeeeee, +) { + foo(); +} + +fn bar< + 'a: 'bbbbbbbbbbbbbbbbbbbbbbbbbbb, + TTTTTTTTTTTTT, + UUUUUUUUUUUUUUUUUUUU: WWWWWWWWWWWWWWWWWWWWWWWW, +>( + a: Aaaaaaaaaaaaaaa, +) { + bar(); +} + +fn baz() +where + X: TTTTTTTT, +{ + baz(); +} + +fn qux() +where + X: TTTTTTTTTTTTTTTTTTTTTTTTTTTT, + X: TTTTTTTTTTTTTTTTTTTTTTTTTTTT, + X: TTTTTTTTTTTTTTTTTTTTTTTTTTTT, + X: TTTTTTTTTTTTTTTTTTTTTTTTTTTT, +{ + baz(); +} + +impl Foo { + fn foo( + self, + a: Aaaaaaaaaaaaaaa, + b: Bbbbbbbbbbbbbbbb, + c: Ccccccccccccccccc, + d: Ddddddddddddddddddddddddd, + e: Eeeeeeeeeeeeeeeeeee, + ) { + foo(); + } + + fn bar< + 'a: 'bbbbbbbbbbbbbbbbbbbbbbbbbbb, + TTTTTTTTTTTTT, + UUUUUUUUUUUUUUUUUUUU: WWWWWWWWWWWWWWWWWWWWWWWW, + >( + a: Aaaaaaaaaaaaaaa, + ) { + bar(); + } + + fn baz() + where + X: TTTTTTTT, + { + baz(); + } +} + +struct Foo< + TTTTTTTTTTTTTTTTTTTTTTTTTTTT, + UUUUUUUUUUUUUUUUUUUUUU, + VVVVVVVVVVVVVVVVVVVVVVVVVVV, + WWWWWWWWWWWWWWWWWWWWWWWW, +> { + foo: Foo, +} diff --git a/tests/target/fn-custom-3.rs b/tests/target/fn-custom-3.rs new file mode 100644 index 000000000000..bfafe4536077 --- /dev/null +++ b/tests/target/fn-custom-3.rs @@ -0,0 +1,71 @@ +// Test different indents. + +fn foo( + a: Aaaaaaaaaaaaaaa, + b: Bbbbbbbbbbbbbbbb, + c: Ccccccccccccccccc, + d: Ddddddddddddddddddddddddd, + e: Eeeeeeeeeeeeeeeeeee, +) { + foo(); +} + +fn bar< + 'a: 'bbbbbbbbbbbbbbbbbbbbbbbbbbb, + TTTTTTTTTTTTT, + UUUUUUUUUUUUUUUUUUUU: WWWWWWWWWWWWWWWWWWWWWWWW, +>( + a: Aaaaaaaaaaaaaaa, +) { + bar(); +} + +fn qux() +where + X: TTTTTTTTTTTTTTTTTTTTTTTTTTTT, + X: TTTTTTTTTTTTTTTTTTTTTTTTTTTT, +{ + baz(); +} + +fn qux() +where + X: TTTTTTTTTTTTTTTTTTTTTTTTTTTT, + X: TTTTTTTTTTTTTTTTTTTTTTTTTTTT, + X: TTTTTTTTTTTTTTTTTTTTTTTTTTTT, + X: TTTTTTTTTTTTTTTTTTTTTTTTTTTT, +{ + baz(); +} + +impl Foo { + fn foo( + self, + a: Aaaaaaaaaaaaaaa, + b: Bbbbbbbbbbbbbbbb, + c: Ccccccccccccccccc, + d: Ddddddddddddddddddddddddd, + e: Eeeeeeeeeeeeeeeeeee, + ) { + foo(); + } + + fn bar< + 'a: 'bbbbbbbbbbbbbbbbbbbbbbbbbbb, + TTTTTTTTTTTTT, + UUUUUUUUUUUUUUUUUUUU: WWWWWWWWWWWWWWWWWWWWWWWW, + >( + a: Aaaaaaaaaaaaaaa, + ) { + bar(); + } +} + +struct Foo< + TTTTTTTTTTTTTTTTTTTTTTTTTTTT, + UUUUUUUUUUUUUUUUUUUUUU, + VVVVVVVVVVVVVVVVVVVVVVVVVVV, + WWWWWWWWWWWWWWWWWWWWWWWW, +> { + foo: Foo, +} diff --git a/tests/target/fn-custom-4.rs b/tests/target/fn-custom-4.rs new file mode 100644 index 000000000000..5de16e251595 --- /dev/null +++ b/tests/target/fn-custom-4.rs @@ -0,0 +1,26 @@ +// Test different indents. + +fn qux() +where + X: TTTTTTTTTTTTTTTTTTTTTTTTTTTT, + X: TTTTTTTTTTTTTTTTTTTTTTTTTTTT, + X: TTTTTTTTTTTTTTTTTTTTTTTTTTTT, + X: TTTTTTTTTTTTTTTTTTTTTTTTTTTT, +{ + baz(); +} + +fn qux() +where + X: TTTTTTTTTTTTTTTTTTTTTTTTTTTT, +{ + baz(); +} + +fn qux(a: Aaaaaaaaaaaaaaaaa) +where + X: TTTTTTTTTTTTTTTTTTTTTTTTTTTT, + X: TTTTTTTTTTTTTTTTTTTTTTTTTTTT, +{ + baz(); +} diff --git a/tests/target/fn-custom-6.rs b/tests/target/fn-custom-6.rs new file mode 100644 index 000000000000..e891f4d58828 --- /dev/null +++ b/tests/target/fn-custom-6.rs @@ -0,0 +1,71 @@ +// rustfmt-brace_style: PreferSameLine +// Test different indents. + +fn foo(a: Aaaaaaaaaaaaaa, b: Bbbbbbbbbbbbbb) { + foo(); +} + +fn bar( + a: Aaaaaaaaaaaaaa, + b: Bbbbbbbbbbbbbb, + c: Cccccccccccccccccc, + d: Dddddddddddddddd, + e: Eeeeeeeeeeeeeee, +) { + bar(); +} + +fn foo(a: Aaaaaaaaaaaaaa, b: Bbbbbbbbbbbbbb) -> String { + foo(); +} + +fn bar( + a: Aaaaaaaaaaaaaa, + b: Bbbbbbbbbbbbbb, + c: Cccccccccccccccccc, + d: Dddddddddddddddd, + e: Eeeeeeeeeeeeeee, +) -> String { + bar(); +} + +fn foo(a: Aaaaaaaaaaaaaa, b: Bbbbbbbbbbbbbb) +where + T: UUUUUUUUUUU, { + foo(); +} + +fn bar( + a: Aaaaaaaaaaaaaa, + b: Bbbbbbbbbbbbbb, + c: Cccccccccccccccccc, + d: Dddddddddddddddd, + e: Eeeeeeeeeeeeeee, +) where + T: UUUUUUUUUUU, { + bar(); +} + +fn foo(a: Aaaaaaaaaaaaaa, b: Bbbbbbbbbbbbbb) -> String +where + T: UUUUUUUUUUU, { + foo(); +} + +fn bar( + a: Aaaaaaaaaaaaaa, + b: Bbbbbbbbbbbbbb, + c: Cccccccccccccccccc, + d: Dddddddddddddddd, + e: Eeeeeeeeeeeeeee, +) -> String +where + T: UUUUUUUUUUU, { + bar(); +} + +trait Test { + fn foo(a: u8) {} + + fn bar(a: u8) -> String {} +} diff --git a/tests/target/fn-custom-7.rs b/tests/target/fn-custom-7.rs new file mode 100644 index 000000000000..f6a1a90c3fc6 --- /dev/null +++ b/tests/target/fn-custom-7.rs @@ -0,0 +1,36 @@ +// rustfmt-normalize_comments: true +// rustfmt-fn_params_layout: Vertical +// rustfmt-brace_style: AlwaysNextLine + +// Case with only one variable. +fn foo(a: u8) -> u8 +{ + bar() +} + +// Case with 2 variables and some pre-comments. +fn foo( + a: u8, // Comment 1 + b: u8, // Comment 2 +) -> u8 +{ + bar() +} + +// Case with 2 variables and some post-comments. +fn foo( + // Comment 1 + a: u8, + // Comment 2 + b: u8, +) -> u8 +{ + bar() +} + +trait Test +{ + fn foo(a: u8) {} + + fn bar(a: u8) -> String {} +} diff --git a/tests/target/fn-custom-8.rs b/tests/target/fn-custom-8.rs new file mode 100644 index 000000000000..29af3fca79db --- /dev/null +++ b/tests/target/fn-custom-8.rs @@ -0,0 +1,77 @@ +// rustfmt-brace_style: PreferSameLine +// Test different indents. + +fn foo(a: Aaaaaaaaaaaaaa, b: Bbbbbbbbbbbbbb) { + foo(); +} + +fn bar( + a: Aaaaaaaaaaaaaa, + b: Bbbbbbbbbbbbbb, + c: Cccccccccccccccccc, + d: Dddddddddddddddd, + e: Eeeeeeeeeeeeeee, +) { + bar(); +} + +fn foo(a: Aaaaaaaaaaaaaa, b: Bbbbbbbbbbbbbb) -> String { + foo(); +} + +fn bar( + a: Aaaaaaaaaaaaaa, + b: Bbbbbbbbbbbbbb, + c: Cccccccccccccccccc, + d: Dddddddddddddddd, + e: Eeeeeeeeeeeeeee, +) -> String { + bar(); +} + +fn foo(a: Aaaaaaaaaaaaaa, b: Bbbbbbbbbbbbbb) +where + T: UUUUUUUUUUU, { + foo(); +} + +fn bar( + a: Aaaaaaaaaaaaaa, + b: Bbbbbbbbbbbbbb, + c: Cccccccccccccccccc, + d: Dddddddddddddddd, + e: Eeeeeeeeeeeeeee, +) where + T: UUUUUUUUUUU, { + bar(); +} + +fn foo(a: Aaaaaaaaaaaaaa, b: Bbbbbbbbbbbbbb) -> String +where + T: UUUUUUUUUUU, { + foo(); +} + +fn bar( + a: Aaaaaaaaaaaaaa, + b: Bbbbbbbbbbbbbb, + c: Cccccccccccccccccc, + d: Dddddddddddddddd, + e: Eeeeeeeeeeeeeee, +) -> String +where + T: UUUUUUUUUUU, { + bar(); +} + +trait Test { + fn foo(a: u8) {} + + fn bar(a: u8) -> String {} + + fn bar(a: u8) -> String + where + Foo: foooo, + Bar: barrr, { + } +} diff --git a/tests/target/fn-custom.rs b/tests/target/fn-custom.rs new file mode 100644 index 000000000000..506d9de34370 --- /dev/null +++ b/tests/target/fn-custom.rs @@ -0,0 +1,19 @@ +// rustfmt-fn_params_layout: Compressed +// Test some of the ways function signatures can be customised. + +// Test compressed layout of args. +fn foo( + a: Aaaaaaaaaaaaaaa, b: Bbbbbbbbbbbbbbbb, c: Ccccccccccccccccc, d: Ddddddddddddddddddddddddd, + e: Eeeeeeeeeeeeeeeeeee, +) { + foo(); +} + +impl Foo { + fn foo( + self, a: Aaaaaaaaaaaaaaa, b: Bbbbbbbbbbbbbbbb, c: Ccccccccccccccccc, + d: Ddddddddddddddddddddddddd, e: Eeeeeeeeeeeeeeeeeee, + ) { + foo(); + } +} diff --git a/tests/target/fn-param-attributes.rs b/tests/target/fn-param-attributes.rs new file mode 100644 index 000000000000..829575518deb --- /dev/null +++ b/tests/target/fn-param-attributes.rs @@ -0,0 +1,64 @@ +// https://github.com/rust-lang/rustfmt/issues/3623 + +fn foo(#[cfg(something)] x: i32, y: i32) -> i32 { + x + y +} + +fn foo_b(#[cfg(something)] x: i32, y: i32) -> i32 { + x + y +} + +fn add( + #[cfg(something)] + #[deny(C)] + x: i32, + y: i32, +) -> i32 { + x + y +} + +struct NamedSelfRefStruct {} +impl NamedSelfRefStruct { + fn foo(#[cfg(something)] self: &Self) {} +} + +struct MutStruct {} +impl MutStruct { + fn foo(#[cfg(foo)] &mut self, #[deny(C)] b: i32) {} +} + +fn main() { + let c = |#[allow(C)] a: u32, + #[cfg(something)] b: i32, + #[cfg_attr(something, cfg(nothing))] + #[deny(C)] + c: i32| {}; + let _ = c(1, 2); +} + +pub fn bar( + /// bar + #[test] + a: u32, + /// Bar + #[must_use] + /// Baz + #[no_mangle] + b: i32, +) { +} + +fn abc( + #[foo] + #[bar] + param: u32, +) { + // ... +} + +fn really_really_really_loooooooooooooooooooong( + #[cfg(some_even_longer_config_feature_that_keeps_going_and_going_and_going_forever_and_ever_and_ever_on_and_on)] + b: i32, +) { + // ... +} diff --git a/tests/target/fn-simple.rs b/tests/target/fn-simple.rs new file mode 100644 index 000000000000..e725269360d2 --- /dev/null +++ b/tests/target/fn-simple.rs @@ -0,0 +1,120 @@ +// rustfmt-normalize_comments: true + +fn simple( + // pre-comment on a function!? + i: i32, // yes, it's possible! + response: NoWay, // hose +) { + fn op( + x: Typ, + key: &[u8], + upd: Box< + Fn( + Option<&memcache::Item>, + ) -> (memcache::Status, Result>), + >, + ) -> MapResult { + } + + "cool" +} + +fn weird_comment( + // /*/ double level */ comment + x: Hello, // /*/* triple, even */*/ + // Does this work? + y: World, +) { + simple(/* does this preserve comments now? */ 42, NoWay) +} + +fn generic(arg: T) -> &SomeType +where + T: Fn( + // First arg + A, + // Second argument + B, + C, + D, + // pre comment + E, // last comment + ) -> &SomeType, +{ + arg(a, b, c, d, e) +} + +fn foo() -> ! {} + +pub fn http_fetch_async( + listener: Box, + script_chan: Box, +) { +} + +fn some_func>(val: T) {} + +fn zzzzzzzzzzzzzzzzzzzz( + selff: Type, + mut handle: node::Handle>, Type, NodeType>, +) -> SearchStack<'a, K, V, Type, NodeType> { +} + +unsafe fn generic_call( + cx: *mut JSContext, + argc: libc::c_uint, + vp: *mut JSVal, + is_lenient: bool, + call: unsafe extern "C" fn( + *const JSJitInfo, + *mut JSContext, + HandleObject, + *mut libc::c_void, + u32, + *mut JSVal, + ) -> u8, +) { + let f: fn(_, _) -> _ = panic!(); +} + +pub fn start_export_thread( + database: &Database, + crypto_scheme: &C, + block_size: usize, + source_path: &Path, +) -> BonzoResult> { +} + +pub fn waltz(cwd: &Path) -> CliAssert { + { + { + formatted_comment = + rewrite_comment(comment, block_style, width, offset, formatting_fig); + } + } +} + +// #2003 +mod foo { + fn __bindgen_test_layout_i_open0_c_open1_char_a_open2_char_close2_close1_close0_instantiation() + { + foo(); + } +} + +// #2082 +pub(crate) fn init() {} + +pub(crate) fn init() {} + +// #2630 +fn make_map String)>(records: &Vec, key_fn: F) -> HashMap {} + +// #2956 +fn bar( + beans: Asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf, + spam: bool, + eggs: bool, +) -> bool { + unimplemented!(); +} diff --git a/tests/target/fn-single-line/version_one.rs b/tests/target/fn-single-line/version_one.rs new file mode 100644 index 000000000000..013b2cd72161 --- /dev/null +++ b/tests/target/fn-single-line/version_one.rs @@ -0,0 +1,71 @@ +// rustfmt-fn_single_line: true +// rustfmt-version: One +// Test single-line functions. + +fn foo_expr() { 1 } + +fn foo_stmt() { foo(); } + +fn foo_decl_local() { let z = 5; } + +fn foo_decl_item(x: &mut i32) { x = 3; } + +fn empty() {} + +fn foo_return() -> String { "yay" } + +fn foo_where() -> T +where + T: Sync, +{ + let x = 2; +} + +fn fooblock() { + { + "inner-block" + } +} + +fn fooblock2(x: i32) { + let z = match x { + _ => 2, + }; +} + +fn comment() { + // this is a test comment + 1 +} + +fn comment2() { + // multi-line comment + let z = 2; + 1 +} + +fn only_comment() { + // Keep this here +} + +fn aaaaaaaaaaaaaaaaa_looooooooooooooooooooooong_name() { + let z = "aaaaaaawwwwwwwwwwwwwwwwwwwwwwwwwwww"; +} + +fn lots_of_space() { 1 } + +fn mac() -> Vec { vec![] } + +trait CoolTypes { + fn dummy(&self) {} +} + +trait CoolerTypes { + fn dummy(&self) {} +} + +fn Foo() +where + T: Bar, +{ +} diff --git a/tests/target/fn-single-line/version_two.rs b/tests/target/fn-single-line/version_two.rs new file mode 100644 index 000000000000..b8053d4c2f50 --- /dev/null +++ b/tests/target/fn-single-line/version_two.rs @@ -0,0 +1,67 @@ +// rustfmt-fn_single_line: true +// rustfmt-version: Two +// Test single-line functions. + +fn foo_expr() { 1 } + +fn foo_stmt() { foo(); } + +fn foo_decl_local() { let z = 5; } + +fn foo_decl_item(x: &mut i32) { x = 3; } + +fn empty() {} + +fn foo_return() -> String { "yay" } + +fn foo_where() -> T +where + T: Sync, +{ + let x = 2; +} + +fn fooblock() { { "inner-block" } } + +fn fooblock2(x: i32) { + let z = match x { + _ => 2, + }; +} + +fn comment() { + // this is a test comment + 1 +} + +fn comment2() { + // multi-line comment + let z = 2; + 1 +} + +fn only_comment() { + // Keep this here +} + +fn aaaaaaaaaaaaaaaaa_looooooooooooooooooooooong_name() { + let z = "aaaaaaawwwwwwwwwwwwwwwwwwwwwwwwwwww"; +} + +fn lots_of_space() { 1 } + +fn mac() -> Vec { vec![] } + +trait CoolTypes { + fn dummy(&self) {} +} + +trait CoolerTypes { + fn dummy(&self) {} +} + +fn Foo() +where + T: Bar, +{ +} diff --git a/tests/target/fn-ty.rs b/tests/target/fn-ty.rs new file mode 100644 index 000000000000..7d48f3b32dbc --- /dev/null +++ b/tests/target/fn-ty.rs @@ -0,0 +1,14 @@ +fn f( + xxxxxxxxxxxxxxxxxx: fn(a, b, b) -> a, + xxxxxxxxxxxxxxxxxx: fn() -> a, + xxxxxxxxxxxxxxxxxx: fn(a, b, b), + xxxxxxxxxxxxxxxxxx: fn(), + xxxxxxxxxxxxxxxxxx: fn(a, b, b) -> !, + xxxxxxxxxxxxxxxxxx: fn() -> !, +) where + F1: Fn(a, b, b) -> a, + F2: Fn(a, b, b), + F3: Fn(), + F4: Fn() -> u32, +{ +} diff --git a/tests/target/fn.rs b/tests/target/fn.rs new file mode 100644 index 000000000000..0ad775ee1ddf --- /dev/null +++ b/tests/target/fn.rs @@ -0,0 +1,120 @@ +// Tests different fns + +fn foo(a: AAAA, b: BBB, c: CCC) -> RetType {} + +fn foo(a: AAAA, b: BBB /* some, weird, inline comment */, c: CCC) -> RetType +where + T: Blah, +{ +} + +fn foo(a: AAA /* (comment) */) +where + T: Blah, +{ +} + +fn foo( + a: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, + b: BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB, +) -> RetType +where + T: Blah, +{ +} + +fn foo( + a: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, + b: BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB, +) -> RetType +where + T: Blah, + U: dsfasdfasdfasd, +{ +} + +fn foo B /* paren inside generics */>() {} + +impl Foo { + fn with_no_errors(&mut self, f: F) -> T + where + F: FnOnce(&mut Resolver) -> T, + { + } + + fn foo(mut self, mut bar: u32) {} + + fn bar(self, mut bazz: u32) {} +} + +pub fn render< + 'a, + N: Clone + 'a, + E: Clone + 'a, + G: Labeller<'a, N, E> + GraphWalk<'a, N, E>, + W: Write, +>( + g: &'a G, + w: &mut W, +) -> io::Result<()> { + render_opts(g, w, &[]) +} + +const fn foo() { + x; +} + +pub const fn foo() { + x; +} + +impl Foo { + const fn foo() { + x; + } +} + +fn homura>(_: T) {} + +fn issue377() -> (Box, Box) {} + +fn main() { + let _ = function(move || 5); + let _ = move || 42; + let _ = || unsafe { abort() }; +} + +// With inner attributes. +fn inner() { + #![inline] + x +} + +#[cfg_attr(rustfmt, rustfmt::skip)] +fn foo(a: i32) -> i32 { + // comment + if a > 0 { 1 } else { 2 } +} + +fn ______________________baz( + a: i32, +) -> *mut ::std::option::Option< + extern "C" fn(arg1: i32, _____________________a: i32, arg3: i32) -> (), +> { +} + +pub fn check_path<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + path: &hir::Path, + id: ast::NodeId, + cb: &mut FnMut(DefId, Span, &Option<&Stability>, &Option), +) { +} + +pub fn check_path<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + path: &hir::Path, + id: ast::NodeId, + cb: &mut FnMut(DefId, Span, &Option<&Stability>, &Option), +) { +} diff --git a/tests/target/fn_args_indent-block.rs b/tests/target/fn_args_indent-block.rs new file mode 100644 index 000000000000..f5232a488171 --- /dev/null +++ b/tests/target/fn_args_indent-block.rs @@ -0,0 +1,143 @@ +// rustfmt-normalize_comments: true + +fn foo() { + foo(); +} + +fn foo(a: Aaaaaaaaaaaaaa, b: Bbbbbbbbbbbbbb) { + foo(); +} + +fn bar( + a: Aaaaaaaaaaaaaa, + b: Bbbbbbbbbbbbbb, + c: Cccccccccccccccccc, + d: Dddddddddddddddd, + e: Eeeeeeeeeeeeeee, +) { + bar(); +} + +fn foo(a: Aaaaaaaaaaaaaa, b: Bbbbbbbbbbbbbb) -> String { + foo(); +} + +fn bar( + a: Aaaaaaaaaaaaaa, + b: Bbbbbbbbbbbbbb, + c: Cccccccccccccccccc, + d: Dddddddddddddddd, + e: Eeeeeeeeeeeeeee, +) -> String { + bar(); +} + +fn foo(a: u8 /* Comment 1 */, b: u8 /* Comment 2 */) -> u8 { + bar() +} + +fn foo( + a: u8, // Comment 1 + b: Bbbbbbbbbbbbbb, + c: Cccccccccccccccccc, + d: Dddddddddddddddd, + e: Eeeeeeeeeeeeeee, // Comment 2 +) -> u8 { + bar() +} + +fn bar( + a: Aaaaaaaaaaaaaa, + b: Bbbbbbbbbbbbbb, + c: Cccccccccccccccccc, + d: Dddddddddddddddd, + e: Eeeeeeeeeeeeeee, +) -> String +where + X: Fooooo, + Y: Baaar, +{ + bar(); +} + +fn foo() -> T { + foo(); +} + +fn foo() -> T +where + X: Foooo, + Y: Baaar, +{ + foo(); +} + +fn foo() +where + X: Foooo, +{ +} + +fn foo() +where + X: Foooo, + Y: Baaar, +{ +} + +fn foo() -> ( + Loooooooooooooooooooooong, + Reeeeeeeeeeeeeeeeeeeeeeeeturn, + iiiiiiiiis, + Looooooooooooooooong, +) { + foo(); +} + +fn foo() { + foo(); +} + +fn foo< + L: Loooooooooooooooooooooong, + G: Geeeeeeeeeeeneric, + I: iiiiiiiiis, + L: Looooooooooooooooong, +>() { + foo(); +} + +fn foo() { + foo(); +} + +trait Test { + fn foo(a: u8) {} + + fn bar( + a: Aaaaaaaaaaaaaa, + b: Bbbbbbbbbbbbbb, + c: Cccccccccccccccccc, + d: Dddddddddddddddd, + e: Eeeeeeeeeeeeeee, + ) -> String { + } +} + +fn foo( + a: Aaaaaaaaaaaaaaaaaaaa, + b: Bbbbbbbbbbbbbbbbb, + c: Cccccccccccccccccc, + d: Dddddddddddddddd, +) { + foo(); +} + +fn foo() -> ( + Looooooooooooooooooooooooooong, + Reeeeeeeeeeeeeeeeeeeeeeeeeeeeeturn, + iiiiiiiiiiiiiis, + Loooooooooooooooooooooong, +) { + foo(); +} diff --git a/tests/target/fn_args_layout-vertical.rs b/tests/target/fn_args_layout-vertical.rs new file mode 100644 index 000000000000..bfeca15c967e --- /dev/null +++ b/tests/target/fn_args_layout-vertical.rs @@ -0,0 +1,39 @@ +// rustfmt-fn_params_layout: Vertical + +// Empty list should stay on one line. +fn do_bar() -> u8 { + bar() +} + +// A single argument should stay on the same line. +fn do_bar(a: u8) -> u8 { + bar() +} + +// Multiple arguments should each get their own line. +fn do_bar( + a: u8, + mut b: u8, + c: &u8, + d: &mut u8, + closure: &Fn(i32) -> i32, +) -> i32 { + // This feature should not affect closures. + let bar = |x: i32, y: i32| -> i32 { x + y }; + bar(a, b) +} + +// If the first argument doesn't fit on the same line with the function name, +// the whole list should probably be pushed to the next line with hanging +// indent. That's not what happens though, so check current behaviour instead. +// In any case, it should maintain single argument per line. +fn do_this_that_and_the_other_thing( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: u8, + b: u8, + c: u8, + d: u8, +) { + this(); + that(); + the_other_thing(); +} diff --git a/tests/target/fn_once.rs b/tests/target/fn_once.rs new file mode 100644 index 000000000000..42b8f98e78ab --- /dev/null +++ b/tests/target/fn_once.rs @@ -0,0 +1,8 @@ +struct Add(usize); + +impl FnOnce<(usize,)> for Add { + type Output = Add; + extern "rust-call" fn call_once(self, to: (usize,)) -> Add { + Add(self.0 + to.0) + } +} diff --git a/tests/target/format_strings/issue-202.rs b/tests/target/format_strings/issue-202.rs new file mode 100644 index 000000000000..2a2c24140ddf --- /dev/null +++ b/tests/target/format_strings/issue-202.rs @@ -0,0 +1,25 @@ +// rustfmt-format_strings: true + +#[test] +fn compile_empty_program() { + let result = get_result(); + let expected = "; ModuleID = \'foo\' + +; Function Attrs: nounwind +declare void @llvm.memset.p0i8.i32(i8* nocapture, i8, i32, i32, i1) #0 + +declare i32 @write(i32, i8*, i32) + +declare i32 @putchar(i32) + +declare i32 @getchar() + +define i32 @main() { +entry: + ret i32 0 +} + +attributes #0 = { nounwind } +"; + assert_eq!(result, CString::new(expected).unwrap()); +} diff --git a/tests/target/format_strings/issue-2833.rs b/tests/target/format_strings/issue-2833.rs new file mode 100644 index 000000000000..70483532528c --- /dev/null +++ b/tests/target/format_strings/issue-2833.rs @@ -0,0 +1,15 @@ +// rustfmt-format_strings: true +// rustfmt-max_width: 80 + +fn test1() { + let expected = "\ +but Doctor Watson has to have it taken out for him and dusted, +"; +} + +fn test2() { + let expected = "\ +[Omitted long matching line] +but Doctor Watson has to have it taken out for him and dusted, +"; +} diff --git a/tests/target/format_strings/issue-3263.rs b/tests/target/format_strings/issue-3263.rs new file mode 100644 index 000000000000..72f7e9cc6277 --- /dev/null +++ b/tests/target/format_strings/issue-3263.rs @@ -0,0 +1,26 @@ +// rustfmt-format_strings: true +// rustfmt-newline_style: Windows + +#[test] +fn compile_empty_program() { + let result = get_result(); + let expected = "; ModuleID = \'foo\' + +; Function Attrs: nounwind +declare void @llvm.memset.p0i8.i32(i8* nocapture, i8, i32, i32, i1) #0 + +declare i32 @write(i32, i8*, i32) + +declare i32 @putchar(i32) + +declare i32 @getchar() + +define i32 @main() { +entry: + ret i32 0 +} + +attributes #0 = { nounwind } +"; + assert_eq!(result, CString::new(expected).unwrap()); +} diff --git a/tests/target/format_strings/issue-687.rs b/tests/target/format_strings/issue-687.rs new file mode 100644 index 000000000000..21d292f9eb98 --- /dev/null +++ b/tests/target/format_strings/issue-687.rs @@ -0,0 +1,10 @@ +// rustfmt-format_strings: true + +fn foo() -> &'static str { + let sql = "ATTACH DATABASE ':memory:' AS my_attached; + BEGIN; + CREATE TABLE my_attached.foo(x INTEGER); + INSERT INTO my_attached.foo VALUES(42); + END;"; + sql +} diff --git a/tests/target/format_strings/issue564.rs b/tests/target/format_strings/issue564.rs new file mode 100644 index 000000000000..d9ef077c2562 --- /dev/null +++ b/tests/target/format_strings/issue564.rs @@ -0,0 +1,7 @@ +// rustfmt-format_strings: true + +const USAGE: &'static str = " +Usage: codegen project + codegen regenerate + codegen verify +"; diff --git a/tests/target/hard-tabs.rs b/tests/target/hard-tabs.rs new file mode 100644 index 000000000000..aca7e09c0eac --- /dev/null +++ b/tests/target/hard-tabs.rs @@ -0,0 +1,98 @@ +// rustfmt-normalize_comments: true +// rustfmt-wrap_comments: true +// rustfmt-hard_tabs: true + +fn main() { + let x = Bar; + + let y = Foo { a: x }; + + Foo { + a: foo(), // comment + // comment + b: bar(), + ..something + }; + + fn foo(a: i32, a: i32, a: i32, a: i32, a: i32, a: i32, a: i32, a: i32, a: i32, a: i32, a: i32) { + } + + let str = "AAAAAAAAAAAAAAaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaAa"; + + if let ( + some_very_large, + tuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuple, + ) = 1 + 2 + 3 + {} + + if cond() { + something(); + } else if different_cond() { + something_else(); + } else { + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + } + + unsafe /* very looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong + * comment */ { + } + + unsafe /* So this is a very long comment. + * Multi-line, too. + * Will it still format correctly? */ { + } + + let chain = funktion_kall() + .go_to_next_line_with_tab() + .go_to_next_line_with_tab() + .go_to_next_line_with_tab(); + + let z = [ + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, + yyyyyyyyyyyyyyyyyyyyyyyyyyy, + zzzzzzzzzzzzzzzzzz, + q, + ]; + + fn generic(arg: T) -> &SomeType + where + T: Fn( + // First arg + A, + // Second argument + B, + C, + D, + // pre comment + E, // last comment + ) -> &SomeType, + { + arg(a, b, c, d, e) + } + + loong_func().quux(move || if true { 1 } else { 2 }); + + fffffffffffffffffffffffffffffffffff(a, { + SCRIPT_TASK_ROOT.with(|root| { + *root.borrow_mut() = Some(&script_task); + }); + }); + a.b.c.d(); + + x().y(|| match cond() { + true => (), + false => (), + }); +} + +// #2296 +impl Foo { + // a comment + // on multiple lines + fn foo() { + // another comment + // on multiple lines + let x = true; + } +} diff --git a/tests/target/hello.rs b/tests/target/hello.rs new file mode 100644 index 000000000000..d9f90b0b5e80 --- /dev/null +++ b/tests/target/hello.rs @@ -0,0 +1,8 @@ +// rustfmt-config: small_tabs.toml +// rustfmt-target: hello.rs + +// Smoke test - hello world. + +fn main() { + println!("Hello world!"); +} diff --git a/tests/target/hex_literal_lower.rs b/tests/target/hex_literal_lower.rs new file mode 100644 index 000000000000..5c27fded1674 --- /dev/null +++ b/tests/target/hex_literal_lower.rs @@ -0,0 +1,5 @@ +// rustfmt-hex_literal_case: Lower +fn main() { + let h1 = 0xcafe_5ea7; + let h2 = 0xcafe_f00du32; +} diff --git a/tests/target/hex_literal_preserve.rs b/tests/target/hex_literal_preserve.rs new file mode 100644 index 000000000000..e8774d0bb24e --- /dev/null +++ b/tests/target/hex_literal_preserve.rs @@ -0,0 +1,5 @@ +// rustfmt-hex_literal_case: Preserve +fn main() { + let h1 = 0xcAfE_5Ea7; + let h2 = 0xCaFe_F00du32; +} diff --git a/tests/target/hex_literal_upper.rs b/tests/target/hex_literal_upper.rs new file mode 100644 index 000000000000..48bb93d2c1c0 --- /dev/null +++ b/tests/target/hex_literal_upper.rs @@ -0,0 +1,5 @@ +// rustfmt-hex_literal_case: Upper +fn main() { + let h1 = 0xCAFE_5EA7; + let h2 = 0xCAFE_F00Du32; +} diff --git a/tests/target/if_while_or_patterns.rs b/tests/target/if_while_or_patterns.rs new file mode 100644 index 000000000000..61a357afcbae --- /dev/null +++ b/tests/target/if_while_or_patterns.rs @@ -0,0 +1,38 @@ +#![feature(if_while_or_patterns)] + +fn main() { + if let 0 | 1 = 0 { + println!("hello, world"); + }; + + if let aaaaaaaaaaaaaaaaaaaaaaaaaa | bbbbbbbbbbbbbbbbbbbbbbbbbbb | cccccccccccccccc | d_100 = 0 { + println!("hello, world"); + } + + if let aaaaaaaaaaaaaaaaaaaaaaaaaa | bbbbbbbbbbbbbbbbbbbbbbb | ccccccccccccccccccccc | d_101 = 0 + { + println!("hello, world"); + } + + if let aaaaaaaaaaaaaaaaaaaaaaaaaaaa | bbbbbbbbbbbbbbbbbbbbbbb | ccccccccccccccccccccc | d_103 = + 0 + { + println!("hello, world"); + } + + if let aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + | bbbbbbbbbbbbbbbbbbbbbbb + | ccccccccccccccccccccc + | d_105 = 0 + { + println!("hello, world"); + } + + while let xxx | xxx | xxx | xxx | xxx | xxx | xxx | xxx | xxx | xxx | xxx | xxx | xxx | xxx + | xxx | xxx | xxx | xxx | xxx | xxx | xxx | xxx | xxx | xxx | xxx | xxx | xxx = foo_bar( + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, + cccccccccccccccccccccccccccccccccccccccc, + ) { + println!("hello, world"); + } +} diff --git a/tests/target/immovable_generators.rs b/tests/target/immovable_generators.rs new file mode 100644 index 000000000000..0bf7a2d91ba1 --- /dev/null +++ b/tests/target/immovable_generators.rs @@ -0,0 +1,7 @@ +#![feature(generators)] + +unsafe fn foo() { + let mut ga = static || { + yield 1; + }; +} diff --git a/tests/target/impl.rs b/tests/target/impl.rs new file mode 100644 index 000000000000..f37fbcf1fcbc --- /dev/null +++ b/tests/target/impl.rs @@ -0,0 +1,43 @@ +// Test impls + +impl JSTraceable for SmallVec<[T; 1]> {} + +impl>> Handle { + // Keep this. +} + +impl Test +where + V: Clone, // This comment is NOT removed by formatting! +{ + pub fn new(value: V) -> Self { + Test { + cloned_value: value.clone(), + value, + } + } +} + +impl X /* comment */ {} +impl Y // comment +{ +} + +impl Foo for T +// comment1 +where + // comment2 + // blah + T: Clone, +{ +} + +// #1823 +default impl Trait for X {} +default unsafe impl Trait for Y {} +pub default unsafe impl Trait for Z {} + +// #2212 +impl ConstWithDefault { + default const CAN_RECONSTRUCT_QUERY_KEY: bool = false; +} diff --git a/tests/target/impls.rs b/tests/target/impls.rs new file mode 100644 index 000000000000..99e02990e417 --- /dev/null +++ b/tests/target/impls.rs @@ -0,0 +1,252 @@ +// rustfmt-normalize_comments: true +impl Foo for Bar { + fn foo() { + "hi" + } +} + +pub impl Foo for Bar { + // Associated Constants + const Baz: i32 = 16; + // Associated Types + type FooBar = usize; + // Comment 1 + fn foo() { + "hi" + } + // Comment 2 + fn foo() { + "hi" + } + // Comment 3 +} + +#[inherent] +impl Visible for Bar { + pub const C: i32; + pub type T; + pub fn f(); + pub fn g() {} +} + +pub unsafe impl<'a, 'b, X, Y: Foo> !Foo<'a, X> for Bar<'b, Y> +where + X: Foo<'a, Z>, +{ + fn foo() { + "hi" + } +} + +impl<'a, 'b, X, Y: Foo> Foo<'a, X> for Bar<'b, Y> +where + X: Fooooooooooooooooooooooooooooo<'a, Z>, +{ + fn foo() { + "hi" + } +} + +impl<'a, 'b, X, Y: Foo> Foo<'a, X> for Bar<'b, Y> +where + X: Foooooooooooooooooooooooooooo<'a, Z>, +{ + fn foo() { + "hi" + } +} + +impl Foo for Bar where T: Baz {} + +impl Foo for Bar +where + T: Baz, +{ + // Comment +} + +impl Foo { + fn foo() {} +} + +impl Boo { + // BOO + fn boo() {} + // FOO +} + +mod a { + impl Foo { + // Hello! + fn foo() {} + } +} + +mod b { + mod a { + impl Foo { + fn foo() {} + } + } +} + +impl Foo { + add_fun!(); +} + +impl Blah { + fn boop() {} + add_fun!(); +} + +impl X { + fn do_parse(mut self: X) {} +} + +impl Y5000 { + fn bar(self: X<'a, 'b>, y: Y) {} + + fn bad(&self, (x, y): CoorT) {} + + fn turbo_bad(self: X<'a, 'b>, (x, y): CoorT) {} +} + +pub impl Foo for Bar +where + T: Foo, +{ + fn foo() { + "hi" + } +} + +pub impl Foo for Bar +where + T: Foo, + Z: Baz, +{ +} + +mod m { + impl PartialEq for S + where + T: PartialEq, + { + fn eq(&self, other: &Self) { + true + } + } + + impl PartialEq for S where T: PartialEq {} +} + +impl + Handle, HandleType> +{ +} + +impl PartialEq + for Handle, HandleType> +{ +} + +mod x { + impl Foo + where + A: 'static, + B: 'static, + C: 'static, + D: 'static, + { + } +} + +impl + Issue1249 +{ + // Creates a new flow constructor. + fn foo() {} +} + +// #1600 +impl<#[may_dangle] K, #[may_dangle] V> Drop for RawTable { + fn drop() {} +} + +// #1168 +pub trait Number: + Copy + + Eq + + Not + + Shl + + Shr + + BitAnd + + BitOr + + BitAndAssign + + BitOrAssign +{ + // test + fn zero() -> Self; +} + +// #1642 +pub trait SomeTrait: + Clone + + Eq + + PartialEq + + Ord + + PartialOrd + + Default + + Hash + + Debug + + Display + + Write + + Read + + FromStr +{ + // comment +} + +// #1995 +impl Foo { + fn f( + S { + aaaaaaaaaa: aaaaaaaaaa, + bbbbbbbbbb: bbbbbbbbbb, + cccccccccc: cccccccccc, + }: S, + ) -> u32 { + 1 + } +} + +// #2491 +impl<'a, 'b, 'c> SomeThing + for ( + &'a mut SomethingLong, + &'b mut SomethingLong, + &'c mut SomethingLong, + ) +{ + fn foo() {} +} + +// #2746 +impl<'seq1, 'seq2, 'body, 'scope, Channel> + Adc12< + Dual, + MasterRunningDma<'seq1, 'body, 'scope, Channel>, + SlaveRunningDma<'seq2, 'body, 'scope>, + > +where + Channel: DmaChannel, +{ +} + +// #4084 +impl const std::default::Default for Struct { + #[inline] + fn default() -> Self { + Self { f: 12.5 } + } +} diff --git a/tests/target/imports/import-fencepost-length.rs b/tests/target/imports/import-fencepost-length.rs new file mode 100644 index 000000000000..fd09d50d72d9 --- /dev/null +++ b/tests/target/imports/import-fencepost-length.rs @@ -0,0 +1,7 @@ +use aaaaaaaaaaaaaaa::bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb; +use aaaaaaaaaaaaaaa::{ + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, ccccccccccccccccccccccccccccccc, dddddddd, +}; +use aaaaaaaaaaaaaaa::{ + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, ccccccccccccccccccccccccccccccc, ddddddddd, +}; diff --git a/tests/target/imports/imports-impl-only-use.rs b/tests/target/imports/imports-impl-only-use.rs new file mode 100644 index 000000000000..d290d8d91858 --- /dev/null +++ b/tests/target/imports/imports-impl-only-use.rs @@ -0,0 +1,4 @@ +#![feature(underscore_imports)] + +use attr; +use std::iter::Iterator as _; diff --git a/tests/target/imports/imports-reorder-lines-and-items.rs b/tests/target/imports/imports-reorder-lines-and-items.rs new file mode 100644 index 000000000000..98a5afe43480 --- /dev/null +++ b/tests/target/imports/imports-reorder-lines-and-items.rs @@ -0,0 +1,7 @@ +use std::cmp::{a, b, c, d}; +use std::ddd::aaa; +use std::ddd::{a, b, c as g, d as p}; +/// This comment should stay with `use std::str;` +use std::str; +// This comment should stay with `use std::ddd:bbb;` +use std::ddd::bbb; diff --git a/tests/target/imports/imports-reorder-lines.rs b/tests/target/imports/imports-reorder-lines.rs new file mode 100644 index 000000000000..5b85503b55d0 --- /dev/null +++ b/tests/target/imports/imports-reorder-lines.rs @@ -0,0 +1,31 @@ +use std::cmp::{a, b, c, d}; +use std::cmp::{b, e, f, g}; +use std::ddd::aaa; +use std::str; +// This comment should stay with `use std::ddd;` +use std::ddd; +use std::ddd::bbb; + +mod test {} + +use aaa; +use aaa::bbb; +use aaa::*; + +mod test {} +// If item names are equal, order by rename + +use test::{a as bb, b}; +use test::{a as aa, c}; + +mod test {} +// If item names are equal, order by rename - no rename comes before a rename + +use test::{a as bb, b}; +use test::{a, c}; + +mod test {} +// `self` always comes first + +use test::{self as bb, b}; +use test::{a as aa, c}; diff --git a/tests/target/imports/imports-reorder.rs b/tests/target/imports/imports-reorder.rs new file mode 100644 index 000000000000..84e97c0224f8 --- /dev/null +++ b/tests/target/imports/imports-reorder.rs @@ -0,0 +1,5 @@ +// rustfmt-normalize_comments: true + +use path::{self /* self */, /* A */ A, B /* B */, C}; + +use {aa, ab, ac, b, Z}; diff --git a/tests/target/imports/imports.rs b/tests/target/imports/imports.rs new file mode 100644 index 000000000000..87584d89f663 --- /dev/null +++ b/tests/target/imports/imports.rs @@ -0,0 +1,129 @@ +// rustfmt-normalize_comments: true + +// Imports. + +// Long import. +use exceedingly::loooooooooooooooooooooooooooooooooooooooooooooooooooooooong::import::path::{ + ItemA, ItemB, +}; +use exceedingly::looooooooooooooooooooooooooooooooooooooooooooooooooooooooooong::import::path::{ + ItemA, ItemB, +}; +use rustc_ast::ast::{ItemDefaultImpl, ItemForeignMod, ItemImpl, ItemMac, ItemMod, ItemStatic}; + +use list::{ + // Another item + AnotherItem, // Another Comment + // Last Item + LastItem, + // Some item + SomeItem, // Comment +}; + +use test::{/* A */ self /* B */, Other /* C */}; + +pub use rustc_ast::ast::{Expr, ExprAssign, ExprCall, ExprMethodCall, ExprPath, Expr_}; +use rustc_ast::{self}; +use Foo::{Bar, Baz}; +use {Bar /* comment */, /* Pre-comment! */ Foo}; + +use std::io; +use std::io::{self}; + +mod Foo { + pub use rustc_ast::ast::{ + ItemDefaultImpl, ItemForeignMod, ItemImpl, ItemMac, ItemMod, ItemStatic, + }; + + mod Foo2 { + pub use rustc_ast::ast::{ + self, ItemDefaultImpl, ItemForeignMod, ItemImpl, ItemMac, ItemMod, ItemStatic, + }; + } +} + +fn test() { + use Baz::*; + use Qux; +} + +// Simple imports +use bar::quux as kaas; +use foo; +use foo::bar::baz; + +// With aliases. +use foo::qux as bar; +use foo::{self as bar}; +use foo::{self as bar, baz}; +use foo::{baz, qux as bar}; + +// With absolute paths +use foo; +use foo::Bar; +use foo::{Bar, Baz}; +use Foo; +use {Bar, Baz}; + +// Root globs +use *; +use *; + +// spaces used to cause glob imports to disappear (#1356) +use super::*; +use foo::issue_1356::*; + +// We shouldn't remove imports which have attributes attached (#1858) +#[cfg(unix)] +use self::unix::{}; + +// nested imports +use foo::{ + a, b, + bar::{ + baz, + foo::{a, b, cxxxxxxxxxxxxx, yyyyyyyyyyyyyy, zzzzzzzzzzzzzzzz}, + qux, xxxxxxxxxxx, yyyyyyyyyyyyy, zzzzzzzzzzzzzzzz, + }, + boo, c, +}; + +use fooo::{ + baar::foobar::{ + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy, + zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz, + }, + bar, + bar::*, + x, y, z, +}; + +use exonum::{ + api::{Api, ApiError}, + blockchain::{self, BlockProof, Blockchain, Transaction, TransactionSet}, + crypto::{Hash, PublicKey}, + helpers::Height, + node::TransactionSend, + storage::{ListProof, MapProof}, +}; + +// nested imports with a single sub-tree. +use a::b::c::d; +use a::b::c::*; +use a::b::c::{xxx, yyy, zzz}; + +// #2645 +/// This line is not affected. +// This line is deleted. +use c; + +// #2670 +#[macro_use] +use imports_with_attr; + +// #2888 +use std::f64::consts::{E, PI, SQRT_2}; + +// #3273 +#[rustfmt::skip] +use std::fmt::{self, {Display, Formatter}}; diff --git a/tests/target/imports/imports_2021_edition.rs b/tests/target/imports/imports_2021_edition.rs new file mode 100644 index 000000000000..34dcc866a0bd --- /dev/null +++ b/tests/target/imports/imports_2021_edition.rs @@ -0,0 +1,3 @@ +// rustfmt-edition: 2021 + +use ::happy::new::year; diff --git a/tests/target/imports/imports_block_indent.rs b/tests/target/imports/imports_block_indent.rs new file mode 100644 index 000000000000..8c90f7ce29c8 --- /dev/null +++ b/tests/target/imports/imports_block_indent.rs @@ -0,0 +1,4 @@ +// #2569 +use apns2::request::notification::{ + Notificatio, NotificationBuilder, Priority, SilentNotificationBuilder, +}; diff --git a/tests/target/imports/imports_granularity_crate.rs b/tests/target/imports/imports_granularity_crate.rs new file mode 100644 index 000000000000..36e01558ff04 --- /dev/null +++ b/tests/target/imports/imports_granularity_crate.rs @@ -0,0 +1,59 @@ +// rustfmt-imports_granularity: Crate + +use a::{a, b, c, d, e, f, g}; + +#[doc(hidden)] +use a::b; +use a::{c, d}; + +#[doc(hidden)] +use a::b; +use a::{c, d, e}; + +use foo::{a, b, c}; +pub use foo::{bar, foobar}; + +use a::b::c::{d, xxx, yyy, zzz, *}; + +// https://github.com/rust-lang/rustfmt/issues/3808 +use d::{self}; +use e::{self as foo}; +use f::{self, b}; +use g::{self, a, b}; +use h::a; +use i::a::{self}; +use j::a::{self}; + +use k::{a, b, c, d}; +use l::{a, b, c, d}; + +use b::q::{self /* After b::q::self */}; +use b::r; // After b::r +use b::s::{ + a, + b, // After b::s::b +}; +use b::t::{/* Before b::t::self */ self}; +use b::t::{ + // Before b::t::a + a, + b, +}; +use b::v::{ + // Before b::v::a + a, + // Before b::v::b + b, +}; +use b::{ + c, d, e, + u::{a, b}, +}; +use b::{ + f::g, + h::{i, j}, /* After b::h group */ +}; +use b::{ + /* Before b::l group */ l::{self, m, n::o, p::*}, + q, +}; diff --git a/tests/target/imports/imports_granularity_default-with-dups.rs b/tests/target/imports/imports_granularity_default-with-dups.rs new file mode 100644 index 000000000000..5da6d588e6dd --- /dev/null +++ b/tests/target/imports/imports_granularity_default-with-dups.rs @@ -0,0 +1,6 @@ +use crate::lexer; +use crate::lexer; +use crate::lexer::tokens::TokenData; +use crate::lexer::tokens::TokenData; +use crate::lexer::{self}; +use crate::lexer::{self, tokens::TokenData}; diff --git a/tests/target/imports/imports_granularity_item-with-dups-StdExternalCrate-no-reorder.rs b/tests/target/imports/imports_granularity_item-with-dups-StdExternalCrate-no-reorder.rs new file mode 100644 index 000000000000..ed4df544d6f9 --- /dev/null +++ b/tests/target/imports/imports_granularity_item-with-dups-StdExternalCrate-no-reorder.rs @@ -0,0 +1,7 @@ +// rustfmt-imports_granularity: Item +// rustfmt-reorder_imports: false +// rustfmt-group_imports: StdExternalCrate + +use crate::lexer; +use crate::lexer::tokens::TokenData; +use crate::lexer::{self}; diff --git a/tests/target/imports/imports_granularity_item-with-dups.rs b/tests/target/imports/imports_granularity_item-with-dups.rs new file mode 100644 index 000000000000..00df37f93322 --- /dev/null +++ b/tests/target/imports/imports_granularity_item-with-dups.rs @@ -0,0 +1,5 @@ +// rustfmt-imports_granularity: Item + +use crate::lexer; +use crate::lexer::tokens::TokenData; +use crate::lexer::{self}; diff --git a/tests/target/imports/imports_granularity_item.rs b/tests/target/imports/imports_granularity_item.rs new file mode 100644 index 000000000000..d2f5496fdaca --- /dev/null +++ b/tests/target/imports/imports_granularity_item.rs @@ -0,0 +1,45 @@ +// rustfmt-imports_granularity: Item + +use a::b; +use a::c; +use a::d; +use a::f::g; +use a::h::i; +use a::h::j; +use a::l::m; +use a::l::n::o; +use a::l::p::*; +use a::l::{self}; +use a::q::{self}; + +use b::c; +use b::d; +use b::e; +use b::q::{self /* After b::q::self */}; +use b::r; // After b::r +use b::s::{ + a, + b, // After b::s::b +}; +use b::t::{/* Before b::t::self */ self}; +use b::t::{ + // Before b::t::a + a, + b, +}; +use b::u::a; +use b::u::b; +use b::v::{ + // Before b::v::a + a, + // Before b::v::b + b, +}; +use b::{ + f::g, + h::{i, j}, /* After b::h group */ +}; +use b::{ + /* Before b::l group */ l::{self, m, n::o, p::*}, + q, +}; diff --git a/tests/target/imports/imports_granularity_module.rs b/tests/target/imports/imports_granularity_module.rs new file mode 100644 index 000000000000..14f341016ff9 --- /dev/null +++ b/tests/target/imports/imports_granularity_module.rs @@ -0,0 +1,55 @@ +// rustfmt-imports_granularity: Module + +use a::b::c; +use a::d::e; +use a::f; +use a::g::{h, i}; +use a::j::k::{self, l}; +use a::j::{self, m}; +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)] +use foo::{a::b, c::d}; + +use bar::{ + // comment + a::b, + // more comment + c::d, + e::f, +}; + +use b::q::{self /* After b::q::self */}; +use b::r; // After b::r +use b::s::{ + a, + b, // After b::s::b +}; +use b::t::{/* Before b::t::self */ self}; +use b::t::{ + // Before b::t::a + a, + b, +}; +use b::u::{a, b}; +use b::v::{ + // Before b::v::a + a, + // Before b::v::b + b, +}; +use b::{c, d, e}; +use b::{ + f::g, + h::{i, j}, /* After b::h group */ +}; +use b::{ + /* Before b::l group */ l::{self, m, n::o, p::*}, + q, +}; diff --git a/tests/target/imports_granularity_one.rs b/tests/target/imports_granularity_one.rs new file mode 100644 index 000000000000..da4c6678db14 --- /dev/null +++ b/tests/target/imports_granularity_one.rs @@ -0,0 +1,109 @@ +// rustfmt-imports_granularity: One + +use { + a::{ + aa::*, + ab, + ac::{aca, acb}, + }, + b, +}; + +use { + a::{self as x, aa, ab}, + b::ba, +}; + +use a::{ + aa::{aaa, *}, + ab::aba as x, +}; + +#[cfg(test)] +use a::{ab, ac::aca}; +#[cfg(test)] +use b::{ + ba, bb, + bc::bca::{bcaa, bcab}, +}; +use { + a::{aa, ad::ada}, + b, +}; + +pub use { + a::{aa, ae}, + b::{bb, bc::bca}, +}; +use { + a::{ab, ac, ad}, + b::ba, +}; + +use { + a::{ + aa::{aaa, *}, + ab, + ac::{aca, acb}, + }, + b::{ + ba, + bb::{self, bba}, + }, +}; + +use { + crate::{a, b::ba}, + c::ca, +}; + +use { + super::{a, b::ba}, + c::ca, +}; + +use { + super::b, + crate::a, + c::{self, ca}, +}; + +use a::{ + // some comment + aa::{aaa, aab}, + ab, + // another comment + ac::aca, +}; +use {a::ad::ada, b as x}; + +use b::q::{self /* After b::q::self */}; +use b::r; // After b::r +use b::s::{ + a, + b, // After b::s::b +}; +use b::t::{/* Before b::t::self */ self}; +use b::t::{ + // Before b::t::a + a, + b, +}; +use b::v::{ + // Before b::v::a + a, + // Before b::v::b + b, +}; +use b::{ + c, d, e, + u::{a, b}, +}; +use b::{ + f::g, + h::{i, j}, /* After b::h group */ +}; +use b::{ + /* Before b::l group */ l::{self, m, n::o, p::*}, + q, +}; diff --git a/tests/target/imports_raw_identifiers/version_One.rs b/tests/target/imports_raw_identifiers/version_One.rs new file mode 100644 index 000000000000..bc4b5b135696 --- /dev/null +++ b/tests/target/imports_raw_identifiers/version_One.rs @@ -0,0 +1,5 @@ +// rustfmt-version:One + +use websocket::client::ClientBuilder; +use websocket::r#async::futures::Stream; +use websocket::result::WebSocketError; diff --git a/tests/target/imports_raw_identifiers/version_Two.rs b/tests/target/imports_raw_identifiers/version_Two.rs new file mode 100644 index 000000000000..22bfe93122f9 --- /dev/null +++ b/tests/target/imports_raw_identifiers/version_Two.rs @@ -0,0 +1,5 @@ +// rustfmt-version:Two + +use websocket::r#async::futures::Stream; +use websocket::client::ClientBuilder; +use websocket::result::WebSocketError; diff --git a/tests/target/indented-impl.rs b/tests/target/indented-impl.rs new file mode 100644 index 000000000000..eff579dddac7 --- /dev/null +++ b/tests/target/indented-impl.rs @@ -0,0 +1,13 @@ +// rustfmt-brace_style: AlwaysNextLine +mod x +{ + struct X(i8); + + impl Y for X + { + fn y(self) -> () + { + println!("ok"); + } + } +} diff --git a/tests/target/inner-module-path/b.rs b/tests/target/inner-module-path/b.rs new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/tests/target/inner-module-path/b.rs @@ -0,0 +1 @@ + diff --git a/tests/target/inner-module-path/c/d.rs b/tests/target/inner-module-path/c/d.rs new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/tests/target/inner-module-path/c/d.rs @@ -0,0 +1 @@ + diff --git a/tests/target/inner-module-path/lib.rs b/tests/target/inner-module-path/lib.rs new file mode 100644 index 000000000000..60d246dd5fb4 --- /dev/null +++ b/tests/target/inner-module-path/lib.rs @@ -0,0 +1,8 @@ +#[path = "."] +mod a { + mod b; +} + +mod c { + mod d; +} diff --git a/tests/target/invalid-rust-code-in-doc-comment.rs b/tests/target/invalid-rust-code-in-doc-comment.rs new file mode 100644 index 000000000000..f8479d4e3451 --- /dev/null +++ b/tests/target/invalid-rust-code-in-doc-comment.rs @@ -0,0 +1,18 @@ +// rustfmt-format_code_in_doc_comments: true + +/// ```rust +/// if (true) { … } +/// ``` +fn a() {} + +/// ```rust +/// if foo() { +/// … +/// } +/// ``` +fn a() {} + +/// ```rust +/// k1 == k2 ⇒ hash(k1) == hash(k2) +/// ``` +pub struct a; diff --git a/tests/target/issue-1021.rs b/tests/target/issue-1021.rs new file mode 100644 index 000000000000..ba1029d4e61e --- /dev/null +++ b/tests/target/issue-1021.rs @@ -0,0 +1,22 @@ +// rustfmt-normalize_comments: true +fn main() { + match x { + S(true, .., true) => (), + S(true, ..) => (), + S(.., true) => (), + S(..) => (), + S(_) => (), + S(/* .. */ ..) => (), + S(/* .. */ .., true) => (), + } + + match y { + (true, .., true) => (), + (true, ..) => (), + (.., true) => (), + (..) => (), + (_,) => (), + (/* .. */ ..) => (), + (/* .. */ .., true) => (), + } +} diff --git a/tests/target/issue-1049.rs b/tests/target/issue-1049.rs new file mode 100644 index 000000000000..c788519ca9a1 --- /dev/null +++ b/tests/target/issue-1049.rs @@ -0,0 +1,29 @@ +// Test overlong function signature +pub unsafe fn reborrow_mut( + &mut X: Abcde, +) -> Handle, HandleType> { +} + +pub fn merge( + mut X: Abcdef, +) -> Handle, K, V, marker::Internal>, marker::Edge> { +} + +impl Handle { + pub fn merge( + a: Abcd, + ) -> Handle, K, V, marker::Internal>, marker::Edge> { + } +} + +// Long function without return type that should not be reformatted. +fn veeeeeeeeeeeeeeeeeeeeery_long_name(a: FirstTypeeeeeeeeee, b: SecondTypeeeeeeeeeeeeeeeeeeeeeee) {} + +fn veeeeeeeeeeeeeeeeeeeeeery_long_name(a: FirstTypeeeeeeeeee, b: SecondTypeeeeeeeeeeeeeeeeeeeeeee) { +} + +fn veeeeeeeeeeeeeeeeeeeeeeery_long_name( + a: FirstTypeeeeeeeeee, + b: SecondTypeeeeeeeeeeeeeeeeeeeeeee, +) { +} diff --git a/tests/target/issue-1055.rs b/tests/target/issue-1055.rs new file mode 100644 index 000000000000..ee143e792b02 --- /dev/null +++ b/tests/target/issue-1055.rs @@ -0,0 +1,3 @@ +fn issue_1055() { + let foo = (|| {})(); +} diff --git a/tests/target/issue-1096.rs b/tests/target/issue-1096.rs new file mode 100644 index 000000000000..de78e736483f --- /dev/null +++ b/tests/target/issue-1096.rs @@ -0,0 +1,71 @@ +struct StructA /* comment 1 */ { + t: T, +} + +struct StructB /* comment 2 */; + +struct StructC /* comment 3 */; + +struct StructD /* comment 4 */ { + t: usize, +} + +struct StructE +/* comment 5 */ +where + T: Clone, +{ + t: usize, +} + +struct StructF +/* comment 6 */ +where + T: Clone, +{ + t: usize, +} + +struct StructG +/* comment 7 */ +// why a line comment?? +{ + t: T, +} + +struct StructH +/* comment 8 */ +// why a line comment?? +where + T: Clone, +{ + t: T, +} + +enum EnumA /* comment 8 */ { + Field(T), +} + +enum EnumB /* comment 9 */ { + Field, +} + +// Issue 2781 +struct StructX1 +// where +// T: Clone +{ + inner: String, +} + +struct StructX2< + T, + U: Iterator, + V: Iterator, + W: Iterator, +> +// where +// T: Clone +{ + inner: String, +} diff --git a/tests/target/issue-1111.rs b/tests/target/issue-1111.rs new file mode 100644 index 000000000000..2e1a89ad78eb --- /dev/null +++ b/tests/target/issue-1111.rs @@ -0,0 +1 @@ +use bar; diff --git a/tests/target/issue-1113.rs b/tests/target/issue-1113.rs new file mode 100644 index 000000000000..1245bcd057ca --- /dev/null +++ b/tests/target/issue-1113.rs @@ -0,0 +1,33 @@ +pub fn foo() -> fmt::Result +//pub fn writeStringToken +{ + panic!() +} + +pub fn foo() -> fmt::Result // pub fn writeStringToken +{ + panic!() +} + +pub fn foo() -> fmt::Result /* pub fn writeStringToken */ { + panic!() +} + +pub fn foo() -> fmt::Result +/* pub fn writeStringToken */ { + panic!() +} + +pub fn foo() -> fmt::Result +/* pub fn writeStringToken */ +{ + panic!() +} + +pub fn foo() -> fmt::Result /* + * + * + */ +{ + panic!() +} diff --git a/tests/target/issue-1120.rs b/tests/target/issue-1120.rs new file mode 100644 index 000000000000..f44597e7d1ee --- /dev/null +++ b/tests/target/issue-1120.rs @@ -0,0 +1,11 @@ +// rustfmt-reorder_imports: true + +// Ensure that a use at the start of an inline module is correctly formatted. +mod foo { + use bar; +} + +// Ensure that an indented `use` gets the correct indentation. +mod foo { + use bar; +} diff --git a/tests/target/issue-1124.rs b/tests/target/issue-1124.rs new file mode 100644 index 000000000000..f0fc485a3c21 --- /dev/null +++ b/tests/target/issue-1124.rs @@ -0,0 +1,21 @@ +// rustfmt-reorder_imports: true + +use a; +use b; +use c; +use d; +// The previous line has a space after the `use a;` + +mod a { + use a; + use b; + use c; + use d; +} + +use z; + +use y; + +use a; +use x; diff --git a/tests/target/issue-1127.rs b/tests/target/issue-1127.rs new file mode 100644 index 000000000000..fb09036d1f88 --- /dev/null +++ b/tests/target/issue-1127.rs @@ -0,0 +1,25 @@ +// rustfmt-max_width: 120 +// rustfmt-match_arm_blocks: false +// rustfmt-match_block_trailing_comma: true + +fn a_very_very_very_very_very_very_very_very_very_very_very_long_function_name() -> i32 { + 42 +} + +enum TestEnum { + AVeryVeryLongEnumName, + AnotherVeryLongEnumName, + TheLastVeryLongEnumName, +} + +fn main() { + let var = TestEnum::AVeryVeryLongEnumName; + let num = match var { + TestEnum::AVeryVeryLongEnumName => + a_very_very_very_very_very_very_very_very_very_very_very_long_function_name(), + TestEnum::AnotherVeryLongEnumName => + a_very_very_very_very_very_very_very_very_very_very_very_long_function_name(), + TestEnum::TheLastVeryLongEnumName => + a_very_very_very_very_very_very_very_very_very_very_very_long_function_name(), + }; +} diff --git a/tests/target/issue-1158.rs b/tests/target/issue-1158.rs new file mode 100644 index 000000000000..2abfa5a29f2d --- /dev/null +++ b/tests/target/issue-1158.rs @@ -0,0 +1,3 @@ +trait T { + itemmacro!(this, is.now().formatted(yay)); +} diff --git a/tests/target/issue-1177.rs b/tests/target/issue-1177.rs new file mode 100644 index 000000000000..dcda39728143 --- /dev/null +++ b/tests/target/issue-1177.rs @@ -0,0 +1,7 @@ +// rustfmt-normalize_comments: true +fn main() { + // Line Comment + // Block Comment + + let d = 5; +} diff --git a/tests/target/issue-1192.rs b/tests/target/issue-1192.rs new file mode 100644 index 000000000000..432fe8cce7c8 --- /dev/null +++ b/tests/target/issue-1192.rs @@ -0,0 +1,3 @@ +fn main() { + assert!(true); +} diff --git a/tests/target/issue-1210/a.rs b/tests/target/issue-1210/a.rs new file mode 100644 index 000000000000..94c1b44e5e56 --- /dev/null +++ b/tests/target/issue-1210/a.rs @@ -0,0 +1,16 @@ +// rustfmt-format_strings: true +// rustfmt-max_width: 50 + +impl Foo { + fn cxx(&self, target: &str) -> &Path { + match self.cxx.get(target) { + Some(p) => p.path(), + None => panic!( + "\n\ntarget `{}` is not \ + configured as a host, + only as a target\n\n", + target + ), + } + } +} diff --git a/tests/target/issue-1210/b.rs b/tests/target/issue-1210/b.rs new file mode 100644 index 000000000000..a7b1e3bcdc89 --- /dev/null +++ b/tests/target/issue-1210/b.rs @@ -0,0 +1,16 @@ +// rustfmt-format_strings: true +// rustfmt-max_width: 50 + +impl Foo { + fn cxx(&self, target: &str) -> &Path { + match self.cxx.get(target) { + Some(p) => p.path(), + None => panic!( + "\ntarget `{}`: is not, \ + configured as a host, + only as a target\n\n", + target + ), + } + } +} diff --git a/tests/target/issue-1210/c.rs b/tests/target/issue-1210/c.rs new file mode 100644 index 000000000000..183d79f92586 --- /dev/null +++ b/tests/target/issue-1210/c.rs @@ -0,0 +1,7 @@ +// rustfmt-format_strings: true +// rustfmt-max_width: 50 + +const foo: String = + "trailing_spaces!! + keep them! Amet neque. Praesent \ + rhoncus eros non velit."; diff --git a/tests/target/issue-1210/d.rs b/tests/target/issue-1210/d.rs new file mode 100644 index 000000000000..9279e6fc9990 --- /dev/null +++ b/tests/target/issue-1210/d.rs @@ -0,0 +1,4 @@ +// rustfmt-wrap_comments: true + +// trailing_spaces_in_comment!! +// remove those from above diff --git a/tests/target/issue-1210/e.rs b/tests/target/issue-1210/e.rs new file mode 100644 index 000000000000..55f80c6c3cc2 --- /dev/null +++ b/tests/target/issue-1210/e.rs @@ -0,0 +1,11 @@ +// rustfmt-format_strings: true +// rustfmt-max_width: 50 + +// explicit line breaks should be kept in order to preserve the layout + +const foo: String = + "Suspendisse vel augue at felis tincidunt \ + sollicitudin. Fusce arcu. + Duis et odio et leo + sollicitudin consequat. Aliquam \ + lobortis. Phasellus condimentum."; diff --git a/tests/target/issue-1211.rs b/tests/target/issue-1211.rs new file mode 100644 index 000000000000..de4c5c87ee67 --- /dev/null +++ b/tests/target/issue-1211.rs @@ -0,0 +1,13 @@ +fn main() { + for iface in &ifaces { + match iface.addr { + get_if_addrs::IfAddr::V4(ref addr) => match addr.broadcast { + Some(ip) => { + sock.send_to(&buf, (ip, 8765)).expect("foobar"); + } + _ => (), + }, + _ => (), + }; + } +} diff --git a/tests/target/issue-1214.rs b/tests/target/issue-1214.rs new file mode 100644 index 000000000000..c622abb3a85b --- /dev/null +++ b/tests/target/issue-1214.rs @@ -0,0 +1,8 @@ +/*! +# Example + +``` + // Here goes some example +``` + */ +struct Item; diff --git a/tests/target/issue-1216.rs b/tests/target/issue-1216.rs new file mode 100644 index 000000000000..d727c158ab8e --- /dev/null +++ b/tests/target/issue-1216.rs @@ -0,0 +1,5 @@ +// rustfmt-normalize_comments: true +enum E { + A, //* I am not a block comment (caused panic) + B, +} diff --git a/tests/target/issue-1239.rs b/tests/target/issue-1239.rs new file mode 100644 index 000000000000..e950200b15bd --- /dev/null +++ b/tests/target/issue-1239.rs @@ -0,0 +1,11 @@ +fn foo() { + let with_alignment = if condition__uses_alignment_for_first_if__0 + || condition__uses_alignment_for_first_if__1 + || condition__uses_alignment_for_first_if__2 + { + } else if condition__no_alignment_for_later_else__0 + || condition__no_alignment_for_later_else__1 + || condition__no_alignment_for_later_else__2 + { + }; +} diff --git a/tests/target/issue-1247.rs b/tests/target/issue-1247.rs new file mode 100644 index 000000000000..16c63e0f53d3 --- /dev/null +++ b/tests/target/issue-1247.rs @@ -0,0 +1,8 @@ +// rustfmt-max_width: 80 + +fn foo() { + polyfill::slice::fill( + &mut self.pending[padding_pos..(self.algorithm.block_len - 8)], + 0, + ); +} diff --git a/tests/target/issue-1255.rs b/tests/target/issue-1255.rs new file mode 100644 index 000000000000..2d4633844a9f --- /dev/null +++ b/tests/target/issue-1255.rs @@ -0,0 +1,10 @@ +// Test for issue #1255 +// Default annotation incorrectly removed on associated types +#![feature(specialization)] + +trait Trait { + type Type; +} +impl Trait for T { + default type Type = u64; // 'default' should not be removed +} diff --git a/tests/target/issue-1278.rs b/tests/target/issue-1278.rs new file mode 100644 index 000000000000..e25376561a94 --- /dev/null +++ b/tests/target/issue-1278.rs @@ -0,0 +1,9 @@ +// rustfmt-indent_style = "block" + +#![feature(pub_restricted)] + +mod inner_mode { + pub(super) fn func_name(abc: i32) -> i32 { + abc + } +} diff --git a/tests/target/issue-1350.rs b/tests/target/issue-1350.rs new file mode 100644 index 000000000000..2cf65509c931 --- /dev/null +++ b/tests/target/issue-1350.rs @@ -0,0 +1,14 @@ +// rustfmt-max_width: 120 +// rustfmt-comment_width: 110 + +impl Struct { + fn fun() { + let result = match ::deserialize(&json) { + Ok(v) => v, + Err(e) => match ::deserialize(&json) { + Ok(v) => return Err(Error::with_json(v)), + Err(e2) => return Err(Error::with_json(e)), + }, + }; + } +} diff --git a/tests/target/issue-1366.rs b/tests/target/issue-1366.rs new file mode 100644 index 000000000000..eee147baab95 --- /dev/null +++ b/tests/target/issue-1366.rs @@ -0,0 +1,13 @@ +fn main() { + fn f() -> Option { + Some("fffffffsssssssssddddssssfffffddddff") + .map(|s| s) + .map(|s| s.to_string()) + .map(|res| match Some(res) { + Some(ref s) if s == "" => 41, + Some(_) => 42, + _ => 43, + }) + } + println!("{:?}", f()) +} diff --git a/tests/target/issue-1397.rs b/tests/target/issue-1397.rs new file mode 100644 index 000000000000..86b7a78411f3 --- /dev/null +++ b/tests/target/issue-1397.rs @@ -0,0 +1,25 @@ +pub enum TransactionState { + Committed(i64), +} + +pub enum Packet { + Transaction { state: TransactionState }, +} + +fn baz(p: Packet) { + loop { + loop { + loop { + loop { + if let Packet::Transaction { + state: TransactionState::Committed(ts, ..), + .. + } = p + { + unreachable!() + } + } + } + } + } +} diff --git a/tests/target/issue-1468.rs b/tests/target/issue-1468.rs new file mode 100644 index 000000000000..4c14a0f746a0 --- /dev/null +++ b/tests/target/issue-1468.rs @@ -0,0 +1,29 @@ +fn issue1468() { + euc_jp_decoder_functions!({ + let trail_minus_offset = byte.wrapping_sub(0xA1); + // Fast-track Hiragana (60% according to Lunde) + // and Katakana (10% according to Lunde). + if jis0208_lead_minus_offset == 0x03 && trail_minus_offset < 0x53 { + // Hiragana + handle.write_upper_bmp(0x3041 + trail_minus_offset as u16) + } else if jis0208_lead_minus_offset == 0x04 && trail_minus_offset < 0x56 { + // Katakana + handle.write_upper_bmp(0x30A1 + trail_minus_offset as u16) + } else if trail_minus_offset > (0xFE - 0xA1) { + if byte < 0x80 { + return ( + DecoderResult::Malformed(1, 0), + unread_handle_trail.unread(), + handle.written(), + ); + } + return ( + DecoderResult::Malformed(2, 0), + unread_handle_trail.consumed(), + handle.written(), + ); + } else { + unreachable!(); + } + }); +} diff --git a/tests/target/issue-1598.rs b/tests/target/issue-1598.rs new file mode 100644 index 000000000000..c7e02c961a65 --- /dev/null +++ b/tests/target/issue-1598.rs @@ -0,0 +1,6 @@ +fn main() { + //foo + /* + */ + format!("hello"); +} diff --git a/tests/target/issue-1624.rs b/tests/target/issue-1624.rs new file mode 100644 index 000000000000..477fc27353f3 --- /dev/null +++ b/tests/target/issue-1624.rs @@ -0,0 +1,6 @@ +// #1624 +pub unsafe fn some_long_function_name( + arg1: Type1, + arg2: Type2, +) -> (SomeLongTypeName, AnotherLongTypeName, AnotherLongTypeName) { +} diff --git a/tests/target/issue-1681.rs b/tests/target/issue-1681.rs new file mode 100644 index 000000000000..902765302eac --- /dev/null +++ b/tests/target/issue-1681.rs @@ -0,0 +1,21 @@ +// rustfmt-max_width: 80 + +// We would like to surround closure body with block when overflowing the last +// argument of function call if the last argument has condition and without +// block it may go multi lines. +fn foo() { + refmut_map_result(self.cache.borrow_mut(), |cache| { + match cache.entry(cache_key) { + Occupied(entry) => Ok(entry.into_mut()), + Vacant(entry) => { + let statement = { + let sql = try!(entry.key().sql(source)); + prepare_fn(&sql) + }; + + Ok(entry.insert(try!(statement))) + } + } + }) + .map(MaybeCached::Cached) +} diff --git a/tests/target/issue-1693.rs b/tests/target/issue-1693.rs new file mode 100644 index 000000000000..85421a123bb5 --- /dev/null +++ b/tests/target/issue-1693.rs @@ -0,0 +1,10 @@ +fn issue1693() { + let pixel_data = vec![ + ( + f16::from_f32(0.82), + f16::from_f32(1.78), + f16::from_f32(0.21) + ); + 256 * 256 + ]; +} diff --git a/tests/target/issue-1703.rs b/tests/target/issue-1703.rs new file mode 100644 index 000000000000..4079ef4cfca3 --- /dev/null +++ b/tests/target/issue-1703.rs @@ -0,0 +1,9 @@ +// rustfmt should not remove doc comments or comments inside attributes. + +/** +This function has a block doc comment. + */ +fn test_function() {} + +#[foo /* do not remove this! */] +fn foo() {} diff --git a/tests/target/issue-1800.rs b/tests/target/issue-1800.rs new file mode 100644 index 000000000000..06c5cfd05672 --- /dev/null +++ b/tests/target/issue-1800.rs @@ -0,0 +1,3 @@ +#![doc(html_root_url = "http://example.com")] +#[cfg(feature = "foo")] +fn a() {} diff --git a/tests/target/issue-1802.rs b/tests/target/issue-1802.rs new file mode 100644 index 000000000000..ef7ee891059d --- /dev/null +++ b/tests/target/issue-1802.rs @@ -0,0 +1,9 @@ +// rustfmt-tab_spaces: 2 +// rustfmt-max_width: 30 + +enum F { + X { + a: dddddddddddddd, + b: eeeeeeeeeeeeeee, + }, +} diff --git a/tests/target/issue-1824.rs b/tests/target/issue-1824.rs new file mode 100644 index 000000000000..1c4c2db46dcb --- /dev/null +++ b/tests/target/issue-1824.rs @@ -0,0 +1,5 @@ +pub trait Ingredient +where + Self: Send, +{ +} diff --git a/tests/target/issue-1914.rs b/tests/target/issue-1914.rs new file mode 100644 index 000000000000..d2d532af1dc3 --- /dev/null +++ b/tests/target/issue-1914.rs @@ -0,0 +1,7 @@ +// rustfmt-max_width: 80 + +extern "C" { + #[link_name = "_ZN7MyClass26example_check_no_collisionE"] + pub static mut MyClass_example_check_no_collision: + *const ::std::os::raw::c_int; +} diff --git a/tests/target/issue-2025.rs b/tests/target/issue-2025.rs new file mode 100644 index 000000000000..38bf369bea32 --- /dev/null +++ b/tests/target/issue-2025.rs @@ -0,0 +1,4 @@ +// See if rustfmt removes empty lines on top of the file. +pub fn foo() { + println!("hello, world"); +} diff --git a/tests/target/issue-2103.rs b/tests/target/issue-2103.rs new file mode 100644 index 000000000000..5a043d54b75b --- /dev/null +++ b/tests/target/issue-2103.rs @@ -0,0 +1,14 @@ +struct X +where + i32: Sized, +{ + x: i32, +} + +struct X +// with comment +where + i32: Sized, +{ + x: i32, +} diff --git a/tests/target/issue-2111.rs b/tests/target/issue-2111.rs new file mode 100644 index 000000000000..42c1862e8864 --- /dev/null +++ b/tests/target/issue-2111.rs @@ -0,0 +1,26 @@ +// An import with single line comments. +use super::{ + DelayChoice, + Destinations, + Holding, + LodaModel, + MethodDescription, + ModelBehaviour, + ModelEdges, + ModelProperties, + ModelRequestGraph, + ModelSelector, + RequestDescription, + StringMap, + Switch, + // ModelMetaData, + // Generated, + // SecondsString, + // DateString, + // ModelConfiguration, + // ModelRequests, + // RestResponse, + // RestResponseCode, + // UniformHolding + SCHEMA_VERSIONS, +}; diff --git a/tests/target/issue-2123.rs b/tests/target/issue-2123.rs new file mode 100644 index 000000000000..5e9917b40234 --- /dev/null +++ b/tests/target/issue-2123.rs @@ -0,0 +1,6 @@ +// rustfmt-wrap_comments: true + +//hello +//world + +fn main() {} diff --git a/tests/target/issue-2164.rs b/tests/target/issue-2164.rs new file mode 100644 index 000000000000..dbf92107ce23 --- /dev/null +++ b/tests/target/issue-2164.rs @@ -0,0 +1,135 @@ +// A stress test against code generated by bindgen. +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct emacs_env_25 { + pub size: isize, + pub private_members: *mut emacs_env_private, + pub make_global_ref: ::std::option::Option< + unsafe extern "C" fn(env: *mut emacs_env, any_reference: emacs_value) -> emacs_value, + >, + pub free_global_ref: ::std::option::Option< + unsafe extern "C" fn(env: *mut emacs_env, global_reference: emacs_value), + >, + pub non_local_exit_check: + ::std::option::Option emacs_funcall_exit>, + pub non_local_exit_clear: ::std::option::Option, + pub non_local_exit_get: ::std::option::Option< + unsafe extern "C" fn( + env: *mut emacs_env, + non_local_exit_symbol_out: *mut emacs_value, + non_local_exit_data_out: *mut emacs_value, + ) -> emacs_funcall_exit, + >, + pub non_local_exit_signal: ::std::option::Option< + unsafe extern "C" fn( + env: *mut emacs_env, + non_local_exit_symbol: emacs_value, + non_local_exit_data: emacs_value, + ), + >, + pub non_local_exit_throw: ::std::option::Option< + unsafe extern "C" fn(env: *mut emacs_env, tag: emacs_value, value: emacs_value), + >, + pub make_function: ::std::option::Option< + unsafe extern "C" fn( + env: *mut emacs_env, + min_arity: isize, + max_arity: isize, + function: ::std::option::Option< + unsafe extern "C" fn( + env: *mut emacs_env, + nargs: isize, + args: *mut emacs_value, + arg1: *mut ::libc::c_void, + ) -> emacs_value, + >, + documentation: *const ::libc::c_char, + data: *mut ::libc::c_void, + ) -> emacs_value, + >, + pub funcall: ::std::option::Option< + unsafe extern "C" fn( + env: *mut emacs_env, + function: emacs_value, + nargs: isize, + args: *mut emacs_value, + ) -> emacs_value, + >, + pub intern: ::std::option::Option< + unsafe extern "C" fn( + env: *mut emacs_env, + symbol_name: *const ::libc::c_char, + ) -> emacs_value, + >, + pub type_of: ::std::option::Option< + unsafe extern "C" fn(env: *mut emacs_env, value: emacs_value) -> emacs_value, + >, + pub is_not_nil: ::std::option::Option< + unsafe extern "C" fn(env: *mut emacs_env, value: emacs_value) -> bool, + >, + pub eq: ::std::option::Option< + unsafe extern "C" fn(env: *mut emacs_env, a: emacs_value, b: emacs_value) -> bool, + >, + pub extract_integer: ::std::option::Option< + unsafe extern "C" fn(env: *mut emacs_env, value: emacs_value) -> intmax_t, + >, + pub make_integer: ::std::option::Option< + unsafe extern "C" fn(env: *mut emacs_env, value: intmax_t) -> emacs_value, + >, + pub extract_float: + ::std::option::Option f64>, + pub make_float: + ::std::option::Option emacs_value>, + pub copy_string_contents: ::std::option::Option< + unsafe extern "C" fn( + env: *mut emacs_env, + value: emacs_value, + buffer: *mut ::libc::c_char, + size_inout: *mut isize, + ) -> bool, + >, + pub make_string: ::std::option::Option< + unsafe extern "C" fn( + env: *mut emacs_env, + contents: *const ::libc::c_char, + length: isize, + ) -> emacs_value, + >, + pub make_user_ptr: ::std::option::Option< + unsafe extern "C" fn( + env: *mut emacs_env, + fin: ::std::option::Option, + ptr: *mut ::libc::c_void, + ) -> emacs_value, + >, + pub get_user_ptr: ::std::option::Option< + unsafe extern "C" fn(env: *mut emacs_env, uptr: emacs_value) -> *mut ::libc::c_void, + >, + pub set_user_ptr: ::std::option::Option< + unsafe extern "C" fn(env: *mut emacs_env, uptr: emacs_value, ptr: *mut ::libc::c_void), + >, + pub get_user_finalizer: ::std::option::Option< + unsafe extern "C" fn( + arg1: *mut ::libc::c_void, + env: *mut emacs_env, + uptr: emacs_value, + ) -> ::std::option::Option< + unsafe extern "C" fn(arg1: *mut ::libc::c_void, env: *mut emacs_env, uptr: emacs_value), + >, + >, + pub set_user_finalizer: ::std::option::Option< + unsafe extern "C" fn( + env: *mut emacs_env, + uptr: emacs_value, + fin: ::std::option::Option, + ), + >, + pub vec_get: ::std::option::Option< + unsafe extern "C" fn(env: *mut emacs_env, vec: emacs_value, i: isize) -> emacs_value, + >, + pub vec_set: ::std::option::Option< + unsafe extern "C" fn(env: *mut emacs_env, vec: emacs_value, i: isize, val: emacs_value), + >, + pub vec_size: + ::std::option::Option isize>, +} diff --git a/tests/target/issue-2179/one.rs b/tests/target/issue-2179/one.rs new file mode 100644 index 000000000000..3f98acc8dcde --- /dev/null +++ b/tests/target/issue-2179/one.rs @@ -0,0 +1,37 @@ +// rustfmt-version: One +// rustfmt-error_on_line_overflow: false + +fn issue_2179() { + let (opts, rustflags, clear_env_rust_log) = { + // We mustn't lock configuration for the whole build process + let rls_config = rls_config.lock().unwrap(); + + let opts = CargoOptions::new(&rls_config); + trace!("Cargo compilation options:\n{:?}", opts); + let rustflags = prepare_cargo_rustflags(&rls_config); + + // Warn about invalid specified bin target or package depending on current mode + // TODO: Return client notifications along with diagnostics to inform the user + if !rls_config.workspace_mode { + let cur_pkg_targets = ws.current().unwrap().targets(); + + if let &Some(ref build_bin) = rls_config.build_bin.as_ref() { + let mut bins = cur_pkg_targets.iter().filter(|x| x.is_bin()); + if let None = bins.find(|x| x.name() == build_bin) { + warn!( + "cargo - couldn't find binary `{}` specified in `build_bin` configuration", + build_bin + ); + } + } + } else { + for package in &opts.package { + if let None = ws.members().find(|x| x.name() == package) { + warn!("cargo - couldn't find member package `{}` specified in `analyze_package` configuration", package); + } + } + } + + (opts, rustflags, rls_config.clear_env_rust_log) + }; +} diff --git a/tests/target/issue-2179/two.rs b/tests/target/issue-2179/two.rs new file mode 100644 index 000000000000..96531509ea2d --- /dev/null +++ b/tests/target/issue-2179/two.rs @@ -0,0 +1,40 @@ +// rustfmt-version: Two +// rustfmt-error_on_line_overflow: false + +fn issue_2179() { + let (opts, rustflags, clear_env_rust_log) = { + // We mustn't lock configuration for the whole build process + let rls_config = rls_config.lock().unwrap(); + + let opts = CargoOptions::new(&rls_config); + trace!("Cargo compilation options:\n{:?}", opts); + let rustflags = prepare_cargo_rustflags(&rls_config); + + // Warn about invalid specified bin target or package depending on current mode + // TODO: Return client notifications along with diagnostics to inform the user + if !rls_config.workspace_mode { + let cur_pkg_targets = ws.current().unwrap().targets(); + + if let &Some(ref build_bin) = rls_config.build_bin.as_ref() { + let mut bins = cur_pkg_targets.iter().filter(|x| x.is_bin()); + if let None = bins.find(|x| x.name() == build_bin) { + warn!( + "cargo - couldn't find binary `{}` specified in `build_bin` configuration", + build_bin + ); + } + } + } else { + for package in &opts.package { + if let None = ws.members().find(|x| x.name() == package) { + warn!( + "cargo - couldn't find member package `{}` specified in `analyze_package` configuration", + package + ); + } + } + } + + (opts, rustflags, rls_config.clear_env_rust_log) + }; +} diff --git a/tests/target/issue-2197.rs b/tests/target/issue-2197.rs new file mode 100644 index 000000000000..d42c08e19dbc --- /dev/null +++ b/tests/target/issue-2197.rs @@ -0,0 +1,17 @@ +// rustfmt-max_width: 79 +// rustfmt-wrap_comments: true + +/// ```rust +/// unsafe fn sum_sse2(x: i32x4) -> i32 { +/// let x = vendor::_mm_add_epi32( +/// x, +/// vendor::_mm_srli_si128(x.into(), 8).into(), +/// ); +/// let x = vendor::_mm_add_epi32( +/// x, +/// vendor::_mm_srli_si128(x.into(), 4).into(), +/// ); +/// vendor::_mm_cvtsi128_si32(x) +/// } +/// ``` +fn foo() {} diff --git a/tests/target/issue-2256.rs b/tests/target/issue-2256.rs new file mode 100644 index 000000000000..0a59c308394e --- /dev/null +++ b/tests/target/issue-2256.rs @@ -0,0 +1,7 @@ +// こんにちは +use std::borrow::Cow; + +/* comment 1 */ +/* comment 2 */ + +/* comment 3 */ diff --git a/tests/target/issue-2324.rs b/tests/target/issue-2324.rs new file mode 100644 index 000000000000..9211b24d81f4 --- /dev/null +++ b/tests/target/issue-2324.rs @@ -0,0 +1,7 @@ +// nested function calls with cast. +fn main() { + self.ptr + .set(intrinsics::arith_offset(self.ptr.get() as *mut u8, 1) as *mut T); + self.ptr + .set(intrinsics::arith_offset(self.ptr.get(), mem::size_of::() as isize) as *mut u8); +} diff --git a/tests/target/issue-2329.rs b/tests/target/issue-2329.rs new file mode 100644 index 000000000000..e36e9546b245 --- /dev/null +++ b/tests/target/issue-2329.rs @@ -0,0 +1,30 @@ +// Comments with characters which must be represented by multibyte. + +// フー +use foo; +// バー +use bar; + +impl MyStruct { + // コメント + fn f1() {} // こんにちは + fn f2() {} // ありがとう + // コメント +} + +trait MyTrait { + // コメント + fn f1() {} // こんにちは + fn f2() {} // ありがとう + // コメント +} + +fn main() { + // コメント + let x = 1; // X + println!( + "x = {}", // xの値 + x, // X + ); + // コメント +} diff --git a/tests/target/issue-2342.rs b/tests/target/issue-2342.rs new file mode 100644 index 000000000000..f9c26857e15c --- /dev/null +++ b/tests/target/issue-2342.rs @@ -0,0 +1,6 @@ +// rustfmt-max_width: 80 + +struct Foo { + #[cfg(feature = "serde")] + bytes: [[u8; 17]; 5], // Same size as signature::ED25519_PKCS8_V2_LEN +} diff --git a/tests/target/issue-2346.rs b/tests/target/issue-2346.rs new file mode 100644 index 000000000000..07817221a510 --- /dev/null +++ b/tests/target/issue-2346.rs @@ -0,0 +1,4 @@ +// rustfmt-normalize_comments: true +// the following empty comment should not have any trailing space added. +// +fn main() {} diff --git a/tests/target/issue-2401.rs b/tests/target/issue-2401.rs new file mode 100644 index 000000000000..ec8f27b732ff --- /dev/null +++ b/tests/target/issue-2401.rs @@ -0,0 +1,7 @@ +// rustfmt-hard_tabs = true +// rustfmt-normalize_comments = true + +/// ``` +/// println!("Hello, World!"); +/// ``` +fn main() {} diff --git a/tests/target/issue-2445.rs b/tests/target/issue-2445.rs new file mode 100644 index 000000000000..463c5d495768 --- /dev/null +++ b/tests/target/issue-2445.rs @@ -0,0 +1,21 @@ +test!(RunPassPretty { + // comment + path: "tests/run-pass/pretty", + mode: "pretty", + suite: "run-pass", + default: false, + host: true // should, force, , no trailing comma here +}); + +test!(RunPassPretty { + // comment + path: "tests/run-pass/pretty", + mode: "pretty", + suite: "run-pass", + default: false, + host: true, // should, , preserve, the trailing comma +}); + +test!(Test { + field: i32, // comment +}); diff --git a/tests/target/issue-2446.rs b/tests/target/issue-2446.rs new file mode 100644 index 000000000000..be62e9c9c311 --- /dev/null +++ b/tests/target/issue-2446.rs @@ -0,0 +1,9 @@ +enum Issue2446 { + V { + f: u8, // x + }, +} + +enum Issue2446TrailingCommentsOnly { + V { f: u8 /* */ }, +} diff --git a/tests/target/issue-2479.rs b/tests/target/issue-2479.rs new file mode 100644 index 000000000000..3683ab220895 --- /dev/null +++ b/tests/target/issue-2479.rs @@ -0,0 +1,12 @@ +// Long attributes. +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum POLARITYR { + #[doc = "Task mode: No effect on pin from OUT[n] task. Event mode: no IN[n] event generated on pin activity."] + NONE, + #[doc = "Task mode: Set pin from OUT[n] task. Event mode: Generate IN[n] event when rising edge on pin."] + LOTOHI, + #[doc = "Task mode: Clear pin from OUT[n] task. Event mode: Generate IN[n] event when falling edge on pin."] + HITOLO, + #[doc = "Task mode: Toggle pin from OUT[n]. Event mode: Generate IN[n] when any change on pin."] + TOGGLE, +} diff --git a/tests/target/issue-2482/a.rs b/tests/target/issue-2482/a.rs new file mode 100644 index 000000000000..fbbcb52a878a --- /dev/null +++ b/tests/target/issue-2482/a.rs @@ -0,0 +1,9 @@ +// rustfmt-reorder_modules: true + +// Do not reorder inline modules. + +mod c; +mod a { + fn a() {} +} +mod b; diff --git a/tests/target/issue-2482/b.rs b/tests/target/issue-2482/b.rs new file mode 100644 index 000000000000..40a8d942126c --- /dev/null +++ b/tests/target/issue-2482/b.rs @@ -0,0 +1 @@ +pub fn b() {} diff --git a/tests/target/issue-2482/c.rs b/tests/target/issue-2482/c.rs new file mode 100644 index 000000000000..d937545511ef --- /dev/null +++ b/tests/target/issue-2482/c.rs @@ -0,0 +1 @@ +pub fn c() {} diff --git a/tests/target/issue-2496.rs b/tests/target/issue-2496.rs new file mode 100644 index 000000000000..60c4f55ddffd --- /dev/null +++ b/tests/target/issue-2496.rs @@ -0,0 +1,14 @@ +// rustfmt-indent_style: Visual +fn main() { + match option { + None => some_function(first_reasonably_long_argument, + second_reasonably_long_argument), + } +} + +fn main() { + match option { + None => some_function(first_reasonably_long_argument, + second_reasonably_long_argument), + } +} diff --git a/tests/target/issue-2520.rs b/tests/target/issue-2520.rs new file mode 100644 index 000000000000..7c134d3972ba --- /dev/null +++ b/tests/target/issue-2520.rs @@ -0,0 +1,13 @@ +// rustfmt-normalize_comments: true +// rustfmt-format_code_in_doc_comments: true + +//! ```rust +//! println!("hello, world"); +//! ``` + +#![deny(missing_docs)] + +//! ```rust +//! println!("hello, world"); + +#![deny(missing_docs)] diff --git a/tests/target/issue-2523.rs b/tests/target/issue-2523.rs new file mode 100644 index 000000000000..612f93249acf --- /dev/null +++ b/tests/target/issue-2523.rs @@ -0,0 +1,20 @@ +// rustfmt-normalize_comments: true +// rustfmt-format_code_in_doc_comments: true + +// Do not unindent macro calls in comment with unformattable syntax. +//! ```rust +//! let x = 3 ; +//! some_macro!(pub fn fn foo() ( +//! println!("Don't unindent me!"); +//! )); +//! ``` + +// Format items that appear as arguments of macro call. +//! ```rust +//! let x = 3; +//! some_macro!( +//! pub fn foo() { +//! println!("Don't unindent me!"); +//! } +//! ); +//! ``` diff --git a/tests/target/issue-2526.rs b/tests/target/issue-2526.rs new file mode 100644 index 000000000000..7dd58aba3187 --- /dev/null +++ b/tests/target/issue-2526.rs @@ -0,0 +1,8 @@ +// Test that rustfmt will not warn about comments exceeding max width around lifetime. +// See #2526. + +// comment comment comment comment comment comment comment comment comment comment comment comment comment +fn foo() -> F<'a> { + bar() +} +// comment comment comment comment comment comment comment comment comment comment comment comment comment diff --git a/tests/target/issue-2534/format_macro_matchers_false.rs b/tests/target/issue-2534/format_macro_matchers_false.rs new file mode 100644 index 000000000000..2038ed7f1d01 --- /dev/null +++ b/tests/target/issue-2534/format_macro_matchers_false.rs @@ -0,0 +1,6 @@ +// rustfmt-format_macro_matchers: false + +macro_rules! foo { + ($a:ident : $b:ty) => {}; + ($a:ident $b:ident $c:ident) => {}; +} diff --git a/tests/target/issue-2534/format_macro_matchers_true.rs b/tests/target/issue-2534/format_macro_matchers_true.rs new file mode 100644 index 000000000000..01d939add4dc --- /dev/null +++ b/tests/target/issue-2534/format_macro_matchers_true.rs @@ -0,0 +1,6 @@ +// rustfmt-format_macro_matchers: true + +macro_rules! foo { + ($a:ident : $b:ty) => {}; + ($a:ident $b:ident $c:ident) => {}; +} diff --git a/tests/target/issue-2551.rs b/tests/target/issue-2551.rs new file mode 100644 index 000000000000..d7b0d625b9e9 --- /dev/null +++ b/tests/target/issue-2551.rs @@ -0,0 +1,3 @@ +mcro!(func(A { + a: 12345667800111111111111, +})); diff --git a/tests/target/issue-2554.rs b/tests/target/issue-2554.rs new file mode 100644 index 000000000000..d5f0563a6f10 --- /dev/null +++ b/tests/target/issue-2554.rs @@ -0,0 +1,13 @@ +// #2554 +// Do not add the beginning vert to the first match arm's pattern. + +fn main() { + match foo(|_| { + bar(|_| { + // + }) + }) { + Ok(()) => (), + Err(_) => (), + } +} diff --git a/tests/target/issue-2582.rs b/tests/target/issue-2582.rs new file mode 100644 index 000000000000..f328e4d9d04c --- /dev/null +++ b/tests/target/issue-2582.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/tests/target/issue-2641.rs b/tests/target/issue-2641.rs new file mode 100644 index 000000000000..fbf5326c330d --- /dev/null +++ b/tests/target/issue-2641.rs @@ -0,0 +1,3 @@ +macro_rules! a { + () => {{}}; +} diff --git a/tests/target/issue-2644.rs b/tests/target/issue-2644.rs new file mode 100644 index 000000000000..a87e4c0b4fee --- /dev/null +++ b/tests/target/issue-2644.rs @@ -0,0 +1,8 @@ +// rustfmt-max_width: 80 +fn foo(e: Enum) { + match e { + Enum::Var { element1, element2 } => { + return; + } + } +} diff --git a/tests/target/issue-2673-nonmodrs-mods/foo.rs b/tests/target/issue-2673-nonmodrs-mods/foo.rs new file mode 100644 index 000000000000..5340816d61e0 --- /dev/null +++ b/tests/target/issue-2673-nonmodrs-mods/foo.rs @@ -0,0 +1,4 @@ +// rustfmt-config: skip_children.toml +mod bar; + +mod baz {} diff --git a/tests/target/issue-2673-nonmodrs-mods/foo/bar.rs b/tests/target/issue-2673-nonmodrs-mods/foo/bar.rs new file mode 100644 index 000000000000..9ceacd59d86f --- /dev/null +++ b/tests/target/issue-2673-nonmodrs-mods/foo/bar.rs @@ -0,0 +1 @@ +fn dummy() {} diff --git a/tests/target/issue-2673-nonmodrs-mods/lib.rs b/tests/target/issue-2673-nonmodrs-mods/lib.rs new file mode 100644 index 000000000000..82425de565a2 --- /dev/null +++ b/tests/target/issue-2673-nonmodrs-mods/lib.rs @@ -0,0 +1,6 @@ +#![feature(non_modrs_mods)] + +// Test that submodules in non-mod.rs files work. This is just an idempotence +// test since we just want to verify that rustfmt doesn't fail. + +mod foo; diff --git a/tests/target/issue-2728.rs b/tests/target/issue-2728.rs new file mode 100644 index 000000000000..6cb41b75b6d0 --- /dev/null +++ b/tests/target/issue-2728.rs @@ -0,0 +1,8 @@ +// rustfmt-wrap_comments: true +// rustfmt-newline_style: Windows + +//! ```rust +//! extern crate uom; +//! ``` + +fn main() {} diff --git a/tests/target/issue-2759.rs b/tests/target/issue-2759.rs new file mode 100644 index 000000000000..b7176ec66f85 --- /dev/null +++ b/tests/target/issue-2759.rs @@ -0,0 +1,65 @@ +// rustfmt-wrap_comments: true +// rustfmt-max_width: 89 + +// Code block in doc comments that will exceed max width. +/// ```rust +/// extern crate actix_web; +/// use actix_web::{actix, server, App, HttpResponse}; +/// +/// fn main() { +/// // Run actix system, this method actually starts all async processes +/// actix::System::run(|| { +/// server::new(|| App::new().resource("/", |r| r.h(|_| HttpResponse::Ok()))) +/// .bind("127.0.0.1:0") +/// .expect("Can not bind to 127.0.0.1:0") +/// .start(); +/// # actix::Arbiter::system().do_send(actix::msgs::SystemExit(0)); +/// }); +/// } +/// ``` +fn foo() {} + +// Code block in doc comments without the closing '```'. +/// ```rust +/// # extern crate actix_web; +/// use actix_web::{App, HttpResponse, http}; +/// +/// fn main() { +/// let app = App::new() +/// .resource( +/// "/", |r| r.method(http::Method::GET).f(|r| HttpResponse::Ok())) +/// .finish(); +/// } +fn bar() {} + +// `#` with indent. +/// ```rust +/// # use std::thread; +/// # extern crate actix_web; +/// use actix_web::{server, App, HttpResponse}; +/// +/// struct State1; +/// +/// struct State2; +/// +/// fn main() { +/// # thread::spawn(|| { +/// server::new(|| { +/// vec![ +/// App::with_state(State1) +/// .prefix("/app1") +/// .resource("/", |r| r.f(|r| HttpResponse::Ok())) +/// .boxed(), +/// App::with_state(State2) +/// .prefix("/app2") +/// .resource("/", |r| r.f(|r| HttpResponse::Ok())) +/// .boxed(), +/// ] +/// }) +/// .bind("127.0.0.1:8080") +/// .unwrap() +/// .run() +/// # }); +/// } +/// ``` +fn foobar() {} diff --git a/tests/target/issue-2761.rs b/tests/target/issue-2761.rs new file mode 100644 index 000000000000..ae4086617f81 --- /dev/null +++ b/tests/target/issue-2761.rs @@ -0,0 +1,15 @@ +const DATA: &'static [u8] = &[ + 0x42, 0x50, 0x54, 0x44, //type + 0x23, 0x00, 0x00, 0x00, //size + 0x00, 0x00, 0x04, 0x00, //flags + 0xEC, 0x0C, 0x00, 0x00, //id + 0x00, 0x00, 0x00, 0x00, //revision + 0x2B, 0x00, //version + 0x00, 0x00, //unknown + 0x42, 0x50, 0x54, 0x4E, //field type + 0x1D, 0x00, //field size + 0x19, 0x00, 0x00, 0x00, //decompressed field size + 0x75, 0xc5, 0x21, 0x0d, 0x00, 0x00, 0x08, 0x05, 0xd1, 0x6c, //field data (compressed) + 0x6c, 0xdc, 0x57, 0x48, 0x3c, 0xfd, 0x5b, 0x5c, 0x02, 0xd4, //field data (compressed) + 0x6b, 0x32, 0xb5, 0xdc, 0xa3, //field data (compressed) +]; diff --git a/tests/target/issue-2781.rs b/tests/target/issue-2781.rs new file mode 100644 index 000000000000..f144d716be96 --- /dev/null +++ b/tests/target/issue-2781.rs @@ -0,0 +1,11 @@ +pub // Oh, no. A line comment. +struct Foo {} + +pub /* Oh, no. A block comment. */ struct Foo {} + +mod inner { + pub // Oh, no. A line comment. + struct Foo {} + + pub /* Oh, no. A block comment. */ struct Foo {} +} diff --git a/tests/target/issue-2794.rs b/tests/target/issue-2794.rs new file mode 100644 index 000000000000..951c0af206d8 --- /dev/null +++ b/tests/target/issue-2794.rs @@ -0,0 +1,12 @@ +// rustfmt-indent_style: Block +// rustfmt-imports_indent: Block +// rustfmt-imports_layout: Vertical + +use std::{ + env, + fs, + io::{ + Read, + Write, + }, +}; diff --git a/tests/target/issue-2810.rs b/tests/target/issue-2810.rs new file mode 100644 index 000000000000..34140c7a1fc6 --- /dev/null +++ b/tests/target/issue-2810.rs @@ -0,0 +1,14 @@ +// rustfmt-newline_style: Windows + +#[macro_export] +macro_rules! hmmm___ffi_error { + ($result:ident) => { + pub struct $result { + success: bool, + } + + impl $result { + pub fn foo(self) {} + } + }; +} diff --git a/tests/target/issue-2835.rs b/tests/target/issue-2835.rs new file mode 100644 index 000000000000..21e8ce411454 --- /dev/null +++ b/tests/target/issue-2835.rs @@ -0,0 +1,4 @@ +// rustfmt-brace_style: AlwaysNextLine +// rustfmt-fn_single_line: true + +fn lorem() -> i32 { 42 } diff --git a/tests/target/issue-2863.rs b/tests/target/issue-2863.rs new file mode 100644 index 000000000000..35a80f7a621e --- /dev/null +++ b/tests/target/issue-2863.rs @@ -0,0 +1,54 @@ +// rustfmt-reorder_impl_items: true + +impl IntoIterator for SafeVec { + type Bar = u32; + type BarFoo = u32; + type FooBar = u32; + // comment on FoooooBar + type FoooooBar = u32; + type IntoIter = self::IntoIter; + type Item = T; + + type E = impl Trait; + type F = impl Trait; + + const AnotherConst: i32 = 100; + const SomeConst: i32 = 100; + + // comment on foo() + fn foo() { + println!("hello, world"); + } + + fn foo1() { + println!("hello, world"); + } + + fn foo2() { + println!("hello, world"); + } + + fn foo3() { + println!("hello, world"); + } + + fn foo4() { + println!("hello, world"); + } + + fn foo5() { + println!("hello, world"); + } + + fn foo6() { + println!("hello, world"); + } + + fn foo7() { + println!("hello, world"); + } + + fn foo8() { + println!("hello, world"); + } +} diff --git a/tests/target/issue-2869.rs b/tests/target/issue-2869.rs new file mode 100644 index 000000000000..6a68c2d95faa --- /dev/null +++ b/tests/target/issue-2869.rs @@ -0,0 +1,41 @@ +// rustfmt-struct_field_align_threshold: 50 + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "PascalCase")] +struct AuditLog1 { + creation_time: String, + id: String, + operation: String, + organization_id: String, + record_type: u32, + result_status: Option, + #[serde(rename = "ClientIP")] + client_ip: Option, + object_id: String, + actor: Option>, + actor_context_id: Option, + actor_ip_address: Option, + azure_active_directory_event_type: Option, + + #[serde(rename = "very")] + aaaaa: String, + #[serde(rename = "cool")] + bb: i32, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "PascalCase")] +struct AuditLog2 { + creation_time: String, + id: String, + operation: String, + organization_id: String, + record_type: u32, + result_status: Option, + client_ip: Option, + object_id: String, + actor: Option>, + actor_context_id: Option, + actor_ip_address: Option, + azure_active_directory_event_type: Option, +} diff --git a/tests/target/issue-2896.rs b/tests/target/issue-2896.rs new file mode 100644 index 000000000000..6fb6b12ed31d --- /dev/null +++ b/tests/target/issue-2896.rs @@ -0,0 +1,165 @@ +extern crate differential_dataflow; +extern crate rand; +extern crate timely; + +use rand::{Rng, SeedableRng, StdRng}; + +use timely::dataflow::operators::*; + +use differential_dataflow::input::InputSession; +use differential_dataflow::operators::*; +use differential_dataflow::AsCollection; + +// mod loglikelihoodratio; + +fn main() { + // define a new timely dataflow computation. + timely::execute_from_args(std::env::args().skip(6), move |worker| { + // capture parameters of the experiment. + let users: usize = std::env::args().nth(1).unwrap().parse().unwrap(); + let items: usize = std::env::args().nth(2).unwrap().parse().unwrap(); + let scale: usize = std::env::args().nth(3).unwrap().parse().unwrap(); + let batch: usize = std::env::args().nth(4).unwrap().parse().unwrap(); + let noisy: bool = std::env::args().nth(5).unwrap() == "noisy"; + + let index = worker.index(); + let peers = worker.peers(); + + let (input, probe) = worker.dataflow(|scope| { + // input of (user, item) collection. + let (input, occurrences) = scope.new_input(); + let occurrences = occurrences.as_collection(); + + //TODO adjust code to only work with upper triangular half of cooccurrence matrix + + /* Compute the cooccurrence matrix C = A'A from the binary interaction matrix A. */ + let cooccurrences = occurrences + .join_map(&occurrences, |_user, &item_a, &item_b| (item_a, item_b)) + .filter(|&(item_a, item_b)| item_a != item_b) + .count(); + + /* compute the rowsums of C indicating how often we encounter individual items. */ + let row_sums = occurrences.map(|(_user, item)| item).count(); + + // row_sums.inspect(|record| println!("[row_sums] {:?}", record)); + + /* Join the cooccurrence pairs with the corresponding row sums. */ + let mut cooccurrences_with_row_sums = cooccurrences + .map(|((item_a, item_b), num_cooccurrences)| (item_a, (item_b, num_cooccurrences))) + .join_map( + &row_sums, + |&item_a, &(item_b, num_cooccurrences), &row_sum_a| { + assert!(row_sum_a > 0); + (item_b, (item_a, num_cooccurrences, row_sum_a)) + }, + ) + .join_map( + &row_sums, + |&item_b, &(item_a, num_cooccurrences, row_sum_a), &row_sum_b| { + assert!(row_sum_a > 0); + assert!(row_sum_b > 0); + (item_a, (item_b, num_cooccurrences, row_sum_a, row_sum_b)) + }, + ); + + // cooccurrences_with_row_sums + // .inspect(|record| println!("[cooccurrences_with_row_sums] {:?}", record)); + + // //TODO compute top-k "similar items" per item + // /* Compute LLR scores for each item pair. */ + // let llr_scores = cooccurrences_with_row_sums.map( + // |(item_a, (item_b, num_cooccurrences, row_sum_a, row_sum_b))| { + + // println!( + // "[llr_scores] item_a={} item_b={}, num_cooccurrences={} row_sum_a={} row_sum_b={}", + // item_a, item_b, num_cooccurrences, row_sum_a, row_sum_b); + + // let k11: isize = num_cooccurrences; + // let k12: isize = row_sum_a as isize - k11; + // let k21: isize = row_sum_b as isize - k11; + // let k22: isize = 10000 - k12 - k21 + k11; + + // let llr_score = loglikelihoodratio::log_likelihood_ratio(k11, k12, k21, k22); + + // ((item_a, item_b), llr_score) + // }); + + if noisy { + cooccurrences_with_row_sums = + cooccurrences_with_row_sums.inspect(|x| println!("change: {:?}", x)); + } + + let probe = cooccurrences_with_row_sums.probe(); + /* + // produce the (item, item) collection + let cooccurrences = occurrences + .join_map(&occurrences, |_user, &item_a, &item_b| (item_a, item_b)); + // count the occurrences of each item. + let counts = cooccurrences + .map(|(item_a,_)| item_a) + .count(); + // produce ((item1, item2), count1, count2, count12) tuples + let cooccurrences_with_counts = cooccurrences + .join_map(&counts, |&item_a, &item_b, &count_item_a| (item_b, (item_a, count_item_a))) + .join_map(&counts, |&item_b, &(item_a, count_item_a), &count_item_b| { + ((item_a, item_b), count_item_a, count_item_b) + }); + let probe = cooccurrences_with_counts + .inspect(|x| println!("change: {:?}", x)) + .probe(); + */ + (input, probe) + }); + + let seed: &[_] = &[1, 2, 3, index]; + let mut rng1: StdRng = SeedableRng::from_seed(seed); // rng for edge additions + let mut rng2: StdRng = SeedableRng::from_seed(seed); // rng for edge deletions + + let mut input = InputSession::from(input); + + for count in 0..scale { + if count % peers == index { + let user = rng1.gen_range(0, users); + let item = rng1.gen_range(0, items); + // println!("[INITIAL INPUT] ({}, {})", user, item); + input.insert((user, item)); + } + } + + // load the initial data up! + while probe.less_than(input.time()) { + worker.step(); + } + + for round in 1.. { + for element in (round * batch)..((round + 1) * batch) { + if element % peers == index { + // advance the input timestamp. + input.advance_to(round * batch); + // insert a new item. + let user = rng1.gen_range(0, users); + let item = rng1.gen_range(0, items); + if noisy { + println!("[INPUT: insert] ({}, {})", user, item); + } + input.insert((user, item)); + // remove an old item. + let user = rng2.gen_range(0, users); + let item = rng2.gen_range(0, items); + if noisy { + println!("[INPUT: remove] ({}, {})", user, item); + } + input.remove((user, item)); + } + } + + input.advance_to(round * batch); + input.flush(); + + while probe.less_than(input.time()) { + worker.step(); + } + } + }) + .unwrap(); +} diff --git a/tests/target/issue-2916.rs b/tests/target/issue-2916.rs new file mode 100644 index 000000000000..fb07cc8065ca --- /dev/null +++ b/tests/target/issue-2916.rs @@ -0,0 +1,2 @@ +a_macro!(name, +); diff --git a/tests/target/issue-2917/minimal.rs b/tests/target/issue-2917/minimal.rs new file mode 100644 index 000000000000..e81e1e6a5a80 --- /dev/null +++ b/tests/target/issue-2917/minimal.rs @@ -0,0 +1,8 @@ +macro_rules! foo { + () => { + // comment + /* + + */ + }; +} diff --git a/tests/target/issue-2917/packed_simd.rs b/tests/target/issue-2917/packed_simd.rs new file mode 100644 index 000000000000..274614f83e2a --- /dev/null +++ b/tests/target/issue-2917/packed_simd.rs @@ -0,0 +1,63 @@ +// rustfmt-wrap_comments: true +//! Implements `From` and `Into` for vector types. + +macro_rules! impl_from_vector { + ([$elem_ty:ident; $elem_count:expr]: $id:ident | $test_tt:tt | $source:ident) => { + impl From<$source> for $id { + #[inline] + fn from(source: $source) -> Self { + fn static_assert_same_number_of_lanes() + where + T: crate::sealed::Simd, + U: crate::sealed::Simd, + { + } + use llvm::simd_cast; + static_assert_same_number_of_lanes::<$id, $source>(); + Simd(unsafe { simd_cast(source.0) }) + } + } + + // FIXME: `Into::into` is not inline, but due to + // the blanket impl in `std`, which is not + // marked `default`, we cannot override it here with + // specialization. + /* + impl Into<$id> for $source { + #[inline] + fn into(self) -> $id { + unsafe { simd_cast(self) } + } + } + */ + + test_if! { + $test_tt: + interpolate_idents! { + mod [$id _from_ $source] { + use super::*; + #[test] + fn from() { + assert_eq!($id::lanes(), $source::lanes()); + let source: $source = Default::default(); + let vec: $id = Default::default(); + + let e = $id::from(source); + assert_eq!(e, vec); + + let e: $id = source.into(); + assert_eq!(e, vec); + } + } + } + } + }; +} + +macro_rules! impl_from_vectors { + ([$elem_ty:ident; $elem_count:expr]: $id:ident | $test_tt:tt | $($source:ident),*) => { + $( + impl_from_vector!([$elem_ty; $elem_count]: $id | $test_tt | $source); + )* + } +} diff --git a/tests/target/issue-2922.rs b/tests/target/issue-2922.rs new file mode 100644 index 000000000000..501f78c78b86 --- /dev/null +++ b/tests/target/issue-2922.rs @@ -0,0 +1,10 @@ +// rustfmt-indent_style: Visual +struct Functions { + RunListenServer: unsafe extern "C" fn(*mut c_void, + *mut c_char, + *mut c_char, + *mut c_char, + *mut c_void, + *mut c_void) + -> c_int, +} diff --git a/tests/target/issue-2927-2.rs b/tests/target/issue-2927-2.rs new file mode 100644 index 000000000000..e895783ba8bb --- /dev/null +++ b/tests/target/issue-2927-2.rs @@ -0,0 +1,7 @@ +// rustfmt-edition: 2015 +#![feature(rust_2018_preview, uniform_paths)] +use futures::prelude::*; +use http_03::cli::Cli; +use hyper::{service::service_fn_ok, Body, Response, Server}; +use log::{error, info, log}; +use structopt::StructOpt; diff --git a/tests/target/issue-2927.rs b/tests/target/issue-2927.rs new file mode 100644 index 000000000000..3267be28d7b7 --- /dev/null +++ b/tests/target/issue-2927.rs @@ -0,0 +1,7 @@ +// rustfmt-edition: 2018 +#![feature(rust_2018_preview, uniform_paths)] +use ::log::{error, info, log}; +use futures::prelude::*; +use http_03::cli::Cli; +use hyper::{service::service_fn_ok, Body, Response, Server}; +use structopt::StructOpt; diff --git a/tests/target/issue-2930.rs b/tests/target/issue-2930.rs new file mode 100644 index 000000000000..41e763a7c08c --- /dev/null +++ b/tests/target/issue-2930.rs @@ -0,0 +1,5 @@ +// rustfmt-indent_style: Visual +fn main() { + let (first_variable, second_variable) = (this_is_something_with_an_extraordinarily_long_name, + this_variable_name_is_also_pretty_long); +} diff --git a/tests/target/issue-2936.rs b/tests/target/issue-2936.rs new file mode 100644 index 000000000000..1d6eb6d60527 --- /dev/null +++ b/tests/target/issue-2936.rs @@ -0,0 +1,21 @@ +struct AStruct { + A: u32, + B: u32, + C: u32, +} + +impl Something for AStruct { + fn a_func() { + match a_val { + ContextualParseError::InvalidMediaRule(ref err) => { + let err: &CStr = match err.kind { + ParseErrorKind::Custom(StyleParseErrorKind::MediaQueryExpectedFeatureName( + .., + )) => { + cstr!("PEMQExpectedFeatureName") + } + }; + } + }; + } +} diff --git a/tests/target/issue-2941.rs b/tests/target/issue-2941.rs new file mode 100644 index 000000000000..3c6c702c2ae0 --- /dev/null +++ b/tests/target/issue-2941.rs @@ -0,0 +1,5 @@ +// rustfmt-wrap_comments: true + +//! ``` +//! \ +//! ``` diff --git a/tests/target/issue-2955.rs b/tests/target/issue-2955.rs new file mode 100644 index 000000000000..799cd36e29ac --- /dev/null +++ b/tests/target/issue-2955.rs @@ -0,0 +1,6 @@ +// rustfmt-condense_wildcard_suffixes: true +fn main() { + match (1, 2, 3) { + (..) => (), + } +} diff --git a/tests/target/issue-2973.rs b/tests/target/issue-2973.rs new file mode 100644 index 000000000000..86574bd86686 --- /dev/null +++ b/tests/target/issue-2973.rs @@ -0,0 +1,158 @@ +#[cfg(test)] +mod test { + summary_test! { + tokenize_recipe_interpolation_eol, + "foo: # some comment + {{hello}} +", + "foo: \ + {{hello}} \ + {{ahah}}", + "N:#$>^{N}$<.", + } + + summary_test! { + tokenize_strings, + r#"a = "'a'" + '"b"' + "'c'" + '"d"'#echo hello"#, + r#"N="+'+"+'#."#, + } + + summary_test! { + tokenize_recipe_interpolation_eol, + "foo: # some comment + {{hello}} +", + "N:#$>^{N}$<.", + } + + summary_test! { + tokenize_recipe_interpolation_eof, + "foo: # more comments + {{hello}} +# another comment +", + "N:#$>^{N}$<#$.", + } + + summary_test! { + tokenize_recipe_complex_interpolation_expression, + "foo: #lol\n {{a + b + \"z\" + blarg}}", + "N:#$>^{N+N+\"+N}<.", + } + + summary_test! { + tokenize_recipe_multiple_interpolations, + "foo:,#ok\n {{a}}0{{b}}1{{c}}", + "N:,#$>^{N}_{N}_{N}<.", + } + + summary_test! { + tokenize_junk, + "bob + +hello blah blah blah : a b c #whatever + ", + "N$$NNNN:NNN#$.", + } + + summary_test! { + tokenize_empty_lines, + " +# this does something +hello: + asdf + bsdf + + csdf + + dsdf # whatever + +# yolo + ", + "$#$N:$>^_$^_$$^_$$^_$$<#$.", + } + + summary_test! { + tokenize_comment_before_variable, + " +# +A='1' +echo: + echo {{A}} + ", + "$#$N='$N:$>^_{N}$<.", + } + + summary_test! { + tokenize_interpolation_backticks, + "hello:\n echo {{`echo hello` + `echo goodbye`}}", + "N:$>^_{`+`}<.", + } + + summary_test! { + tokenize_assignment_backticks, + "a = `echo hello` + `echo goodbye`", + "N=`+`.", + } + + summary_test! { + tokenize_multiple, + " +hello: + a + b + + c + + d + +# hello +bob: + frank + ", + + "$N:$>^_$^_$$^_$$^_$$<#$N:$>^_$<.", + } + + summary_test! { + tokenize_comment, + "a:=#", + "N:=#." + } + + summary_test! { + tokenize_comment_with_bang, + "a:=#foo!", + "N:=#." + } + + summary_test! { + tokenize_order, + r" +b: a + @mv a b + +a: + @touch F + @touch a + +d: c + @rm c + +c: b + @mv b c", + "$N:N$>^_$$^_$^_$$^_$$^_<.", + } + + summary_test! { + tokenize_parens, + r"((())) )abc(+", + "((())))N(+.", + } + + summary_test! { + crlf_newline, + "#\r\n#asdf\r\n", + "#$#$.", + } +} diff --git a/tests/target/issue-2976.rs b/tests/target/issue-2976.rs new file mode 100644 index 000000000000..51c94a84ba29 --- /dev/null +++ b/tests/target/issue-2976.rs @@ -0,0 +1,3 @@ +fn a(_ /*comment*/: u8 /* toto */) {} +fn b(/*comment*/ _: u8 /* tata */) {} +fn c(_: /*comment*/ u8) {} diff --git a/tests/target/issue-2977/block.rs b/tests/target/issue-2977/block.rs new file mode 100644 index 000000000000..d376e370c72e --- /dev/null +++ b/tests/target/issue-2977/block.rs @@ -0,0 +1,11 @@ +macro_rules! atomic_bits { + ($ldrex:expr) => { + execute(|| { + asm!($ldrex + : "=r"(raw) + : "r"(address) + : + : "volatile"); + }) + }; +} diff --git a/tests/target/issue-2977/impl.rs b/tests/target/issue-2977/impl.rs new file mode 100644 index 000000000000..8d7bb9414eb7 --- /dev/null +++ b/tests/target/issue-2977/impl.rs @@ -0,0 +1,44 @@ +macro_rules! atomic_bits { + // the println macro cannot be rewritten because of the asm macro + ($type:ty, $ldrex:expr, $strex:expr) => { + impl AtomicBits for $type { + unsafe fn load_excl(address: usize) -> Self { + let raw: $type; + asm!($ldrex + : "=r"(raw) + : "r"(address) + : + : "volatile"); + raw + } + + unsafe fn store_excl(self, address: usize) -> bool { + let status: $type; + println!("{}", + status); + status == 0 + } + } + }; + + // the println macro should be rewritten here + ($type:ty) => { + fn some_func(self) { + let status: $type; + println!("{}", status); + } + }; + + // unrewritale macro in func + ($type:ty, $ldrex:expr) => { + unsafe fn load_excl(address: usize) -> Self { + let raw: $type; + asm!($ldrex + : "=r"(raw) + : "r"(address) + : + : "volatile"); + raw + } + } +} diff --git a/tests/target/issue-2977/item.rs b/tests/target/issue-2977/item.rs new file mode 100644 index 000000000000..857065ca93f7 --- /dev/null +++ b/tests/target/issue-2977/item.rs @@ -0,0 +1,11 @@ +macro_rules! atomic_bits { + ($ldrex:expr) => { + some_macro!(pub fn foo() { + asm!($ldrex + : "=r"(raw) + : "r"(address) + : + : "volatile"); + }) + }; +} diff --git a/tests/target/issue-2977/trait.rs b/tests/target/issue-2977/trait.rs new file mode 100644 index 000000000000..ae20668cd75f --- /dev/null +++ b/tests/target/issue-2977/trait.rs @@ -0,0 +1,44 @@ +macro_rules! atomic_bits { + // the println macro cannot be rewritten because of the asm macro + ($type:ty, $ldrex:expr, $strex:expr) => { + trait $type { + unsafe fn load_excl(address: usize) -> Self { + let raw: $type; + asm!($ldrex + : "=r"(raw) + : "r"(address) + : + : "volatile"); + raw + } + + unsafe fn store_excl(self, address: usize) -> bool { + let status: $type; + println!("{}", + status); + status == 0 + } + } + }; + + // the println macro should be rewritten here + ($type:ty) => { + fn some_func(self) { + let status: $type; + println!("{}", status); + } + }; + + // unrewritale macro in func + ($type:ty, $ldrex:expr) => { + unsafe fn load_excl(address: usize) -> Self { + let raw: $type; + asm!($ldrex + : "=r"(raw) + : "r"(address) + : + : "volatile"); + raw + } + } +} diff --git a/tests/target/issue-2985.rs b/tests/target/issue-2985.rs new file mode 100644 index 000000000000..faad859236dc --- /dev/null +++ b/tests/target/issue-2985.rs @@ -0,0 +1,36 @@ +// rustfmt-indent_style: Visual +fn foo() { + { + { + let extra_encoder_settings = extra_encoder_settings.iter() + .filter_map(|&(name, value)| { + value.split() + .next() + .something() + .something2() + .something3() + .something4() + }); + let extra_encoder_settings = extra_encoder_settings.iter() + .filter_map(|&(name, value)| { + value.split() + .next() + .something() + .something2() + .something3() + .something4() + }) + .something(); + if let Some(subpod) = pod.subpods.iter().find(|s| { + !s.plaintext + .as_ref() + .map(String::as_ref) + .unwrap_or("") + .is_empty() + }) + { + do_something(); + } + } + } +} diff --git a/tests/target/issue-2995.rs b/tests/target/issue-2995.rs new file mode 100644 index 000000000000..890da8def92f --- /dev/null +++ b/tests/target/issue-2995.rs @@ -0,0 +1,7 @@ +fn issue_2995() { + // '\u{2028}' is inserted in the code below. + + [0, 1]; + [0, /* */ 1]; + [0, 1]; +} diff --git a/tests/target/issue-3029.rs b/tests/target/issue-3029.rs new file mode 100644 index 000000000000..a7ac5c32b066 --- /dev/null +++ b/tests/target/issue-3029.rs @@ -0,0 +1,94 @@ +fn keep_if() { + { + { + { + EvaluateJSReply::NumberValue( + if FromJSValConvertible::from_jsval(cx, rval.handle(), ()) { + unimplemented!(); + }, + ) + } + } + } +} + +fn keep_if_let() { + { + { + { + EvaluateJSReply::NumberValue( + if let Some(e) = FromJSValConvertible::from_jsval(cx, rval.handle(), ()) { + unimplemented!(); + }, + ) + } + } + } +} + +fn keep_for() { + { + { + { + EvaluateJSReply::NumberValue( + for conv in FromJSValConvertible::from_jsval(cx, rval.handle(), ()) { + unimplemented!(); + }, + ) + } + } + } +} + +fn keep_loop() { + { + { + { + EvaluateJSReply::NumberValue(loop { + FromJSValConvertible::from_jsval(cx, rval.handle(), ()); + }) + } + } + } +} + +fn keep_while() { + { + { + { + EvaluateJSReply::NumberValue( + while FromJSValConvertible::from_jsval(cx, rval.handle(), ()) { + unimplemented!(); + }, + ) + } + } + } +} + +fn keep_while_let() { + { + { + { + EvaluateJSReply::NumberValue( + while let Some(e) = FromJSValConvertible::from_jsval(cx, rval.handle(), ()) { + unimplemented!(); + }, + ) + } + } + } +} + +fn keep_match() { + { + { + EvaluateJSReply::NumberValue( + match FromJSValConvertible::from_jsval(cx, rval.handle(), ()) { + Ok(ConversionResult::Success(v)) => v, + _ => unreachable!(), + }, + ) + } + } +} diff --git a/tests/target/issue-3032.rs b/tests/target/issue-3032.rs new file mode 100644 index 000000000000..3533a81fbc4a --- /dev/null +++ b/tests/target/issue-3032.rs @@ -0,0 +1,36 @@ +pub fn get_array_index_from_id(_cx: *mut JSContext, id: HandleId) -> Option { + let raw_id = id.into(); + unsafe { + if RUST_JSID_IS_INT(raw_id) { + return Some(RUST_JSID_TO_INT(raw_id) as u32); + } + None + } + // If `id` is length atom, `-1`, otherwise: + /*return if JSID_IS_ATOM(id) { + let atom = JSID_TO_ATOM(id); + //let s = *GetAtomChars(id); + if s > 'a' && s < 'z' { + return -1; + } + + let i = 0; + let str = AtomToLinearString(JSID_TO_ATOM(id)); + return if StringIsArray(str, &mut i) != 0 { i } else { -1 } + } else { + IdToInt32(cx, id); + }*/ +} + +impl Foo { + fn bar() -> usize { + 42 + /* a block comment */ + } + + fn baz() -> usize { + 42 + // this is a line + /* a block comment */ + } +} diff --git a/tests/target/issue-3038.rs b/tests/target/issue-3038.rs new file mode 100644 index 000000000000..3c398b825d7e --- /dev/null +++ b/tests/target/issue-3038.rs @@ -0,0 +1,29 @@ +impl HTMLTableElement { + fn func() { + if number_of_row_elements == 0 { + if let Some(last_tbody) = node + .rev_children() + .filter_map(DomRoot::downcast::) + .find(|n| { + n.is::() && n.local_name() == &local_name!("tbody") + }) + { + last_tbody + .upcast::() + .AppendChild(new_row.upcast::()) + .expect("InsertRow failed to append first row."); + } + } + + if number_of_row_elements == 0 { + if let Some(last_tbody) = node.find(|n| { + n.is::() && n.local_name() == &local_name!("tbody") + }) { + last_tbody + .upcast::() + .AppendChild(new_row.upcast::()) + .expect("InsertRow failed to append first row."); + } + } + } +} diff --git a/tests/target/issue-3043.rs b/tests/target/issue-3043.rs new file mode 100644 index 000000000000..b54e244a4012 --- /dev/null +++ b/tests/target/issue-3043.rs @@ -0,0 +1,5 @@ +// rustfmt-edition: 2018 + +use ::std::vec::Vec; + +fn main() {} diff --git a/tests/target/issue-3049.rs b/tests/target/issue-3049.rs new file mode 100644 index 000000000000..fad15435433d --- /dev/null +++ b/tests/target/issue-3049.rs @@ -0,0 +1,45 @@ +// rustfmt-indent_style: Visual +fn main() { + something.aaaaaaaaaaaa() + .aaaaaaaaaaaa() + .aaaaaaaaaaaa() + .aaaaaaaaaaaa() + .aaaaaaaaaaaa() + .aaaaaaaaaaaa() + .aaaaaaaaaaaa() + .bench_function(|| { + let x = hello(); + }); + + something.aaaaaaaaaaaa() + .aaaaaaaaaaaa() + .aaaaaaaaaaaa() + .aaaaaaaaaaaa() + .aaaaaaaaaaaa() + .aaaaaaaaaaaa() + .aaaaaaaaaaaa() + .bench_function(arg, || { + let x = hello(); + }); + + something.aaaaaaaaaaaa() + .aaaaaaaaaaaa() + .aaaaaaaaaaaa() + .aaaaaaaaaaaa() + .aaaaaaaaaaaa() + .aaaaaaaaaaaa() + .aaaaaaaaaaaa() + .bench_function(arg, + || { + let x = hello(); + }, + arg); + + AAAAAAAAAAA.function(|| { + let _ = (); + }); + + AAAAAAAAAAA.chain().function(|| { + let _ = (); + }) +} diff --git a/tests/target/issue-3055/backtick.rs b/tests/target/issue-3055/backtick.rs new file mode 100644 index 000000000000..f5bae8d3db4b --- /dev/null +++ b/tests/target/issue-3055/backtick.rs @@ -0,0 +1,10 @@ +// rustfmt-wrap_comments: true + +/// Simple block +/// +/// ```text +/// ` +/// ``` +fn main() { + println!("Hello, world!"); +} diff --git a/tests/target/issue-3055/empty-code-block.rs b/tests/target/issue-3055/empty-code-block.rs new file mode 100644 index 000000000000..566f7ef9b6ea --- /dev/null +++ b/tests/target/issue-3055/empty-code-block.rs @@ -0,0 +1,18 @@ +// rustfmt-wrap_comments: true + +/// Simple block +/// +/// ``` +/// ``` +/// +/// ```no_run +/// ``` +/// +/// ```should_panic +/// ``` +/// +/// ```compile_fail +/// ``` +fn main() { + println!("Hello, world!"); +} diff --git a/tests/target/issue-3055/original.rs b/tests/target/issue-3055/original.rs new file mode 100644 index 000000000000..2df6adbb5e2e --- /dev/null +++ b/tests/target/issue-3055/original.rs @@ -0,0 +1,42 @@ +// rustfmt-wrap_comments: true +// rustfmt-format_code_in_doc_comments: true + +/// Vestibulum elit nibh, rhoncus non, euismod sit amet, pretium eu, enim. Nunc +/// commodo ultricies dui. +/// +/// Should not format with text attribute +/// ```text +/// .--------------. +/// | v +/// Park <- Idle -> Poll -> Probe -> Download -> Install -> Reboot +/// ^ ^ ' ' ' +/// ' ' ' ' ' +/// ' `--------' ' ' +/// `---------------' ' ' +/// `--------------------------' ' +/// `-------------------------------------' +/// ``` +/// +/// Should not format with ignore attribute +/// ```text +/// .--------------. +/// | v +/// Park <- Idle -> Poll -> Probe -> Download -> Install -> Reboot +/// ^ ^ ' ' ' +/// ' ' ' ' ' +/// ' `--------' ' ' +/// `---------------' ' ' +/// `--------------------------' ' +/// `-------------------------------------' +/// ``` +/// +/// Should format with rust attribute +/// ```rust +/// let x = 42; +/// ``` +/// +/// Should format with no attribute as it defaults to rust +/// ``` +/// let x = 42; +/// ``` +fn func() {} diff --git a/tests/target/issue-3059.rs b/tests/target/issue-3059.rs new file mode 100644 index 000000000000..f750c1287595 --- /dev/null +++ b/tests/target/issue-3059.rs @@ -0,0 +1,8 @@ +// rustfmt-wrap_comments: true +// rustfmt-max_width: 80 + +/// Vestibulum elit nibh, rhoncus non, euismod sit amet, pretium eu, enim. Nunc +/// commodo ultricies dui. Cras gravida rutrum massa. Donec accumsan mattis +/// turpis. Quisque sem. Quisque elementum sapien iaculis augue. In dui sem, +/// congue sit amet, feugiat quis, lobortis at, eros. +fn func4() {} diff --git a/tests/target/issue-3066.rs b/tests/target/issue-3066.rs new file mode 100644 index 000000000000..d4dccc97e482 --- /dev/null +++ b/tests/target/issue-3066.rs @@ -0,0 +1,7 @@ +// rustfmt-indent_style: Visual +fn main() { + Struct { field: aaaaaaaaaaa }; + Struct { field: aaaaaaaaaaaa }; + Struct { field: value, + field2: value2 }; +} diff --git a/tests/target/issue-3105.rs b/tests/target/issue-3105.rs new file mode 100644 index 000000000000..4f1123805b82 --- /dev/null +++ b/tests/target/issue-3105.rs @@ -0,0 +1,48 @@ +// rustfmt-wrap_comments: true + +/// Although the indentation of the skipped method is off, it shouldn't be +/// changed. +/// +/// ``` +/// pub unsafe fn _mm256_shufflehi_epi16(a: __m256i, imm8: i32) -> __m256i { +/// let imm8 = (imm8 & 0xFF) as u8; +/// let a = a.as_i16x16(); +/// macro_rules! shuffle_done { +/// ($x01:expr, $x23:expr, $x45:expr, $x67:expr) => { +/// #[cfg_attr(rustfmt, rustfmt_skip)] +/// simd_shuffle16(a, a, [ +/// 0, 1, 2, 3, 4+$x01, 4+$x23, 4+$x45, 4+$x67, +/// 8, 9, 10, 11, 12+$x01, 12+$x23, 12+$x45, 12+$x67 +/// ]); +/// }; +/// } +/// } +/// ``` +pub unsafe fn _mm256_shufflehi_epi16(a: __m256i, imm8: i32) -> __m256i { + let imm8 = (imm8 & 0xFF) as u8; + let a = a.as_i16x16(); + macro_rules! shuffle_done { + ($x01:expr, $x23:expr, $x45:expr, $x67:expr) => { + #[cfg_attr(rustfmt, rustfmt_skip)] + simd_shuffle16(a, a, [ + 0, 1, 2, 3, 4+$x01, 4+$x23, 4+$x45, 4+$x67, + 8, 9, 10, 11, 12+$x01, 12+$x23, 12+$x45, 12+$x67 + ]); + }; + } +} + +/// The skipped method shouldn't right-shift +pub unsafe fn _mm256_shufflehi_epi32(a: __m256i, imm8: i32) -> __m256i { + let imm8 = (imm8 & 0xFF) as u8; + let a = a.as_i16x16(); + macro_rules! shuffle_done { + ($x01:expr, $x23:expr, $x45:expr, $x67:expr) => { + #[cfg_attr(rustfmt, rustfmt_skip)] + simd_shuffle32(a, a, [ + 0, 1, 2, 3, 4+$x01, 4+$x23, 4+$x45, 4+$x67, + 8, 9, 10, 11, 12+$x01, 12+$x23, 12+$x45, 12+$x67 + ]); + }; + } +} diff --git a/tests/target/issue-3118.rs b/tests/target/issue-3118.rs new file mode 100644 index 000000000000..ce73a5c789cf --- /dev/null +++ b/tests/target/issue-3118.rs @@ -0,0 +1,11 @@ +use { + crate::foo::bar, + bytes::{Buf, BufMut}, + std::io, +}; + +mod foo { + pub mod bar {} +} + +fn main() {} diff --git a/tests/target/issue-3124.rs b/tests/target/issue-3124.rs new file mode 100644 index 000000000000..1083050d8f4f --- /dev/null +++ b/tests/target/issue-3124.rs @@ -0,0 +1,14 @@ +pub fn fail1() { + // Some comment. + /**/// +} + +pub fn fail2() { + // Some comment. + /**/ +} + +pub fn fail3() { + // Some comment. + // +} diff --git a/tests/target/issue-3131.rs b/tests/target/issue-3131.rs new file mode 100644 index 000000000000..c7304dd55855 --- /dev/null +++ b/tests/target/issue-3131.rs @@ -0,0 +1,8 @@ +fn main() { + match 3 { + t if match t { + _ => true, + } => {} + _ => {} + } +} diff --git a/tests/target/issue-3132.rs b/tests/target/issue-3132.rs new file mode 100644 index 000000000000..4dffe0ab8360 --- /dev/null +++ b/tests/target/issue-3132.rs @@ -0,0 +1,15 @@ +// rustfmt-version: Two + +fn test() { + /* + a + */ + let x = 42; + /* + aaa + "line 1 + line 2 + line 3" + */ + let x = 42; +} diff --git a/tests/target/issue-3153.rs b/tests/target/issue-3153.rs new file mode 100644 index 000000000000..39e569c0d543 --- /dev/null +++ b/tests/target/issue-3153.rs @@ -0,0 +1,9 @@ +// rustfmt-wrap_comments: true + +/// This may panic if: +/// - there are fewer than `max_header_bytes` bytes preceding the body +/// - there are fewer than `max_footer_bytes` bytes following the body +/// - the sum of the body bytes and post-body bytes is less than the sum of +/// `min_body_and_padding_bytes` and `max_footer_bytes` (in other words, the +/// minimum body and padding byte requirement is not met) +fn foo() {} diff --git a/tests/target/issue-3158.rs b/tests/target/issue-3158.rs new file mode 100644 index 000000000000..4bbbdc1d0392 --- /dev/null +++ b/tests/target/issue-3158.rs @@ -0,0 +1,74 @@ +// rustfmt-format_code_in_doc_comments: true + +/// Should format +/// ```rust +/// assert!(false); +/// ``` +/// +/// Should format +/// ```rust,should_panic +/// assert!(false); +/// ``` +/// +/// Should format +/// ```rust,should_panic,edition2018 +/// assert!(false); +/// ``` +/// +/// Should format +/// ```rust , should_panic , edition2018 +/// assert!(false); +/// ``` +/// +/// Should not format +/// ```ignore +/// assert!( false ); +/// ``` +/// +/// Should not format (not all are rust) +/// ```rust,ignore +/// assert!( false ); +/// ``` +/// +/// Should not format (rust compile_fail) +/// ```compile_fail +/// assert!( false ); +/// ``` +/// +/// Should not format (rust compile_fail) +/// ```rust,compile_fail +/// assert!( false ); +/// ``` +/// +/// Various unspecified ones that should format +/// ``` +/// assert!(false); +/// ``` +/// +/// ```, +/// assert!(false); +/// ``` +/// +/// ```,,,,, +/// assert!(false); +/// ``` +/// +/// ```,,, rust ,, +/// assert!(false); +/// ``` +/// +/// Should not format +/// ```,,, rust , ignore, +/// assert!( false ); +/// ``` +/// +/// Few empty ones +/// ``` +/// ``` +/// +/// ```rust +/// ``` +/// +/// ```ignore +/// ``` +fn foo() {} diff --git a/tests/target/issue-3182.rs b/tests/target/issue-3182.rs new file mode 100644 index 000000000000..d8de8443812e --- /dev/null +++ b/tests/target/issue-3182.rs @@ -0,0 +1,10 @@ +// rustfmt-max_width: 79 +// rustfmt-wrap_comments: true + +/// ```rust +/// # #![cfg_attr(not(dox), feature(cfg_target_feature, target_feature, stdsimd)not(dox), feature(cfg_target_feature, target_feature, stdsimd))] +/// +/// // Est lectus hendrerit lorem, eget dignissim orci nisl sit amet massa. Etiam volutpat lobortis eros. +/// let x = 42; +/// ``` +fn func() {} diff --git a/tests/target/issue-3184.rs b/tests/target/issue-3184.rs new file mode 100644 index 000000000000..f8d9b169f1d5 --- /dev/null +++ b/tests/target/issue-3184.rs @@ -0,0 +1,5 @@ +/*/ +struct Error{ + message: String, +} +*/ diff --git a/tests/target/issue-3194.rs b/tests/target/issue-3194.rs new file mode 100644 index 000000000000..a9614913ed14 --- /dev/null +++ b/tests/target/issue-3194.rs @@ -0,0 +1,52 @@ +mod m { + struct S + where + A: B; +} + +mod n { + struct Foo + where + A: B, + { + foo: usize, + } +} + +mod o { + enum Bar + where + A: B, + { + Bar, + } +} + +mod with_comments { + mod m { + struct S + /* before where */ + where + A: B; /* after where */ + } + + mod n { + struct Foo + /* before where */ + where + A: B, /* after where */ + { + foo: usize, + } + } + + mod o { + enum Bar + /* before where */ + where + A: B, /* after where */ + { + Bar, + } + } +} diff --git a/tests/target/issue-3198.rs b/tests/target/issue-3198.rs new file mode 100644 index 000000000000..9291f181d03a --- /dev/null +++ b/tests/target/issue-3198.rs @@ -0,0 +1,67 @@ +impl TestTrait { + fn foo_one_pre(/* Important comment1 */ self) {} + + fn foo_one_post(self /* Important comment1 */) {} + + fn foo_pre(/* Important comment1 */ self, /* Important comment2 */ a: i32) {} + + fn foo_post(self /* Important comment1 */, a: i32 /* Important comment2 */) {} + + fn bar_pre(/* Important comment1 */ &mut self, /* Important comment2 */ a: i32) {} + + fn bar_post(&mut self /* Important comment1 */, a: i32 /* Important comment2 */) {} + + fn baz_pre( + /* Important comment1 */ + self: X<'a, 'b>, + /* Important comment2 */ + a: i32, + ) { + } + + fn baz_post( + self: X<'a, 'b>, /* Important comment1 */ + a: i32, /* Important comment2 */ + ) { + } + + fn baz_tree_pre( + /* Important comment1 */ + self: X<'a, 'b>, + /* Important comment2 */ + a: i32, + /* Important comment3 */ + b: i32, + ) { + } + + fn baz_tree_post( + self: X<'a, 'b>, /* Important comment1 */ + a: i32, /* Important comment2 */ + b: i32, /* Important comment3 */ + ) { + } + + fn multi_line( + self: X<'a, 'b>, /* Important comment1-1 */ + /* Important comment1-2 */ + a: i32, /* Important comment2 */ + b: i32, /* Important comment3 */ + ) { + } + + fn two_line_comment( + self: X<'a, 'b>, /* Important comment1-1 + Important comment1-2 */ + a: i32, /* Important comment2 */ + b: i32, /* Important comment3 */ + ) { + } + + fn no_first_line_comment( + self: X<'a, 'b>, + /* Important comment2 */ a: i32, + /* Important comment3 */ b: i32, + ) { + } +} diff --git a/tests/target/issue-3213/version_one.rs b/tests/target/issue-3213/version_one.rs new file mode 100644 index 000000000000..307903b128b8 --- /dev/null +++ b/tests/target/issue-3213/version_one.rs @@ -0,0 +1,13 @@ +// rustfmt-version: One + +fn foo() { + match 0 { + 0 => { + return AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + } + 1 => { + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + } + _ => "", + }; +} diff --git a/tests/target/issue-3213/version_two.rs b/tests/target/issue-3213/version_two.rs new file mode 100644 index 000000000000..de93d04ba950 --- /dev/null +++ b/tests/target/issue-3213/version_two.rs @@ -0,0 +1,13 @@ +// rustfmt-version: Two + +fn foo() { + match 0 { + 0 => { + return AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA; + } + 1 => { + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + } + _ => "", + }; +} diff --git a/tests/target/issue-3217.rs b/tests/target/issue-3217.rs new file mode 100644 index 000000000000..403bf4c340a4 --- /dev/null +++ b/tests/target/issue-3217.rs @@ -0,0 +1,22 @@ +fn main() { + let mut res = 0; + 's_39: { + if res == 0i32 { + println!("Hello, world!"); + } + } + 's_40: loop { + println!("res = {}", res); + res += 1; + if res == 3i32 { + break 's_40; + } + } + let toto = || { + if true { + 42 + } else { + 24 + } + }; +} diff --git a/tests/target/issue-3224.rs b/tests/target/issue-3224.rs new file mode 100644 index 000000000000..6476d2117c66 --- /dev/null +++ b/tests/target/issue-3224.rs @@ -0,0 +1,11 @@ +// rustfmt-wrap_comments: true + +//! Test: +//! * aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +//! * [`examples/simple`] – Demonstrates use of the [`init`] API with plain +//! structs. +//! * [`examples/simple_flatbuffer`] – Demonstrates use of the [`init`] API with +//! FlatBuffers. +//! * [`examples/gravity`] – Demonstrates use of the [`RLBot::set_game_state`] +//! API +fn foo() {} diff --git a/tests/target/issue-3227/one.rs b/tests/target/issue-3227/one.rs new file mode 100644 index 000000000000..fcc8331000d3 --- /dev/null +++ b/tests/target/issue-3227/one.rs @@ -0,0 +1,13 @@ +// rustfmt-version: One + +fn main() { + thread::spawn(|| { + while true { + println!("iteration"); + } + }); + + thread::spawn(|| loop { + println!("iteration"); + }); +} diff --git a/tests/target/issue-3227/two.rs b/tests/target/issue-3227/two.rs new file mode 100644 index 000000000000..374ab54305d9 --- /dev/null +++ b/tests/target/issue-3227/two.rs @@ -0,0 +1,15 @@ +// rustfmt-version: Two + +fn main() { + thread::spawn(|| { + while true { + println!("iteration"); + } + }); + + thread::spawn(|| { + loop { + println!("iteration"); + } + }); +} diff --git a/tests/target/issue-3234.rs b/tests/target/issue-3234.rs new file mode 100644 index 000000000000..c7d9d42bdbe2 --- /dev/null +++ b/tests/target/issue-3234.rs @@ -0,0 +1,14 @@ +macro_rules! fuzz_target { + (|$data:ident: &[u8]| $body:block) => {}; +} + +fuzz_target!(|data: &[u8]| { + if let Ok(app_img) = AppImage::parse(data) { + if let Ok(app_img) = + app_img.sign_for_secureboot(include_str!("../../test-data/signing-key")) + { + assert!(app_img.is_signed()); + Gbl::from_app_image(app_img).to_bytes(); + } + } +}); diff --git a/tests/target/issue-3241.rs b/tests/target/issue-3241.rs new file mode 100644 index 000000000000..60b452abdde9 --- /dev/null +++ b/tests/target/issue-3241.rs @@ -0,0 +1,11 @@ +// rustfmt-edition: 2018 + +use ::baz::{bar, foo}; +use ::ignore; +use ::ignore::some::more; +use ::*; +use ::{bar, foo}; + +fn main() { + println!("Hello, world!"); +} diff --git a/tests/target/issue-3253/bar.rs b/tests/target/issue-3253/bar.rs new file mode 100644 index 000000000000..6c6ab945b88f --- /dev/null +++ b/tests/target/issue-3253/bar.rs @@ -0,0 +1,2 @@ +// Empty +fn empty() {} diff --git a/tests/target/issue-3253/foo.rs b/tests/target/issue-3253/foo.rs new file mode 100644 index 000000000000..1a42a10159e6 --- /dev/null +++ b/tests/target/issue-3253/foo.rs @@ -0,0 +1,3 @@ +pub fn hello() { + println!("Hello World!"); +} diff --git a/tests/target/issue-3253/lib.rs b/tests/target/issue-3253/lib.rs new file mode 100644 index 000000000000..3eef586bd679 --- /dev/null +++ b/tests/target/issue-3253/lib.rs @@ -0,0 +1,14 @@ +#[macro_use] +extern crate cfg_if; + +cfg_if! { + if #[cfg(target_family = "unix")] { + mod foo; + #[path = "paths/bar_foo.rs"] + mod bar_foo; + } else { + mod bar; + #[path = "paths/foo_bar.rs"] + mod foo_bar; + } +} diff --git a/tests/target/issue-3253/paths/bar_foo.rs b/tests/target/issue-3253/paths/bar_foo.rs new file mode 100644 index 000000000000..f7e1de29a306 --- /dev/null +++ b/tests/target/issue-3253/paths/bar_foo.rs @@ -0,0 +1,3 @@ +fn foo_decl_item(x: &mut i32) { + x = 3; +} diff --git a/tests/target/issue-3253/paths/excluded.rs b/tests/target/issue-3253/paths/excluded.rs new file mode 100644 index 000000000000..9ab88414d465 --- /dev/null +++ b/tests/target/issue-3253/paths/excluded.rs @@ -0,0 +1,17 @@ +// This module is not imported in the cfg_if macro in lib.rs so it is ignored +// while the foo and bar mods are formatted. +// Check the corresponding file in tests/source/issue-3253/paths/excluded.rs + + + + + fn Foo() where T: Bar { +} + + + +trait CoolerTypes { fn dummy(&self) { +} +} + + diff --git a/tests/target/issue-3253/paths/foo_bar.rs b/tests/target/issue-3253/paths/foo_bar.rs new file mode 100644 index 000000000000..f52ac11b7399 --- /dev/null +++ b/tests/target/issue-3253/paths/foo_bar.rs @@ -0,0 +1,5 @@ +fn Foo() +where + T: Bar, +{ +} diff --git a/tests/target/issue-3265.rs b/tests/target/issue-3265.rs new file mode 100644 index 000000000000..7db1dbd8b732 --- /dev/null +++ b/tests/target/issue-3265.rs @@ -0,0 +1,14 @@ +// rustfmt-newline_style: Windows +#[cfg(test)] +mod test { + summary_test! { + tokenize_recipe_interpolation_eol, + "foo: # some comment + {{hello}} +", + "foo: \ + {{hello}} \ + {{ahah}}", + "N:#$>^{N}$<.", + } +} diff --git a/tests/target/issue-3270/one.rs b/tests/target/issue-3270/one.rs new file mode 100644 index 000000000000..78de94732432 --- /dev/null +++ b/tests/target/issue-3270/one.rs @@ -0,0 +1,12 @@ +// rustfmt-version: One + +pub fn main() { + /* let s = String::from( + " + hello + world + ", + ); */ + + assert_eq!(s, "\nhello\nworld\n"); +} diff --git a/tests/target/issue-3270/two.rs b/tests/target/issue-3270/two.rs new file mode 100644 index 000000000000..e48b59213291 --- /dev/null +++ b/tests/target/issue-3270/two.rs @@ -0,0 +1,12 @@ +// rustfmt-version: Two + +pub fn main() { + /* let s = String::from( + " +hello +world +", + ); */ + + assert_eq!(s, "\nhello\nworld\n"); +} diff --git a/tests/target/issue-3270/wrap.rs b/tests/target/issue-3270/wrap.rs new file mode 100644 index 000000000000..7435c5f0866e --- /dev/null +++ b/tests/target/issue-3270/wrap.rs @@ -0,0 +1,13 @@ +// rustfmt-wrap_comments: true +// rustfmt-version: Two + +// check that a line below max_width does not get over the limit when wrapping +// it in a block comment +fn func() { + let x = 42; + /* + let something = "one line line line line line line line line line line line line line + two lines + three lines"; + */ +} diff --git a/tests/target/issue-3272/v1.rs b/tests/target/issue-3272/v1.rs new file mode 100644 index 000000000000..aab201027d56 --- /dev/null +++ b/tests/target/issue-3272/v1.rs @@ -0,0 +1,15 @@ +// rustfmt-version: One + +fn main() { + assert!(HAYSTACK + .par_iter() + .find_any(|&&x| x[0] % 1000 == 999) + .is_some()); + + assert( + HAYSTACK + .par_iter() + .find_any(|&&x| x[0] % 1000 == 999) + .is_some(), + ); +} diff --git a/tests/target/issue-3272/v2.rs b/tests/target/issue-3272/v2.rs new file mode 100644 index 000000000000..a42a2fccd5b0 --- /dev/null +++ b/tests/target/issue-3272/v2.rs @@ -0,0 +1,17 @@ +// rustfmt-version: Two + +fn main() { + assert!( + HAYSTACK + .par_iter() + .find_any(|&&x| x[0] % 1000 == 999) + .is_some() + ); + + assert( + HAYSTACK + .par_iter() + .find_any(|&&x| x[0] % 1000 == 999) + .is_some(), + ); +} diff --git a/tests/target/issue-3278/version_one.rs b/tests/target/issue-3278/version_one.rs new file mode 100644 index 000000000000..580679fbae34 --- /dev/null +++ b/tests/target/issue-3278/version_one.rs @@ -0,0 +1,8 @@ +// rustfmt-version: One + +pub fn parse_conditional<'a, I: 'a>( +) -> impl Parser + 'a +where + I: Stream, +{ +} diff --git a/tests/target/issue-3278/version_two.rs b/tests/target/issue-3278/version_two.rs new file mode 100644 index 000000000000..c17b1742d396 --- /dev/null +++ b/tests/target/issue-3278/version_two.rs @@ -0,0 +1,8 @@ +// rustfmt-version: Two + +pub fn parse_conditional<'a, I: 'a>() +-> impl Parser + 'a +where + I: Stream, +{ +} diff --git a/tests/target/issue-3295/two.rs b/tests/target/issue-3295/two.rs new file mode 100644 index 000000000000..3e669a0bb756 --- /dev/null +++ b/tests/target/issue-3295/two.rs @@ -0,0 +1,14 @@ +// rustfmt-version: Two +pub enum TestEnum { + a, + b, +} + +fn the_test(input: TestEnum) { + match input { + TestEnum::a => String::from("aaa"), + TestEnum::b => String::from( + "this is a very very very very very very very very very very very very very very very ong string", + ), + }; +} diff --git a/tests/target/issue-3302.rs b/tests/target/issue-3302.rs new file mode 100644 index 000000000000..146cb9838193 --- /dev/null +++ b/tests/target/issue-3302.rs @@ -0,0 +1,43 @@ +// rustfmt-version: Two + +macro_rules! moo1 { + () => { + bar! { + " +" + } + }; +} + +macro_rules! moo2 { + () => { + bar! { + " +" + } + }; +} + +macro_rules! moo3 { + () => { + 42 + /* + bar! { + " + toto +tata" + } + */ + }; +} + +macro_rules! moo4 { + () => { + bar! { + " + foo + bar +baz" + } + }; +} diff --git a/tests/target/issue-3304.rs b/tests/target/issue-3304.rs new file mode 100644 index 000000000000..cc1910ce2571 --- /dev/null +++ b/tests/target/issue-3304.rs @@ -0,0 +1,42 @@ +// rustfmt-error_on_line_overflow: true + +#[rustfmt::skip] use one::two::three::four::five::six::seven::eight::night::ten::eleven::twelve::thirteen::fourteen::fiveteen; +#[rustfmt::skip] + +use one::two::three::four::five::six::seven::eight::night::ten::eleven::twelve::thirteen::fourteen::fiveteen; + +macro_rules! test_macro { + ($($id:ident),*) => {}; +} + +macro_rules! test_macro2 { + ($($id:ident),*) => { + 1 + }; +} + +fn main() { + #[rustfmt::skip] test_macro! { one, two, three, four, five, six, seven, eight, night, ten, eleven, twelve, thirteen, fourteen, fiveteen }; + #[rustfmt::skip] + + test_macro! { one, two, three, four, five, six, seven, eight, night, ten, eleven, twelve, thirteen, fourteen, fiveteen }; +} + +fn test_local() { + #[rustfmt::skip] let x = test_macro! { one, two, three, four, five, six, seven, eight, night, ten, eleven, twelve, thirteen, fourteen, fiveteen }; + #[rustfmt::skip] + + let x = test_macro! { one, two, three, four, five, six, seven, eight, night, ten, eleven, twelve, thirteen, fourteen, fiveteen }; +} + +fn test_expr(_: [u32]) -> u32 { + #[rustfmt::skip] test_expr([9999999999999, 9999999999999, 9999999999999, 9999999999999, 9999999999999, 9999999999999, 9999999999999, 9999999999999]); + #[rustfmt::skip] + + test_expr([9999999999999, 9999999999999, 9999999999999, 9999999999999, 9999999999999, 9999999999999, 9999999999999, 9999999999999]) +} + +#[rustfmt::skip] mod test { use one::two::three::four::five::six::seven::eight::night::ten::eleven::twelve::thirteen::fourteen::fiveteen; } +#[rustfmt::skip] + +mod test { use one::two::three::four::five::six::seven::eight::night::ten::eleven::twelve::thirteen::fourteen::fiveteen; } diff --git a/tests/target/issue-3314.rs b/tests/target/issue-3314.rs new file mode 100644 index 000000000000..1cd32afb02f9 --- /dev/null +++ b/tests/target/issue-3314.rs @@ -0,0 +1,5 @@ +/*code +/*code*/ +if true { + println!("1"); +}*/ diff --git a/tests/target/issue-3343.rs b/tests/target/issue-3343.rs new file mode 100644 index 000000000000..d0497758e66c --- /dev/null +++ b/tests/target/issue-3343.rs @@ -0,0 +1,44 @@ +// rustfmt-inline_attribute_width: 50 + +#[cfg(feature = "alloc")] use core::slice; + +#[cfg(feature = "alloc")] use total_len_is::_50__; + +#[cfg(feature = "alloc")] +use total_len_is::_51___; + +#[cfg(feature = "alloc")] extern crate len_is_50_; + +#[cfg(feature = "alloc")] +extern crate len_is_51__; + +/// this is a comment to test is_sugared_doc property +use core::convert; + +#[fooooo] +#[barrrrr] +use total_len_is_::_51______; + +#[cfg(not(all( + feature = "std", + any( + target_os = "linux", + target_os = "android", + target_os = "netbsd", + target_os = "dragonfly", + target_os = "haiku", + target_os = "emscripten", + target_os = "solaris", + target_os = "cloudabi", + target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "openbsd", + target_os = "redox", + target_os = "fuchsia", + windows, + all(target_arch = "wasm32", feature = "stdweb"), + all(target_arch = "wasm32", feature = "wasm-bindgen"), + ) +)))] +use core::slice; diff --git a/tests/target/issue-3423.rs b/tests/target/issue-3423.rs new file mode 100644 index 000000000000..cd60251771df --- /dev/null +++ b/tests/target/issue-3423.rs @@ -0,0 +1,5 @@ +/* a nice comment with a trailing whitespace */ +fn foo() {} + +/* a nice comment with a trailing tab */ +fn bar() {} diff --git a/tests/target/issue-3434/lib.rs b/tests/target/issue-3434/lib.rs new file mode 100644 index 000000000000..2fd7aea21c75 --- /dev/null +++ b/tests/target/issue-3434/lib.rs @@ -0,0 +1,57 @@ +#![rustfmt::skip::macros(skip_macro_mod)] + +mod no_entry; + +#[rustfmt::skip::macros(html, skip_macro)] +fn main() { + let macro_result1 = html! {
+this should be skipped
+ } + .to_string(); + + let macro_result2 = not_skip_macro! {
+ this should be mangled
+ } + .to_string(); + + skip_macro! { +this should be skipped +}; + + foo(); +} + +fn foo() { + let macro_result1 = html! {
+ this should be mangled
+ } + .to_string(); +} + +fn bar() { + let macro_result1 = skip_macro_mod! {
+this should be skipped
+ } + .to_string(); +} + +fn visitor_made_from_same_context() { + let pair = ( + || { + foo!(
+ this should be mangled
+ ); + skip_macro_mod!(
+this should be skipped
+ ); + }, + || { + foo!(
+ this should be mangled
+ ); + skip_macro_mod!(
+this should be skipped
+ ); + }, + ); +} diff --git a/tests/target/issue-3434/no_entry.rs b/tests/target/issue-3434/no_entry.rs new file mode 100644 index 000000000000..a2ecf2c2f99b --- /dev/null +++ b/tests/target/issue-3434/no_entry.rs @@ -0,0 +1,19 @@ +#[rustfmt::skip::macros(another_macro)] +fn foo() { + another_macro!( +This should be skipped. + ); +} + +fn bar() { + skip_macro_mod!( +This should be skipped. + ); +} + +fn baz() { + let macro_result1 = no_skip_macro! {
+ this should be mangled
+ } + .to_string(); +} diff --git a/tests/target/issue-3434/not_skip_macro.rs b/tests/target/issue-3434/not_skip_macro.rs new file mode 100644 index 000000000000..c90d09744b28 --- /dev/null +++ b/tests/target/issue-3434/not_skip_macro.rs @@ -0,0 +1,8 @@ +#[this::is::not::skip::macros(ouch)] + +fn main() { + let macro_result1 = ouch! {
+ this should be mangled
+ } + .to_string(); +} diff --git a/tests/target/issue-3442.rs b/tests/target/issue-3442.rs new file mode 100644 index 000000000000..3664c50ee7a8 --- /dev/null +++ b/tests/target/issue-3442.rs @@ -0,0 +1,10 @@ +// rustfmt-file_lines: [{"file":"tests/target/issue-3442.rs","range":[5,5]},{"file":"tests/target/issue-3442.rs","range":[8,8]}] + +extern crate alpha; // comment 1 +extern crate beta; // comment 2 +#[allow(aaa)] // comment 3 +#[macro_use] +extern crate gamma; +#[allow(bbb)] // comment 4 +#[macro_use] +extern crate lazy_static; diff --git a/tests/target/issue-3465.rs b/tests/target/issue-3465.rs new file mode 100644 index 000000000000..9e2680f0feec --- /dev/null +++ b/tests/target/issue-3465.rs @@ -0,0 +1,42 @@ +fn main() { + ((((((((((((((((((((((((((((((((((((((((((0) + 1) + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1) + + 1); +} diff --git a/tests/target/issue-3494/crlf.rs b/tests/target/issue-3494/crlf.rs new file mode 100644 index 000000000000..cae615a06664 --- /dev/null +++ b/tests/target/issue-3494/crlf.rs @@ -0,0 +1,8 @@ +// rustfmt-file_lines: [{"file":"tests/source/issue-3494/crlf.rs","range":[4,5]}] + +pub fn main() { + let world1 = "world"; + println!("Hello, {}!", world1); +let world2 = "world"; println!("Hello, {}!", world2); +let world3 = "world"; println!("Hello, {}!", world3); +} diff --git a/tests/target/issue-3494/lf.rs b/tests/target/issue-3494/lf.rs new file mode 100644 index 000000000000..60aafe19a029 --- /dev/null +++ b/tests/target/issue-3494/lf.rs @@ -0,0 +1,8 @@ +// rustfmt-file_lines: [{"file":"tests/source/issue-3494/lf.rs","range":[4,5]}] + +pub fn main() { + let world1 = "world"; + println!("Hello, {}!", world1); +let world2 = "world"; println!("Hello, {}!", world2); +let world3 = "world"; println!("Hello, {}!", world3); +} diff --git a/tests/target/issue-3499.rs b/tests/target/issue-3499.rs new file mode 100644 index 000000000000..88fd7f7e1653 --- /dev/null +++ b/tests/target/issue-3499.rs @@ -0,0 +1 @@ +test![]; diff --git a/tests/target/issue-3508.rs b/tests/target/issue-3508.rs new file mode 100644 index 000000000000..5f4e156582d4 --- /dev/null +++ b/tests/target/issue-3508.rs @@ -0,0 +1,22 @@ +fn foo(foo2: F) +where + F: Fn( + // this comment is deleted + ), +{ +} + +fn foo_block(foo2: F) +where + F: Fn(/* this comment is deleted */), +{ +} + +fn bar( + bar2: impl Fn( + // this comment is deleted + ), +) { +} + +fn bar_block(bar2: impl Fn(/* this comment is deleted */)) {} diff --git a/tests/target/issue-3515.rs b/tests/target/issue-3515.rs new file mode 100644 index 000000000000..b59d03c6c82a --- /dev/null +++ b/tests/target/issue-3515.rs @@ -0,0 +1,6 @@ +// rustfmt-reorder_imports: false + +use std::fmt::{self, Display}; +use std::collections::HashMap; + +fn main() {} diff --git a/tests/target/issue-3532.rs b/tests/target/issue-3532.rs new file mode 100644 index 000000000000..f41902620cd1 --- /dev/null +++ b/tests/target/issue-3532.rs @@ -0,0 +1,6 @@ +fn foo(a: T) { + match a { + 1 => {} + 0 => {} // _ => panic!("doesn't format!"), + } +} diff --git a/tests/target/issue-3539.rs b/tests/target/issue-3539.rs new file mode 100644 index 000000000000..aa2fa72ece32 --- /dev/null +++ b/tests/target/issue-3539.rs @@ -0,0 +1,8 @@ +use std::io::Error; + +fn main() { + let _read_num: fn() -> Result<(i32), Error> = || -> Result<(i32), Error> { + let a = 1; + Ok(a) + }; +} diff --git a/tests/target/issue-3554.rs b/tests/target/issue-3554.rs new file mode 100644 index 000000000000..4ece90403e1f --- /dev/null +++ b/tests/target/issue-3554.rs @@ -0,0 +1,4 @@ +#![feature(const_generics)] + +pub struct S; +impl S<{ 0 }> {} diff --git a/tests/target/issue-3567.rs b/tests/target/issue-3567.rs new file mode 100644 index 000000000000..3cf08628db0b --- /dev/null +++ b/tests/target/issue-3567.rs @@ -0,0 +1,3 @@ +fn check() { + vec![vec!(0; 10); 10]; +} diff --git a/tests/target/issue-3568.rs b/tests/target/issue-3568.rs new file mode 100644 index 000000000000..a146f3df24dd --- /dev/null +++ b/tests/target/issue-3568.rs @@ -0,0 +1 @@ +use a::b::{self}; diff --git a/tests/target/issue-3585/extern_crate.rs b/tests/target/issue-3585/extern_crate.rs new file mode 100644 index 000000000000..dc7c9e0246b2 --- /dev/null +++ b/tests/target/issue-3585/extern_crate.rs @@ -0,0 +1,10 @@ +// rustfmt-inline_attribute_width: 100 + +#[macro_use] extern crate static_assertions; + +#[cfg(unix)] extern crate static_assertions; + +// a comment before the attribute +#[macro_use] +// some comment after +extern crate static_assertions; diff --git a/tests/target/issue-3585/reorder_imports_disabled.rs b/tests/target/issue-3585/reorder_imports_disabled.rs new file mode 100644 index 000000000000..f9637729b7eb --- /dev/null +++ b/tests/target/issue-3585/reorder_imports_disabled.rs @@ -0,0 +1,8 @@ +// rustfmt-inline_attribute_width: 100 +// rustfmt-reorder_imports: false + +#[cfg(unix)] extern crate crateb; +#[cfg(unix)] extern crate cratea; + +#[cfg(unix)] use crateb; +#[cfg(unix)] use cratea; diff --git a/tests/target/issue-3585/reorder_imports_enabled.rs b/tests/target/issue-3585/reorder_imports_enabled.rs new file mode 100644 index 000000000000..d040d0ed3414 --- /dev/null +++ b/tests/target/issue-3585/reorder_imports_enabled.rs @@ -0,0 +1,8 @@ +// rustfmt-inline_attribute_width: 100 +// rustfmt-reorder_imports: true + +#[cfg(unix)] extern crate cratea; +#[cfg(unix)] extern crate crateb; + +#[cfg(unix)] use cratea; +#[cfg(unix)] use crateb; diff --git a/tests/target/issue-3585/use.rs b/tests/target/issue-3585/use.rs new file mode 100644 index 000000000000..c76a9eaacc82 --- /dev/null +++ b/tests/target/issue-3585/use.rs @@ -0,0 +1,5 @@ +// rustfmt-inline_attribute_width: 100 + +#[macro_use] use static_assertions; + +#[cfg(unix)] use static_assertions; diff --git a/tests/target/issue-3595.rs b/tests/target/issue-3595.rs new file mode 100644 index 000000000000..3e06538a4d32 --- /dev/null +++ b/tests/target/issue-3595.rs @@ -0,0 +1,4 @@ +struct ReqMsg(); +struct RespMsg(); + +pub type TestType = fn() -> (ReqMsg, fn(RespMsg) -> ()); diff --git a/tests/target/issue-3601.rs b/tests/target/issue-3601.rs new file mode 100644 index 000000000000..c86ca24e7075 --- /dev/null +++ b/tests/target/issue-3601.rs @@ -0,0 +1,11 @@ +#![feature(const_generics)] + +trait A { + fn foo(&self); +} + +pub struct B([usize; N]); + +impl A for B<{ N }> { + fn foo(&self) {} +} diff --git a/tests/target/issue-3614/version_one.rs b/tests/target/issue-3614/version_one.rs new file mode 100644 index 000000000000..8ab28304732c --- /dev/null +++ b/tests/target/issue-3614/version_one.rs @@ -0,0 +1,15 @@ +// rustfmt-version: One + +fn main() { + let toto = || { + if true { + 42 + } else { + 24 + } + }; + + { + T + } +} diff --git a/tests/target/issue-3614/version_two.rs b/tests/target/issue-3614/version_two.rs new file mode 100644 index 000000000000..5d6f8e7a313b --- /dev/null +++ b/tests/target/issue-3614/version_two.rs @@ -0,0 +1,8 @@ +// rustfmt-version: Two + +fn main() { + let toto = || { + if true { 42 } else { 24 } + }; + { T } +} diff --git a/tests/target/issue-3636.rs b/tests/target/issue-3636.rs new file mode 100644 index 000000000000..d467ed7389f6 --- /dev/null +++ b/tests/target/issue-3636.rs @@ -0,0 +1,8 @@ +// rustfmt-file_lines: [{"file":"tests/source/issue-3636.rs","range":[4,7]},{"file":"tests/target/issue-3636.rs","range":[3,6]}] + +fn foo() { + let x = 42; + let y = 42; + let z = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + let z = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"; +} diff --git a/tests/target/issue-3639.rs b/tests/target/issue-3639.rs new file mode 100644 index 000000000000..e8fddce2de62 --- /dev/null +++ b/tests/target/issue-3639.rs @@ -0,0 +1,5 @@ +trait Foo {} +struct Bar {} +struct Bax; +struct Baz(String); +impl Foo for Bar {} diff --git a/tests/target/issue-3645.rs b/tests/target/issue-3645.rs new file mode 100644 index 000000000000..14bf96e6383d --- /dev/null +++ b/tests/target/issue-3645.rs @@ -0,0 +1,3 @@ +mod x { + use super::self as x; +} diff --git a/tests/target/issue-3651.rs b/tests/target/issue-3651.rs new file mode 100644 index 000000000000..4a95a1712ee5 --- /dev/null +++ b/tests/target/issue-3651.rs @@ -0,0 +1,7 @@ +fn f() -> Box< + dyn FnMut() -> Thing< + WithType = LongItemName, + Error = LONGLONGLONGLONGLONGONGEvenLongerErrorNameLongerLonger, + >, +> { +} diff --git a/tests/target/issue-3665/lib.rs b/tests/target/issue-3665/lib.rs new file mode 100644 index 000000000000..c313f320368a --- /dev/null +++ b/tests/target/issue-3665/lib.rs @@ -0,0 +1,31 @@ +#![rustfmt::skip::attributes(skip_mod_attr)] + +mod sub_mod; + +#[rustfmt::skip::attributes(other, skip_attr)] +fn main() { + #[other(should, +skip, + this, format)] + struct S {} + + #[skip_attr(should, skip, +this, format,too)] + fn doesnt_mater() {} + + #[skip_mod_attr(should, skip, +this, format, + enerywhere)] + fn more() {} + + #[not_skip(not, skip, me)] + struct B {} +} + +#[other(should, not, skip, this, format, here)] +fn foo() {} + +#[skip_mod_attr(should, skip, +this, format,in, master, + and, sub, module)] +fn bar() {} diff --git a/tests/target/issue-3665/not_skip_attribute.rs b/tests/target/issue-3665/not_skip_attribute.rs new file mode 100644 index 000000000000..a4e8b9487366 --- /dev/null +++ b/tests/target/issue-3665/not_skip_attribute.rs @@ -0,0 +1,4 @@ +#![this::is::not::skip::attribute(ouch)] + +#[ouch(not, skip, me)] +fn main() {} diff --git a/tests/target/issue-3665/sub_mod.rs b/tests/target/issue-3665/sub_mod.rs new file mode 100644 index 000000000000..30a2b0fd9d96 --- /dev/null +++ b/tests/target/issue-3665/sub_mod.rs @@ -0,0 +1,13 @@ +#[rustfmt::skip::attributes(more_skip)] +#[more_skip(should, + skip, +this, format)] +fn foo() {} + +#[skip_mod_attr(should, skip, +this, format,in, master, + and, sub, module)] +fn bar() {} + +#[skip_attr(should, not, skip, this, attribute, here)] +fn baz() {} diff --git a/tests/target/issue-3672.rs b/tests/target/issue-3672.rs new file mode 100644 index 000000000000..8cc3d3fd2c55 --- /dev/null +++ b/tests/target/issue-3672.rs @@ -0,0 +1,3 @@ +fn main() { + let x = 5; +} diff --git a/tests/target/issue-3675.rs b/tests/target/issue-3675.rs new file mode 100644 index 000000000000..62d986e77341 --- /dev/null +++ b/tests/target/issue-3675.rs @@ -0,0 +1,6 @@ +fn main() { + println!( + "{}", // comment + 111 + ); +} diff --git a/tests/target/issue-3701/one.rs b/tests/target/issue-3701/one.rs new file mode 100644 index 000000000000..9d1ef9eed9ae --- /dev/null +++ b/tests/target/issue-3701/one.rs @@ -0,0 +1,12 @@ +// rustfmt-version: One + +fn build_sorted_static_get_entry_names( + mut entries: Vec<(u8, &'static str)>, +) -> (impl Fn( + AlphabeticalTraversal, + Box>, +) -> BoxFuture<'static, Result, Status>> + + Send + + Sync + + 'static) { +} diff --git a/tests/target/issue-3701/two.rs b/tests/target/issue-3701/two.rs new file mode 100644 index 000000000000..62ffc9d823dd --- /dev/null +++ b/tests/target/issue-3701/two.rs @@ -0,0 +1,14 @@ +// rustfmt-version: Two + +fn build_sorted_static_get_entry_names( + mut entries: Vec<(u8, &'static str)>, +) -> ( + impl Fn( + AlphabeticalTraversal, + Box>, + ) -> BoxFuture<'static, Result, Status>> + + Send + + Sync + + 'static +) { +} diff --git a/tests/target/issue-3709.rs b/tests/target/issue-3709.rs new file mode 100644 index 000000000000..0f3eae048d40 --- /dev/null +++ b/tests/target/issue-3709.rs @@ -0,0 +1,10 @@ +// rustfmt-edition: 2018 + +macro_rules! token { + ($t:tt) => {}; +} + +fn main() { + token!(dyn); + token!(dyn); +} diff --git a/tests/target/issue-3711.rs b/tests/target/issue-3711.rs new file mode 100644 index 000000000000..62d986e77341 --- /dev/null +++ b/tests/target/issue-3711.rs @@ -0,0 +1,6 @@ +fn main() { + println!( + "{}", // comment + 111 + ); +} diff --git a/tests/target/issue-3717.rs b/tests/target/issue-3717.rs new file mode 100644 index 000000000000..b769cd3ecce2 --- /dev/null +++ b/tests/target/issue-3717.rs @@ -0,0 +1,7 @@ +fn main() { + { + #[rustfmt::skip] + let _ = + [1]; + } +} diff --git a/tests/target/issue-3718.rs b/tests/target/issue-3718.rs new file mode 100644 index 000000000000..8ad21ffc708c --- /dev/null +++ b/tests/target/issue-3718.rs @@ -0,0 +1,7 @@ +fn main() { + let x: &[i32] = &[2, 2]; + match x { + [_a, _] => println!("Wrong username or password"), + _ => println!("Logged in"), + } +} diff --git a/tests/target/issue-3740.rs b/tests/target/issue-3740.rs new file mode 100644 index 000000000000..995a6bee3527 --- /dev/null +++ b/tests/target/issue-3740.rs @@ -0,0 +1,8 @@ +impl IntoNormalized for Vector +where + Vector: Div>, + for<'a> &'a Vector: IntoLength, +{ + type Output = Vector; + fn into_normalized(self) -> Self::Output {} +} diff --git a/tests/target/issue-3741.rs b/tests/target/issue-3741.rs new file mode 100644 index 000000000000..34d22dc91eb2 --- /dev/null +++ b/tests/target/issue-3741.rs @@ -0,0 +1,5 @@ +pub enum PublishedFileVisibility { + Public = sys::ERemoteStoragePublishedFileVisibility_k_ERemoteStoragePublishedFileVisibilityPublic as i32, + FriendsOnly = sys::ERemoteStoragePublishedFileVisibility_k_ERemoteStoragePublishedFileVisibilityFriendsOnly as i32, + Private = sys::ERemoteStoragePublishedFileVisibility_k_ERemoteStoragePublishedFileVisibilityPrivate as i32, +} diff --git a/tests/target/issue-3750.rs b/tests/target/issue-3750.rs new file mode 100644 index 000000000000..6875f8d3897b --- /dev/null +++ b/tests/target/issue-3750.rs @@ -0,0 +1,15 @@ +// rustfmt-imports_granularity: Crate + +pub mod foo { + pub mod bar { + pub struct Bar; + } + + pub fn bar() {} +} + +use foo::{bar, bar::Bar}; + +fn main() { + bar(); +} diff --git a/tests/target/issue-3751.rs b/tests/target/issue-3751.rs new file mode 100644 index 000000000000..e5a03956e9d0 --- /dev/null +++ b/tests/target/issue-3751.rs @@ -0,0 +1,10 @@ +// rustfmt-format_code_in_doc_comments: true + +//! Empty pound line +//! +//! ```rust +//! # +//! # fn main() { +//! foo(); +//! # } +//! ``` diff --git a/tests/target/issue-3759.rs b/tests/target/issue-3759.rs new file mode 100644 index 000000000000..b53f5391a344 --- /dev/null +++ b/tests/target/issue-3759.rs @@ -0,0 +1,27 @@ +fn main() { + let Test { + #[cfg(feature = "test")] + x, + } = Test { + #[cfg(feature = "test")] + x: 1, + }; + + let Test { + #[cfg(feature = "test")] + // comment + x, + } = Test { + #[cfg(feature = "test")] + x: 1, + }; + + let Test { + // comment + #[cfg(feature = "test")] + x, + } = Test { + #[cfg(feature = "test")] + x: 1, + }; +} diff --git a/tests/target/issue-3779/ice.rs b/tests/target/issue-3779/ice.rs new file mode 100644 index 000000000000..cde21412d945 --- /dev/null +++ b/tests/target/issue-3779/ice.rs @@ -0,0 +1,3 @@ +pub fn bar() { + 1x; +} diff --git a/tests/target/issue-3779/lib.rs b/tests/target/issue-3779/lib.rs new file mode 100644 index 000000000000..a5673a4db7ec --- /dev/null +++ b/tests/target/issue-3779/lib.rs @@ -0,0 +1,9 @@ +// rustfmt-unstable: true +// rustfmt-config: issue-3779.toml + +#[path = "ice.rs"] +mod ice; + +fn foo() { + println!("abc"); +} diff --git a/tests/target/issue-3786.rs b/tests/target/issue-3786.rs new file mode 100644 index 000000000000..d90cba15d201 --- /dev/null +++ b/tests/target/issue-3786.rs @@ -0,0 +1,9 @@ +fn main() { + let _ = r#" +this is a very long string exceeded maximum width in this case maximum 100. (current this line width is about 115) +"#; + + let _with_newline = r#" +this is a very long string exceeded maximum width in this case maximum 100. (current this line width is about 115) +"#; +} diff --git a/tests/target/issue-3787.rs b/tests/target/issue-3787.rs new file mode 100644 index 000000000000..32cf7e3d7559 --- /dev/null +++ b/tests/target/issue-3787.rs @@ -0,0 +1,13 @@ +// rustfmt-wrap_comments: true + +//! URLs in items +//! * [This is a link with a very loooooooooooooooooooooooooooooooooooooooooong URL.](https://example.com/This/is/a/link/with/a/very/loooooooooooooooooooooooooooooooooooooooooong/URL) +//! * This is a [link](https://example.com/This/is/a/link/with/a/very/loooooooooooooooooooooooooooooooooooooooooong/URL) +//! with a very loooooooooooooooooooooooooooooooooooooooooong URL. +//! * there is no link here: In hac habitasse platea dictumst. Maecenas in +//! ligula. Duis tincidunt odio sollicitudin quam. Nullam non mauris. +//! Phasellus lacinia, velit sit amet bibendum euismod, leo diam interdum +//! ligula, eu scelerisque sem purus in tellus. +fn main() { + println!("Hello, world!"); +} diff --git a/tests/target/issue-3815.rs b/tests/target/issue-3815.rs new file mode 100644 index 000000000000..eff27e2de3c1 --- /dev/null +++ b/tests/target/issue-3815.rs @@ -0,0 +1,4 @@ +pub type Type = impl Deref; + +pub type Type = + impl VeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeryLongType; diff --git a/tests/target/issue-3840/version-one_hard-tabs.rs b/tests/target/issue-3840/version-one_hard-tabs.rs new file mode 100644 index 000000000000..4aa905ce9e5a --- /dev/null +++ b/tests/target/issue-3840/version-one_hard-tabs.rs @@ -0,0 +1,25 @@ +// rustfmt-hard_tabs: true + +impl< + Target: FromEvent + FromEvent, + A: Widget2, + B: Widget2, + C: for<'a> CtxFamily<'a>, + > Widget2 for WidgetEventLifter +{ + type Ctx = C; + type Event = Vec; +} + +mod foo { + impl< + Target: FromEvent + FromEvent, + A: Widget2, + B: Widget2, + C: for<'a> CtxFamily<'a>, + > Widget2 for WidgetEventLifter + { + type Ctx = C; + type Event = Vec; + } +} diff --git a/tests/target/issue-3840/version-one_soft-tabs.rs b/tests/target/issue-3840/version-one_soft-tabs.rs new file mode 100644 index 000000000000..099e68018230 --- /dev/null +++ b/tests/target/issue-3840/version-one_soft-tabs.rs @@ -0,0 +1,23 @@ +impl< + Target: FromEvent + FromEvent, + A: Widget2, + B: Widget2, + C: for<'a> CtxFamily<'a>, + > Widget2 for WidgetEventLifter +{ + type Ctx = C; + type Event = Vec; +} + +mod foo { + impl< + Target: FromEvent + FromEvent, + A: Widget2, + B: Widget2, + C: for<'a> CtxFamily<'a>, + > Widget2 for WidgetEventLifter + { + type Ctx = C; + type Event = Vec; + } +} diff --git a/tests/target/issue-3840/version-two_hard-tabs.rs b/tests/target/issue-3840/version-two_hard-tabs.rs new file mode 100644 index 000000000000..084db3d14656 --- /dev/null +++ b/tests/target/issue-3840/version-two_hard-tabs.rs @@ -0,0 +1,26 @@ +// rustfmt-hard_tabs: true +// rustfmt-version: Two + +impl< + Target: FromEvent + FromEvent, + A: Widget2, + B: Widget2, + C: for<'a> CtxFamily<'a>, +> Widget2 for WidgetEventLifter +{ + type Ctx = C; + type Event = Vec; +} + +mod foo { + impl< + Target: FromEvent + FromEvent, + A: Widget2, + B: Widget2, + C: for<'a> CtxFamily<'a>, + > Widget2 for WidgetEventLifter + { + type Ctx = C; + type Event = Vec; + } +} diff --git a/tests/target/issue-3840/version-two_soft-tabs.rs b/tests/target/issue-3840/version-two_soft-tabs.rs new file mode 100644 index 000000000000..bc59b0baa56c --- /dev/null +++ b/tests/target/issue-3840/version-two_soft-tabs.rs @@ -0,0 +1,25 @@ +// rustfmt-version: Two + +impl< + Target: FromEvent + FromEvent, + A: Widget2, + B: Widget2, + C: for<'a> CtxFamily<'a>, +> Widget2 for WidgetEventLifter +{ + type Ctx = C; + type Event = Vec; +} + +mod foo { + impl< + Target: FromEvent + FromEvent, + A: Widget2, + B: Widget2, + C: for<'a> CtxFamily<'a>, + > Widget2 for WidgetEventLifter + { + type Ctx = C; + type Event = Vec; + } +} diff --git a/tests/target/issue-3845.rs b/tests/target/issue-3845.rs new file mode 100644 index 000000000000..877c05b86a90 --- /dev/null +++ b/tests/target/issue-3845.rs @@ -0,0 +1,8 @@ +fn main() { + || { + #[allow(deprecated)] + { + u8::max_value() + } + }; +} diff --git a/tests/target/issue-3882.rs b/tests/target/issue-3882.rs new file mode 100644 index 000000000000..5eb442af9747 --- /dev/null +++ b/tests/target/issue-3882.rs @@ -0,0 +1,4 @@ +// rustfmt-version: Two +fn bar(_t: T, // bar +) { +} diff --git a/tests/target/issue-3974.rs b/tests/target/issue-3974.rs new file mode 100644 index 000000000000..a9f992ebd5c6 --- /dev/null +++ b/tests/target/issue-3974.rs @@ -0,0 +1,10 @@ +fn emulate_foreign_item() { + match link_name { + // A comment here will duplicate the attribute + #[rustfmt::skip] + | "pthread_mutexattr_init" + | "pthread_mutexattr_settype" + | "pthread_mutex_init" + => {} + } +} diff --git a/tests/target/issue-3987/format_macro_bodies_false.rs b/tests/target/issue-3987/format_macro_bodies_false.rs new file mode 100644 index 000000000000..1352b762e450 --- /dev/null +++ b/tests/target/issue-3987/format_macro_bodies_false.rs @@ -0,0 +1,26 @@ +// rustfmt-format_macro_bodies: false + +// with comments +macro_rules! macros { + () => {{ + Struct { + field: ( + 42 + //comment 1 + 42 + //comment 2 + ), + }; + }}; +} + +// without comments +macro_rules! macros { + () => {{ + Struct { + field: ( + 42 + + 42 + ), + }; + }}; +} diff --git a/tests/target/issue-3987/format_macro_bodies_true.rs b/tests/target/issue-3987/format_macro_bodies_true.rs new file mode 100644 index 000000000000..88d57159c859 --- /dev/null +++ b/tests/target/issue-3987/format_macro_bodies_true.rs @@ -0,0 +1,21 @@ +// rustfmt-format_macro_bodies: true + +// with comments +macro_rules! macros { + () => {{ + Struct { + field: ( + 42 + //comment 1 + 42 + //comment 2 + ), + }; + }}; +} + +// without comments +macro_rules! macros { + () => {{ + Struct { field: (42 + 42) }; + }}; +} diff --git a/tests/target/issue-4018.rs b/tests/target/issue-4018.rs new file mode 100644 index 000000000000..cef3be061486 --- /dev/null +++ b/tests/target/issue-4018.rs @@ -0,0 +1,11 @@ +fn main() { + /* extra comment */ +} + +fn main() { + println!(""); + // comment 1 + // comment 2 + // comment 3 + // comment 4 +} diff --git a/tests/target/issue-4020.rs b/tests/target/issue-4020.rs new file mode 100644 index 000000000000..f29ecec028b7 --- /dev/null +++ b/tests/target/issue-4020.rs @@ -0,0 +1,9 @@ +// rustfmt-wrap_comments: true + +/** foobar */ +const foo1: u32 = 0; + +/** + * foobar + */ +const foo2: u32 = 0; diff --git a/tests/target/issue-4029.rs b/tests/target/issue-4029.rs new file mode 100644 index 000000000000..314d01805885 --- /dev/null +++ b/tests/target/issue-4029.rs @@ -0,0 +1,7 @@ +// issue #4029 +#[derive(Debug, Clone, Default Hash)] +struct S; + +// issue #3898 +#[derive(Debug, Clone, Default,, Hash)] +struct T; diff --git a/tests/target/issue-4036/one.rs b/tests/target/issue-4036/one.rs new file mode 100644 index 000000000000..54e490b7fbea --- /dev/null +++ b/tests/target/issue-4036/one.rs @@ -0,0 +1,12 @@ +// rustfmt-format_strings: true + +macro_rules! test { + () => { + fn from() { + None.expect( + "We asserted that `buffer.len()` is exactly `$n` so we can expect \ + `ApInt::from_iter` to be successful.", + ) + } + }; +} diff --git a/tests/target/issue-4036/three.rs b/tests/target/issue-4036/three.rs new file mode 100644 index 000000000000..394dc8633f53 --- /dev/null +++ b/tests/target/issue-4036/three.rs @@ -0,0 +1,17 @@ +// rustfmt-format_strings: true +// rustfmt-hard_tabs: true + +macro_rules! test { + () => { + fn from() { + None.expect( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor \ + incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis \ + nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. \ + Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu \ + fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in \ + culpa qui officia deserunt mollit anim id est laborum.", + ) + } + }; +} diff --git a/tests/target/issue-4036/two.rs b/tests/target/issue-4036/two.rs new file mode 100644 index 000000000000..01cafa76b684 --- /dev/null +++ b/tests/target/issue-4036/two.rs @@ -0,0 +1,16 @@ +// rustfmt-format_strings: true + +macro_rules! test { + () => { + fn from() { + None.expect( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor \ + incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis \ + nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. \ + Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu \ + fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in \ + culpa qui officia deserunt mollit anim id est laborum.", + ) + } + }; +} diff --git a/tests/target/issue-4041.rs b/tests/target/issue-4041.rs new file mode 100644 index 000000000000..e9c693836f2c --- /dev/null +++ b/tests/target/issue-4041.rs @@ -0,0 +1,6 @@ +// rustfmt-wrap_comments: true +//! List: +//! - Sub list: +//! + very long #1 blah blah blah blah blah blah blah blah blah blah blah blah +//! foo baar baxxxxxxxx long line 1231421230912i3091238192038 +//! + very long #2 blah blah blah blah blah blah blah blah blah blah blah blah diff --git a/tests/target/issue-4068.rs b/tests/target/issue-4068.rs new file mode 100644 index 000000000000..cd8a1f2765f1 --- /dev/null +++ b/tests/target/issue-4068.rs @@ -0,0 +1,3 @@ +fn main() { + extern "C" fn packet_records_options_impl_layout_length_encoding_option_len_multiplier(); +} diff --git a/tests/target/issue-4079.rs b/tests/target/issue-4079.rs new file mode 100644 index 000000000000..1871c5b8a133 --- /dev/null +++ b/tests/target/issue-4079.rs @@ -0,0 +1,11 @@ +// rustfmt-wrap_comments: true + +/*! + * Lorem ipsum dolor sit amet, consectetur adipiscing elit. In lacinia + * ullamcorper lorem, non hendrerit enim convallis ut. Curabitur id sem + * volutpat + */ + +/*! Lorem ipsum dolor sit amet, consectetur adipiscing elit. In lacinia + * ullamcorper lorem, non hendrerit enim convallis ut. Curabitur id sem + * volutpat */ diff --git a/tests/target/issue-4115.rs b/tests/target/issue-4115.rs new file mode 100644 index 000000000000..0dd7bdbd04f0 --- /dev/null +++ b/tests/target/issue-4115.rs @@ -0,0 +1,8 @@ +#[derive( + A, + B, + C, + D, + // E, +)] +fn foo() {} diff --git a/tests/target/issue-4120.rs b/tests/target/issue-4120.rs new file mode 100644 index 000000000000..a7d461dcfdb5 --- /dev/null +++ b/tests/target/issue-4120.rs @@ -0,0 +1,85 @@ +fn main() { + let x = if true { + 1 + // In if + } else { + 0 + // In else + }; + + let x = if true { + 1 + /* In if */ + } else { + 0 + /* In else */ + }; + + let z = if true { + if true { + 1 + + // In if level 2 + } else { + 2 + } + } else { + 3 + }; + + let a = if true { + 1 + // In if + } else { + 0 + // In else + }; + + let a = if true { + 1 + + // In if + } else { + 0 + // In else + }; + + let b = if true { + 1 + + // In if + } else { + 0 + // In else + }; + + let c = if true { + 1 + + // In if + } else { + 0 + // In else + }; + for i in 0..2 { + println!("Something"); + // In for + } + + for i in 0..2 { + println!("Something"); + /* In for */ + } + + extern "C" { + fn first(); + + // In foreign mod + } + + extern "C" { + fn first(); + + /* In foreign mod */ + } +} diff --git a/tests/target/issue-4152.rs b/tests/target/issue-4152.rs new file mode 100644 index 000000000000..80f9ff5e3043 --- /dev/null +++ b/tests/target/issue-4152.rs @@ -0,0 +1,18 @@ +// rustfmt-hard_tabs: true + +macro_rules! bit { + ($bool:expr) => { + if $bool { + 1; + 1 + } else { + 0; + 0 + } + }; +} +macro_rules! add_one { + ($vec:expr) => {{ + $vec.push(1); + }}; +} diff --git a/tests/target/issue-4159.rs b/tests/target/issue-4159.rs new file mode 100644 index 000000000000..2f8cf20da2c9 --- /dev/null +++ b/tests/target/issue-4159.rs @@ -0,0 +1,18 @@ +extern "C" { + type A: Ord; + + type A<'a> + where + 'a: 'static; + + type A + where + T: 'static; + + type A = u8; + + type A<'a: 'static, T: Ord + 'static>: Eq + PartialEq + where + T: 'static + Copy, + = Vec; +} diff --git a/tests/target/issue-4210-disabled.rs b/tests/target/issue-4210-disabled.rs new file mode 100644 index 000000000000..3ba87aab8c54 --- /dev/null +++ b/tests/target/issue-4210-disabled.rs @@ -0,0 +1,15 @@ +// rustfmt-wrap_comments: false + +/// Table that is > 80 symbols: +/// +/// | table | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | +/// |-------|-----------------------------------------------------------------------------| +/// | val | x | +pub struct Item; + +/// Table value that is > 80 symbols: +/// +/// | table | heading +/// |----------|----------------------------------------------------------------------------- +/// | long val | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +pub struct Item2; diff --git a/tests/target/issue-4210.rs b/tests/target/issue-4210.rs new file mode 100644 index 000000000000..3fd966390826 --- /dev/null +++ b/tests/target/issue-4210.rs @@ -0,0 +1,15 @@ +// rustfmt-wrap_comments: true + +/// Table that is > 80 symbols: +/// +/// | table | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | +/// |-------|-----------------------------------------------------------------------------| +/// | val | x | +pub struct Item; + +/// Table value that is > 80 symbols: +/// +/// | table | heading +/// |----------|----------------------------------------------------------------------------- +/// | long val | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +pub struct Item2; diff --git a/tests/target/issue-4243.rs b/tests/target/issue-4243.rs new file mode 100644 index 000000000000..67fa1d2a312f --- /dev/null +++ b/tests/target/issue-4243.rs @@ -0,0 +1,28 @@ +fn main() { + type A: AA /*AA*/ + /*AB*/ AB + AC = AA + /*AA*/ + + + /*AB*/ + AB + + AC; + + type B: BA /*BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA*/ + + /*BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB*/ BB + + BC = BA /*BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA*/ + + /*BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB*/ BB + + BC; + + type C: CA // CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + // CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + + + // CBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB + // CBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB + CB + + CC = CA // CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + // CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + + + // CBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB + // CBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB + CB + + CC; +} diff --git a/tests/target/issue-4244.rs b/tests/target/issue-4244.rs new file mode 100644 index 000000000000..8958ba99e80a --- /dev/null +++ b/tests/target/issue-4244.rs @@ -0,0 +1,20 @@ +pub struct SS {} + +pub type A /* A Comment */ = SS; + +pub type B // Comment + // B + = SS; + +pub type C + /* Comment C */ + = SS; + +pub trait D { + type E /* Comment E */ = SS; +} + +type F<'a: 'static, T: Ord + 'static>: Eq + PartialEq +where + T: 'static + Copy, /* x */ += Vec; diff --git a/tests/target/issue-4245.rs b/tests/target/issue-4245.rs new file mode 100644 index 000000000000..e3d40eb42674 --- /dev/null +++ b/tests/target/issue-4245.rs @@ -0,0 +1,34 @@ +fn a( + a: & // Comment + // Another comment + 'a File, +) { +} + +fn b(b: & /* Another Comment */ 'a File) {} + +fn c(c: &'a /*Comment */ mut /*Comment */ File) {} + +fn d( + c: & // Comment + 'b // Multi Line + // Comment + mut // Multi Line + // Comment + File, +) { +} + +fn e( + c: & // Comment + File, +) { +} + +fn d( + c: & // Comment + mut // Multi Line + // Comment + File, +) { +} diff --git a/tests/target/issue-4310.rs b/tests/target/issue-4310.rs new file mode 100644 index 000000000000..6cf494fc5b83 --- /dev/null +++ b/tests/target/issue-4310.rs @@ -0,0 +1,9 @@ +#![feature(const_generics)] + +fn foo< + const N: [u8; { + struct Inner<'a>(&'a ()); + 3 + }], +>() { +} diff --git a/tests/target/issue-4312.rs b/tests/target/issue-4312.rs new file mode 100644 index 000000000000..b36b0efdb9e2 --- /dev/null +++ b/tests/target/issue-4312.rs @@ -0,0 +1,22 @@ +// issue 4312 +fn main() { + /* " */ + println!("Hello, world!"); + /* abc " */ + println!("Hello, world!"); + /* " abc */ + println!("Hello, world!"); + let y = 4; + let x = match 1 + y == 3 { + True => 3, + False => 4, + /* " unreachable */ + }; +} + +// issue 4806 +enum X { + A, + B, + /*"*/ +} diff --git a/tests/target/issue-4313.rs b/tests/target/issue-4313.rs new file mode 100644 index 000000000000..c390ee6ba30f --- /dev/null +++ b/tests/target/issue-4313.rs @@ -0,0 +1,5 @@ +extern "C" { + fn f() { + fn g() {} + } +} diff --git a/tests/target/issue-4382.rs b/tests/target/issue-4382.rs new file mode 100644 index 000000000000..740fa9bfe0a8 --- /dev/null +++ b/tests/target/issue-4382.rs @@ -0,0 +1,10 @@ +pub const NAME_MAX: usize = { + #[cfg(target_os = "linux")] + { + 1024 + } + #[cfg(target_os = "freebsd")] + { + 255 + } +}; diff --git a/tests/target/issue-4398.rs b/tests/target/issue-4398.rs new file mode 100644 index 000000000000..2ca894528e99 --- /dev/null +++ b/tests/target/issue-4398.rs @@ -0,0 +1,19 @@ +impl Struct { + /// Documentation for `foo` + #[rustfmt::skip] // comment on why use a skip here + pub fn foo(&self) {} +} + +impl Struct { + /// Documentation for `foo` + #[rustfmt::skip] // comment on why use a skip here + pub fn foo(&self) {} +} + +/// Documentation for `Struct` +#[rustfmt::skip] // comment +impl Struct { + /// Documentation for `foo` + #[rustfmt::skip] // comment on why use a skip here + pub fn foo(&self) {} +} diff --git a/tests/target/issue-4427.rs b/tests/target/issue-4427.rs new file mode 100644 index 000000000000..c8a37ead8cb2 --- /dev/null +++ b/tests/target/issue-4427.rs @@ -0,0 +1,30 @@ +const A: usize = + // Some constant + 2; + +const B: usize = + /* constant */ + 3; + +const C: usize = /* foo */ 5; + +const D: usize = // baz + /* Some constant */ + /* ba */ + { + 3 + // foo + }; +const E: usize = /* foo */ 5; +const F: usize = { 7 }; +const G: usize = + /* foooooooooooooooooooooooooooooooooooooooooooooooooooooooo0000000000000000xx00 */ + 5; +const H: usize = /* asdfasdf */ + match G > 1 { + true => 1, + false => 3, + }; + +pub static FOO_BAR: Vec = //f + { vec![] }; diff --git a/tests/target/issue-447.rs b/tests/target/issue-447.rs new file mode 100644 index 000000000000..d41cdb65cd0a --- /dev/null +++ b/tests/target/issue-447.rs @@ -0,0 +1,40 @@ +// rustfmt-normalize_comments: true + +fn main() { + if + // shouldn't be dropped + // shouldn't be dropped + cond + // shouldn't be dropped + // shouldn't be dropped + { + } + // shouldn't be dropped + // shouldn't be dropped + else + // shouldn't be dropped + // shouldn't be dropped + if + // shouldn't be dropped + // shouldn't be dropped + cond + // shouldn't be dropped + // shouldn't be dropped + { + } + // shouldn't be dropped + // shouldn't be dropped + else + // shouldn't be dropped + // shouldn't be dropped + { + } + + if + // shouldn't be dropped + // shouldn't be dropped + let Some(x) = y + // shouldn't be dropped + // shouldn't be dropped + {} +} diff --git a/tests/target/issue-4530.rs b/tests/target/issue-4530.rs new file mode 100644 index 000000000000..296dc559a934 --- /dev/null +++ b/tests/target/issue-4530.rs @@ -0,0 +1,9 @@ +// rustfmt-version: Two +fn main() { + let [ + aaaaaaaaaaaaaaaaaaaaaaaaaa, + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, + cccccccccccccccccccccccccc, + ddddddddddddddddddddddddd, + ] = panic!(); +} diff --git a/tests/target/issue-4577.rs b/tests/target/issue-4577.rs new file mode 100644 index 000000000000..1bd9eb6b8060 --- /dev/null +++ b/tests/target/issue-4577.rs @@ -0,0 +1,15 @@ +fn main() { + let s: String = "ABAABBAA" + .chars() + .filter(|c| if *c == 'A' { true } else { false }) + .map(|c| -> char { + if c == 'A' { + '0' + } else { + '1' + } + }) + .collect(); + + println!("{}", s); +} diff --git a/tests/target/issue-4603.rs b/tests/target/issue-4603.rs new file mode 100644 index 000000000000..e8c368a247fd --- /dev/null +++ b/tests/target/issue-4603.rs @@ -0,0 +1,47 @@ +// Formatting when original macro snippet is used + +// Original issue #4603 code +#![feature(or_patterns)] +macro_rules! t_or_f { + () => { + (true // some comment + | false) + }; +} + +// Other test cases variations +macro_rules! RULES { + () => { + ( + xxxxxxx // COMMENT + | yyyyyyy + ) + }; +} +macro_rules! RULES { + () => { + (xxxxxxx // COMMENT + | yyyyyyy) + }; +} + +fn main() { + macro_rules! RULES { + () => { + (xxxxxxx // COMMENT + | yyyyyyy) + }; + } +} + +macro_rules! RULES { + () => { + (xxxxxxx /* COMMENT */ | yyyyyyy) + }; +} +macro_rules! RULES { + () => { + (xxxxxxx /* COMMENT */ + | yyyyyyy) + }; +} diff --git a/tests/target/issue-4615/minimum_example.rs b/tests/target/issue-4615/minimum_example.rs new file mode 100644 index 000000000000..223b89b812d3 --- /dev/null +++ b/tests/target/issue-4615/minimum_example.rs @@ -0,0 +1,5 @@ +info!( + //debug + "{}: sending function_code={:04x} data={:04x} crc=0x{:04X} data={:02X?}", + self.name, function_code, data, crc, output_cmd +); diff --git a/tests/target/issue-4643.rs b/tests/target/issue-4643.rs new file mode 100644 index 000000000000..ef99e4db382c --- /dev/null +++ b/tests/target/issue-4643.rs @@ -0,0 +1,19 @@ +// output doesn't get corrupted when using comments within generic type parameters of a trait + +pub trait Something< + A, + // some comment + B, + C, +> +{ + fn a(&self, x: A) -> i32; + fn b(&self, x: B) -> i32; + fn c(&self, x: C) -> i32; +} + +pub trait SomethingElse { + fn a(&self, x: A) -> i32; + fn b(&self, x: B) -> i32; + fn c(&self, x: C) -> i32; +} diff --git a/tests/target/issue-4646.rs b/tests/target/issue-4646.rs new file mode 100644 index 000000000000..4e149399f062 --- /dev/null +++ b/tests/target/issue-4646.rs @@ -0,0 +1,20 @@ +trait Foo { + fn bar(&self) + // where + // Self: Bar + ; +} + +trait Foo { + fn bar(&self) + // where + // Self: Bar + ; +} + +trait Foo { + fn bar(&self) + // where + // Self: Bar + ; +} diff --git a/tests/target/issue-4656/format_me_please.rs b/tests/target/issue-4656/format_me_please.rs new file mode 100644 index 000000000000..421e195a2fbd --- /dev/null +++ b/tests/target/issue-4656/format_me_please.rs @@ -0,0 +1 @@ +pub fn hello() {} diff --git a/tests/target/issue-4656/lib.rs b/tests/target/issue-4656/lib.rs new file mode 100644 index 000000000000..5dac91b8aab2 --- /dev/null +++ b/tests/target/issue-4656/lib.rs @@ -0,0 +1,7 @@ +extern crate cfg_if; + +cfg_if::cfg_if! { + if #[cfg(target_family = "unix")] { + mod format_me_please; + } +} diff --git a/tests/target/issue-4656/lib2.rs b/tests/target/issue-4656/lib2.rs new file mode 100644 index 000000000000..b17fffc58e1d --- /dev/null +++ b/tests/target/issue-4656/lib2.rs @@ -0,0 +1,3 @@ +its_a_macro! { + // Contents +} diff --git a/tests/target/issue-4689/one.rs b/tests/target/issue-4689/one.rs new file mode 100644 index 000000000000..7735e34f3b5e --- /dev/null +++ b/tests/target/issue-4689/one.rs @@ -0,0 +1,150 @@ +// rustfmt-version: One + +// Based on the issue description +pub trait PrettyPrinter<'tcx>: + Printer< + 'tcx, + Error = fmt::Error, + Path = Self, + Region = Self, + Type = Self, + DynExistential = Self, + Const = Self, +> +{ + // +} +pub trait PrettyPrinter<'tcx>: + Printer< + 'tcx, + Error = fmt::Error, + Path = Self, + Region = Self, + Type = Self, + DynExistential = Self, + Const = Self, + > + fmt::Write +{ + // +} +pub trait PrettyPrinter<'tcx>: + Printer< + 'tcx, + Error = fmt::Error, + Path = Self, + Region = Self, + Type = Self, + DynExistential = Self, + Const = Self, + > + fmt::Write1 + + fmt::Write2 +{ + // +} +pub trait PrettyPrinter<'tcx>: + fmt::Write + + Printer< + 'tcx, + Error = fmt::Error, + Path = Self, + Region = Self, + Type = Self, + DynExistential = Self, + Const = Self, + > +{ + // +} +pub trait PrettyPrinter<'tcx>: + fmt::Write + + Printer1< + 'tcx, + Error = fmt::Error, + Path = Self, + Region = Self, + Type = Self, + DynExistential = Self, + Const = Self, + > + Printer2< + 'tcx, + Error = fmt::Error, + Path = Self, + Region = Self, + Type = Self, + DynExistential = Self, + Const = Self, + > +{ + // +} + +// Some test cases to ensure other cases formatting were not changed +fn f() -> Box< + FnMut() -> Thing< + WithType = LongItemName, + Error = LONGLONGLONGLONGLONGONGEvenLongerErrorNameLongerLonger, + >, +> { +} +fn f() -> Box< + FnMut() -> Thing< + WithType = LongItemName, + Error = LONGLONGLONGLONGLONGONGEvenLongerErrorNameLongerLonger, + > + fmt::Write1 + + fmt::Write2, +> { +} + +fn foo(foo2: F) +where + F: Fn( + // this comment is deleted + ), +{ +} +fn foo(foo2: F) +where + F: Fn( + // this comment is deleted + ) + fmt::Write, +{ +} + +fn elaborate_bounds(mut mk_cand: F) +where + F: FnMut( + &mut ProbeContext, + ty::PolyTraitRefffffffffffffffffffffffffffffffff, + tyyyyyyyyyyyyyyyyyyyyy::AssociatedItem, + ), +{ +} +fn elaborate_bounds(mut mk_cand: F) +where + F: FnMut( + &mut ProbeContext, + ty::PolyTraitRefffffffffffffffffffffffffffffffff, + tyyyyyyyyyyyyyyyyyyyyy::AssociatedItem, + ) + fmt::Write, +{ +} + +fn build_sorted_static_get_entry_names( + mut entries: entryyyyyyyy, +) -> (impl Fn( + AlphabeticalTraversal, + Seconddddddddddddddddddddddddddddddddddd, +) -> Parammmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm + + Sendddddddddddddddddddddddddddddddddddddddddddd) { +} + +pub trait SomeTrait: + Cloneeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee + + Eqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq +{ +} + +trait B = where + for<'b> &'b Self: Send + + Cloneeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee + + Copyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy; diff --git a/tests/target/issue-4689/two.rs b/tests/target/issue-4689/two.rs new file mode 100644 index 000000000000..e3b5cd22810b --- /dev/null +++ b/tests/target/issue-4689/two.rs @@ -0,0 +1,152 @@ +// rustfmt-version: Two + +// Based on the issue description +pub trait PrettyPrinter<'tcx>: + Printer< + 'tcx, + Error = fmt::Error, + Path = Self, + Region = Self, + Type = Self, + DynExistential = Self, + Const = Self, + > +{ + // +} +pub trait PrettyPrinter<'tcx>: + Printer< + 'tcx, + Error = fmt::Error, + Path = Self, + Region = Self, + Type = Self, + DynExistential = Self, + Const = Self, + > + fmt::Write +{ + // +} +pub trait PrettyPrinter<'tcx>: + Printer< + 'tcx, + Error = fmt::Error, + Path = Self, + Region = Self, + Type = Self, + DynExistential = Self, + Const = Self, + > + fmt::Write1 + + fmt::Write2 +{ + // +} +pub trait PrettyPrinter<'tcx>: + fmt::Write + + Printer< + 'tcx, + Error = fmt::Error, + Path = Self, + Region = Self, + Type = Self, + DynExistential = Self, + Const = Self, + > +{ + // +} +pub trait PrettyPrinter<'tcx>: + fmt::Write + + Printer1< + 'tcx, + Error = fmt::Error, + Path = Self, + Region = Self, + Type = Self, + DynExistential = Self, + Const = Self, + > + Printer2< + 'tcx, + Error = fmt::Error, + Path = Self, + Region = Self, + Type = Self, + DynExistential = Self, + Const = Self, + > +{ + // +} + +// Some test cases to ensure other cases formatting were not changed +fn f() -> Box< + FnMut() -> Thing< + WithType = LongItemName, + Error = LONGLONGLONGLONGLONGONGEvenLongerErrorNameLongerLonger, + >, +> { +} +fn f() -> Box< + FnMut() -> Thing< + WithType = LongItemName, + Error = LONGLONGLONGLONGLONGONGEvenLongerErrorNameLongerLonger, + > + fmt::Write1 + + fmt::Write2, +> { +} + +fn foo(foo2: F) +where + F: Fn( + // this comment is deleted + ), +{ +} +fn foo(foo2: F) +where + F: Fn( + // this comment is deleted + ) + fmt::Write, +{ +} + +fn elaborate_bounds(mut mk_cand: F) +where + F: FnMut( + &mut ProbeContext, + ty::PolyTraitRefffffffffffffffffffffffffffffffff, + tyyyyyyyyyyyyyyyyyyyyy::AssociatedItem, + ), +{ +} +fn elaborate_bounds(mut mk_cand: F) +where + F: FnMut( + &mut ProbeContext, + ty::PolyTraitRefffffffffffffffffffffffffffffffff, + tyyyyyyyyyyyyyyyyyyyyy::AssociatedItem, + ) + fmt::Write, +{ +} + +fn build_sorted_static_get_entry_names( + mut entries: entryyyyyyyy, +) -> ( + impl Fn( + AlphabeticalTraversal, + Seconddddddddddddddddddddddddddddddddddd, + ) -> Parammmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm + + Sendddddddddddddddddddddddddddddddddddddddddddd +) { +} + +pub trait SomeTrait: + Cloneeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee + + Eqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq +{ +} + +trait B = where + for<'b> &'b Self: Send + + Cloneeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee + + Copyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy; diff --git a/tests/target/issue-4791/buggy.rs b/tests/target/issue-4791/buggy.rs new file mode 100644 index 000000000000..fff58be99a50 --- /dev/null +++ b/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/tests/target/issue-4791/issue_4928.rs b/tests/target/issue-4791/issue_4928.rs new file mode 100644 index 000000000000..29f6bda90631 --- /dev/null +++ b/tests/target/issue-4791/issue_4928.rs @@ -0,0 +1,70 @@ +// rustfmt-brace_style: SameLineWhere +// rustfmt-comment_width: 100 +// rustfmt-edition: 2018 +// rustfmt-fn_params_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/tests/target/issue-4791/no_trailing_comma.rs b/tests/target/issue-4791/no_trailing_comma.rs new file mode 100644 index 000000000000..4a37163969ae --- /dev/null +++ b/tests/target/issue-4791/no_trailing_comma.rs @@ -0,0 +1,8 @@ +// rustfmt-struct_field_align_threshold: 0 +// rustfmt-trailing_comma: Never + +pub struct Baz { + group_a: u8, + + group_b: u8 +} diff --git a/tests/target/issue-4791/trailing_comma.rs b/tests/target/issue-4791/trailing_comma.rs new file mode 100644 index 000000000000..29a224b3f6d9 --- /dev/null +++ b/tests/target/issue-4791/trailing_comma.rs @@ -0,0 +1,14 @@ +// rustfmt-struct_field_align_threshold: 30 +// rustfmt-trailing_comma: Always + +struct Foo { + group_a: u8, + + group_b: u8, +} + +struct Bar { + group_a: u8, + + group_b: u8, +} diff --git a/tests/target/issue-4816/lib.rs b/tests/target/issue-4816/lib.rs new file mode 100644 index 000000000000..246e775e1feb --- /dev/null +++ b/tests/target/issue-4816/lib.rs @@ -0,0 +1,35 @@ +#![feature(const_generics_defaults)] +struct Foo; +struct Bar; +struct Lots< + const N1BlahFooUwU: usize = { 10 + 28 + 1872 / 10 * 3 }, + const N2SecondParamOhmyyy: usize = { N1BlahFooUwU / 2 + 10 * 2 }, +>; +struct NamesRHard; +struct FooBar< + const LessThan100ButClose: usize = { + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + }, +>; +struct FooBarrrrrrrr< + const N: usize = { + 13478234326456456444323871 + + 1 + + 1 + + 1 + + 1 + + 1 + + 1 + + 1 + + 1 + + 1 + + 1 + + 1 + + 1 + + 1 + + 1 + + 1 + + 1 + + 1 + }, +>; diff --git a/tests/target/issue-4908-2.rs b/tests/target/issue-4908-2.rs new file mode 100644 index 000000000000..023b323cb279 --- /dev/null +++ b/tests/target/issue-4908-2.rs @@ -0,0 +1,20 @@ +#![feature(more_qualified_paths)] + +fn main() { + // destructure through a qualified path + let ::Assoc { br } = StructStruct { br: 2 }; +} + +struct StructStruct { + br: i8, +} + +struct Foo; + +trait A { + type Assoc; +} + +impl A for Foo { + type Assoc = StructStruct; +} diff --git a/tests/target/issue-4908.rs b/tests/target/issue-4908.rs new file mode 100644 index 000000000000..ac5357abe2a1 --- /dev/null +++ b/tests/target/issue-4908.rs @@ -0,0 +1,34 @@ +#![feature(more_qualified_paths)] + +mod foo_bar { + pub enum Example { + Example1 {}, + Example2 {}, + } +} + +fn main() { + foo!(crate::foo_bar::Example, Example1); + + let i1 = foo_bar::Example::Example1 {}; + + assert_eq!(i1.foo_example(), 1); + + let i2 = foo_bar::Example::Example2 {}; + + assert_eq!(i2.foo_example(), 2); +} + +#[macro_export] +macro_rules! foo { + ($struct:path, $variant:ident) => { + impl $struct { + pub fn foo_example(&self) -> i32 { + match self { + <$struct>::$variant { .. } => 1, + _ => 2, + } + } + } + }; +} diff --git a/tests/target/issue-4926/deeply_nested_struct.rs b/tests/target/issue-4926/deeply_nested_struct.rs new file mode 100644 index 000000000000..072cf2f6674a --- /dev/null +++ b/tests/target/issue-4926/deeply_nested_struct.rs @@ -0,0 +1,38 @@ +// rustfmt-struct_field_align_threshold: 30 + +struct X { + a: i32, + b: i32, + c: i32, +} + +fn test(x: X) { + let d = { + let e = { + let f = { + let g = { + let h = { + let i = { + let j = { + matches!( + x, + X { + a: 1_000, + b: 1_000, + .. + } + ) + }; + j + }; + i + }; + h + }; + g + }; + f + }; + e + }; +} diff --git a/tests/target/issue-4926/deeply_nested_struct_with_long_field_names.rs b/tests/target/issue-4926/deeply_nested_struct_with_long_field_names.rs new file mode 100644 index 000000000000..c7bc7f7296d6 --- /dev/null +++ b/tests/target/issue-4926/deeply_nested_struct_with_long_field_names.rs @@ -0,0 +1,44 @@ +// rustfmt-struct_field_align_threshold: 30 + +struct X { + really_really_long_field_a: i32, + really_really_really_long_field_b: i32, + really_really_really_really_long_field_c: i32, + really_really_really_really_really_long_field_d: i32, + really_really_really_really_really_really_long_field_e: i32, + f: i32, +} + +fn test(x: X) { + let d = { + let e = { + let f = { + let g = { + let h = { + let i = { + let j = { + matches!( + x, + X { + really_really_long_field_a: 10, + really_really_really_long_field_b: 10, + really_really_really_really_long_field_c: 10, + really_really_really_really_really_long_field_d: 10, + really_really_really_really_really_really_long_field_e: 10, + .. + } + ) + }; + j + }; + i + }; + h + }; + g + }; + f + }; + e + }; +} diff --git a/tests/target/issue-4926/deeply_nested_struct_with_many_fields.rs b/tests/target/issue-4926/deeply_nested_struct_with_many_fields.rs new file mode 100644 index 000000000000..69793162519a --- /dev/null +++ b/tests/target/issue-4926/deeply_nested_struct_with_many_fields.rs @@ -0,0 +1,54 @@ +// rustfmt-struct_field_align_threshold: 30 + +struct X { + a: i32, + b: i32, + c: i32, + d: i32, + e: i32, + f: i32, + g: i32, + h: i32, + i: i32, + j: i32, + k: i32, +} + +fn test(x: X) { + let d = { + let e = { + let f = { + let g = { + let h = { + let i = { + let j = { + matches!( + x, + X { + a: 1_000, + b: 1_000, + c: 1_000, + d: 1_000, + e: 1_000, + f: 1_000, + g: 1_000, + h: 1_000, + i: 1_000, + j: 1_000, + .. + } + ) + }; + j + }; + i + }; + h + }; + g + }; + f + }; + e + }; +} diff --git a/tests/target/issue-4926/enum_struct_field.rs b/tests/target/issue-4926/enum_struct_field.rs new file mode 100644 index 000000000000..2471df84653c --- /dev/null +++ b/tests/target/issue-4926/enum_struct_field.rs @@ -0,0 +1,41 @@ +// rustfmt-struct_field_align_threshold: 30 +// rustfmt-enum_discrim_align_threshold: 30 +// rustfmt-imports_layout: HorizontalVertical + +#[derive(Default)] +struct InnerStructA { + bbbbbbbbb: i32, + cccccccc: i32, +} + +enum SomeEnumNamedD { + E(InnerStructA), + F { + ggggggggggggggggggggggggg: bool, + h: bool, + }, +} + +impl SomeEnumNamedD { + fn f_variant() -> Self { + Self::F { + ggggggggggggggggggggggggg: true, + h: true, + } + } +} + +fn main() { + let kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk = SomeEnumNamedD::f_variant(); + let something_we_care_about = matches!( + kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk, + SomeEnumNamedD::F { + ggggggggggggggggggggggggg: true, + .. + } + ); + + if something_we_care_about { + println!("Yup it happened"); + } +} diff --git a/tests/target/issue-4926/minimum_example.rs b/tests/target/issue-4926/minimum_example.rs new file mode 100644 index 000000000000..06e18427465c --- /dev/null +++ b/tests/target/issue-4926/minimum_example.rs @@ -0,0 +1,10 @@ +// rustfmt-struct_field_align_threshold: 30 + +struct X { + a: i32, + b: i32, +} + +fn test(x: X) { + let y = matches!(x, X { a: 1, .. }); +} diff --git a/tests/target/issue-4926/struct_with_long_field_names.rs b/tests/target/issue-4926/struct_with_long_field_names.rs new file mode 100644 index 000000000000..ac4674ab5d52 --- /dev/null +++ b/tests/target/issue-4926/struct_with_long_field_names.rs @@ -0,0 +1,24 @@ +// rustfmt-struct_field_align_threshold: 30 + +struct X { + really_really_long_field_a: i32, + really_really_really_long_field_b: i32, + really_really_really_really_long_field_c: i32, + really_really_really_really_really_long_field_d: i32, + really_really_really_really_really_really_long_field_e: i32, + f: i32, +} + +fn test(x: X) { + let y = matches!( + x, + X { + really_really_long_field_a: 10, + really_really_really_long_field_b: 10, + really_really_really_really_long_field_c: 10, + really_really_really_really_really_long_field_d: 10, + really_really_really_really_really_really_long_field_e: 10, + .. + } + ); +} diff --git a/tests/target/issue-4926/struct_with_many_fields.rs b/tests/target/issue-4926/struct_with_many_fields.rs new file mode 100644 index 000000000000..96dfe14bf7dd --- /dev/null +++ b/tests/target/issue-4926/struct_with_many_fields.rs @@ -0,0 +1,34 @@ +// rustfmt-struct_field_align_threshold: 30 + +struct X { + a: i32, + b: i32, + c: i32, + d: i32, + e: i32, + f: i32, + g: i32, + h: i32, + i: i32, + j: i32, + k: i32, +} + +fn test(x: X) { + let y = matches!( + x, + X { + a: 1_000, + b: 1_000, + c: 1_000, + d: 1_000, + e: 1_000, + f: 1_000, + g: 1_000, + h: 1_000, + i: 1_000, + j: 1_000, + .. + } + ); +} diff --git a/tests/target/issue-4984/minimum_example.rs b/tests/target/issue-4984/minimum_example.rs new file mode 100644 index 000000000000..f0599c5d694b --- /dev/null +++ b/tests/target/issue-4984/minimum_example.rs @@ -0,0 +1,2 @@ +#[derive(/*Debug, */ Clone)] +struct Foo; diff --git a/tests/target/issue-4984/multi_line_derive.rs b/tests/target/issue-4984/multi_line_derive.rs new file mode 100644 index 000000000000..5fbd9784adc9 --- /dev/null +++ b/tests/target/issue-4984/multi_line_derive.rs @@ -0,0 +1,26 @@ +#[derive( + /* ---------- Some really important comment that just had to go inside the derive --------- */ + Debug, + Clone, + Eq, + PartialEq, +)] +struct Foo { + a: i32, + b: T, +} + +#[derive( + /* + Some really important comment that just had to go inside the derive. + Also had to be put over multiple lines + */ + Debug, + Clone, + Eq, + PartialEq, +)] +struct Bar { + a: i32, + b: T, +} diff --git a/tests/target/issue-4984/multiple_comments_within.rs b/tests/target/issue-4984/multiple_comments_within.rs new file mode 100644 index 000000000000..d2924f0d0f2e --- /dev/null +++ b/tests/target/issue-4984/multiple_comments_within.rs @@ -0,0 +1,11 @@ +#[derive( + /* ---------- Some really important comment that just had to go inside the derive --------- */ + Debug, + Clone, + /* Another comment */ Eq, + PartialEq, +)] +struct Foo { + a: i32, + b: T, +} diff --git a/tests/target/issue-4984/should_not_change.rs b/tests/target/issue-4984/should_not_change.rs new file mode 100644 index 000000000000..e46ee511084f --- /dev/null +++ b/tests/target/issue-4984/should_not_change.rs @@ -0,0 +1,5 @@ +#[derive(Clone, Debug, Eq, PartialEq)] +struct Foo; + +#[derive(Clone)] +struct Bar; diff --git a/tests/target/issue-5005/minimum_example.rs b/tests/target/issue-5005/minimum_example.rs new file mode 100644 index 000000000000..11cc645fa535 --- /dev/null +++ b/tests/target/issue-5005/minimum_example.rs @@ -0,0 +1,9 @@ +#![feature(more_qualified_paths)] +macro_rules! show { + ($ty:ty, $ex:expr) => { + match $ex { + <$ty>::A(_val) => println!("got a"), // formatting should not remove <$ty>:: + <$ty>::B => println!("got b"), + } + }; +} diff --git a/tests/target/issue-5009/1_minimum_example.rs b/tests/target/issue-5009/1_minimum_example.rs new file mode 100644 index 000000000000..55836f4bf52c --- /dev/null +++ b/tests/target/issue-5009/1_minimum_example.rs @@ -0,0 +1,4 @@ +fn main() { + // the "in" inside the pattern produced invalid syntax + for variable_in_here /* ... */ in 0..1 {} +} diff --git a/tests/target/issue-5009/2_many_in_connectors_in_pattern.rs b/tests/target/issue-5009/2_many_in_connectors_in_pattern.rs new file mode 100644 index 000000000000..d83590c6852f --- /dev/null +++ b/tests/target/issue-5009/2_many_in_connectors_in_pattern.rs @@ -0,0 +1,3 @@ +fn main() { + for in_in_in_in_in_in_in_in /* ... */ in 0..1 {} +} diff --git a/tests/target/issue-5009/3_nested_for_loop_with_connector_in_pattern.rs b/tests/target/issue-5009/3_nested_for_loop_with_connector_in_pattern.rs new file mode 100644 index 000000000000..9c800723939b --- /dev/null +++ b/tests/target/issue-5009/3_nested_for_loop_with_connector_in_pattern.rs @@ -0,0 +1,5 @@ +fn main() { + for variable_in_x /* ... */ in 0..1 { + for variable_in_y /* ... */ in 0..1 {} + } +} diff --git a/tests/target/issue-5009/4_nested_for_loop_with_if_elseif_else.rs b/tests/target/issue-5009/4_nested_for_loop_with_if_elseif_else.rs new file mode 100644 index 000000000000..a716d0d3082a --- /dev/null +++ b/tests/target/issue-5009/4_nested_for_loop_with_if_elseif_else.rs @@ -0,0 +1,13 @@ +fn main() { + for variable_in_x /* ... */ in 0..1 { + for variable_in_y /* ... */ in 0..1 { + if false { + + } else if false { + + } else { + + } + } + } +} diff --git a/tests/target/issue-5009/5_nested_for_loop_with_connector_in_if_elseif_else.rs b/tests/target/issue-5009/5_nested_for_loop_with_connector_in_if_elseif_else.rs new file mode 100644 index 000000000000..41ea46d4cb9b --- /dev/null +++ b/tests/target/issue-5009/5_nested_for_loop_with_connector_in_if_elseif_else.rs @@ -0,0 +1,15 @@ +fn main() { + let in_ = false; + + for variable_in_x /* ... */ in 0..1 { + for variable_in_y /* ... */ in 0..1 { + if in_ { + + } else if in_ { + + } else { + + } + } + } +} diff --git a/tests/target/issue-5009/6_deeply_nested_for_loop_with_connector_in_pattern.rs b/tests/target/issue-5009/6_deeply_nested_for_loop_with_connector_in_pattern.rs new file mode 100644 index 000000000000..789e54f7e5fb --- /dev/null +++ b/tests/target/issue-5009/6_deeply_nested_for_loop_with_connector_in_pattern.rs @@ -0,0 +1,32 @@ +fn main() { + for variable_in_a /* ... */ in 0..1 { + for variable_in_b /* ... */ in 0..1 { + for variable_in_c /* ... */ in 0..1 { + for variable_in_d /* ... */ in 0..1 { + for variable_in_e /* ... */ in 0..1 { + for variable_in_f /* ... */ in 0..1 { + for variable_in_g /* ... */ in 0..1 { + for variable_in_h /* ... */ in 0..1 { + for variable_in_i /* ... */ in 0..1 { + for variable_in_j /* ... */ in 0..1 { + for variable_in_k /* ... */ in 0..1 { + for variable_in_l /* ... */ in 0..1 { + for variable_in_m /* ... */ in 0..1 { + for variable_in_n /* ... */ in 0..1 { + for variable_in_o /* ... */ in 0..1 { + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } +} diff --git a/tests/target/issue-5011.rs b/tests/target/issue-5011.rs new file mode 100644 index 000000000000..9ad4a1929bdb --- /dev/null +++ b/tests/target/issue-5011.rs @@ -0,0 +1,8 @@ +pub(crate) struct ASlash( + // hello + i32, +); + +pub(crate) struct AStar(/* hello */ i32); + +pub(crate) struct BStar(/* hello */ i32); diff --git a/tests/target/issue-5012/trailing_comma_always.rs b/tests/target/issue-5012/trailing_comma_always.rs new file mode 100644 index 000000000000..ff9c40fbbd8d --- /dev/null +++ b/tests/target/issue-5012/trailing_comma_always.rs @@ -0,0 +1,8 @@ +// rustfmt-trailing_comma: Always + +pub struct Matrix +where + [T; R * C]:, +{ + contents: [T; R * C], +} diff --git a/tests/target/issue-5012/trailing_comma_never.rs b/tests/target/issue-5012/trailing_comma_never.rs new file mode 100644 index 000000000000..2fac8eae52b8 --- /dev/null +++ b/tests/target/issue-5012/trailing_comma_never.rs @@ -0,0 +1,8 @@ +// rustfmt-trailing_comma: Never + +pub struct Matrix +where + [T; R * C]: +{ + contents: [T; R * C] +} diff --git a/tests/target/issue-5023.rs b/tests/target/issue-5023.rs new file mode 100644 index 000000000000..4e84c7d98427 --- /dev/null +++ b/tests/target/issue-5023.rs @@ -0,0 +1,23 @@ +// rustfmt-wrap_comments: true + +/// A comment to test special unicode characters on boundaries +/// 是,是,是,是,是,是,是,是,是,是,是,是 it should break right here +/// this goes to the next line +fn main() { + if xxx { + let xxx = xxx + .into_iter() + .filter(|(xxx, xxx)| { + if let Some(x) = Some(1) { + // xxxxxxxxxxxxxxxxxx, xxxxxxxxxxxx, xxxxxxxxxxxxxxxxxxxx xxx xxxxxxx, xxxxx xxx + // xxxxxxxxxx. xxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxx xxx xxxxxxx + // 是sdfadsdfxxxxxxxxx,sdfaxxxxxx_xxxxx_masdfaonxxx, + if false { + return true; + } + } + false + }) + .collect(); + } +} diff --git a/tests/target/issue-5030.rs b/tests/target/issue-5030.rs new file mode 100644 index 000000000000..8ac3888bdbee --- /dev/null +++ b/tests/target/issue-5030.rs @@ -0,0 +1,21 @@ +// rustfmt-imports_granularity: Item +// rustfmt-group_imports: One + +// Confirm that attributes are duplicated to all items in the use statement +#[cfg(feature = "foo")] +use std::collections::HashMap; +#[cfg(feature = "foo")] +use std::collections::HashSet; + +// Separate the imports below from the ones above +const A: usize = 0; + +// Copying attrs works with import grouping as well +#[cfg(feature = "spam")] +use qux::bar; +#[cfg(feature = "spam")] +use qux::baz; +#[cfg(feature = "foo")] +use std::collections::HashMap; +#[cfg(feature = "foo")] +use std::collections::HashSet; diff --git a/tests/target/issue-5033/minimum_example.rs b/tests/target/issue-5033/minimum_example.rs new file mode 100644 index 000000000000..0e7df41deb2d --- /dev/null +++ b/tests/target/issue-5033/minimum_example.rs @@ -0,0 +1,8 @@ +// leading comment + +#![rustfmt::skip] +fn main() { + println!("main"); // commented +} + +// post comment diff --git a/tests/target/issue-5033/nested_modules.rs b/tests/target/issue-5033/nested_modules.rs new file mode 100644 index 000000000000..7a11133b60bb --- /dev/null +++ b/tests/target/issue-5033/nested_modules.rs @@ -0,0 +1,11 @@ +#![rustfmt::skip] + +mod a { + mod b { + + } + + // trailing comment b +} + +// trailing comment a diff --git a/tests/target/issue-5042/multi-line_comment_with_trailing_comma.rs b/tests/target/issue-5042/multi-line_comment_with_trailing_comma.rs new file mode 100644 index 000000000000..1ae1212b488d --- /dev/null +++ b/tests/target/issue-5042/multi-line_comment_with_trailing_comma.rs @@ -0,0 +1,24 @@ +fn main() { + // 5042 deals with trailing commas, not the indentation issue of these comments + // When a future PR fixes the inentation issues these test can be updated + let _ = std::ops::Add::add( + 10, 20, // ... + // ..., + ); + + let _ = std::ops::Add::add( + 10, 20, /* ... */ + // ..., + ); + + let _ = std::ops::Add::add( + 10, 20, // ..., + // ..., + ); + + let _ = std::ops::Add::add( + 10, 20, // ..., + /* ... + */ + ); +} diff --git a/tests/target/issue-5042/multi-line_comment_without_trailing_comma.rs b/tests/target/issue-5042/multi-line_comment_without_trailing_comma.rs new file mode 100644 index 000000000000..30d174664c9c --- /dev/null +++ b/tests/target/issue-5042/multi-line_comment_without_trailing_comma.rs @@ -0,0 +1,24 @@ +fn main() { + // 5042 deals with trailing commas, not the indentation issue of these comments + // When a future PR fixes the inentation issues these test can be updated + let _ = std::ops::Add::add( + 10, 20, // ... + // ... + ); + + let _ = std::ops::Add::add( + 10, 20, /* ... */ + // ... + ); + + let _ = std::ops::Add::add( + 10, 20, // ... + // ... + ); + + let _ = std::ops::Add::add( + 10, 20, // ... + /* ... + */ + ); +} diff --git a/tests/target/issue-5042/single-line_comment_with_trailing_comma.rs b/tests/target/issue-5042/single-line_comment_with_trailing_comma.rs new file mode 100644 index 000000000000..87b651dd285e --- /dev/null +++ b/tests/target/issue-5042/single-line_comment_with_trailing_comma.rs @@ -0,0 +1,7 @@ +fn main() { + let _ = std::ops::Add::add( + 10, 20, // ..., + ); + + let _ = std::ops::Add::add(10, 20 /* ... */); +} diff --git a/tests/target/issue-5042/single-line_comment_without_trailing_comma.rs b/tests/target/issue-5042/single-line_comment_without_trailing_comma.rs new file mode 100644 index 000000000000..116df86a4b55 --- /dev/null +++ b/tests/target/issue-5042/single-line_comment_without_trailing_comma.rs @@ -0,0 +1,7 @@ +fn main() { + let _ = std::ops::Add::add( + 10, 20, // ... + ); + + let _ = std::ops::Add::add(10, 20 /* ... */); +} diff --git a/tests/target/issue-5066/multi_line_struct_trailing_comma_always_struct_lit_width_0.rs b/tests/target/issue-5066/multi_line_struct_trailing_comma_always_struct_lit_width_0.rs new file mode 100644 index 000000000000..c7122c676237 --- /dev/null +++ b/tests/target/issue-5066/multi_line_struct_trailing_comma_always_struct_lit_width_0.rs @@ -0,0 +1,10 @@ +// rustfmt-trailing_comma: Always +// rustfmt-struct_lit_single_line: false +// rustfmt-struct_lit_width: 0 + +fn main() { + let Foo { + a, + .. + } = b; +} diff --git a/tests/target/issue-5066/multi_line_struct_trailing_comma_never_struct_lit_width_0.rs b/tests/target/issue-5066/multi_line_struct_trailing_comma_never_struct_lit_width_0.rs new file mode 100644 index 000000000000..68e89c4179f7 --- /dev/null +++ b/tests/target/issue-5066/multi_line_struct_trailing_comma_never_struct_lit_width_0.rs @@ -0,0 +1,10 @@ +// rustfmt-trailing_comma: Never +// rustfmt-struct_lit_single_line: false +// rustfmt-struct_lit_width: 0 + +fn main() { + let Foo { + a, + .. + } = b; +} diff --git a/tests/target/issue-5066/multi_line_struct_with_trailing_comma_always.rs b/tests/target/issue-5066/multi_line_struct_with_trailing_comma_always.rs new file mode 100644 index 000000000000..3368f0703868 --- /dev/null +++ b/tests/target/issue-5066/multi_line_struct_with_trailing_comma_always.rs @@ -0,0 +1,10 @@ +// rustfmt-trailing_comma: Always +// rustfmt-struct_lit_single_line: false + +// There is an issue with how this is formatted. +// formatting should look like ./multi_line_struct_trailing_comma_always_struct_lit_width_0.rs +fn main() { + let Foo { + a, .. + } = b; +} diff --git a/tests/target/issue-5066/multi_line_struct_with_trailing_comma_never.rs b/tests/target/issue-5066/multi_line_struct_with_trailing_comma_never.rs new file mode 100644 index 000000000000..cf63c4c983c4 --- /dev/null +++ b/tests/target/issue-5066/multi_line_struct_with_trailing_comma_never.rs @@ -0,0 +1,10 @@ +// rustfmt-trailing_comma: Never +// rustfmt-struct_lit_single_line: false + +// There is an issue with how this is formatted. +// formatting should look like ./multi_line_struct_trailing_comma_never_struct_lit_width_0.rs +fn main() { + let Foo { + a, .. + } = b; +} diff --git a/tests/target/issue-5066/with_trailing_comma_always.rs b/tests/target/issue-5066/with_trailing_comma_always.rs new file mode 100644 index 000000000000..e20bcec93169 --- /dev/null +++ b/tests/target/issue-5066/with_trailing_comma_always.rs @@ -0,0 +1,5 @@ +// rustfmt-trailing_comma: Always + +fn main() { + let Foo { a, .. } = b; +} diff --git a/tests/target/issue-5066/with_trailing_comma_never.rs b/tests/target/issue-5066/with_trailing_comma_never.rs new file mode 100644 index 000000000000..8b95bb137bca --- /dev/null +++ b/tests/target/issue-5066/with_trailing_comma_never.rs @@ -0,0 +1,5 @@ +// rustfmt-trailing_comma: Never + +fn main() { + let Foo { a, .. } = b; +} diff --git a/tests/target/issue-5088/deeply_nested_long_comment_wrap_comments_false.rs b/tests/target/issue-5088/deeply_nested_long_comment_wrap_comments_false.rs new file mode 100644 index 000000000000..f4801de01848 --- /dev/null +++ b/tests/target/issue-5088/deeply_nested_long_comment_wrap_comments_false.rs @@ -0,0 +1,33 @@ +// rustfmt-wrap_comments: false + +fn main() { + { + { + { + { + { + { + { + { + { + { + { + // - aaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa bbbbbbbbbb bbbbbbbbb bbbbbbbbb ccc cccccccccc ccccccc cccccccc + + // * aaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa bbbbbbbbbb bbbbbbbbb bbbbbbbbb ccc cccccccccc ccccccc cccccccc + + /* - aaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa bbbbbbbbbb bbbbbbbbb bbbbbbbbb ccc cccccccccc ccccccc cccccccc */ + + /* * aaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa bbbbbbbbbb bbbbbbbbb bbbbbbbbb ccc cccccccccc ccccccc cccccccc */ + }; + }; + }; + }; + }; + }; + }; + }; + }; + }; + }; +} diff --git a/tests/target/issue-5088/deeply_nested_long_comment_wrap_comments_true.rs b/tests/target/issue-5088/deeply_nested_long_comment_wrap_comments_true.rs new file mode 100644 index 000000000000..b289c9f859e0 --- /dev/null +++ b/tests/target/issue-5088/deeply_nested_long_comment_wrap_comments_true.rs @@ -0,0 +1,49 @@ +// rustfmt-wrap_comments: true + +fn main() { + { + { + { + { + { + { + { + { + { + { + { + // - aaaa aaaaaaaaa aaaaaaaaa + // aaaaaaaaa aaaaaaaaa + // bbbbbbbbbb bbbbbbbbb + // bbbbbbbbb ccc cccccccccc + // ccccccc cccccccc + + // * aaaa aaaaaaaaa aaaaaaaaa + // aaaaaaaaa aaaaaaaaa + // bbbbbbbbbb bbbbbbbbb + // bbbbbbbbb ccc cccccccccc + // ccccccc cccccccc + + /* - aaaa aaaaaaaaa aaaaaaaaa + * aaaaaaaaa aaaaaaaaa + * bbbbbbbbbb bbbbbbbbb + * bbbbbbbbb ccc cccccccccc + * ccccccc cccccccc */ + + /* * aaaa aaaaaaaaa aaaaaaaaa + * aaaaaaaaa aaaaaaaaa + * bbbbbbbbbb bbbbbbbbb + * bbbbbbbbb ccc cccccccccc + * ccccccc cccccccc */ + }; + }; + }; + }; + }; + }; + }; + }; + }; + }; + }; +} diff --git a/tests/target/issue-5088/multi_line_itemized_block_wrap_comments_false.rs b/tests/target/issue-5088/multi_line_itemized_block_wrap_comments_false.rs new file mode 100644 index 000000000000..60beed1b0482 --- /dev/null +++ b/tests/target/issue-5088/multi_line_itemized_block_wrap_comments_false.rs @@ -0,0 +1,17 @@ +// rustfmt-wrap_comments: false + +// - some itemized block 1 +// - some itemized block 2 + +// * some itemized block 3 +// * some itemized block 4 + +/* + * - some itemized block 5 + * - some itemized block 6 + */ + +/* + * * some itemized block 7 + * * some itemized block 8 + */ diff --git a/tests/target/issue-5088/multi_line_itemized_block_wrap_comments_true.rs b/tests/target/issue-5088/multi_line_itemized_block_wrap_comments_true.rs new file mode 100644 index 000000000000..84fba4b7c198 --- /dev/null +++ b/tests/target/issue-5088/multi_line_itemized_block_wrap_comments_true.rs @@ -0,0 +1,17 @@ +// rustfmt-wrap_comments: true + +// - some itemized block 1 +// - some itemized block 2 + +// * some itemized block 3 +// * some itemized block 4 + +/* + * - some itemized block 5 + * - some itemized block 6 + */ + +/* + * * some itemized block 7 + * * some itemized block 8 + */ diff --git a/tests/target/issue-5088/multi_line_text_with_itemized_block_wrap_comments_false.rs b/tests/target/issue-5088/multi_line_text_with_itemized_block_wrap_comments_false.rs new file mode 100644 index 000000000000..d1bf44f6c741 --- /dev/null +++ b/tests/target/issue-5088/multi_line_text_with_itemized_block_wrap_comments_false.rs @@ -0,0 +1,37 @@ +// rustfmt-wrap_comments: false + +// Some text +// - some itemized block 1 +// - some itemized block 2 +// Some more text +// - some itemized block 3 +// - some itemized block 4 +// Even more text + +// Some text +// * some itemized block 5 +// * some itemized block 6 +// Some more text +// * some itemized block 7 +// * some itemized block 8 +// Even more text + +/* + * Some text + * - some itemized block 9 + * - some itemized block 10 + * Some more text + * - some itemized block 11 + * - some itemized block 12 + * Even more text + */ + +/* + * Some text + * * some itemized block 13 + * * some itemized block 14 + * Some more text + * * some itemized block 15 + * * some itemized block 16 + * Even more text + */ diff --git a/tests/target/issue-5088/multi_line_text_with_itemized_block_wrap_comments_true.rs b/tests/target/issue-5088/multi_line_text_with_itemized_block_wrap_comments_true.rs new file mode 100644 index 000000000000..f767491f902d --- /dev/null +++ b/tests/target/issue-5088/multi_line_text_with_itemized_block_wrap_comments_true.rs @@ -0,0 +1,37 @@ +// rustfmt-wrap_comments: true + +// Some text +// - some itemized block 1 +// - some itemized block 2 +// Some more text +// - some itemized block 3 +// - some itemized block 4 +// Even more text + +// Some text +// * some itemized block 5 +// * some itemized block 6 +// Some more text +// * some itemized block 7 +// * some itemized block 8 +// Even more text + +/* + * Some text + * - some itemized block 9 + * - some itemized block 10 + * Some more text + * - some itemized block 11 + * - some itemized block 12 + * Even more text + */ + +/* + * Some text + * * some itemized block 13 + * * some itemized block 14 + * Some more text + * * some itemized block 15 + * * some itemized block 16 + * Even more text + */ diff --git a/tests/target/issue-5088/single_line_itemized_block_wrap_comments_false.rs b/tests/target/issue-5088/single_line_itemized_block_wrap_comments_false.rs new file mode 100644 index 000000000000..2cd85c787f97 --- /dev/null +++ b/tests/target/issue-5088/single_line_itemized_block_wrap_comments_false.rs @@ -0,0 +1,9 @@ +// rustfmt-wrap_comments: false + +// - some itemized block 1 + +// * some itemized block 2 + +/* - some itemized block 3 */ + +/* * some itemized block 4 */ diff --git a/tests/target/issue-5088/single_line_itemized_block_wrap_comments_true.rs b/tests/target/issue-5088/single_line_itemized_block_wrap_comments_true.rs new file mode 100644 index 000000000000..e9f343d75d5e --- /dev/null +++ b/tests/target/issue-5088/single_line_itemized_block_wrap_comments_true.rs @@ -0,0 +1,9 @@ +// rustfmt-wrap_comments: true + +// - some itemized block 1 + +// * some itemized block 2 + +/* - some itemized block 3 */ + +/* * some itemized block 4 */ diff --git a/tests/target/issue-5088/start_with_empty_comment_very_long_itemized_block_wrap_comments_false.rs b/tests/target/issue-5088/start_with_empty_comment_very_long_itemized_block_wrap_comments_false.rs new file mode 100644 index 000000000000..97bb7733d189 --- /dev/null +++ b/tests/target/issue-5088/start_with_empty_comment_very_long_itemized_block_wrap_comments_false.rs @@ -0,0 +1,19 @@ +// rustfmt-wrap_comments: false + +// +// - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +// - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + +// +// * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +// * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + +/* + * - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/ +/* + * - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/ + +/* + * * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/ +/* + * * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/ diff --git a/tests/target/issue-5088/start_with_empty_comment_very_long_itemized_block_wrap_comments_true.rs b/tests/target/issue-5088/start_with_empty_comment_very_long_itemized_block_wrap_comments_true.rs new file mode 100644 index 000000000000..c8af8383e058 --- /dev/null +++ b/tests/target/issue-5088/start_with_empty_comment_very_long_itemized_block_wrap_comments_true.rs @@ -0,0 +1,27 @@ +// rustfmt-wrap_comments: true + +// +// - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod +// tempor incididunt ut labore et dolore magna aliqua. +// - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod +// tempor incididunt ut labore et dolore magna aliqua. + +// +// * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod +// tempor incididunt ut labore et dolore magna aliqua. +// * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod +// tempor incididunt ut labore et dolore magna aliqua. + +/* + * - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + * tempor incididunt ut labore et dolore magna aliqua. */ +/* + * - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + * tempor incididunt ut labore et dolore magna aliqua. */ + +/* + * * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + * tempor incididunt ut labore et dolore magna aliqua. */ +/* + * * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + * tempor incididunt ut labore et dolore magna aliqua. */ diff --git a/tests/target/issue-5088/start_with_empty_comment_wrap_comments_false.rs b/tests/target/issue-5088/start_with_empty_comment_wrap_comments_false.rs new file mode 100644 index 000000000000..75cc42c0e66b --- /dev/null +++ b/tests/target/issue-5088/start_with_empty_comment_wrap_comments_false.rs @@ -0,0 +1,17 @@ +// rustfmt-wrap_comments: false + +// +// - some itemized block 1 +// - some itemized block 2 + +// +// * some itemized block 3 +// * some itemized block 4 + +/* + * - some itemized block 5 + * - some itemized block 6 */ + +/* + * * some itemized block 7 + * * some itemized block 8 */ diff --git a/tests/target/issue-5088/start_with_empty_comment_wrap_comments_true.rs b/tests/target/issue-5088/start_with_empty_comment_wrap_comments_true.rs new file mode 100644 index 000000000000..ef2c8f90cd30 --- /dev/null +++ b/tests/target/issue-5088/start_with_empty_comment_wrap_comments_true.rs @@ -0,0 +1,17 @@ +// rustfmt-wrap_comments: true + +// +// - some itemized block 1 +// - some itemized block 2 + +// +// * some itemized block 3 +// * some itemized block 4 + +/* + * - some itemized block 5 + * - some itemized block 6 */ + +/* + * * some itemized block 7 + * * some itemized block 8 */ diff --git a/tests/target/issue-5088/very_long_comment_wrap_comments_false.rs b/tests/target/issue-5088/very_long_comment_wrap_comments_false.rs new file mode 100644 index 000000000000..c826cc5d4da6 --- /dev/null +++ b/tests/target/issue-5088/very_long_comment_wrap_comments_false.rs @@ -0,0 +1,13 @@ +// rustfmt-wrap_comments: false + +// - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +// - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + +// * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +// * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + +/* - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/ +/* - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/ + +/* * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/ +/* * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/ diff --git a/tests/target/issue-5088/very_long_comment_wrap_comments_true.rs b/tests/target/issue-5088/very_long_comment_wrap_comments_true.rs new file mode 100644 index 000000000000..7f764dbd8a22 --- /dev/null +++ b/tests/target/issue-5088/very_long_comment_wrap_comments_true.rs @@ -0,0 +1,21 @@ +// rustfmt-wrap_comments: true + +// - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod +// tempor incididunt ut labore et dolore magna aliqua. +// - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod +// tempor incididunt ut labore et dolore magna aliqua. + +// * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod +// tempor incididunt ut labore et dolore magna aliqua. +// * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod +// tempor incididunt ut labore et dolore magna aliqua. + +/* - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + * tempor incididunt ut labore et dolore magna aliqua. */ +/* - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + * tempor incididunt ut labore et dolore magna aliqua. */ + +/* * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + * tempor incididunt ut labore et dolore magna aliqua. */ +/* * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + * tempor incididunt ut labore et dolore magna aliqua. */ diff --git a/tests/target/issue-5095.rs b/tests/target/issue-5095.rs new file mode 100644 index 000000000000..6981a65808c9 --- /dev/null +++ b/tests/target/issue-5095.rs @@ -0,0 +1,27 @@ +// rustfmt-wrap_comments: true + +pub mod a_long_name { + pub mod b_long_name { + pub mod c_long_name { + pub mod d_long_name { + pub mod e_long_name { + pub struct Bananas; + impl Bananas { + pub fn fantastic() {} + } + + pub mod f_long_name { + pub struct Apples; + } + } + } + } + } +} + +/// Check out [my other struct] ([`Bananas`]) and [the method it has]. +/// +/// [my other struct]: a_long_name::b_long_name::c_long_name::d_long_name::e_long_name::f_long_name::Apples +/// [`Bananas`]: a_long_name::b_long_name::c_long_name::d_long_name::e_long_name::Bananas::fantastic() +/// [the method it has]: a_long_name::b_long_name::c_long_name::d_long_name::e_long_name::Bananas::fantastic() +pub struct A; diff --git a/tests/target/issue-510.rs b/tests/target/issue-510.rs new file mode 100644 index 000000000000..a166b68498fa --- /dev/null +++ b/tests/target/issue-510.rs @@ -0,0 +1,41 @@ +impl ISizeAndMarginsComputer for AbsoluteNonReplaced { + fn solve_inline_size_constraints( + &self, + block: &mut BlockFlow, + input: &ISizeConstraintInput, + ) -> ISizeConstraintSolution { + let (inline_start, inline_size, margin_inline_start, margin_inline_end) = match ( + inline_startssssssxxxxxxsssssxxxxxxxxxssssssxxx, + inline_startssssssxxxxxxsssssxxxxxxxxxssssssxxx, + ) { + (MaybeAuto::Auto, MaybeAuto::Auto, MaybeAuto::Auto) => { + let margin_start = inline_start_margin.specified_or_zero(); + let margin_end = inline_end_margin.specified_or_zero(); + // Now it is the same situation as inline-start Specified and inline-end + // and inline-size Auto. + // + // Set inline-end to zero to calculate inline-size. + let inline_size = block.get_shrink_to_fit_inline_size( + available_inline_size - (margin_start + margin_end), + ); + (Au(0), inline_size, margin_start, margin_end) + } + }; + + let (inline_start, inline_size, margin_inline_start, margin_inline_end) = + match (inline_start, inline_end, computed_inline_size) { + (MaybeAuto::Auto, MaybeAuto::Auto, MaybeAuto::Auto) => { + let margin_start = inline_start_margin.specified_or_zero(); + let margin_end = inline_end_margin.specified_or_zero(); + // Now it is the same situation as inline-start Specified and inline-end + // and inline-size Auto. + // + // Set inline-end to zero to calculate inline-size. + let inline_size = block.get_shrink_to_fit_inline_size( + available_inline_size - (margin_start + margin_end), + ); + (Au(0), inline_size, margin_start, margin_end) + } + }; + } +} diff --git a/tests/target/issue-5125/attributes_in_formal_fuction_parameter.rs b/tests/target/issue-5125/attributes_in_formal_fuction_parameter.rs new file mode 100644 index 000000000000..5d167932828f --- /dev/null +++ b/tests/target/issue-5125/attributes_in_formal_fuction_parameter.rs @@ -0,0 +1,6 @@ +fn foo( + #[unused] a: >::ForeignType, +) { +} diff --git a/tests/target/issue-5125/long_parameter_in_different_positions.rs b/tests/target/issue-5125/long_parameter_in_different_positions.rs new file mode 100644 index 000000000000..cab20381ce8f --- /dev/null +++ b/tests/target/issue-5125/long_parameter_in_different_positions.rs @@ -0,0 +1,24 @@ +fn middle( + a: usize, + b: >::ForeignType, + c: bool, +) { +} + +fn last( + a: usize, + b: >::ForeignType, +) { +} + +fn first( + a: >::ForeignType, + b: usize, +) { +} diff --git a/tests/target/issue-5125/minimum_example.rs b/tests/target/issue-5125/minimum_example.rs new file mode 100644 index 000000000000..8003e66968c7 --- /dev/null +++ b/tests/target/issue-5125/minimum_example.rs @@ -0,0 +1,6 @@ +fn foo( + a: >::ForeignType, +) { +} diff --git a/tests/target/issue-5125/with_leading_and_inline_comments.rs b/tests/target/issue-5125/with_leading_and_inline_comments.rs new file mode 100644 index 000000000000..2340b2f3472e --- /dev/null +++ b/tests/target/issue-5125/with_leading_and_inline_comments.rs @@ -0,0 +1,7 @@ +fn foo( + // Pre Comment + a: >::ForeignType, // Inline comment +) { +} diff --git a/tests/target/issue-5151/minimum_example.rs b/tests/target/issue-5151/minimum_example.rs new file mode 100644 index 000000000000..2ed3d936e32a --- /dev/null +++ b/tests/target/issue-5151/minimum_example.rs @@ -0,0 +1,16 @@ +#![feature(more_qualified_paths)] + +struct Struct {} + +trait Trait { + type Type; +} + +impl Trait for Struct { + type Type = Self; +} + +fn main() { + // keep the qualified path details + let _ = ::Type {}; +} diff --git a/tests/target/issue-5157/indented_itemized_markdown_blockquote.rs b/tests/target/issue-5157/indented_itemized_markdown_blockquote.rs new file mode 100644 index 000000000000..e47677f20390 --- /dev/null +++ b/tests/target/issue-5157/indented_itemized_markdown_blockquote.rs @@ -0,0 +1,6 @@ +// rustfmt-wrap_comments: true + +/// > For each sample received, the middleware internally maintains a +/// > sample_state relative to each DataReader. The sample_state can +/// > either be READ or NOT_READ. +fn block_quote() {} diff --git a/tests/target/issue-5157/nested_itemized_markdown_blockquote.rs b/tests/target/issue-5157/nested_itemized_markdown_blockquote.rs new file mode 100644 index 000000000000..079510442b79 --- /dev/null +++ b/tests/target/issue-5157/nested_itemized_markdown_blockquote.rs @@ -0,0 +1,18 @@ +// rustfmt-wrap_comments: true + +/// > For each sample received, the middleware internally maintains a +/// > sample_state relative to each DataReader. The sample_state can either be +/// > READ or NOT_READ. +/// +/// > > For each sample received, the middleware internally maintains a +/// > > sample_state relative to each DataReader. The sample_state can either be +/// > > READ or NOT_READ. +/// +/// > > > For each sample received, the middleware internally maintains a +/// > > > sample_state relative to each DataReader. The sample_state can either +/// > > > be READ or NOT_READ. +/// +/// > > > > > > > > For each sample received, the middleware internally +/// > > > > > > > > maintains a sample_state relative to each DataReader. The +/// > > > > > > > > sample_state can either be READ or NOT_READ. +fn block_quote() {} diff --git a/tests/target/issue-5157/support_itemized_markdown_blockquote.rs b/tests/target/issue-5157/support_itemized_markdown_blockquote.rs new file mode 100644 index 000000000000..029ee37d22a8 --- /dev/null +++ b/tests/target/issue-5157/support_itemized_markdown_blockquote.rs @@ -0,0 +1,6 @@ +// rustfmt-wrap_comments: true + +/// > For each sample received, the middleware internally maintains a +/// > sample_state relative to each DataReader. The sample_state can either be +/// > READ or NOT_READ. +fn block_quote() {} diff --git a/tests/target/issue-5234.rs b/tests/target/issue-5234.rs new file mode 100644 index 000000000000..7ee9e46d1efa --- /dev/null +++ b/tests/target/issue-5234.rs @@ -0,0 +1,47 @@ +// rustfmt-format_code_in_doc_comments: true + +/// ``` +/// ``` +fn foo() {} + +/// ``` +/// Something +/// ``` +fn foo() {} + +/// ``` +/// ``` +fn foo() {} + +/// /// ``` +fn foo() {} + +/// /// ``` +/// ``` +/// ``` +/// ``` +fn foo() {} + +fn foo() { + /// ``` + /// ``` + struct bar {} +} + +/// ``` +/// fn com() { +/// let i = 5; +/// +/// let j = 6; +/// } +/// ``` +fn foo() {} + +fn foo() { + /// ``` + /// fn com() { + /// let i = 5; + /// } + /// ``` + struct bar {} +} diff --git a/tests/target/issue-5238/markdown_header_wrap_comments_false.rs b/tests/target/issue-5238/markdown_header_wrap_comments_false.rs new file mode 100644 index 000000000000..229c6e5753d2 --- /dev/null +++ b/tests/target/issue-5238/markdown_header_wrap_comments_false.rs @@ -0,0 +1,11 @@ +// rustfmt-wrap_comments: false + +/// no markdown header so rustfmt should wrap this comment when `format_code_in_doc_comments = true` and `wrap_comments = true` +fn not_documented_with_markdown_header() { + // This is just a normal inline comment so rustfmt should wrap this comment when `wrap_comments = true` +} + +/// # We're using a markdown header here so rustfmt should refuse to wrap this comment in all circumstances +fn documented_with_markdown_header() { + // # We're using a markdown header in an inline comment. rustfmt should be able to wrap this comment when `wrap_comments = true` +} diff --git a/tests/target/issue-5238/markdown_header_wrap_comments_true.rs b/tests/target/issue-5238/markdown_header_wrap_comments_true.rs new file mode 100644 index 000000000000..87dae58eccd7 --- /dev/null +++ b/tests/target/issue-5238/markdown_header_wrap_comments_true.rs @@ -0,0 +1,14 @@ +// rustfmt-wrap_comments: true + +/// no markdown header so rustfmt should wrap this comment when +/// `format_code_in_doc_comments = true` and `wrap_comments = true` +fn not_documented_with_markdown_header() { + // This is just a normal inline comment so rustfmt should wrap this comment + // when `wrap_comments = true` +} + +/// # We're using a markdown header here so rustfmt should refuse to wrap this comment in all circumstances +fn documented_with_markdown_header() { + // # We're using a markdown header in an inline comment. rustfmt should be + // able to wrap this comment when `wrap_comments = true` +} diff --git a/tests/target/issue-5260.rs b/tests/target/issue-5260.rs new file mode 100644 index 000000000000..171f6fa51b78 --- /dev/null +++ b/tests/target/issue-5260.rs @@ -0,0 +1,13 @@ +// rustfmt-wrap_comments: true + +/// [MyType](VeryLongPathToMyType::NoLineBreak::Here::Okay::ThatWouldBeNice::Thanks) +fn documented_with_longtype() { + // # We're using a long type link, rustfmt should not break line + // on the type when `wrap_comments = true` +} + +/// VeryLongPathToMyType::JustMyType::But::VeryVery::Long::NoLineBreak::Here::Okay::ThatWouldBeNice::Thanks +fn documented_with_verylongtype() { + // # We're using a long type link, rustfmt should not break line + // on the type when `wrap_comments = true` +} diff --git a/tests/target/issue-5270/merge_derives_false.rs b/tests/target/issue-5270/merge_derives_false.rs new file mode 100644 index 000000000000..3b6f7e66993c --- /dev/null +++ b/tests/target/issue-5270/merge_derives_false.rs @@ -0,0 +1,62 @@ +// rustfmt-merge_derives:false + +#[rustfmt::skip::attributes(derive)] +#[allow(dead_code)] +#[derive(StructField)] +#[derive(Clone)] +struct DoNotMergeDerives { + field: String, +} + +#[allow(dead_code)] +#[derive(StructField)] +#[rustfmt::skip::attributes(derive)] +#[derive(Clone)] +struct DoNotMergeDerivesSkipInMiddle { + field: String, +} + +#[allow(dead_code)] +#[derive(StructField)] +#[derive(Clone)] +#[rustfmt::skip::attributes(derive)] +struct DoNotMergeDerivesSkipAtEnd { + field: String, +} + +#[allow(dead_code)] +#[derive(StructField)] +#[derive(Clone)] +struct MergeDerives { + field: String, +} + +mod inner_attribute_derive_skip { + #![rustfmt::skip::attributes(derive)] + + #[allow(dead_code)] + #[derive(StructField)] + #[derive(Clone)] + struct DoNotMergeDerives { + field: String, + } +} + +#[rustfmt::skip::attributes(derive)] +mod outer_attribute_derive_skip { + #[allow(dead_code)] + #[derive(StructField)] + #[derive(Clone)] + struct DoNotMergeDerives { + field: String, + } +} + +mod no_derive_skip { + #[allow(dead_code)] + #[derive(StructField)] + #[derive(Clone)] + struct MergeDerives { + field: String, + } +} diff --git a/tests/target/issue-5270/merge_derives_true.rs b/tests/target/issue-5270/merge_derives_true.rs new file mode 100644 index 000000000000..5f488b4542d0 --- /dev/null +++ b/tests/target/issue-5270/merge_derives_true.rs @@ -0,0 +1,60 @@ +// rustfmt-merge_derives:true + +#[rustfmt::skip::attributes(derive)] +#[allow(dead_code)] +#[derive(StructField)] +#[derive(Clone)] +struct DoNotMergeDerives { + field: String, +} + +#[allow(dead_code)] +#[derive(StructField)] +#[rustfmt::skip::attributes(derive)] +#[derive(Clone)] +struct DoNotMergeDerivesSkipInMiddle { + field: String, +} + +#[allow(dead_code)] +#[derive(StructField)] +#[derive(Clone)] +#[rustfmt::skip::attributes(derive)] +struct DoNotMergeDerivesSkipAtEnd { + field: String, +} + +#[allow(dead_code)] +#[derive(StructField, Clone)] +struct MergeDerives { + field: String, +} + +mod inner_attribute_derive_skip { + #![rustfmt::skip::attributes(derive)] + + #[allow(dead_code)] + #[derive(StructField)] + #[derive(Clone)] + struct DoNotMergeDerives { + field: String, + } +} + +#[rustfmt::skip::attributes(derive)] +mod outer_attribute_derive_skip { + #[allow(dead_code)] + #[derive(StructField)] + #[derive(Clone)] + struct DoNotMergeDerives { + field: String, + } +} + +mod no_derive_skip { + #[allow(dead_code)] + #[derive(StructField, Clone)] + struct MergeDerives { + field: String, + } +} diff --git a/tests/target/issue-5358.rs b/tests/target/issue-5358.rs new file mode 100644 index 000000000000..d4bf4909ad76 --- /dev/null +++ b/tests/target/issue-5358.rs @@ -0,0 +1,4 @@ +// Test /* comment */ inside trait generics does not get duplicated. +trait Test {} + +trait TestTwo {} diff --git a/tests/target/issue-539.rs b/tests/target/issue-539.rs new file mode 100644 index 000000000000..adeb33555fb5 --- /dev/null +++ b/tests/target/issue-539.rs @@ -0,0 +1,3 @@ +// rustfmt-normalize_comments: true +// FIXME (#3300): Should allow items to be anonymous. Right now +// we just use dummy names for anon items. diff --git a/tests/target/issue-5488.rs b/tests/target/issue-5488.rs new file mode 100644 index 000000000000..0cb37c56f393 --- /dev/null +++ b/tests/target/issue-5488.rs @@ -0,0 +1,17 @@ +// rustfmt-use_field_init_shorthand: true + +struct MyStruct(u32); +struct AnotherStruct { + a: u32, +} + +fn main() { + // Since MyStruct is a tuple struct, it should not be shorthanded to + // MyStruct { 0 } even if use_field_init_shorthand is enabled. + let instance = MyStruct { 0: 0 }; + + // Since AnotherStruct is not a tuple struct, the shorthand should + // apply. + let a = 10; + let instance = AnotherStruct { a }; +} diff --git a/tests/target/issue-5586.rs b/tests/target/issue-5586.rs new file mode 100644 index 000000000000..7033ae975b3b --- /dev/null +++ b/tests/target/issue-5586.rs @@ -0,0 +1,177 @@ +// rustfmt-version: Two +fn main() { + // sample 1 + { + { + { + { + { + let push_ident = if let Some(&node_id) = + subgraph_nodes.get(pull_to_push_idx) + { + self.node_id_as_ident(node_id, false) + } else { + // Entire subgraph is pull (except for a single send/push handoff output). + assert_eq!( + 1, + send_ports.len(), + "If entire subgraph is pull, should have only one handoff output." + ); + send_ports[0].clone() + }; + } + } + } + } + } + + // sample 2 + { + { + { + { + { + let push_ident = if let Some(&node_id) = + subgraph_nodes.get(pull_to_push_idx) + { + self.node_id_as_ident(node_id, false) + } else { + // Entire subgraph is pull (except for a single send/push handoff output). + assert_eq!( + 1, + send_ports.len(), + "If entire subgraph is pull, should have only one handoff output." + ); + send_ports[0].clone() + }; + } + } + } + } + } + + // sample 3 + { + { + { + { + { + let push_ident = if let Some(&node_id) = + subgraph_nodes.get(pull_to_push_idx) + { + self.node_id_as_ident(node_id, false) + } else { + // Entire subgraph is pull (except for a single send/push handoff output). + assert_eq!( + 1, + send_ports.len(), + "If entire subgraph is pull, should have only one handoff output." + ); + send_ports[0].clone() + }; + } + } + } + } + } + + // sample 4 + { + { + { + { + { + let push_ident = if let Some(&node_id) = + subgraph_nodes.get(pull_to_push_idx) + { + self.node_id_as_ident(node_id, false) + } else { + // Entire subgraph is pull (except for a single send/push handoff output). + assert_eq!( + 1, + send_ports.len(), + "If entire subgraph is pull, should have only one handoff output." + ); + send_ports[0].clone() + }; + } + } + } + } + } + + // sample 5 + { + { + { + { + { + let push_ident = if let Some(&node_id) = + subgraph_nodes.get(pull_to_push_idx) + { + self.node_id_as_ident(node_id, false) + } else { + // Entire subgraph is pull (except for a single send/push handoff output). + assert_eq!( + 1, + send_ports.len(), + "If entire subgraph is pull, should have only one handoff output." + ); + send_ports[0].clone() + }; + } + } + } + } + } + + // sample 6 + { + { + { + { + { + let push_ident = if let Some(&node_id) = + subgraph_nodes.get(pull_to_push_idx) + { + self.node_id_as_ident(node_id, false) + } else { + // Entire subgraph is pull (except for a single send/push handoff output). + assert_eq!( + 1, + send_ports.len(), + "If entire subgraph is pull, should have only one handoff output." + ); + send_ports[0].clone() + }; + } + } + } + } + } + + // sample 7 + { + { + { + { + { + let push_ident = if let Some(&node_id) = + subgraph_nodes.get(pull_to_push_idx) + { + self.node_id_as_ident(node_id, false) + } else { + // Entire subgraph is pull (except for a single send/push handoff output). + assert_eq!( + 1, + send_ports.len(), + "If entire subgraph is pull, should have only one handoff output." + ); + send_ports[0].clone() + }; + } + } + } + } + } +} diff --git a/tests/target/issue-64.rs b/tests/target/issue-64.rs new file mode 100644 index 000000000000..c0660630203a --- /dev/null +++ b/tests/target/issue-64.rs @@ -0,0 +1,7 @@ +// Regression test for issue 64 + +pub fn header_name() -> &'static str { + let name = ::header_name(); + let func = ::header_name; + name +} diff --git a/tests/target/issue-683.rs b/tests/target/issue-683.rs new file mode 100644 index 000000000000..adeb33555fb5 --- /dev/null +++ b/tests/target/issue-683.rs @@ -0,0 +1,3 @@ +// rustfmt-normalize_comments: true +// FIXME (#3300): Should allow items to be anonymous. Right now +// we just use dummy names for anon items. diff --git a/tests/target/issue-691.rs b/tests/target/issue-691.rs new file mode 100644 index 000000000000..7473d070eff2 --- /dev/null +++ b/tests/target/issue-691.rs @@ -0,0 +1,9 @@ +// rustfmt-normalize_comments: true + +//! `std` or `core` and simply link to this library. In case the target +//! platform has no hardware +//! support for some operation, software implementations provided by this +//! library will be used automagically. +// TODO: provide instructions to override default libm link and how to link to +// this library. +fn foo() {} diff --git a/tests/target/issue-770.rs b/tests/target/issue-770.rs new file mode 100644 index 000000000000..5fbedd7b7345 --- /dev/null +++ b/tests/target/issue-770.rs @@ -0,0 +1,10 @@ +fn main() { + if false { + if false { + } else { + // A let binding here seems necessary to trigger it. + let _ = (); + } + } else if let false = false { + } +} diff --git a/tests/target/issue-811.rs b/tests/target/issue-811.rs new file mode 100644 index 000000000000..b7a89b5d0f9d --- /dev/null +++ b/tests/target/issue-811.rs @@ -0,0 +1,19 @@ +trait FooTrait: Sized { + type Bar: BarTrait; +} + +trait BarTrait: Sized { + type Baz; + fn foo(); +} + +type Foo = <>::Bar as BarTrait>::Baz; +type Bar = >::Baz; + +fn some_func, U>() { + <>::Bar as BarTrait>::foo(); +} + +fn some_func>() { + >::foo(); +} diff --git a/tests/target/issue-831.rs b/tests/target/issue-831.rs new file mode 100644 index 000000000000..1d6327c21580 --- /dev/null +++ b/tests/target/issue-831.rs @@ -0,0 +1,9 @@ +fn main() { + let y = a.iter().any(|x| { + println!("a"); + }) || b.iter().any(|x| { + println!("b"); + }) || c.iter().any(|x| { + println!("c"); + }); +} diff --git a/tests/target/issue-850.rs b/tests/target/issue-850.rs new file mode 100644 index 000000000000..c939716a6a88 --- /dev/null +++ b/tests/target/issue-850.rs @@ -0,0 +1 @@ +const unsafe fn x() {} diff --git a/tests/target/issue-855.rs b/tests/target/issue-855.rs new file mode 100644 index 000000000000..0430cc629548 --- /dev/null +++ b/tests/target/issue-855.rs @@ -0,0 +1,27 @@ +fn main() { + 'running: loop { + for event in event_pump.poll_iter() { + match event { + Event::Quit { .. } + | Event::KeyDown { + keycode: Some(Keycode::Escape), + .. + } => break 'running, + } + } + } +} + +fn main2() { + 'running: loop { + for event in event_pump.poll_iter() { + match event { + Event::Quit { .. } + | Event::KeyDownXXXXXXXXXXXXX { + keycode: Some(Keycode::Escape), + .. + } => break 'running, + } + } + } +} diff --git a/tests/target/issue-913.rs b/tests/target/issue-913.rs new file mode 100644 index 000000000000..a2b5800a7451 --- /dev/null +++ b/tests/target/issue-913.rs @@ -0,0 +1,22 @@ +mod client { + impl Client { + fn test(self) -> Result<()> { + let next_state = match self.state { + State::V5(v5::State::Command(v5::coand::State::WriteVersion(ref mut response))) => { + let x = reformat.meeee(); + } + }; + + let next_state = match self.state { + State::V5(v5::State::Command(v5::comand::State::WriteVersion( + ref mut response, + ))) => { + // The pattern cannot be formatted in a way that the match stays + // within the column limit. The rewrite should therefore be + // skipped. + let x = dont.reformat.meeee(); + } + }; + } + } +} diff --git a/tests/target/issue-945.rs b/tests/target/issue-945.rs new file mode 100644 index 000000000000..d46c69a4f093 --- /dev/null +++ b/tests/target/issue-945.rs @@ -0,0 +1,17 @@ +impl Bar { + default const unsafe fn foo() { + "hi" + } +} + +impl Baz { + default unsafe extern "C" fn foo() { + "hi" + } +} + +impl Foo for Bar { + default fn foo() { + "hi" + } +} diff --git a/tests/target/issue-977.rs b/tests/target/issue-977.rs new file mode 100644 index 000000000000..3784a3874503 --- /dev/null +++ b/tests/target/issue-977.rs @@ -0,0 +1,16 @@ +// rustfmt-normalize_comments: true + +trait NameC { + // comment +} +struct FooC { + // comment +} +enum MooC { + // comment +} +mod BarC { // comment +} +extern "C" { + // comment +} diff --git a/tests/target/issue_1306.rs b/tests/target/issue_1306.rs new file mode 100644 index 000000000000..6bb514cdfe55 --- /dev/null +++ b/tests/target/issue_1306.rs @@ -0,0 +1,33 @@ +// rustfmt-max_width: 160 +// rustfmt-fn_call_width: 96 +// rustfmt-fn_args_layout: Compressed +// rustfmt-trailing_comma: Always +// rustfmt-wrap_comments: true + +fn foo() { + for elem in try!(gen_epub_book::ops::parse_descriptor_file( + &mut try!(File::open(&opts.source_file.1).map_err(|_| { + gen_epub_book::Error::Io { + desc: "input file", + op: "open", + more: None, + } + })), + "input file" + )) { + println!("{}", elem); + } +} + +fn write_content() { + io::copy( + try!(File::open(in_f).map_err(|_| { + Error::Io { + desc: "Content", + op: "open", + more: None, + } + })), + w, + ); +} diff --git a/tests/target/issue_3033.rs b/tests/target/issue_3033.rs new file mode 100644 index 000000000000..e12249a6da6f --- /dev/null +++ b/tests/target/issue_3033.rs @@ -0,0 +1,2 @@ +use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding::BluetoothRemoteGATTServerBinding:: + BluetoothRemoteGATTServerMethods; diff --git a/tests/target/issue_3245.rs b/tests/target/issue_3245.rs new file mode 100644 index 000000000000..8f442f1181a5 --- /dev/null +++ b/tests/target/issue_3245.rs @@ -0,0 +1,4 @@ +fn main() { + let x = 1; + let y = 3; +} diff --git a/tests/target/issue_3561.rs b/tests/target/issue_3561.rs new file mode 100644 index 000000000000..846a14d86a5f --- /dev/null +++ b/tests/target/issue_3561.rs @@ -0,0 +1,7 @@ +fn main() { + 7 +} + +fn main() { + 7 +} diff --git a/tests/target/issue_3839.rs b/tests/target/issue_3839.rs new file mode 100644 index 000000000000..b7bdf4c7579f --- /dev/null +++ b/tests/target/issue_3839.rs @@ -0,0 +1,8 @@ +struct Foo { + a: i32, + /* + asd + */ + // foo + b: i32, +} diff --git a/tests/target/issue_3844.rs b/tests/target/issue_3844.rs new file mode 100644 index 000000000000..81d2083463e0 --- /dev/null +++ b/tests/target/issue_3844.rs @@ -0,0 +1,3 @@ +fn main() { + || {}; +} diff --git a/tests/target/issue_3853.rs b/tests/target/issue_3853.rs new file mode 100644 index 000000000000..eae59eff94e5 --- /dev/null +++ b/tests/target/issue_3853.rs @@ -0,0 +1,47 @@ +fn by_ref_with_block_before_ident() { + if let Some(ref /*def*/ state) = foo { + println!("asdfasdfasdf"); + } +} + +fn mut_block_before_ident() { + if let Some(mut /*def*/ state) = foo { + println!("123"); + } +} + +fn ref_and_mut_blocks_before_ident() { + if let Some(ref /*abc*/ mut /*def*/ state) = foo { + println!("deefefefefefwea"); + } +} + +fn sub_pattern() { + let foo @ /*foo*/ bar(f) = 42; +} + +fn no_prefix_block_before_ident() { + if let Some(/*def*/ state) = foo { + println!("129387123123"); + } +} + +fn issue_3853() { + if let Some(ref /*mut*/ state) = foo {} +} + +fn double_slash_comment_between_lhs_and_rhs() { + if let Some(e) = + // self.foo.bar(e, tx) + packet.transaction.state.committed + { + // body + println!("a2304712836123"); + } +} + +fn block_comment_between_lhs_and_rhs() { + if let Some(ref /*def*/ mut /*abc*/ state) = /*abc*/ foo { + println!("asdfasdfasdf"); + } +} diff --git a/tests/target/issue_3854.rs b/tests/target/issue_3854.rs new file mode 100644 index 000000000000..3051335c27da --- /dev/null +++ b/tests/target/issue_3854.rs @@ -0,0 +1,3 @@ +fn main() { + println!("{:?}", -1. ..1.); +} diff --git a/tests/target/issue_3868.rs b/tests/target/issue_3868.rs new file mode 100644 index 000000000000..067241359287 --- /dev/null +++ b/tests/target/issue_3868.rs @@ -0,0 +1,9 @@ +fn foo() {} + +fn bar() { + for _ in 0..1 {} +} + +fn baz() { + (); +} diff --git a/tests/target/issue_3934.rs b/tests/target/issue_3934.rs new file mode 100644 index 000000000000..68f9fd0aeca0 --- /dev/null +++ b/tests/target/issue_3934.rs @@ -0,0 +1,8 @@ +mod repro { + pub fn push() -> Result<(), ()> { + self.api.map_api_result(|api| { + #[allow(deprecated)] + match api.apply_extrinsic_before_version_4_with_context()? {} + }) + } +} diff --git a/tests/target/issue_3937.rs b/tests/target/issue_3937.rs new file mode 100644 index 000000000000..806731085005 --- /dev/null +++ b/tests/target/issue_3937.rs @@ -0,0 +1,13 @@ +// rustfmt-format_code_in_doc_comments:true + +struct Foo { + // a: i32, + // + // b: i32, +} + +struct Foo { + a: i32, + // + // b: i32, +} diff --git a/tests/target/issue_4031.rs b/tests/target/issue_4031.rs new file mode 100644 index 000000000000..065d5395c7e7 --- /dev/null +++ b/tests/target/issue_4031.rs @@ -0,0 +1,21 @@ +fn foo() { + with_woff2_glyf_table("tests/fonts/woff2/SFNT-TTF-Composite.woff2", |glyf| { + let actual = glyf + .records + .iter() + .map(|glyph| match glyph { + GlyfRecord::Parsed( + found @ Glyph { + data: GlyphData::Composite { .. }, + .. + }, + ) => Some(found), + _ => None, + }) + .find(|candidate| candidate.is_some()) + .unwrap() + .unwrap(); + + assert_eq!(*actual, expected) + }); +} diff --git a/tests/target/issue_4032.rs b/tests/target/issue_4032.rs new file mode 100644 index 000000000000..2e7e624ca6e6 --- /dev/null +++ b/tests/target/issue_4032.rs @@ -0,0 +1,18 @@ +fn a1( + #[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa] + a: u8, +) { +} +fn b1( + #[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa] + bb: u8, +) { +} +fn a2( + #[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa] a: u8, +) { +} +fn b2( + #[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa] bb: u8, +) { +} diff --git a/tests/target/issue_4049.rs b/tests/target/issue_4049.rs new file mode 100644 index 000000000000..fe025a0f6494 --- /dev/null +++ b/tests/target/issue_4049.rs @@ -0,0 +1,26 @@ +// rustfmt-max_width: 110 +// rustfmt-use_small_heuristics: Max +// rustfmt-hard_tabs: true +// rustfmt-use_field_init_shorthand: true +// rustfmt-overflow_delimited_expr: true + +// https://github.com/rust-lang/rustfmt/issues/4049 +fn foo() { + { + { + if let Some(MpcEv::PlayDrum(pitch, vel)) = + // self.mpc.handle_input(e, /*btn_ctrl_down,*/ tx_launch_to_daw, state_view) + self.mpc.handle_input(e, &mut MyBorrowedState { tx_launch_to_daw, state_view }) + { + println!("bar"); + } + + if let Some(e) = + // self.note_input.handle_input(e, /*btn_ctrl_down,*/ tx_launch_to_daw, state_view) + self.note_input.handle_input(e, &mut MyBorrowedState { tx_launch_to_daw, state_view }) + { + println!("baz"); + } + } + } +} diff --git a/tests/target/issue_4057.rs b/tests/target/issue_4057.rs new file mode 100644 index 000000000000..467e67bca7c3 --- /dev/null +++ b/tests/target/issue_4057.rs @@ -0,0 +1,15 @@ +// rustfmt-format_code_in_doc_comments: true + +/// ``` +/// # #[rustversion::since(1.36)] +/// # fn dox() { +/// # use std::pin::Pin; +/// # type Projection<'a> = &'a (); +/// # type ProjectionRef<'a> = &'a (); +/// # trait Dox { +/// fn project_ex(self: Pin<&mut Self>) -> Projection<'_>; +/// fn project_ref(self: Pin<&Self>) -> ProjectionRef<'_>; +/// # } +/// # } +/// ``` +struct Foo; diff --git a/tests/target/issue_4086.rs b/tests/target/issue_4086.rs new file mode 100644 index 000000000000..959d3b3d4682 --- /dev/null +++ b/tests/target/issue_4086.rs @@ -0,0 +1,2 @@ +#[cfg(any())] +extern "C++" {} diff --git a/tests/target/issue_4110.rs b/tests/target/issue_4110.rs new file mode 100644 index 000000000000..d3734e90b7ff --- /dev/null +++ b/tests/target/issue_4110.rs @@ -0,0 +1,56 @@ +fn bindings() { + let err = match (place_desc, explanation) { + ( + Some(ref name), + BorrowExplanation::MustBeValidFor { + category: + category @ (ConstraintCategory::Return + | ConstraintCategory::CallArgument + | ConstraintCategory::OpaqueType), + from_closure: false, + ref region_name, + span, + .. + }, + ) if borrow_spans.for_generator() | borrow_spans.for_closure() => self + .report_escaping_closure_capture( + borrow_spans, + borrow_span, + region_name, + category, + span, + &format!("`{}`", name), + "function", + ), + ( + ref name, + BorrowExplanation::MustBeValidFor { + category: ConstraintCategory::Assignment, + from_closure: false, + region_name: + RegionName { + source: RegionNameSource::AnonRegionFromUpvar(upvar_span, ref upvar_name), + .. + }, + span, + .. + }, + ) => self.report_escaping_data(borrow_span, name, upvar_span, upvar_name, span), + (Some(name), explanation) => self.report_local_value_does_not_live_long_enough( + location, + &name, + &borrow, + drop_span, + borrow_spans, + explanation, + ), + (None, explanation) => self.report_temporary_value_does_not_live_long_enough( + location, + &borrow, + drop_span, + borrow_spans, + proper_span, + explanation, + ), + }; +} diff --git a/tests/target/issue_4257.rs b/tests/target/issue_4257.rs new file mode 100644 index 000000000000..309a66c8dc3c --- /dev/null +++ b/tests/target/issue_4257.rs @@ -0,0 +1,15 @@ +trait Trait { + type Type<'a> + where + T: 'a; + fn foo(x: &T) -> Self::Type<'_>; +} +impl Trait for () { + type Type<'a> + where + T: 'a, + = &'a T; + fn foo(x: &T) -> Self::Type<'_> { + x + } +} diff --git a/tests/target/issue_4322.rs b/tests/target/issue_4322.rs new file mode 100644 index 000000000000..0ec0547119f4 --- /dev/null +++ b/tests/target/issue_4322.rs @@ -0,0 +1,5 @@ +trait Bar { + type X<'a> + where + Self: 'a; +} diff --git a/tests/target/issue_4350.rs b/tests/target/issue_4350.rs new file mode 100644 index 000000000000..a94c5c32188d --- /dev/null +++ b/tests/target/issue_4350.rs @@ -0,0 +1,13 @@ +//rustfmt-format_macro_bodies: true + +macro_rules! mto_text_left { + ($buf:ident, $n:ident, $pos:ident, $state:ident) => {{ + let cursor = loop { + state = match iter.next() { + None if $pos == DP::Start => break last_char_idx($buf), + None /*some comment */ => break 0, + }; + }; + Ok(saturate_cursor($buf, cursor)) + }}; +} diff --git a/tests/target/issue_4374.rs b/tests/target/issue_4374.rs new file mode 100644 index 000000000000..f5bf657bbc00 --- /dev/null +++ b/tests/target/issue_4374.rs @@ -0,0 +1,13 @@ +fn a(_f: F) -> () +where + F: FnOnce() -> (), +{ +} +fn main() { + a(|| { + #[allow(irrefutable_let_patterns)] + while let _ = 0 { + break; + } + }); +} diff --git a/tests/target/issue_4467.rs b/tests/target/issue_4467.rs new file mode 100644 index 000000000000..f5ee96c4c148 --- /dev/null +++ b/tests/target/issue_4467.rs @@ -0,0 +1,6 @@ +pub fn main() { + #[cfg(feature = "std")] + { + // Comment + } +} diff --git a/tests/target/issue_4475.rs b/tests/target/issue_4475.rs new file mode 100644 index 000000000000..ea6726c5a01d --- /dev/null +++ b/tests/target/issue_4475.rs @@ -0,0 +1,29 @@ +fn main() { + #[cfg(debug_assertions)] + { + println!("DEBUG"); + } +} + +fn main() { + #[cfg(feature = "foo")] + { + /* + let foo = 0 + */ + } +} + +fn main() { + #[cfg(feature = "foo")] + { /* let foo = 0; */ } +} + +fn main() { + #[foo] + #[bar] + #[baz] + { + // let foo = 0; + } +} diff --git a/tests/target/issue_4522.rs b/tests/target/issue_4522.rs new file mode 100644 index 000000000000..5ca70e1c090c --- /dev/null +++ b/tests/target/issue_4522.rs @@ -0,0 +1,6 @@ +fn main() { + #[cfg(feature = "foo")] + { + // let foo = 0; + } +} diff --git a/tests/target/issue_4528.rs b/tests/target/issue_4528.rs new file mode 100644 index 000000000000..7828804b098e --- /dev/null +++ b/tests/target/issue_4528.rs @@ -0,0 +1,8 @@ +#![allow(clippy::no_effect)] + +extern "C" { + // N.B., mutability can be easily incorrect in FFI calls -- as + // in C, the default is mutable pointers. + fn ffi(c: *mut u8); + fn int_ffi(c: *mut i32); +} diff --git a/tests/target/issue_4545.rs b/tests/target/issue_4545.rs new file mode 100644 index 000000000000..f87c81036577 --- /dev/null +++ b/tests/target/issue_4545.rs @@ -0,0 +1,5 @@ +#[derive(Debug, Foo)] +enum Bar {} + +#[derive(Debug, , Default)] +struct Struct(i32); diff --git a/tests/target/issue_4573.rs b/tests/target/issue_4573.rs new file mode 100644 index 000000000000..82cfe4f53598 --- /dev/null +++ b/tests/target/issue_4573.rs @@ -0,0 +1,245 @@ +// rustmft-version:Two +// rustmft-use_small_heuristics:Max +// rustmft-merge_derives:false +// These are the same rustfmt configuration options that are used +// in the comiler as of ce39461ca75a and 8eb7c58dbb7b +// These are commits in https://github.com/rust-lang/rust + +#![no_std] // inner attribute comment +// inner attribute comment +#![no_implicit_prelude] +// post inner attribute comment + +#[cfg(not(miri))] // inline comment +#[no_link] +extern crate foo; + +// before attributes +#[no_link] +// between attributes +#[cfg(not(miri))] // inline comment +extern crate foo as bar; + +#[cfg(not(miri))] // inline comment +// between attribute and use +use foo; + +#[cfg(not(miri))] // inline comment +use foo; + +/* pre attributre */ +#[cfg(not(miri))] +use foo::bar; + +#[cfg(not(miri))] // inline comment +use foo::bar as FooBar; + +#[cfg(not(miri))] // inline comment +#[allow(unused)] +#[deprecated( + since = "5.2", // inline inner comment + note = "FOO was rarely used. Users should instead use BAR" +)] +#[allow(unused)] +static FOO: i32 = 42; + +#[used] +#[export_name = "FOO"] +#[cfg(not(miri))] // inline comment +#[deprecated( + since = "5.2", + note = "FOO was rarely used. Users should instead use BAR" +)] +static FOO: i32 = 42; + +#[cfg(not(miri))] // inline comment +#[export_name = "FOO"] +static BAR: &'static str = "bar"; + +#[cfg(not(miri))] // inline comment +const BAR: i32 = 42; + +#[cfg(not(miri))] // inline comment +#[no_mangle] +#[link_section = ".example_section"] +fn foo(bar: usize) { + #[cfg(not(miri))] // inline comment + println!("hello world!"); +} + +#[cfg(not(miri))] // inline comment +mod foo {} + +#[cfg(not(miri))] // inline comment +extern "C" { + fn my_c_function(x: i32) -> bool; +} + +#[cfg(not(miri))] // inline comment +#[link(name = "CoreFoundation", kind = "framework")] +extern "C" { + + #[link_name = "actual_symbol_name"] // inline comment + // between attribute and function + fn my_c_function(x: i32) -> bool; +} + +#[cfg(not(miri))] // inline comment +pub extern "C" fn callable_from_c(x: i32) -> bool { + x % 3 == 0 +} + +#[cfg(not(miri))] // inline comment +/* between attribute block comment */ +#[no_mangle] +/* between attribute and type */ +type Foo = Bar; + +#[no_mangle] +#[cfg(not(miri))] // inline comment +#[non_exhaustive] // inline comment +enum Foo { + Bar, + Baz, +} + +#[no_mangle] +#[cfg(not(miri))] /* inline comment */ +struct Foo { + x: A, +} + +#[cfg(not(miri))] // inline comment +union Foo { + x: A, + y: B, +} + +#[cfg(not(miri))] // inline comment +trait Foo {} + +#[cfg(not(miri))] // inline comment +trait Foo = Bar + Quux; + +#[cfg(not(miri))] // inline comment +impl Foo {} + +#[cfg(not(miri))] // inline comment +macro_rules! bar { + (3) => {}; +} + +mod nested { + #[cfg(not(miri))] // inline comment + // between attribute and use + use foo; + + #[cfg(not(miri))] // inline comment + use foo; + + #[cfg(not(miri))] // inline comment + use foo::bar; + + #[cfg(not(miri))] // inline comment + use foo::bar as FooBar; + + #[cfg(not(miri))] // inline comment + static FOO: i32 = 42; + + #[cfg(not(miri))] // inline comment + static FOO: i32 = 42; + + #[cfg(not(miri))] // inline comment + static FOO: &'static str = "bar"; + + #[cfg(not(miri))] // inline comment + const FOO: i32 = 42; + + #[cfg(not(miri))] // inline comment + fn foo(bar: usize) { + #[cfg(not(miri))] // inline comment + println!("hello world!"); + } + + #[cfg(not(miri))] // inline comment + mod foo {} + + #[cfg(not(miri))] // inline comment + mod foo {} + + #[cfg(not(miri))] // inline comment + extern "C" { + fn my_c_function(x: i32) -> bool; + } + + #[cfg(not(miri))] // inline comment + #[link(name = "CoreFoundation", kind = "framework")] + extern "C" { + + #[link_name = "actual_symbol_name"] // inline comment + // between attribute and function + fn my_c_function(x: i32) -> bool; + } + + #[cfg(not(miri))] // inline comment + pub extern "C" fn callable_from_c(x: i32) -> bool { + x % 3 == 0 + } + + #[cfg(not(miri))] // inline comment + type Foo = Bar; + + #[cfg(not(miri))] // inline comment + #[non_exhaustive] // inline comment + enum Foo { + // comment + #[attribute_1] + #[attribute_2] // comment + // comment! + Bar, + /* comment */ + #[attribute_1] + #[attribute_2] /* comment */ + #[attribute_3] + #[attribute_4] + /* comment! */ + Baz, + } + + #[cfg(not(miri))] // inline comment + struct Foo { + x: A, + } + + #[cfg(not(miri))] // inline comment + union Foo { + #[attribute_1] + #[attribute_2] /* comment */ + #[attribute_3] + #[attribute_4] // comment + x: A, + y: B, + } + + #[cfg(not(miri))] // inline comment + #[allow(missing_docs)] + trait Foo { + #[must_use] /* comment + * that wrappes to + * the next line */ + fn bar() {} + } + + #[allow(missing_docs)] + #[cfg(not(miri))] // inline comment + trait Foo = Bar + Quux; + + #[allow(missing_docs)] + #[cfg(not(miri))] // inline comment + impl Foo {} + + #[cfg(not(miri))] // inline comment + macro_rules! bar { + (3) => {}; + } +} diff --git a/tests/target/issue_4579.rs b/tests/target/issue_4579.rs new file mode 100644 index 000000000000..7b0a5f3a62e4 --- /dev/null +++ b/tests/target/issue_4579.rs @@ -0,0 +1,16 @@ +// rustfmt-hard_tabs: true + +#[macro_export] +macro_rules! main { + () => { + #[spirv(fragment)] + pub fn main_fs( + mut out_color: ::spirv_std::storage_class::Output, + #[spirv(descriptor_set = 1)] + iChannelResolution: ::spirv_std::storage_class::UniformConstant< + [::spirv_std::glam::Vec3A; 4], + >, + ) { + } + }; +} diff --git a/tests/target/issue_4584.rs b/tests/target/issue_4584.rs new file mode 100644 index 000000000000..20255beadb2c --- /dev/null +++ b/tests/target/issue_4584.rs @@ -0,0 +1,32 @@ +// rustfmt-indent_style: Visual + +#[derive(Debug)] +pub enum Case { + Upper, + Lower, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Case { + Upper, + Lower, +} + +// NB - This formatting looks potentially off the desired state, but is +// consistent with current behavior. Included here to provide a line wrapped +// derive case with the changes applied to resolve issue #4584 +#[derive(Add, + Sub, + Mul, + Div, + Clone, + Copy, + Eq, + PartialEq, + Ord, + PartialOrd, + Debug, + Hash, + Serialize, + Mul)] +struct Foo {} diff --git a/tests/target/issue_4636.rs b/tests/target/issue_4636.rs new file mode 100644 index 000000000000..a6465e29a020 --- /dev/null +++ b/tests/target/issue_4636.rs @@ -0,0 +1,13 @@ +pub trait PrettyPrinter<'tcx>: + Printer< + 'tcx, + Error = fmt::Error, + Path = Self, + Region = Self, + Type = Self, + DynExistential = Self, + Const = Self, + > + fmt::Write +{ + // +} diff --git a/tests/target/issue_4675.rs b/tests/target/issue_4675.rs new file mode 100644 index 000000000000..a65f86832eef --- /dev/null +++ b/tests/target/issue_4675.rs @@ -0,0 +1,8 @@ +macro_rules! foo { + ($s:ident ( $p:pat )) => { + Foo { + name: Name::$s($p), + .. + } + }; +} diff --git a/tests/target/issue_4823.rs b/tests/target/issue_4823.rs new file mode 100644 index 000000000000..de17467c0efa --- /dev/null +++ b/tests/target/issue_4823.rs @@ -0,0 +1,5 @@ +macro_rules! m { + () => { + type Type; + }; +} diff --git a/tests/target/issue_4850.rs b/tests/target/issue_4850.rs new file mode 100644 index 000000000000..7d4da9022fe4 --- /dev/null +++ b/tests/target/issue_4850.rs @@ -0,0 +1,4 @@ +impl ThisIsALongStructNameToPushTheWhereToWrapLolololol where + [(); this_is_a_long_const_function_name()]: +{ +} diff --git a/tests/target/issue_4854.rs b/tests/target/issue_4854.rs new file mode 100644 index 000000000000..a81c5a5171fb --- /dev/null +++ b/tests/target/issue_4854.rs @@ -0,0 +1,115 @@ +struct Struct { + // Multiline comment + // should be formatted + // properly. +} + +struct Struct2 { + // This formatting + // Should be changed +} + +struct Struct3( + // This + // is + // correct +); + +struct Struct4( + // This + // is + // not + // correct +); + +struct Struct5 { + /* + Comment block + with many lines. + */ +} + +struct Struct6( + /* + Comment block + with many lines. + */ +); + +struct Struct7 { + /* + Invalid + format + */ +} + +struct Struct8( + /* + Invalid + format + */ +); + +struct Struct9 {/* bar */} + +struct Struct10 { + /* bar + baz + */ +} + +mod module { + struct Struct { + // Multiline comment + // should be formatted + // properly. + } + + struct Struct2 { + // This formatting + // Should be changed + } + + struct Struct3( + // This + // is + // correct + ); + + struct Struct4( + // This + // is + // not + // correct + ); + + struct Struct5 { + /* + Comment block + with many lines. + */ + } + + struct Struct6( + /* + Comment block + with many lines. + */ + ); + + struct Struct7 { + /* + Invalid + format + */ + } + + struct Struct8( + /* + Invalid + format + */ + ); + + struct Struct9 {/* bar */} +} diff --git a/tests/target/issue_4868.rs b/tests/target/issue_4868.rs new file mode 100644 index 000000000000..763a82c3231f --- /dev/null +++ b/tests/target/issue_4868.rs @@ -0,0 +1,17 @@ +enum NonAscii { + Abcd, + Éfgh, +} + +use NonAscii::*; + +fn f(x: NonAscii) -> bool { + match x { + Éfgh => true, + _ => false, + } +} + +fn main() { + dbg!(f(Abcd)); +} diff --git a/tests/target/issue_4911.rs b/tests/target/issue_4911.rs new file mode 100644 index 000000000000..0f64aa7f766f --- /dev/null +++ b/tests/target/issue_4911.rs @@ -0,0 +1,8 @@ +#![feature(min_type_alias_impl_trait)] + +impl SomeTrait for SomeType { + type SomeGAT<'a> + where + Self: 'a, + = impl SomeOtherTrait; +} diff --git a/tests/target/issue_4936.rs b/tests/target/issue_4936.rs new file mode 100644 index 000000000000..c19e505fd03a --- /dev/null +++ b/tests/target/issue_4936.rs @@ -0,0 +1,10 @@ +#[discard_params_doc] +trait Trait { + fn foo( + &self, + /// some docs + bar: String, + /// another docs + baz: i32, + ); +} diff --git a/tests/target/issue_4943.rs b/tests/target/issue_4943.rs new file mode 100644 index 000000000000..bc8f1a366da2 --- /dev/null +++ b/tests/target/issue_4943.rs @@ -0,0 +1,8 @@ +impl SomeStruct { + fn process(v: T) -> ::R + where + Self: GAT = T>, + { + SomeStruct::do_something(v) + } +} diff --git a/tests/target/issue_4954.rs b/tests/target/issue_4954.rs new file mode 100644 index 000000000000..aa5e79befe9c --- /dev/null +++ b/tests/target/issue_4954.rs @@ -0,0 +1,7 @@ +trait Foo { + type Arg<'a>; +} + +struct Bar(T) +where + for<'a> T: Foo = ()>; diff --git a/tests/target/issue_4963.rs b/tests/target/issue_4963.rs new file mode 100644 index 000000000000..0c3c13579c13 --- /dev/null +++ b/tests/target/issue_4963.rs @@ -0,0 +1,9 @@ +mod test { + extern "C" { + fn test(); + } +} + +extern "C" { + fn test(); +} diff --git a/tests/target/issue_5027.rs b/tests/target/issue_5027.rs new file mode 100644 index 000000000000..26d771720b6c --- /dev/null +++ b/tests/target/issue_5027.rs @@ -0,0 +1,17 @@ +// rustfmt-version: Two + +pub type Iter<'a, D> = impl DoubleEndedIterator)> + + ExactSizeIterator + + 'a; + +trait FOo { + pub type Iter<'a, D> = impl DoubleEndedIterator)> + + ExactSizeIterator + + 'a; +} + +impl Bar { + type Iter<'a, D> = impl DoubleEndedIterator)> + + ExactSizeIterator + + 'a; +} diff --git a/tests/target/issue_5086.rs b/tests/target/issue_5086.rs new file mode 100644 index 000000000000..7a0be06f7917 --- /dev/null +++ b/tests/target/issue_5086.rs @@ -0,0 +1,2 @@ +#[cfg(any())] +type Type: Bound; diff --git a/tests/target/issue_5273.rs b/tests/target/issue_5273.rs new file mode 100644 index 000000000000..3bb9048a5fd3 --- /dev/null +++ b/tests/target/issue_5273.rs @@ -0,0 +1,3 @@ +struct Example { + // +} diff --git a/tests/target/issue_5399.rs b/tests/target/issue_5399.rs new file mode 100644 index 000000000000..17364c38919a --- /dev/null +++ b/tests/target/issue_5399.rs @@ -0,0 +1,48 @@ +// rustfmt-max_width: 140 + +impl NotificationRepository { + fn set_status_changed( + &self, + repo_tx_conn: &RepoTxConn, + rid: &RoutableId, + changed_at: NaiveDateTime, + ) -> NukeResult> { + repo_tx_conn.run(move |conn| { + let res = diesel::update(client_notification::table) + .filter( + client_notification::routable_id.eq(DieselRoutableId(rid.clone())).and( + client_notification::changed_at + .lt(changed_at) + .or(client_notification::changed_at.is_null()), + ), + ) + .set(client_notification::changed_at.eq(changed_at)) + .returning(( + client_notification::id, + client_notification::changed_at, + client_notification::polled_at, + client_notification::notified_at, + )) + .get_result::<(Uuid, Option, Option, Option)>(conn) + .optional()?; + + match res { + Some(row) => { + let client_id = client_contract::table + .inner_join(client_notification::table) + .filter(client_notification::id.eq(row.0)) + .select(client_contract::client_id) + .get_result::(conn)?; + + Ok(Some(NotificationStatus { + client_id: client_id.into(), + changed_at: row.1, + polled_at: row.2, + notified_at: row.3, + })) + } + None => Ok(None), + } + }) + } +} diff --git a/tests/target/issue_5668.rs b/tests/target/issue_5668.rs new file mode 100644 index 000000000000..bbd9a530b81c --- /dev/null +++ b/tests/target/issue_5668.rs @@ -0,0 +1,8 @@ +type Foo = impl Send; +struct Struct< + const C: usize = { + let _: Foo = (); + //~^ ERROR: mismatched types + 0 + }, +>; diff --git a/tests/target/issue_5686.rs b/tests/target/issue_5686.rs new file mode 100644 index 000000000000..993f12b5316d --- /dev/null +++ b/tests/target/issue_5686.rs @@ -0,0 +1,42 @@ +#[repr(u8)] +enum MyEnum { + UnitWithExplicitDiscriminant = 0, + EmptyStructSingleLineBlockComment {/* Comment */} = 1, + EmptyStructMultiLineBlockComment { + /* + * Comment + */ + } = 2, + EmptyStructLineComment { + // comment + } = 3, + EmptyTupleSingleLineBlockComment(/* Comment */) = 4, + EmptyTupleMultiLineBlockComment( + /* + * Comment + */ + ) = 5, + EmptyTupleLineComment( + // comment + ) = 6, +} + +enum Animal { + Dog(/* tuple variant closer in comment -> ) */) = 1, + #[hello(world)] + Cat(/* tuple variant close in leading attribute */) = 2, + Bee( + /* tuple variant closer on associated field attribute */ #[hello(world)] usize, + ) = 3, + Fox(/* tuple variant closer on const fn call */) = some_const_fn(), + Ant(/* tuple variant closer on macro call */) = some_macro!(), + Snake {/* stuct variant closer in comment -> } */} = 6, + #[hell{world}] + Cobra {/* struct variant close in leading attribute */} = 6, + Eagle { + /* struct variant closer on associated field attribute */ + #[hell{world}] + value: Sting, + } = 7, + Koala {/* struct variant closer on macro call */} = some_macro! {}, +} diff --git a/tests/target/issue_5691.rs b/tests/target/issue_5691.rs new file mode 100644 index 000000000000..e3aad15db0d5 --- /dev/null +++ b/tests/target/issue_5691.rs @@ -0,0 +1,16 @@ +struct S +where + [(); { num_slots!(C) }]:, { + /* An asterisk-based, or a double-slash-prefixed, comment here is + required to trigger the fmt bug. + + A single-line triple-slash-prefixed comment (with a field following it) is not enough - it will not trigger the fmt bug. + + Side note: If you have a combination of two, or all three of the + above mentioned types of comments here, some of them disappear + after `cargo fmt`. + + The bug gets triggered even if a field definition following the + (asterisk-based, or a double-slash-prefixed) comment, too. + */ +} diff --git a/tests/target/issue_5728.rs b/tests/target/issue_5728.rs new file mode 100644 index 000000000000..e1355416faf4 --- /dev/null +++ b/tests/target/issue_5728.rs @@ -0,0 +1,5 @@ +cfg_if::cfg_if! { + if #[cfg(windows)] { + } else if #(&cpus) { + } else [libc::CTL_HW, libc::HW_NCPU, 0, 0] +} diff --git a/tests/target/issue_5729.rs b/tests/target/issue_5729.rs new file mode 100644 index 000000000000..d63c83e88f84 --- /dev/null +++ b/tests/target/issue_5729.rs @@ -0,0 +1,5 @@ +cfg_if::cfg_if! { + if { + } else if #(&cpus) { + } else [libc::CTL_HW, libc::HW_NCPU, 0, 0] +} diff --git a/tests/target/item-brace-style-always-next-line.rs b/tests/target/item-brace-style-always-next-line.rs new file mode 100644 index 000000000000..4935fac04f11 --- /dev/null +++ b/tests/target/item-brace-style-always-next-line.rs @@ -0,0 +1,71 @@ +// rustfmt-brace_style: AlwaysNextLine + +mod M +{ + enum A + { + A, + } + + struct B + { + b: i32, + } + + // For empty enums and structs, the brace remains on the same line. + enum C {} + + struct D {} + + enum A + where + T: Copy, + { + A, + } + + struct B + where + T: Copy, + { + b: i32, + } + + // For empty enums and structs, the brace remains on the same line. + enum C + where + T: Copy, {} + + struct D + where + T: Copy, {} +} + +fn function() {} + +trait Trait {} + +impl Trait for T {} + +trait Trait2 +where + T: Copy + Display + Write + Read + FromStr, +{ +} + +trait Trait3 +where + T: Something + + SomethingElse + + Sync + + Send + + Display + + Debug + + Copy + + Hash + + Debug + + Display + + Write + + Read, +{ +} diff --git a/tests/target/item-brace-style-prefer-same-line.rs b/tests/target/item-brace-style-prefer-same-line.rs new file mode 100644 index 000000000000..ef8dc028c2d0 --- /dev/null +++ b/tests/target/item-brace-style-prefer-same-line.rs @@ -0,0 +1,35 @@ +// rustfmt-brace_style: PreferSameLine + +mod M { + enum A { + A, + } + + struct B { + b: i32, + } + + enum C {} + + struct D {} + + enum A + where + T: Copy, { + A, + } + + struct B + where + T: Copy, { + b: i32, + } + + enum C + where + T: Copy, {} + + struct D + where + T: Copy, {} +} diff --git a/tests/target/item-brace-style-same-line-where.rs b/tests/target/item-brace-style-same-line-where.rs new file mode 100644 index 000000000000..fabe5822c89c --- /dev/null +++ b/tests/target/item-brace-style-same-line-where.rs @@ -0,0 +1,37 @@ +mod M { + enum A { + A, + } + + struct B { + b: i32, + } + + // For empty enums and structs, the brace remains on the same line. + enum C {} + + struct D {} + + enum A + where + T: Copy, + { + A, + } + + struct B + where + T: Copy, + { + b: i32, + } + + // For empty enums and structs, the brace remains on the same line. + enum C + where + T: Copy, {} + + struct D + where + T: Copy, {} +} diff --git a/tests/target/itemized-blocks/no_wrap.rs b/tests/target/itemized-blocks/no_wrap.rs new file mode 100644 index 000000000000..86818b447459 --- /dev/null +++ b/tests/target/itemized-blocks/no_wrap.rs @@ -0,0 +1,81 @@ +// rustfmt-normalize_comments: true +// rustfmt-format_code_in_doc_comments: true + +//! This is an itemized markdown list (see also issue #3224): +//! * Outer +//! * Outer +//! * Inner +//! * Inner with lots of text so that it could be reformatted something something something lots of text so that it could be reformatted something something something +//! +//! This example shows how to configure fern to output really nicely colored logs +//! - when the log level is error, the whole line is red +//! - when the log level is warn, the whole line is yellow +//! - when the log level is info, the level name is green and the rest of the line is white +//! - when the log level is debug, the whole line is white +//! - when the log level is trace, the whole line is gray ("bright black") +//! +//! This is a numbered markdown list (see also issue #5416): +//! 1. Long long long long long long long long long long long long long long long long long line +//! 2. Another very long long long long long long long long long long long long long long long line +//! 3. Nested list +//! 1. Long long long long long long long long long long long long long long long long line +//! 2. Another very long long long long long long long long long long long long long long line +//! 4. Last item +//! +//! Using the ')' instead of '.' character after the number: +//! 1) Long long long long long long long long long long long long long long long long long line +//! 2) Another very long long long long long long long long long long long long long long long line +//! +//! Deep list that mixes various bullet and number formats: +//! 1. First level with a long long long long long long long long long long long long long long +//! long long long line +//! 2. First level with another very long long long long long long long long long long long long +//! long long long line +//! * Second level with a long long long long long long long long long long long long long +//! long long long line +//! * Second level with another very long long long long long long long long long long long +//! long long long line +//! 1) Third level with a long long long long long long long long long long long long long +//! long long long line +//! 2) Third level with another very long long long long long long long long long long +//! long long long long line +//! - Forth level with a long long long long long long long long long long long long +//! long long long long line +//! - Forth level with another very long long long long long long long long long long +//! long long long long line +//! 3) One more item at the third level +//! 4) Last item of the third level +//! * Last item of second level +//! 3. Last item of first level + +/// All the parameters ***except for `from_theater`*** should be inserted as sent by the remote +/// theater, i.e., as passed to [`Theater::send`] on the remote actor: +/// * `from` is the sending (remote) [`ActorId`], as reported by the remote theater by theater-specific means +/// * `to` is the receiving (local) [`ActorId`], as requested by the remote theater +/// * `tag` is a tag that identifies the message type +/// * `msg` is the (serialized) message +/// All the parameters ***except for `from_theater`*** should be inserted as sent by the remote +/// theater, i.e., as passed to [`Theater::send`] on the remote actor +fn func1() {} + +/// All the parameters ***except for `from_theater`*** should be inserted as sent by the remote +/// theater, i.e., as passed to [`Theater::send`] on the remote actor: +/// * `from` is the sending (remote) [`ActorId`], as reported by the remote theater by theater-specific means +/// * `to` is the receiving (local) [`ActorId`], as requested by the remote theater +/// * `tag` is a tag that identifies the message type +/// * `msg` is the (serialized) message +/// ``` +/// let x = 42; +/// ``` +fn func2() {} + +/// Look: +/// +/// ``` +/// let x = 42; +/// ``` +/// * `from` is the sending (remote) [`ActorId`], as reported by the remote theater by theater-specific means +/// * `to` is the receiving (local) [`ActorId`], as requested by the remote theater +/// * `tag` is a tag that identifies the message type +/// * `msg` is the (serialized) message +fn func3() {} diff --git a/tests/target/itemized-blocks/rewrite_fail.rs b/tests/target/itemized-blocks/rewrite_fail.rs new file mode 100644 index 000000000000..a118ef6faa18 --- /dev/null +++ b/tests/target/itemized-blocks/rewrite_fail.rs @@ -0,0 +1,14 @@ +// rustfmt-wrap_comments: true +// rustfmt-max_width: 50 + +// This example shows how to configure fern to +// output really nicely colored logs +// - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +// - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +// - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +// - when the log level is info, the level +// name is green and the rest of the line is +// white +// - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +// - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +fn func1() {} diff --git a/tests/target/itemized-blocks/urls.rs b/tests/target/itemized-blocks/urls.rs new file mode 100644 index 000000000000..bc46ea47e164 --- /dev/null +++ b/tests/target/itemized-blocks/urls.rs @@ -0,0 +1,25 @@ +// rustfmt-wrap_comments: true +// rustfmt-max_width: 79 + +//! CMSIS: Cortex Microcontroller Software Interface Standard +//! +//! The version 5 of the standard can be found at: +//! +//! http://arm-software.github.io/CMSIS_5/Core/html/index.html +//! +//! The API reference of the standard can be found at: +//! +//! - example -- http://example.org -- something something something something +//! something something +//! - something something something something something something more -- http://example.org +//! - http://example.org/something/something/something/something/something/something +//! and the rest +//! - Core function access -- http://arm-software.github.io/CMSIS_5/Core/html/group__Core__Register__gr.html +//! - Intrinsic functions for CPU instructions -- http://arm-software.github.io/CMSIS_5/Core/html/group__intrinsic__CPU__gr.html +//! - Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Vestibulum sem +//! lacus, commodo vitae. +//! +//! The reference C implementation used as the base of this Rust port can be +//! found at +//! +//! https://github.com/ARM-software/CMSIS_5/blob/5.3.0/CMSIS/Core/Include/cmsis_gcc.h diff --git a/tests/target/itemized-blocks/wrap.rs b/tests/target/itemized-blocks/wrap.rs new file mode 100644 index 000000000000..4826590ea59d --- /dev/null +++ b/tests/target/itemized-blocks/wrap.rs @@ -0,0 +1,149 @@ +// rustfmt-wrap_comments: true +// rustfmt-format_code_in_doc_comments: true +// rustfmt-max_width: 50 + +//! This is an itemized markdown list (see also +//! issue #3224): +//! * Outer +//! * Outer +//! * Inner +//! * Inner with lots of text so that it could +//! be reformatted something something +//! something lots of text so that it could be +//! reformatted something something something +//! +//! This example shows how to configure fern to +//! output really nicely colored logs +//! - when the log level is error, the whole line +//! is red +//! - when the log level is warn, the whole line +//! is yellow +//! - when the log level is info, the level name +//! is green and the rest of the line is white +//! - when the log level is debug, the whole line +//! is white +//! - when the log level is trace, the whole line +//! is gray ("bright black") +//! +//! This is a numbered markdown list (see also +//! issue #5416): +//! 1. Long long long long long long long long +//! long long long long long long long long +//! long line +//! 2. Another very long long long long long long +//! long long long long long long long long +//! long line +//! 3. Nested list +//! 1. Long long long long long long long long +//! long long long long long long long long +//! line +//! 2. Another very long long long long long +//! long long long long long long long long +//! long line +//! 4. Last item +//! +//! Using the ')' instead of '.' character after +//! the number: +//! 1) Long long long long long long long long +//! long long long long long long long long +//! long line +//! 2) Another very long long long long long long +//! long long long long long long long long +//! long line +//! +//! Deep list that mixes various bullet and number +//! formats: +//! 1. First level with a long long long long long +//! long long long long long long long long +//! long long long long line +//! 2. First level with another very long long +//! long long long long long long long long +//! long long long long long line +//! * Second level with a long long long long +//! long long long long long long long long +//! long long long long line +//! * Second level with another very long long +//! long long long long long long long long +//! long long long long line +//! 1) Third level with a long long long +//! long long long long long long long +//! long long long long long long line +//! 2) Third level with another very long +//! long long long long long long long +//! long long long long long long line +//! - Forth level with a long long +//! long long long long long long +//! long long long long long long +//! long long line +//! - Forth level with another very +//! long long long long long long +//! long long long long long long +//! long long line +//! 3) One more item at the third level +//! 4) Last item of the third level +//! * Last item of second level +//! 3. Last item of first level + +// This example shows how to configure fern to +// output really nicely colored logs +// - when the log level is error, the whole line +// is red +// - when the log level is warn, the whole line +// is yellow +// - when the log level is info, the level +// name is green and the rest of the line is +// white +// - when the log level is debug, the whole line +// is white +// - when the log level is trace, the whole line +// is gray ("bright black") + +/// All the parameters ***except for +/// `from_theater`*** should be inserted as sent +/// by the remote theater, i.e., as passed to +/// [`Theater::send`] on the remote actor: +/// * `from` is the sending (remote) [`ActorId`], +/// as reported by the remote theater by +/// theater-specific means +/// * `to` is the receiving (local) [`ActorId`], +/// as requested by the remote theater +/// * `tag` is a tag that identifies the message +/// type +/// * `msg` is the (serialized) message +/// All the parameters ***except for +/// `from_theater`*** should be inserted as sent +/// by the remote theater, i.e., as passed to +/// [`Theater::send`] on the remote actor +fn func1() {} + +/// All the parameters ***except for +/// `from_theater`*** should be inserted as sent +/// by the remote theater, i.e., as passed to +/// [`Theater::send`] on the remote actor: +/// * `from` is the sending (remote) [`ActorId`], +/// as reported by the remote theater by +/// theater-specific means +/// * `to` is the receiving (local) [`ActorId`], +/// as requested by the remote theater +/// * `tag` is a tag that identifies the message +/// type +/// * `msg` is the (serialized) message +/// ``` +/// let x = 42; +/// ``` +fn func2() {} + +/// Look: +/// +/// ``` +/// let x = 42; +/// ``` +/// * `from` is the sending (remote) [`ActorId`], +/// as reported by the remote theater by +/// theater-specific means +/// * `to` is the receiving (local) [`ActorId`], +/// as requested by the remote theater +/// * `tag` is a tag that identifies the message +/// type +/// * `msg` is the (serialized) message +fn func3() {} diff --git a/tests/target/label_break.rs b/tests/target/label_break.rs new file mode 100644 index 000000000000..728d78137c99 --- /dev/null +++ b/tests/target/label_break.rs @@ -0,0 +1,28 @@ +// format with label break value. +fn main() { + 'empty_block: {} + + 'block: { + do_thing(); + if condition_not_met() { + break 'block; + } + do_next_thing(); + if condition_not_met() { + break 'block; + } + do_last_thing(); + } + + let result = 'block: { + if foo() { + // comment + break 'block 1; + } + if bar() { + /* comment */ + break 'block 2; + } + 3 + }; +} diff --git a/tests/target/large-block.rs b/tests/target/large-block.rs new file mode 100644 index 000000000000..09e9169f3408 --- /dev/null +++ b/tests/target/large-block.rs @@ -0,0 +1,5 @@ +fn issue1351() { + std_fmt_Arguments_new_v1_std_rt_begin_panic_fmt_sdfasfasdfasdf({ + static __STATIC_FMTSTR: &'static [&'static str] = &[]; + }); +} diff --git a/tests/target/large_vec.rs b/tests/target/large_vec.rs new file mode 100644 index 000000000000..95d1fc43c03e --- /dev/null +++ b/tests/target/large_vec.rs @@ -0,0 +1,42 @@ +// See #1470. + +impl Environment { + pub fn new_root() -> Rc> { + let mut env = Environment::new(); + let builtin_functions = &[ + ( + "println", + Function::NativeVoid( + CallSign { + num_params: 0, + variadic: true, + param_types: vec![], + }, + native_println, + ), + ), + ( + "run_http_server", + Function::NativeVoid( + CallSign { + num_params: 1, + variadic: false, + param_types: vec![Some(ConstraintType::Function)], + }, + native_run_http_server, + ), + ), + ( + "len", + Function::NativeReturning( + CallSign { + num_params: 1, + variadic: false, + param_types: vec![None], + }, + native_len, + ), + ), + ]; + } +} diff --git a/tests/target/lazy_static.rs b/tests/target/lazy_static.rs new file mode 100644 index 000000000000..3625e0a5f5c1 --- /dev/null +++ b/tests/target/lazy_static.rs @@ -0,0 +1,49 @@ +// Format `lazy_static!`. + +lazy_static! { + static ref CONFIG_NAME_REGEX: regex::Regex = + regex::Regex::new(r"^## `([^`]+)`").expect("Failed creating configuration pattern"); + static ref CONFIG_VALUE_REGEX: regex::Regex = regex::Regex::new(r#"^#### `"?([^`"]+)"?`"#) + .expect("Failed creating configuration value pattern"); +} + +// We need to be able to format `lazy_static!` without known syntax. +lazy_static!(xxx, yyyy, zzzzz); + +lazy_static! {} + +// #2354 +lazy_static! { + pub static ref Sbase64_encode_string: ::lisp::LispSubrRef = { + let subr = ::remacs_sys::Lisp_Subr { + header: ::remacs_sys::Lisp_Vectorlike_Header { + size: ((::remacs_sys::PseudovecType::PVEC_SUBR as ::libc::ptrdiff_t) + << ::remacs_sys::PSEUDOVECTOR_AREA_BITS), + }, + function: self::Fbase64_encode_string as *const ::libc::c_void, + min_args: 1i16, + max_args: 2i16, + symbol_name: (b"base64-encode-string\x00").as_ptr() as *const ::libc::c_char, + intspec: ::std::ptr::null(), + doc: ::std::ptr::null(), + lang: ::remacs_sys::Lisp_Subr_Lang_Rust, + }; + unsafe { + let ptr = ::remacs_sys::xmalloc(::std::mem::size_of::<::remacs_sys::Lisp_Subr>()) + as *mut ::remacs_sys::Lisp_Subr; + ::std::ptr::copy_nonoverlapping(&subr, ptr, 1); + ::std::mem::forget(subr); + ::lisp::ExternalPtr::new(ptr) + } + }; +} + +lazy_static! { + static ref FOO: HashMap< + String, + ( + &'static str, + fn(Foo) -> Result, Either> + ), + > = HashMap::new(); +} diff --git a/tests/target/let_else.rs b/tests/target/let_else.rs new file mode 100644 index 000000000000..6554a0961c0d --- /dev/null +++ b/tests/target/let_else.rs @@ -0,0 +1,254 @@ +// rustfmt-single_line_let_else_max_width: 100 + +fn main() { + // Although this won't compile it still parses so make sure we can format empty else blocks + let Some(x) = opt else {}; + + // let-else may be formatted on a single line if they are "short" + // and only contain a single expression + let Some(x) = opt else { return }; + + let Some(x) = opt else { return }; + + let Some(x) = opt else { + return; + }; + + let Some(x) = opt else { + // nope + return; + }; + + let Some(x) = opt else { + let y = 1; + return y; + }; + + let Some(x) = y.foo( + "abc", + fairly_long_identifier, + "def", + "123456", + "string", + "cheese", + ) else { + bar() + }; + + let Some(x) = abcdef() + .foo( + "abc", + some_really_really_really_long_ident, + "ident", + "123456", + ) + .bar() + .baz() + .qux("fffffffffffffffff") + else { + foo_bar() + }; +} + +fn with_comments_around_else_keyword() { + let Some(x) = opt + /* pre else keyword block-comment */ + else { + return; + }; + + let Some(x) = opt else + /* post else keyword block-comment */ + { + return; + }; + + let Some(x) = opt + /* pre else keyword block-comment */ + else + /* post else keyword block-comment */ + { + return; + }; + + let Some(x) = opt + // pre else keyword line-comment + else { + return; + }; + + let Some(x) = opt else + // post else keyword line-comment + { + return; + }; + + let Some(x) = opt + // pre else keyword line-comment + else + // post else keyword line-comment + { + return; + }; +} + +fn unbreakable_initializer_expr_pre_formatting_let_else_length_near_max_width() { + // Pre Formatting: + // The length of `(indent)let pat = init else block;` is 100 (max_width) + // Post Formatting: + // The formatting is left unchanged! + let Some(x) = some_really_really_really_really_really_really_really_long_name_A else { return }; + + // Pre Formatting: + // The length of `(indent)let pat = init else block;` is 100 (max_width) + // Post Formatting: + // The else keyword and opening brace remain on the same line as the initializer expr, + // and the else block is formatted over multiple lines because we can't fit the + // else block on the same line as the initializer expr. + let Some(x) = some_really_really_really_really_really_really_really_long_name___B else { + return; + }; + + // Pre Formatting: + // The length of `(indent)let pat = init else block;` is 100 (max_width) + // Post Formatting: + // The else keyword and opening brace remain on the same line as the initializer expr, + // and the else block is formatted over multiple lines because we can't fit the + // else block on the same line as the initializer expr. + let Some(x) = some_really_really_really_really_long_name_____C else { + some_divergent_function() + }; + + // Pre Formatting: + // The length of `(indent)let pat = init else block;` is 101 (> max_width) + // Post Formatting: + // The else keyword and opening brace remain on the same line as the initializer expr, + // and the else block is formatted over multiple lines because we can't fit the + // else block on the same line as the initializer expr. + let Some(x) = some_really_really_really_really_really_really_really_long_name__D else { + return; + }; +} + +fn unbreakable_initializer_expr_pre_formatting_length_up_to_opening_brace_near_max_width() { + // Pre Formatting: + // The length of `(indent)let pat = init else {` is 99 (< max_width) + // Post Formatting: + // The else keyword and opening brace remain on the same line as the initializer expr, + // and the else block is formatted over multiple lines because we can't fit the + // else block on the same line as the initializer expr. + let Some(x) = some_really_really_really_really_really_really_really_really_long_name___E else { + return; + }; + + // Pre Formatting: + // The length of `(indent)let pat = init else {` is 101 (> max_width) + // Post Formatting: + // The else keyword and opening brace cannot fit on the same line as the initializer expr. + // They are formatted on the next line. + let Some(x) = some_really_really_really_really_really_really_really_really_long_name_____F + else { + return; + }; +} + +fn unbreakable_initializer_expr_pre_formatting_length_through_initializer_expr_near_max_width() { + // Pre Formatting: + // The length of `(indent)let pat = init` is 99 (< max_width) + // Post Formatting: + // The else keyword and opening brace cannot fit on the same line as the initializer expr. + // They are formatted on the next line. + let Some(x) = some_really_really_really_really_really_really_really_really_really_long_name___G + else { + return; + }; + + // Pre Formatting: + // The length of `(indent)let pat = init` is 100 (max_width) + // Post Formatting: + // Break after the `=` and put the initializer expr on it's own line. + // Because the initializer expr is multi-lined the else is placed on it's own line. + let Some(x) = + some_really_really_really_really_really_really_really_really_really_long_name____H + else { + return; + }; + + // Pre Formatting: + // The length of `(indent)let pat = init` is 109 (> max_width) + // Post Formatting: + // Break after the `=` and put the initializer expr on it's own line. + // Because the initializer expr is multi-lined the else is placed on it's own line. + // The initializer expr has a length of 91, which when indented on the next line + // The `(indent)init` line has a lengh of 99. This is the max length that the `init` can be + // before we start running into max_width issues. I suspect this is becuase the shape is + // accounting for the `;` at the end of the `let-else` statement. + let Some(x) = + some_really_really_really_really_really_really_really_really_really_really_long_name______I + else { + return; + }; + + // Pre Formatting: + // The length of `(indent)let pat = init` is 110 (> max_width) + // Post Formatting: + // Max length issues prevent us from formatting. + // The initializer expr has a length of 92, which if it would be indented on the next line + // the `(indent)init` line has a lengh of 100 which == max_width of 100. + // One might expect formatting to succeed, but I suspect the reason we hit max_width issues is + // because the Shape is accounting for the `;` at the end of the `let-else` statement. + let Some(x) = some_really_really_really_really_really_really_really_really_really_really_really_long_nameJ else {return}; +} + +fn long_patterns() { + let Foo { + x: Bar(..), + y: FooBar(..), + z: Baz(..), + } = opt + else { + return; + }; + + // with version=One we don't wrap long array patterns + let [aaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbb, cccccccccccccccccc, dddddddddddddddddd] = opt else { + return; + }; + + let ("aaaaaaaaaaaaaaaaaaa" + | "bbbbbbbbbbbbbbbbb" + | "cccccccccccccccccccccccc" + | "dddddddddddddddd" + | "eeeeeeeeeeeeeeee") = opt + else { + return; + }; + + let Some(Ok((Message::ChangeColor(super::color::Color::Rgb(r, g, b)), Point { x, y, z }))) = + opt + else { + return; + }; +} + +fn with_trailing_try_operator() { + // Currently the trailing ? forces the else on the next line + // This may be revisited in style edition 2024 + let Some(next_bucket) = ranking_rules[cur_ranking_rule_index].next_bucket( + ctx, + logger, + &ranking_rule_universes[cur_ranking_rule_index], + )? + else { + return; + }; + + // Maybe this is a workaround? + let Ok(Some(next_bucket)) = ranking_rules[cur_ranking_rule_index].next_bucket( + ctx, + logger, + &ranking_rule_universes[cur_ranking_rule_index], + ) else { + return; + }; +} diff --git a/tests/target/long-fn-1/version_one.rs b/tests/target/long-fn-1/version_one.rs new file mode 100644 index 000000000000..05f69953c26d --- /dev/null +++ b/tests/target/long-fn-1/version_one.rs @@ -0,0 +1,29 @@ +// rustfmt-version: One +// Tests that a function which is almost short enough, but not quite, gets +// formatted correctly. + +impl Foo { + fn some_input( + &mut self, + input: Input, + input_path: Option, + ) -> (Input, Option) { + } + + fn some_inpu(&mut self, input: Input, input_path: Option) -> (Input, Option) { + } +} + +// #1843 +#[allow(non_snake_case)] +pub extern "C" fn Java_com_exonum_binding_storage_indices_ValueSetIndexProxy_nativeContainsByHash( +) -> bool { + false +} + +// #3009 +impl Something { + fn my_function_name_is_way_to_long_but_used_as_a_case_study_or_an_example_its_fine( + ) -> Result<(), String> { + } +} diff --git a/tests/target/long-fn-1/version_two.rs b/tests/target/long-fn-1/version_two.rs new file mode 100644 index 000000000000..32794bccde2e --- /dev/null +++ b/tests/target/long-fn-1/version_two.rs @@ -0,0 +1,29 @@ +// rustfmt-version: Two +// Tests that a function which is almost short enough, but not quite, gets +// formatted correctly. + +impl Foo { + fn some_input( + &mut self, + input: Input, + input_path: Option, + ) -> (Input, Option) { + } + + fn some_inpu(&mut self, input: Input, input_path: Option) -> (Input, Option) { + } +} + +// #1843 +#[allow(non_snake_case)] +pub extern "C" fn Java_com_exonum_binding_storage_indices_ValueSetIndexProxy_nativeContainsByHash() +-> bool { + false +} + +// #3009 +impl Something { + fn my_function_name_is_way_to_long_but_used_as_a_case_study_or_an_example_its_fine() + -> Result<(), String> { + } +} diff --git a/tests/target/long-match-arms-brace-newline.rs b/tests/target/long-match-arms-brace-newline.rs new file mode 100644 index 000000000000..aeb384e7203e --- /dev/null +++ b/tests/target/long-match-arms-brace-newline.rs @@ -0,0 +1,15 @@ +// rustfmt-format_strings: true +// rustfmt-max_width: 80 +// rustfmt-control_brace_style: AlwaysNextLine + +fn main() { + match x + { + aaaaaaaa::Bbbbb::Ccccccccccccc(_, Some(ref x)) + if x == "aaaaaaaaaaa aaaaaaa aaaaaa" => + { + Ok(()) + } + _ => Err(x), + } +} diff --git a/tests/target/long-use-statement-issue-3154.rs b/tests/target/long-use-statement-issue-3154.rs new file mode 100644 index 000000000000..877241e3b6fd --- /dev/null +++ b/tests/target/long-use-statement-issue-3154.rs @@ -0,0 +1,3 @@ +// rustfmt-reorder_imports: false + +pub use self::super::super::super::root::mozilla::detail::StringClassFlags as nsTStringRepr_ClassFlags; diff --git a/tests/target/long_field_access.rs b/tests/target/long_field_access.rs new file mode 100644 index 000000000000..349d2c2f639b --- /dev/null +++ b/tests/target/long_field_access.rs @@ -0,0 +1,4 @@ +fn f() { + block_flow.base.stacking_relative_position_of_display_port = + self.base.stacking_relative_position_of_display_port; +} diff --git a/tests/target/loop.rs b/tests/target/loop.rs new file mode 100644 index 000000000000..f669e7e2c584 --- /dev/null +++ b/tests/target/loop.rs @@ -0,0 +1,34 @@ +fn main() { + loop { + return some_val; + } + + let x = loop { + do_forever(); + }; + + 'label: loop { + // Just comments + } + + 'a: while loooooooooooooooooooooooooooooooooong_variable_name + another_value > some_other_value + { + } + + while aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa > bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb { + } + + while aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa { + } + + 'b: for xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx in some_iter(arg1, arg2) + { + // do smth + } + + while let Some(i) = x.find('s') { + x.update(); + continue; + continue 'foo; + } +} diff --git a/tests/target/macro_not_expr.rs b/tests/target/macro_not_expr.rs new file mode 100644 index 000000000000..45f85ff2c93e --- /dev/null +++ b/tests/target/macro_not_expr.rs @@ -0,0 +1,7 @@ +macro_rules! test { + ($($t:tt)*) => {}; +} + +fn main() { + test!( a : B => c d ); +} diff --git a/tests/target/macro_rules.rs b/tests/target/macro_rules.rs new file mode 100644 index 000000000000..97444aef4044 --- /dev/null +++ b/tests/target/macro_rules.rs @@ -0,0 +1,360 @@ +// rustfmt-format_macro_matchers: true + +macro_rules! m { + () => {}; + ($x:ident) => {}; + ($m1:ident, $m2:ident, $x:ident) => {}; + ($($beginning:ident),*; $middle:ident; $($end:ident),*) => {}; + ( + $($beginning:ident),*; + $middle:ident; + $($end:ident),*; + $($beginning:ident),*; + $middle:ident; + $($end:ident),* + ) => {}; + ($name:ident($($dol:tt $var:ident)*) $($body:tt)*) => {}; + ( + $($i:ident : $ty:ty, $def:expr, $stb:expr, $($dstring:tt),+);+ $(;)* + $($i:ident : $ty:ty, $def:expr, $stb:expr, $($dstring:tt),+);+ $(;)* + ) => {}; + ($foo:tt foo[$attr:meta] $name:ident) => {}; + ($foo:tt[$attr:meta] $name:ident) => {}; + ($foo:tt &'a[$attr:meta] $name:ident) => {}; + ($foo:tt foo #[$attr:meta] $name:ident) => {}; + ($foo:tt #[$attr:meta] $name:ident) => {}; + ($foo:tt &'a #[$attr:meta] $name:ident) => {}; + ($x:tt foo bar foo bar foo bar $y:tt => x * y * z $z:tt, $($a:tt),*) => {}; +} + +macro_rules! impl_a_method { + ($n:ident($a:ident : $ta:ty) -> $ret:ty { $body:expr }) => { + fn $n($a: $ta) -> $ret { + $body + } + macro_rules! $n { + ($va: expr) => { + $n($va) + }; + } + }; + ($n:ident($a:ident : $ta:ty, $b:ident : $tb:ty) -> $ret:ty { $body:expr }) => { + fn $n($a: $ta, $b: $tb) -> $ret { + $body + } + macro_rules! $n { + ($va: expr,$vb: expr) => { + $n($va, $vb) + }; + } + }; + ( + $n:ident($a:ident : $ta:ty, $b:ident : $tb:ty, $c:ident : $tc:ty) -> $ret:ty { $body:expr } + ) => { + fn $n($a: $ta, $b: $tb, $c: $tc) -> $ret { + $body + } + macro_rules! $n { + ($va: expr,$vb: expr,$vc: expr) => { + $n($va, $vb, $vc) + }; + } + }; + ( + $n:ident($a:ident : $ta:ty, $b:ident : $tb:ty, $c:ident : $tc:ty, $d:ident : $td:ty) -> + $ret:ty { $body:expr } + ) => { + fn $n($a: $ta, $b: $tb, $c: $tc, $d: $td) -> $ret { + $body + } + macro_rules! $n { + ($va: expr,$vb: expr,$vc: expr,$vd: expr) => { + $n($va, $vb, $vc, $vd) + }; + } + }; +} + +macro_rules! m { + // a + ($expr:expr, $($func:ident)*) => {{ + let x = $expr; + $func(x) + }}; + + /* b */ + () => { + /* c */ + }; + + (@tag) => {}; + + // d + ($item:ident) => { + mod macro_item { + struct $item; + } + }; +} + +macro m2 { + // a + ($expr:expr, $($func:ident)*) => {{ + let x = $expr; + $func(x) + }} + + /* b */ + () => { + /* c */ + } + + (@tag) => {} + + // d + ($item:ident) => { + mod macro_item { + struct $item; + } + } +} + +// #2438, #2476 +macro_rules! m { + () => { + fn foo() { + this_line_is_98_characters_long_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx(); + } + }; +} +macro_rules! m { + () => { + fn foo() { + this_line_is_99_characters_long_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx( + ); + } + }; +} +macro_rules! m { + () => { + fn foo() { + this_line_is_100_characters_long_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx( + ); + } + }; +} +macro_rules! m { + () => { + fn foo() { + this_line_is_101_characters_long_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx( + ); + } + }; +} + +// #2439 +macro_rules! m { + ( + $line0_xxxxxxxxxxxxxxxxx:expr, + $line1_xxxxxxxxxxxxxxxxx:expr, + $line2_xxxxxxxxxxxxxxxxx:expr, + $line3_xxxxxxxxxxxxxxxxx:expr, + ) => {}; +} + +// #2466 +// Skip formatting `macro_rules!` that are not using `{}`. +macro_rules! m ( + () => () +); +macro_rules! m [ + () => () +]; + +// #2470 +macro foo($type_name:ident, $docs:expr) { + #[allow(non_camel_case_types)] + #[doc=$docs] + #[derive(Debug, Clone, Copy)] + pub struct $type_name; +} + +// #2534 +macro_rules! foo { + ($a:ident : $b:ty) => {}; + ($a:ident $b:ident $c:ident) => {}; +} + +// #2538 +macro_rules! add_message_to_notes { + ($msg:expr) => {{ + let mut lines = message.lines(); + notes.push_str(&format!("\n{}: {}", level, lines.next().unwrap())); + for line in lines { + notes.push_str(&format!( + "\n{:indent$}{line}", + "", + indent = level.len() + 2, + line = line, + )); + } + }}; +} + +// #2560 +macro_rules! binary { + ($_self:ident, $expr:expr, $lhs:expr, $func:ident) => { + while $_self.matched($expr) { + let op = $_self.get_binary_op()?; + + let rhs = Box::new($_self.$func()?); + + $lhs = Spanned { + span: $lhs.get_span().to(rhs.get_span()), + value: Expression::Binary { + lhs: Box::new($lhs), + op, + rhs, + }, + } + } + }; +} + +// #2558 +macro_rules! m { + ($x:) => {}; + ($($foo:expr)()?) => {}; +} + +// #2749 +macro_rules! foo { + ($(x)* {}) => {}; + ($(x)*()) => {}; + ($(x)*[]) => {}; +} +macro_rules! __wundergraph_expand_sqlite_mutation { + ( + $mutation_name:ident $((context = $($context:tt)*))* { + $( + $entity_name:ident( + $(insert = $insert:ident,)* + $(update = $update:ident,)* + $(delete = $($delete:tt)+)* + ), + )* + } + ) => {}; +} + +// #2607 +macro_rules! bench { + ($ty:ident) => { + criterion_group!( + name = benches; + config = ::common_bench::reduced_samples(); + targets = call, map; + ); + }; +} + +// #2770 +macro_rules! save_regs { + () => { + asm!("push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11" + :::: "intel", "volatile"); + }; +} + +// #2721 +macro_rules! impl_as_byte_slice_arrays { + ($n:expr,) => {}; + ($n:expr, $N:ident, $($NN:ident,)*) => { + impl_as_byte_slice_arrays!($n - 1, $($NN,)*); + + impl AsByteSliceMut for [T; $n] where [T]: AsByteSliceMut { + fn as_byte_slice_mut(&mut self) -> &mut [u8] { + self[..].as_byte_slice_mut() + } + + fn to_le(&mut self) { + self[..].to_le() + } + } + }; + (!div $n:expr,) => {}; + (!div $n:expr, $N:ident, $($NN:ident,)*) => { + impl_as_byte_slice_arrays!(!div $n / 2, $($NN,)*); + + impl AsByteSliceMut for [T; $n] where [T]: AsByteSliceMut { + fn as_byte_slice_mut(&mut self) -> &mut [u8] { + self[..].as_byte_slice_mut() + } + + fn to_le(&mut self) { + self[..].to_le() + } + } + }; +} + +// #2919 +fn foo() { + { + macro_rules! touch_value { + ($func:ident, $value:expr) => {{ + let result = API::get_cached().$func( + self, + key.as_ptr(), + $value, + ffi::VSPropAppendMode::paTouch, + ); + let result = API::get_cached().$func(self, key.as_ptr(), $value, ffi::VSPropAppend); + let result = + API::get_cached().$func(self, key.as_ptr(), $value, ffi::VSPropAppendM); + let result = + APIIIIIIIII::get_cached().$func(self, key.as_ptr(), $value, ffi::VSPropAppendM); + let result = API::get_cached().$func( + self, + key.as_ptr(), + $value, + ffi::VSPropAppendMMMMMMMMMM, + ); + debug_assert!(result == 0); + }}; + } + } +} + +// #2642 +macro_rules! template { + ($name:expr) => { + format_args!( + r##" +"http://example.com" + +# test +"##, + $name + ) + }; +} + +macro_rules! template { + () => { + format_args!( + r" +// + +" + ) + }; +} diff --git a/tests/target/macro_rules_semi.rs b/tests/target/macro_rules_semi.rs new file mode 100644 index 000000000000..84e12d16e6e9 --- /dev/null +++ b/tests/target/macro_rules_semi.rs @@ -0,0 +1,22 @@ +macro_rules! expr { + (no_semi) => { + return true + }; + (semi) => { + return true; + }; +} + +fn foo() -> bool { + match true { + true => expr!(no_semi), + false if false => { + expr!(semi) + } + false => { + expr!(semi); + } + } +} + +fn main() {} diff --git a/tests/target/macros.rs b/tests/target/macros.rs new file mode 100644 index 000000000000..7b4574349df3 --- /dev/null +++ b/tests/target/macros.rs @@ -0,0 +1,1058 @@ +// rustfmt-normalize_comments: true +// rustfmt-format_macro_matchers: true +itemmacro!(this, is.now().formatted(yay)); + +itemmacro!( + really, + long.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbb() + .is + .formatted() +); + +itemmacro! {this, is.brace().formatted()} + +fn main() { + foo!(); + + foo!(,); + + bar!(a, b, c); + + bar!(a, b, c,); + + baz!(1 + 2 + 3, quux.kaas()); + + quux!( + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, + BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB + ); + + kaas!( + // comments + a, // post macro + b // another + ); + + trailingcomma!(a, b, c,); + // Preserve trailing comma only when necessary. + ok!(file.seek(SeekFrom::Start( + table.map(|table| fixture.offset(table)).unwrap_or(0), + ))); + + noexpr!( i am not an expression, OK? ); + + vec![a, b, c]; + + vec![ + AAAAAA, + AAAAAA, + AAAAAA, + AAAAAA, + AAAAAA, + AAAAAA, + AAAAAA, + AAAAAA, + AAAAAA, + BBBBB, + 5, + 100 - 30, + 1.33, + b, + b, + b, + ]; + + vec![a /* comment */]; + + // Trailing spaces after a comma + vec![a]; + + vec![a; b]; + vec![a; b]; + vec![a; b]; + + vec![a, b; c]; + vec![a; b, c]; + + vec![ + a; + (|x| { + let y = x + 1; + let z = y + 1; + z + })(2) + ]; + vec![ + a; + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + ]; + vec![a; unsafe { x + 1 }]; + + unknown_bracket_macro__comma_should_not_be_stripped![a,]; + + foo(makro!(1, 3)); + + hamkaas! { () }; + + macrowithbraces! {dont, format, me} + + x!(fn); + + some_macro!(); + + some_macro![]; + + some_macro! { + // comment + }; + + some_macro! { + // comment + }; + + some_macro!( + // comment + not function like + ); + + // #1712 + let image = gray_image!( + 00, 01, 02; + 10, 11, 12; + 20, 21, 22); + + // #1092 + chain!(input, a:take!(max_size), || []); + + // #2727 + foo!("bar"); +} + +impl X { + empty_invoc! {} + empty_invoc! {} +} + +fn issue_1279() { + println!("dsfs"); // a comment +} + +fn issue_1555() { + let hello = &format!( + "HTTP/1.1 200 OK\r\nServer: {}\r\n\r\n{}", + "65454654654654654654654655464", "4" + ); +} + +fn issue1178() { + macro_rules! foo { + (#[$attr:meta] $name:ident) => {}; + } + + foo!( + #[doc = "bar"] + baz + ); +} + +fn issue1739() { + sql_function!(add_rss_item, + add_rss_item_t, + (a: types::Integer, + b: types::Timestamptz, + c: types::Text, + d: types::Text, + e: types::Text)); + + w.slice_mut(s![ + .., + init_size[1] - extreeeeeeeeeeeeeeeeeeeeeeeem..init_size[1], + .. + ]) + .par_map_inplace(|el| *el = 0.); +} + +fn issue_1885() { + let threads = people + .into_iter() + .map(|name| { + chan_select! { + rx.recv() => {} + } + }) + .collect::>(); +} + +fn issue_1917() { + mod x { + quickcheck! { + fn test(a: String, s: String, b: String) -> TestResult { + if a.find(&s).is_none() { + + TestResult::from_bool(true) + } else { + TestResult::discard() + } + } + } + } +} + +fn issue_1921() { + // Macro with tabs. + lazy_static! { + static ref ONE: u32 = 1; + static ref TWO: u32 = 2; + static ref THREE: u32 = 3; + static ref FOUR: u32 = { + let mut acc = 1; + acc += 1; + acc += 2; + acc + }; + } +} + +// #1577 +fn issue1577() { + let json = json!({ + "foo": "bar", + }); +} + +// #3174 +fn issue_3174() { + let data = if let Some(debug) = error.debug_info() { + json!({ + "errorKind": format!("{:?}", error.err_kind()), + "debugMessage": debug.message, + }) + } else { + json!({"errorKind": format!("{:?}", error.err_kind())}) + }; +} + +gfx_pipeline!(pipe { + vbuf: gfx::VertexBuffer = (), + out: gfx::RenderTarget = "Target0", +}); + +// #1919 +#[test] +fn __bindgen_test_layout_HandleWithDtor_open0_int_close0_instantiation() { + assert_eq!( + ::std::mem::size_of::>(), + 8usize, + concat!( + "Size of template specialization: ", + stringify!(HandleWithDtor<::std::os::raw::c_int>) + ) + ); + assert_eq!( + ::std::mem::align_of::>(), + 8usize, + concat!( + "Alignment of template specialization: ", + stringify!(HandleWithDtor<::std::os::raw::c_int>) + ) + ); +} + +// #878 +macro_rules! try_opt { + ($expr:expr) => { + match $expr { + Some(val) => val, + + None => { + return None; + } + } + }; +} + +// #2214 +// macro call whose argument is an array with trailing comma. +fn issue2214() { + make_test!( + str_searcher_ascii_haystack, + "bb", + "abbcbbd", + [ + Reject(0, 1), + Match(1, 3), + Reject(3, 4), + Match(4, 6), + Reject(6, 7), + ] + ); +} + +fn special_case_macros() { + let p = eprint!(); + let q = eprint!("{}", 1); + let r = eprint!( + "{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 + ); + let s = eprint!( + "{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26 + ); + + let q = eprintln!("{}", 1); + let r = eprintln!( + "{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 + ); + let s = eprintln!( + "{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26 + ); + + let q = format!("{}", 1); + let r = format!( + "{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 + ); + let s = format!( + "{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26 + ); + + let q = format_args!("{}", 1); + let r = format_args!( + "{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 + ); + let s = format_args!( + "{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26 + ); + + let q = print!("{}", 1); + let r = print!( + "{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 + ); + let s = print!( + "{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26 + ); + + let q = println!("{}", 1); + let r = println!( + "{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 + ); + let s = println!( + "{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26 + ); + + let q = unreachable!("{}", 1); + let r = unreachable!( + "{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 + ); + let s = unreachable!( + "{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26 + ); + + debug!("{}", 1); + debug!( + "{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 + ); + debug!( + "{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26 + ); + + error!("{}", 1); + error!( + "{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 + ); + error!( + "{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26 + ); + + info!("{}", 1); + info!( + "{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 + ); + info!( + "{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26 + ); + + panic!("{}", 1); + panic!( + "{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 + ); + panic!( + "{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26 + ); + + warn!("{}", 1); + warn!( + "{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 + ); + warn!( + "{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26 + ); + + assert!(); + assert!(result == 42); + assert!(result == 42, "Ahoy there, {}!", target); + assert!( + result == 42, + "Arr! While plunderin' the hold, we got '{}' when given '{}' (we expected '{}')", + result, + input, + expected + ); + assert!( + result == 42, + "{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26 + ); + + assert_eq!(); + assert_eq!(left); + assert_eq!(left, right); + assert_eq!(left, right, "Ahoy there, {}!", target); + assert_eq!( + left, right, + "Arr! While plunderin' the hold, we got '{}' when given '{}' (we expected '{}')", + result, input, expected + ); + assert_eq!( + first_realllllllllllly_long_variable_that_doesnt_fit_one_one_line, + second_reallllllllllly_long_variable_that_doesnt_fit_one_one_line, + "Arr! While plunderin' the hold, we got '{}' when given '{}' (we expected '{}')", + result, + input, + expected + ); + assert_eq!( + left + 42, + right, + "Arr! While plunderin' the hold, we got '{}' when given '{}' (we expected '{}')", + result, + input, + expected + ); + assert_eq!( + left, + right, + "{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26 + ); + + write!(&mut s, "Ahoy there, {}!", target); + write!( + &mut s, + "Arr! While plunderin' the hold, we got '{}' when given '{}' (we expected '{}')", + result, input, expected + ); + write!( + &mut s, + "{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26 + ); + + writeln!(&mut s, "Ahoy there, {}!", target); + writeln!( + &mut s, + "Arr! While plunderin' the hold, we got '{}' when given '{}' (we expected '{}')", + result, input, expected + ); + writeln!( + &mut s, + "{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26 + ); +} + +// #1209 +impl Foo { + /// foo + pub fn foo(&self) -> Bar {} +} + +// #819 +fn macro_in_pattern_position() { + let x = match y { + foo!() => (), + bar!(a, b, c) => (), + bar!(a, b, c,) => (), + baz!(1 + 2 + 3, quux.kaas()) => (), + quux!( + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, + BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB + ) => (), + }; +} + +macro foo() {} + +pub macro bar($x:ident + $y:expr;) { + fn foo($x: Foo) { + long_function( + a_long_argument_to_a_long_function_is_what_this_is(AAAAAAAAAAAAAAAAAAAAAAAAAAAA), + $x.bar($y), + ); + } +} + +macro foo() { + // a comment + fn foo() { + // another comment + bar(); + } +} + +// #2574 +macro_rules! test { + () => {{}}; +} + +macro lex_err($kind: ident $(, $body: expr)*) { + Err(QlError::LexError(LexError::$kind($($body,)*))) +} + +// Preserve trailing comma on item-level macro with `()` or `[]`. +methods![get, post, delete,]; +methods!(get, post, delete,); + +// #2588 +macro_rules! m { + () => { + r#" + test + "# + }; +} +fn foo() { + f! {r#" + test + "#}; +} + +// #2591 +fn foo() { + match 0u32 { + 0 => (), + _ => unreachable!(/* obviously */), + } +} + +fn foo() { + let _ = column!(/* here */); +} + +// #2616 +// Preserve trailing comma when using mixed layout for macro call. +fn foo() { + foo!( + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + ); + foo!( + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + ); +} + +// #2830 +// Preserve trailing comma-less/ness inside nested macro. +named!( + do_parse_gsv, + map_res!( + do_parse!( + number_of_sentences: map_res!(digit, parse_num::) + >> char!(',') + >> sentence_index: map_res!(digit, parse_num::) + >> char!(',') + >> total_number_of_sats: map_res!(digit, parse_num::) + >> char!(',') + >> sat0: opt!(complete!(parse_gsv_sat_info)) + >> sat1: opt!(complete!(parse_gsv_sat_info)) + >> sat2: opt!(complete!(parse_gsv_sat_info)) + >> sat3: opt!(complete!(parse_gsv_sat_info)) + >> ( + number_of_sentences, + sentence_index, + total_number_of_sats, + sat0, + sat1, + sat2, + sat3 + ) + ), + construct_gsv_data + ) +); + +// #2857 +convert_args!(vec!(1, 2, 3)); + +// #3031 +thread_local!( + /// TLV Holds a set of JSTraceables that need to be rooted + static ROOTED_TRACEABLES: RefCell = RefCell::new(RootedTraceableSet::new()); +); + +thread_local![ + /// TLV Holds a set of JSTraceables that need to be rooted + static ROOTED_TRACEABLES: RefCell = RefCell::new(RootedTraceableSet::new()); + + /// TLV Holds a set of JSTraceables that need to be rooted + static ROOTED_TRACEABLES: RefCell = + RefCell::new(RootedTraceableSet::new(0)); + + /// TLV Holds a set of JSTraceables that need to be rooted + static ROOTED_TRACEABLES: RefCell = + RefCell::new(RootedTraceableSet::new(), xxx, yyy); + + /// TLV Holds a set of JSTraceables that need to be rooted + static ROOTED_TRACEABLES: RefCell = + RefCell::new(RootedTraceableSet::new(1234)); +]; + +fn issue3004() { + foo!(|_| { () }); + stringify!((foo+)); +} + +// #3331 +pub fn fold_abi(_visitor: &mut V, _i: Abi) -> Abi { + Abi { + extern_token: Token![extern](tokens_helper(_visitor, &_i.extern_token.span)), + name: (_i.name).map(|it| _visitor.fold_lit_str(it)), + } +} + +// #3463 +x! {()} + +// #3746 +f!(match a { + 4 => &[ + (3, false), // Missing + (4, true) // I-frame + ][..], +}); + +// #3583 +foo!(|x = y|); diff --git a/tests/target/markdown-comment-with-options.rs b/tests/target/markdown-comment-with-options.rs new file mode 100644 index 000000000000..ede2bc0d035f --- /dev/null +++ b/tests/target/markdown-comment-with-options.rs @@ -0,0 +1,17 @@ +// rustfmt-wrap_comments: true + +// Preserve two trailing whitespaces in doc comment, +// but trim any whitespaces in normal comment. + +//! hello world +//! hello world + +/// hello world +/// hello world +/// hello world +fn foo() { + // hello world + // hello world + let x = 3; + println!("x = {}", x); +} diff --git a/tests/target/markdown-comment.rs b/tests/target/markdown-comment.rs new file mode 100644 index 000000000000..71a9921d2862 --- /dev/null +++ b/tests/target/markdown-comment.rs @@ -0,0 +1,15 @@ +// Preserve two trailing whitespaces in doc comment, +// but trim any whitespaces in normal comment. + +//! hello world +//! hello world + +/// hello world +/// hello world +/// hello world +fn foo() { + // hello world + // hello world + let x = 3; + println!("x = {}", x); +} diff --git a/tests/target/match-block-trailing-comma.rs b/tests/target/match-block-trailing-comma.rs new file mode 100644 index 000000000000..5ab433a2e6cf --- /dev/null +++ b/tests/target/match-block-trailing-comma.rs @@ -0,0 +1,26 @@ +// rustfmt-match_block_trailing_comma: true +// Match expressions, no unwrapping of block arms or wrapping of multiline +// expressions. + +fn foo() { + match x { + a => { + "line1"; + "line2" + }, + ThisIsA::Guard if true => { + "line1"; + "line2" + }, + ThisIsA::ReallyLongPattern(ThatWillForce::TheGuard, ToWrapOnto::TheFollowingLine) + if true => + { + "line1"; + "line2" + }, + b => ( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, + ), + } +} diff --git a/tests/target/match-flattening.rs b/tests/target/match-flattening.rs new file mode 100644 index 000000000000..f246952a08d4 --- /dev/null +++ b/tests/target/match-flattening.rs @@ -0,0 +1,23 @@ +fn main() { + match option { + None => { + if condition { + true + } else { + false + } + } + } +} + +fn main() { + match option { + None => { + if condition { + true + } else { + false + } + } + } +} diff --git a/tests/target/match-nowrap-trailing-comma.rs b/tests/target/match-nowrap-trailing-comma.rs new file mode 100644 index 000000000000..19ef2144822a --- /dev/null +++ b/tests/target/match-nowrap-trailing-comma.rs @@ -0,0 +1,17 @@ +// rustfmt-match_arm_blocks: false +// rustfmt-match_block_trailing_comma: true +// Match expressions, no unwrapping of block arms or wrapping of multiline +// expressions. + +fn foo() { + match x { + a => { + "line1"; + "line2" + }, + b => ( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, + ), + } +} diff --git a/tests/target/match-nowrap.rs b/tests/target/match-nowrap.rs new file mode 100644 index 000000000000..9e674b1e2f54 --- /dev/null +++ b/tests/target/match-nowrap.rs @@ -0,0 +1,13 @@ +// rustfmt-match_arm_blocks: false +// Match expressions, no unwrapping of block arms or wrapping of multiline +// expressions. + +fn foo() { + match x { + a => foo(), + b => ( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, + ), + } +} diff --git a/tests/target/match.rs b/tests/target/match.rs new file mode 100644 index 000000000000..1bf3fb758ee8 --- /dev/null +++ b/tests/target/match.rs @@ -0,0 +1,629 @@ +// rustfmt-normalize_comments: true +// Match expressions. + +fn foo() { + // A match expression. + match x { + // Some comment. + a => foo(), + b if 0 < 42 => foo(), + c => { + // Another comment. + // Comment. + an_expression; + foo() + } + Foo(ref bar) => { + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + } + Pattern1 | Pattern2 | Pattern3 => false, + Paternnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn + | Paternnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn => blah, + Patternnnnnnnnnnnnnnnnnnn + | Patternnnnnnnnnnnnnnnnnnn + | Patternnnnnnnnnnnnnnnnnnn + | Patternnnnnnnnnnnnnnnnnnn => meh, + + Patternnnnnnnnnnnnnnnnnnn | Patternnnnnnnnnnnnnnnnnnn if looooooooooooooooooong_guard => { + meh + } + + Patternnnnnnnnnnnnnnnnnnnnnnnnn | Patternnnnnnnnnnnnnnnnnnnnnnnnn + if looooooooooooooooooooooooooooooooooooooooong_guard => + { + meh + } + + // Test that earlier patterns can take the guard space + (aaaa, bbbbb, ccccccc, aaaaa, bbbbbbbb, cccccc, aaaa, bbbbbbbb, cccccc, dddddd) + | Patternnnnnnnnnnnnnnnnnnnnnnnnn + if loooooooooooooooooooooooooooooooooooooooooong_guard => {} + + _ => {} + ast::PathParameters::AngleBracketedParameters(ref data) + if data.lifetimes.len() > 0 || data.types.len() > 0 || data.bindings.len() > 0 => {} + } + + let whatever = match something { + /// DOC COMMENT! + Some(_) => 42, + // Comment on an attribute. + #[an_attribute] + // Comment after an attribute. + None => 0, + #[rustfmt::skip] + Blurb => { } + }; +} + +// Test that a match on an overflow line is laid out properly. +fn main() { + let sub_span = + match xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx { + Some(sub_span) => Some(sub_span), + None => sub_span, + }; +} + +// Test that one-line bodies align. +fn main() { + match r { + Variableeeeeeeeeeeeeeeeee => ( + "variable", + vec!["id", "name", "qualname", "value", "type", "scopeid"], + true, + true, + ), + Enummmmmmmmmmmmmmmmmmmmm => ( + "enum", + vec!["id", "qualname", "scopeid", "value"], + true, + true, + ), + Variantttttttttttttttttttttttt => ( + "variant", + vec!["id", "name", "qualname", "type", "value", "scopeid"], + true, + true, + ), + }; + + match x { + y => { /*Block with comment. Preserve me.*/ } + z => { + stmt(); + } + } +} + +fn matches() { + match 1 { + -1 => 10, + 1 => 1, // foo + 2 => 2, + // bar + 3 => 3, + _ => 0, // baz + } +} + +fn match_skip() { + let _ = match Some(1) { + #[rustfmt::skip] + Some( n ) => n, + None => 1, + }; +} + +fn issue339() { + match a { + b => {} + c => {} + d => {} + e => {} + // collapsing here is safe + ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff => {} + // collapsing here exceeds line length + ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffg => { + } + h => { // comment above block + } + i => {} // comment below block + j => { + // comment inside block + } + j2 => { + // comments inside... + } // ... and after + // TODO uncomment when vertical whitespace is handled better + // k => { + // + // // comment with WS above + // } + // l => { + // // comment with ws below + // + // } + m => {} + n => {} + o => {} + p => { // Don't collapse me + } + q => {} + r => {} + s => 0, // s comment + // t comment + t => 1, + u => 2, + v => {} /* funky block + * comment */ + /* final comment */ + } +} + +fn issue355() { + match mac { + a => println!("a", b), + b => vec![1, 2], + c => vec![3; 4], + d => { + println!("a", b) + } + e => { + vec![1, 2] + } + f => { + vec![3; 4] + } + h => println!("a", b), // h comment + i => vec![1, 2], // i comment + j => vec![3; 4], // j comment + // k comment + k => println!("a", b), + // l comment + l => vec![1, 2], + // m comment + m => vec![3; 4], + // Rewrite splits macro + nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn => { + println!("a", b) + } + // Rewrite splits macro + oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo => { + vec![1, 2] + } + // Macro support fails to recognise this macro as splittable + // We push the whole expr to a new line, TODO split this macro as well + pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp => { + vec![3; 4] + } + // q, r and s: Rewrite splits match arm + qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq => { + println!("a", b) + } + rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr => { + vec![1, 2] + } + ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss => { + vec![3; 4] + } + // Funky bracketing styles + t => println! {"a", b}, + u => vec![1, 2], + v => vec![3; 4], + w => println!["a", b], + x => vec![1, 2], + y => vec![3; 4], + // Brackets with comments + tc => println! {"a", b}, // comment + uc => vec![1, 2], // comment + vc => vec![3; 4], // comment + wc => println!["a", b], // comment + xc => vec![1, 2], // comment + yc => vec![3; 4], // comment + yd => looooooooooooooooooooooooooooooooooooooooooooooooooooooooong_func( + aaaaaaaaaa, bbbbbbbbbb, cccccccccc, dddddddddd, + ), + } +} + +fn issue280() { + { + match x { + CompressionMode::DiscardNewline | CompressionMode::CompressWhitespaceNewline => { + ch == '\n' + } + ast::ItemConst(ref typ, ref expr) => { + self.process_static_or_const_item(item, &typ, &expr) + } + } + } +} + +fn issue383() { + match resolution.last_private { + LastImport { .. } => false, + _ => true, + }; +} + +fn issue507() { + match 1 { + 1 => unsafe { std::intrinsics::abort() }, + _ => (), + } +} + +fn issue508() { + match s.type_id() { + Some(NodeTypeId::Element(ElementTypeId::HTMLElement( + HTMLElementTypeId::HTMLCanvasElement, + ))) => true, + Some(NodeTypeId::Element(ElementTypeId::HTMLElement( + HTMLElementTypeId::HTMLObjectElement, + ))) => s.has_object_data(), + Some(NodeTypeId::Element(_)) => false, + } +} + +fn issue496() { + { + { + { + match def { + def::DefConst(def_id) | def::DefAssociatedConst(def_id) => { + match const_eval::lookup_const_by_id(cx.tcx, def_id, Some(self.pat.id)) { + Some(const_expr) => x, + } + } + } + } + } + } +} + +fn issue494() { + { + match stmt.node { + hir::StmtExpr(ref expr, id) | hir::StmtSemi(ref expr, id) => { + result.push(StmtRef::Mirror(Box::new(Stmt { + span: stmt.span, + kind: StmtKind::Expr { + scope: cx.tcx.region_maps.node_extent(id), + expr: expr.to_ref(), + }, + }))) + } + } + } +} + +fn issue386() { + match foo { + BiEq | BiLt | BiLe | BiNe | BiGt | BiGe => true, + BiAnd | BiOr | BiAdd | BiSub | BiMul | BiDiv | BiRem | BiBitXor | BiBitAnd | BiBitOr + | BiShl | BiShr => false, + } +} + +fn guards() { + match foo { + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + if foooooooooooooo && barrrrrrrrrrrr => {} + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + if foooooooooooooo && barrrrrrrrrrrr => {} + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + if fooooooooooooooooooooo + && (bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + || cccccccccccccccccccccccccccccccccccccccc) => {} + } +} + +fn issue1371() { + Some(match type_ { + sfEvtClosed => Closed, + sfEvtResized => { + let e = unsafe { *event.size.as_ref() }; + + Resized { + width: e.width, + height: e.height, + } + } + sfEvtLostFocus => LostFocus, + sfEvtGainedFocus => GainedFocus, + sfEvtTextEntered => TextEntered { + unicode: unsafe { + ::std::char::from_u32((*event.text.as_ref()).unicode) + .expect("Invalid unicode encountered on TextEntered event") + }, + }, + sfEvtKeyPressed => { + let e = unsafe { event.key.as_ref() }; + + KeyPressed { + code: unsafe { ::std::mem::transmute(e.code) }, + alt: e.alt.to_bool(), + ctrl: e.control.to_bool(), + shift: e.shift.to_bool(), + system: e.system.to_bool(), + } + } + sfEvtKeyReleased => { + let e = unsafe { event.key.as_ref() }; + + KeyReleased { + code: unsafe { ::std::mem::transmute(e.code) }, + alt: e.alt.to_bool(), + ctrl: e.control.to_bool(), + shift: e.shift.to_bool(), + system: e.system.to_bool(), + } + } + }) +} + +fn issue1395() { + let bar = Some(true); + let foo = Some(true); + let mut x = false; + bar.and_then(|_| match foo { + None => None, + Some(b) => { + x = true; + Some(b) + } + }); +} + +fn issue1456() { + Ok(Recording { + artists: match reader.evaluate(".//mb:recording/mb:artist-credit/mb:name-credit")? { + Nodeset(nodeset) => { + let res: Result, ReadError> = nodeset + .iter() + .map(|node| { + XPathNodeReader::new(node, &context).and_then(|r| ArtistRef::from_xml(&r)) + }) + .collect(); + res? + } + _ => Vec::new(), + }, + }) +} + +fn issue1460() { + let _ = match foo { + REORDER_BUFFER_CHANGE_INTERNAL_SPEC_INSERT => { + "internal_spec_insert_internal_spec_insert_internal_spec_insert" + } + _ => "reorder_something", + }; +} + +fn issue525() { + foobar( + f, + "{}", + match *self { + TaskState::Started => "started", + TaskState::Success => "success", + TaskState::Failed => "failed", + }, + ); +} + +// #1838, #1839 +fn match_with_near_max_width() { + let (this_line_uses_99_characters_and_is_formatted_properly, x012345) = match some_expression { + _ => unimplemented!(), + }; + + let (should_be_formatted_like_the_line_above_using_100_characters, x0) = match some_expression { + _ => unimplemented!(), + }; + + let (should_put_the_brace_on_the_next_line_using_101_characters, x0000) = match some_expression + { + _ => unimplemented!(), + }; + match m { + Variant::Tag + | Variant::Tag2 + | Variant::Tag3 + | Variant::Tag4 + | Variant::Tag5 + | Variant::Tag6 => {} + } +} + +fn match_with_trailing_spaces() { + match x { + Some(..) => 0, + None => 1, + } +} + +fn issue_2099() { + let a = match x {}; + let b = match x {}; + + match x {} +} + +// #2021 +impl<'tcx> Const<'tcx> { + pub fn from_constval<'a>() -> Const<'tcx> { + let val = match *cv { + ConstVal::Variant(_) | ConstVal::Aggregate(..) | ConstVal::Unevaluated(..) => bug!( + "MIR must not use `{:?}` (aggregates are expanded to MIR rvalues)", + cv + ), + }; + } +} + +// #2151 +fn issue_2151() { + match either { + x => {} + y => (), + } +} + +// #2152 +fn issue_2152() { + match m { + "aaaaaaaaaaaaa" | "bbbbbbbbbbbbb" | "cccccccccccccccccccccccccccccccccccccccccccc" + if true => {} + "bind" | "writev" | "readv" | "sendmsg" | "recvmsg" if android && (aarch64 || x86_64) => { + true + } + } +} + +// #2376 +// Preserve block around expressions with condition. +fn issue_2376() { + let mut x = None; + match x { + Some(0) => { + for i in 1..11 { + x = Some(i); + } + } + Some(ref mut y) => { + while *y < 10 { + *y += 1; + } + } + None => { + while let None = x { + x = Some(10); + } + } + } +} + +// #2621 +// Strip leading `|` in match arm patterns +fn issue_2621() { + let x = Foo::A; + match x { + Foo::A => println!("No vert single condition"), + Foo::B | Foo::C => println!("Center vert two conditions"), + Foo::D => println!("Preceding vert single condition"), + Foo::E | Foo::F => println!("Preceding vert over two lines"), + Foo::G | Foo::H => println!("Trailing vert over two lines"), + // Comment on its own line + Foo::I => println!("With comment"), // Comment after line + } +} + +fn issue_2377() { + match tok { + Tok::Not + | Tok::BNot + | Tok::Plus + | Tok::Minus + | Tok::PlusPlus + | Tok::MinusMinus + | Tok::Void + | Tok::Delete + if prec <= 16 => + { + // code here... + } + Tok::TypeOf if prec <= 16 => {} + } +} + +// #3040 +fn issue_3040() { + { + match foo { + DevtoolScriptControlMsg::WantsLiveNotifications(id, to_send) => { + match documents.find_window(id) { + Some(window) => { + devtools::handle_wants_live_notifications(window.upcast(), to_send) + } + None => return warn!("Message sent to closed pipeline {}.", id), + } + } + } + } +} + +// #3030 +fn issue_3030() { + match input.trim().parse::() { + Ok(val) + if !( + // A valid number is the same as what rust considers to be valid, + // except for +1., NaN, and Infinity. + val.is_infinite() || val.is_nan() || input.ends_with(".") || input.starts_with("+") + ) => {} + } +} + +fn issue_3005() { + match *token { + Token::Dimension { + value, ref unit, .. + } if num_context.is_ok(context.parsing_mode, value) => { + return NoCalcLength::parse_dimension(context, value, unit) + .map(LengthOrPercentage::Length) + .map_err(|()| location.new_unexpected_token_error(token.clone())); + } + } +} + +// #3774 +fn issue_3774() { + { + { + { + match foo { + Lam(_, _, _) | Pi(_, _, _) | Let(_, _, _, _) | Embed(_) | Var(_) => unreachab(), + Lam(_, _, _) | Pi(_, _, _) | Let(_, _, _, _) | Embed(_) | Var(_) => unreacha!(), + Lam(_, _, _) | Pi(_, _, _) | Let(_, _, _, _) | Embed(_) | Var(_) => { + unreachabl() + } + Lam(_, _, _) | Pi(_, _, _) | Let(_, _, _, _) | Embed(_) | Var(_) => { + unreachae!() + } + Lam(_, _, _) | Pi(_, _, _) | Let(_, _, _, _) | Embed(_) | Var(_) => { + unreachable() + } + Lam(_, _, _) | Pi(_, _, _) | Let(_, _, _, _) | Embed(_) | Var(_) => { + unreachable!() + } + Lam(_, _, _) | Pi(_, _, _) | Let(_, _, _, _) | Embed(_) | Var(_) => { + rrunreachable!() + } + } + } + } + } +} + +// #4109 +fn issue_4109() { + match () { + _ => { + #[cfg(debug_assertions)] + { + println!("Foo"); + } + } + } + + match () { + _ => { + #[allow(unsafe_code)] + unsafe {} + } + } +} diff --git a/tests/target/match_overflow_expr.rs b/tests/target/match_overflow_expr.rs new file mode 100644 index 000000000000..b817879d1f48 --- /dev/null +++ b/tests/target/match_overflow_expr.rs @@ -0,0 +1,50 @@ +// rustfmt-overflow_delimited_expr: true + +fn main() { + println!("Foobar: {}", match "input" { + "a" => "", + "b" => "", + "c" => "", + "d" => "", + "e" => "", + "f" => "", + "g" => "", + "h" => "", + "i" => "", + "j" => "", + "k" => "", + "l" => "", + "m" => "", + "n" => "", + "o" => "", + "p" => "", + "q" => "", + "r" => "Rust", + }); +} + +fn main() { + println!( + "Very Long Input String Which Makes It Impossible To Fit On The Same Line: {}", + match "input" { + "a" => "", + "b" => "", + "c" => "", + "d" => "", + "e" => "", + "f" => "", + "g" => "", + "h" => "", + "i" => "", + "j" => "", + "k" => "", + "l" => "", + "m" => "", + "n" => "", + "o" => "", + "p" => "", + "q" => "", + "r" => "Rust", + } + ); +} diff --git a/tests/target/max-line-length-in-chars.rs b/tests/target/max-line-length-in-chars.rs new file mode 100644 index 000000000000..d49fbb7e30e5 --- /dev/null +++ b/tests/target/max-line-length-in-chars.rs @@ -0,0 +1,4 @@ +// rustfmt-max_width: 25 + +// абвгдеёжзийклмнопрст +fn main() {} diff --git a/tests/target/merge_imports_true_compat.rs b/tests/target/merge_imports_true_compat.rs new file mode 100644 index 000000000000..46cd0a3b8a0a --- /dev/null +++ b/tests/target/merge_imports_true_compat.rs @@ -0,0 +1,3 @@ +// rustfmt-merge_imports: true + +use a::{b, c}; diff --git a/tests/target/mod-1.rs b/tests/target/mod-1.rs new file mode 100644 index 000000000000..4118d123dd06 --- /dev/null +++ b/tests/target/mod-1.rs @@ -0,0 +1,37 @@ +// Deeply indented modules. + +mod foo { + mod bar { + mod baz {} + } +} + +mod foo { + mod bar { + mod baz { + fn foo() { + bar() + } + } + } + + mod qux {} +} + +mod boxed { + pub use std::boxed::{Box, HEAP}; +} + +pub mod x { + pub fn freopen( + filename: *const c_char, + mode: *const c_char, + mode2: *const c_char, + mode3: *const c_char, + file: *mut FILE, + ) -> *mut FILE { + } +} + +mod y { // sup boooooiiii +} diff --git a/tests/target/mod-2.rs b/tests/target/mod-2.rs new file mode 100644 index 000000000000..1a093bd52012 --- /dev/null +++ b/tests/target/mod-2.rs @@ -0,0 +1,5 @@ +// Some nested mods + +#[cfg(test)] +mod nestedmod; +pub mod no_new_line_beginning; diff --git a/tests/target/mod_skip_child.rs b/tests/target/mod_skip_child.rs new file mode 100644 index 000000000000..d48c4a37e810 --- /dev/null +++ b/tests/target/mod_skip_child.rs @@ -0,0 +1,2 @@ +// rustfmt-skip_children: true +mod nested_skipped; diff --git a/tests/target/mulit-file.rs b/tests/target/mulit-file.rs new file mode 100644 index 000000000000..1f829b36f3f0 --- /dev/null +++ b/tests/target/mulit-file.rs @@ -0,0 +1,10 @@ +// Tests that where a single file is referred to in multiple places, we don't +// crash. + +#[cfg(all(foo))] +#[path = "closure.rs"] +pub mod imp; + +#[cfg(all(bar))] +#[path = "closure.rs"] +pub mod imp; diff --git a/tests/target/multiline_string_in_macro_def.rs b/tests/target/multiline_string_in_macro_def.rs new file mode 100644 index 000000000000..dafc738f8dbd --- /dev/null +++ b/tests/target/multiline_string_in_macro_def.rs @@ -0,0 +1,14 @@ +macro_rules! assert_approx_eq { + ($a:expr, $b:expr, $eps:expr) => {{ + let (a, b) = (&$a, &$b); + assert!( + (*a - *b).abs() < $eps, + "assertion failed: `(left !== right)` \ + (left: `{:?}`, right: `{:?}`, expect diff: `{:?}`, real diff: `{:?}`)", + *a, + *b, + $eps, + (*a - *b).abs() + ); + }}; +} diff --git a/tests/target/multiple.rs b/tests/target/multiple.rs new file mode 100644 index 000000000000..ee6ef220c4bc --- /dev/null +++ b/tests/target/multiple.rs @@ -0,0 +1,180 @@ +// rustfmt-normalize_comments: true +// rustfmt-wrap_comments: true +// rustfmt-format_strings: true +// Test of lots of random stuff. +// FIXME split this into multiple, self-contained tests. + +#[attr1] +extern crate foo; +#[attr2] +#[attr3] +extern crate foo; +#[attr1] +extern crate foo; +#[attr2] +#[attr3] +extern crate foo; + +use std::cell::*; +use std::{ + self, any, ascii, borrow, borrow, borrow, borrow, borrow, borrow, borrow, borrow, borrow, + borrow, borrow, boxed, boxed, boxed, boxed, boxed, boxed, boxed, boxed, boxed, boxed, char, + char, char, char, char, char, char, char, char, char, +}; + +mod doc; +mod other; + +// sfdgfffffffffffffffffffffffffffffffffffffffffffffffffffffff +// ffffffffffffffffffffffffffffffffffffffffff + +fn foo(a: isize, b: u32 /* blah blah */, c: f64) {} + +fn foo() -> Box +where + 'a: 'b, + for<'a> D<'b>: 'a, +{ + hello!() +} + +fn baz< + 'a: 'b, // comment on 'a + T: SomsssssssssssssssssssssssssssssssssssssssssssssssssssssseType, // comment on T +>( + a: A, + b: B, // comment on b + c: C, +) -> Bob { + #[attr1] + extern crate foo; + #[attr2] + #[attr3] + extern crate foo; + #[attr1] + extern crate foo; + #[attr2] + #[attr3] + extern crate foo; +} + +#[rustfmt::skip] +fn qux(a: dadsfa, // Comment 1 + b: sdfasdfa, // Comment 2 + c: dsfdsafa) // Comment 3 +{ + +} + +/// Blah blah blah. +impl Bar { + fn foo( + &mut self, + a: sdfsdfcccccccccccccccccccccccccccccccccccccccccccccccccc, // comment on a + b: sdfasdfsdfasfs, // closing comment + ) -> isize { + } + + /// Blah blah blah. + pub fn f2(self) { + (foo, bar) + } + + #[an_attribute] + fn f3(self) -> Dog {} +} + +/// The `nodes` and `edges` method each return instantiations of +/// `Cow<[T]>` to leave implementers the freedom to create + +/// entirely new vectors or to pass back slices into internally owned +/// vectors. +pub trait GraphWalk<'a, N, E> { + /// Returns all the nodes in this graph. + fn nodes(&'a self) -> Nodes<'a, N>; + /// Returns all of the edges in this graph. + fn edges(&'a self) -> Edges<'a, E>; + /// The source node for `edge`. + fn source(&'a self, edge: &E) -> N; + /// The target node for `edge`. + fn target(&'a self, edge: &E) -> N; +} + +/// A Doc comment +#[AnAttribute] +pub struct Foo { + #[rustfmt::skip] + f : SomeType, // Comment beside a field + f: SomeType, // Comment beside a field + // Comment on a field + g: SomeOtherType, + /// A doc comment on a field + h: AThirdType, +} + +struct Bar; + +// With a where-clause and generics. +pub struct Foo<'a, Y: Baz> +where + X: Whatever, +{ + f: SomeType, // Comment beside a field +} + +fn foo(ann: &'a (PpAnn + 'a)) {} + +fn main() { + for i in 0i32..4 { + println!("{}", i); + } + + while true { + hello(); + } + + let rc = Cell::new( + 42usize, + 42usize, + Cell::new( + 42usize, + remaining_widthremaining_widthremaining_widthremaining_width, + ), + 42usize, + ); + let rc = RefCell::new(42usize, remaining_width, remaining_width); // a comment + let x = "Hello!!!!!!!!! abcd abcd abcd abcd abcd abcd\n abcd abcd abcd abcd abcd abcd abcd \ + abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd \ + abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd \ + abcd abcd"; + let s = expand(a, b); +} + +fn deconstruct() -> ( + SocketAddr, + Method, + Headers, + RequestUri, + HttpVersion, + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, +) { +} + +fn deconstruct( + foo: Bar, +) -> ( + SocketAddr, + Method, + Headers, + RequestUri, + HttpVersion, + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, +) { +} + +#[rustfmt::skip] +mod a{ +fn foo(x: T) { + let x: T = dfasdf; +} +} diff --git a/tests/target/negative-bounds.rs b/tests/target/negative-bounds.rs new file mode 100644 index 000000000000..4fb35cccf668 --- /dev/null +++ b/tests/target/negative-bounds.rs @@ -0,0 +1,11 @@ +fn negative() +where + i32: !Copy, +{ +} + +fn maybe_const_negative() +where + i32: ~const !Copy, +{ +} diff --git a/tests/target/negative-impl.rs b/tests/target/negative-impl.rs new file mode 100644 index 000000000000..16ce7e26a99a --- /dev/null +++ b/tests/target/negative-impl.rs @@ -0,0 +1,14 @@ +impl !Display for JoinHandle {} + +impl !Box {} + +impl !std::fmt::Display + for JoinHandle +{ +} + +impl + !JoinHandle + std::marker::Send + std::marker::Sync + 'static> + + 'static +{ +} diff --git a/tests/target/nested-if-else.rs b/tests/target/nested-if-else.rs new file mode 100644 index 000000000000..9a54789ddcd6 --- /dev/null +++ b/tests/target/nested-if-else.rs @@ -0,0 +1,11 @@ +fn issue1518() { + Some(Object { + field: if a { + a_thing + } else if b { + b_thing + } else { + c_thing + }, + }) +} diff --git a/tests/target/nested-visual-block.rs b/tests/target/nested-visual-block.rs new file mode 100644 index 000000000000..fe7190d0abaa --- /dev/null +++ b/tests/target/nested-visual-block.rs @@ -0,0 +1,60 @@ +fn main() { + // #1078 + let items = itemize_list( + context.source_map, + field_iter, + "}", + |item| match *item { + StructLitField::Regular(ref field) => field.span.lo(), + StructLitField::Base(ref expr) => { + let last_field_hi = fields.last().map_or(span.lo(), |field| field.span.hi()); + let snippet = context.snippet(mk_sp(last_field_hi, expr.span.lo())); + let pos = snippet.find_uncommented("..").unwrap(); + last_field_hi + BytePos(pos as u32) + } + }, + |item| match *item { + StructLitField::Regular(ref field) => field.span.hi(), + StructLitField::Base(ref expr) => expr.span.hi(), + }, + |item| { + match *item { + StructLitField::Regular(ref field) => rewrite_field( + inner_context, + &field, + &Constraints::new(v_budget.checked_sub(1).unwrap_or(0), indent), + ), + StructLitField::Base(ref expr) => { + // 2 = .. + expr.rewrite( + inner_context, + &Constraints::new(try_opt!(v_budget.checked_sub(2)), indent + 2), + ) + .map(|s| format!("..{}", s)) + } + } + }, + context.source_map.span_after(span, "{"), + span.hi(), + ); + + // #1580 + self.0.pool.execute(move || { + let _timer = segments.0.rotate_timer.time(); + if let Err(e) = segments.rotate_async(wal) { + error!("error compacting segment storage WAL", unsafe { error: e.display() }); + } + }); + + // #1581 + bootstrap.checks.register("PERSISTED_LOCATIONS", move || { + if locations2.0.inner_mut.lock().poisoned { + Check::new( + State::Error, + "Persisted location storage is poisoned due to a write failure", + ) + } else { + Check::new(State::Healthy, "Persisted location storage is healthy") + } + }); +} diff --git a/tests/target/nested_skipped/mod.rs b/tests/target/nested_skipped/mod.rs new file mode 100644 index 000000000000..0ab6f081e449 --- /dev/null +++ b/tests/target/nested_skipped/mod.rs @@ -0,0 +1,3 @@ +fn ugly() { + 92; +} diff --git a/tests/target/nestedmod/mod.rs b/tests/target/nestedmod/mod.rs new file mode 100644 index 000000000000..1df462931846 --- /dev/null +++ b/tests/target/nestedmod/mod.rs @@ -0,0 +1,12 @@ +mod mod2a; +mod mod2b; + +mod mymod1 { + use mod2a::{Bar, Foo}; + mod mod3a; +} + +#[path = "mod2c.rs"] +mod mymod2; + +mod submod2; diff --git a/tests/target/nestedmod/mod2a.rs b/tests/target/nestedmod/mod2a.rs new file mode 100644 index 000000000000..5df457a83165 --- /dev/null +++ b/tests/target/nestedmod/mod2a.rs @@ -0,0 +1,4 @@ +// This is an empty file containing only +// comments + +// ................... diff --git a/tests/target/nestedmod/mod2b.rs b/tests/target/nestedmod/mod2b.rs new file mode 100644 index 000000000000..9b6ea844e65d --- /dev/null +++ b/tests/target/nestedmod/mod2b.rs @@ -0,0 +1,2 @@ +#[path = "mod2a.rs"] +mod c; diff --git a/tests/target/nestedmod/mod2c.rs b/tests/target/nestedmod/mod2c.rs new file mode 100644 index 000000000000..7db4572e777c --- /dev/null +++ b/tests/target/nestedmod/mod2c.rs @@ -0,0 +1,3 @@ +// A standard mod + +fn a() {} diff --git a/tests/target/nestedmod/mymod1/mod3a.rs b/tests/target/nestedmod/mymod1/mod3a.rs new file mode 100644 index 000000000000..ae09d8ddac0d --- /dev/null +++ b/tests/target/nestedmod/mymod1/mod3a.rs @@ -0,0 +1,2 @@ +// Another mod +fn a() {} diff --git a/tests/target/nestedmod/submod2/a.rs b/tests/target/nestedmod/submod2/a.rs new file mode 100644 index 000000000000..120b17145e3a --- /dev/null +++ b/tests/target/nestedmod/submod2/a.rs @@ -0,0 +1,6 @@ +// Yet Another mod +// Nested + +use c::a; + +fn foo() {} diff --git a/tests/target/nestedmod/submod2/mod.rs b/tests/target/nestedmod/submod2/mod.rs new file mode 100644 index 000000000000..52f8be910227 --- /dev/null +++ b/tests/target/nestedmod/submod2/mod.rs @@ -0,0 +1,5 @@ +// Another mod + +mod a; + +use a::a; diff --git a/tests/target/no_arg_with_commnet.rs b/tests/target/no_arg_with_commnet.rs new file mode 100644 index 000000000000..69f61b60f29f --- /dev/null +++ b/tests/target/no_arg_with_commnet.rs @@ -0,0 +1 @@ +fn foo(/* cooment */) {} diff --git a/tests/target/no_new_line_beginning.rs b/tests/target/no_new_line_beginning.rs new file mode 100644 index 000000000000..f328e4d9d04c --- /dev/null +++ b/tests/target/no_new_line_beginning.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/tests/target/normalize_doc_attributes_should_not_imply_format_doc_comments.rs b/tests/target/normalize_doc_attributes_should_not_imply_format_doc_comments.rs new file mode 100644 index 000000000000..562d9565e9a9 --- /dev/null +++ b/tests/target/normalize_doc_attributes_should_not_imply_format_doc_comments.rs @@ -0,0 +1,15 @@ +// rustfmt-normalize_doc_attributes: true + +/// Foo +/// +/// # Example +/// ``` +/// # #![cfg_attr(not(dox), feature(cfg_target_feature, target_feature, stdsimd))] +/// # #![cfg_attr(not(dox), no_std)] +/// fn foo() { } +/// ``` +/// +fn foo() {} + +///Bar documents +fn bar() {} diff --git a/tests/target/normalize_multiline_doc_attribute.rs b/tests/target/normalize_multiline_doc_attribute.rs new file mode 100644 index 000000000000..890c9bb20cd8 --- /dev/null +++ b/tests/target/normalize_multiline_doc_attribute.rs @@ -0,0 +1,12 @@ +// rustfmt-unstable: true +// rustfmt-normalize_doc_attributes: true + +///This comment +///is split +///on multiple lines +fn foo() {} + +/// B1 +/// +/// A1 +fn bar() {} diff --git a/tests/target/obsolete_in_place.rs b/tests/target/obsolete_in_place.rs new file mode 100644 index 000000000000..3f364c1aecdc --- /dev/null +++ b/tests/target/obsolete_in_place.rs @@ -0,0 +1,9 @@ +// #2953 + +macro_rules! demo { + ($a:ident <- $b:expr) => {}; +} + +fn main() { + demo!(i <- 0); +} diff --git a/tests/target/one_line_if_v1.rs b/tests/target/one_line_if_v1.rs new file mode 100644 index 000000000000..b3c6c4cbeb20 --- /dev/null +++ b/tests/target/one_line_if_v1.rs @@ -0,0 +1,46 @@ +// rustfmt-version: One + +fn plain_if(x: bool) -> u8 { + if x { + 0 + } else { + 1 + } +} + +fn paren_if(x: bool) -> u8 { + (if x { 0 } else { 1 }) +} + +fn let_if(x: bool) -> u8 { + let x = if x { foo() } else { bar() }; + x +} + +fn return_if(x: bool) -> u8 { + return if x { 0 } else { 1 }; +} + +fn multi_if() { + use std::io; + if x { + foo() + } else { + bar() + } + if x { + foo() + } else { + bar() + } +} + +fn middle_if() { + use std::io; + if x { + foo() + } else { + bar() + } + let x = 1; +} diff --git a/tests/target/one_line_if_v2.rs b/tests/target/one_line_if_v2.rs new file mode 100644 index 000000000000..81ca4c8b8b47 --- /dev/null +++ b/tests/target/one_line_if_v2.rs @@ -0,0 +1,38 @@ +// rustfmt-version: Two + +fn plain_if(x: bool) -> u8 { + if x { 0 } else { 1 } +} + +fn paren_if(x: bool) -> u8 { + (if x { 0 } else { 1 }) +} + +fn let_if(x: bool) -> u8 { + let x = if x { foo() } else { bar() }; + x +} + +fn return_if(x: bool) -> u8 { + return if x { 0 } else { 1 }; +} + +fn multi_if() { + use std::io; + if x { + foo() + } else { + bar() + } + if x { foo() } else { bar() } +} + +fn middle_if() { + use std::io; + if x { + foo() + } else { + bar() + } + let x = 1; +} diff --git a/tests/target/other.rs b/tests/target/other.rs new file mode 100644 index 000000000000..dfce84fcdc47 --- /dev/null +++ b/tests/target/other.rs @@ -0,0 +1,5 @@ +// Part of multiple.rs + +fn bob() { + println!("hello other!"); +} diff --git a/tests/target/paren.rs b/tests/target/paren.rs new file mode 100644 index 000000000000..f7714d85dd1f --- /dev/null +++ b/tests/target/paren.rs @@ -0,0 +1,6 @@ +fn main() { + let x = (1); + let y = (/* comment */(2)); + let z = ((3)/* comment */); + let a = (4/* comment */); +} diff --git a/tests/target/path_clarity/foo.rs b/tests/target/path_clarity/foo.rs new file mode 100644 index 000000000000..cd247fabfe34 --- /dev/null +++ b/tests/target/path_clarity/foo.rs @@ -0,0 +1,2 @@ +// rustfmt-edition: 2018 +mod bar; diff --git a/tests/target/path_clarity/foo/bar.rs b/tests/target/path_clarity/foo/bar.rs new file mode 100644 index 000000000000..b18a7d3499c5 --- /dev/null +++ b/tests/target/path_clarity/foo/bar.rs @@ -0,0 +1,3 @@ +pub fn fn_in_bar() { + println!("foo/bar.rs"); +} diff --git a/tests/target/paths.rs b/tests/target/paths.rs new file mode 100644 index 000000000000..0d2ba797eb79 --- /dev/null +++ b/tests/target/paths.rs @@ -0,0 +1,28 @@ +// rustfmt-normalize_comments: true + +fn main() { + let constellation_chan = + Constellation::::start( + compositor_proxy, + resource_task, + image_cache_task, + font_cache_task, + time_profiler_chan, + mem_profiler_chan, + devtools_chan, + storage_task, + supports_clipboard, + ); + + Quux::< + ParamOne, // Comment 1 + ParamTwo, // Comment 2 + >::some_func(); + + <*mut JSObject>::relocate(entry); + + let x: Foo; + let x: Foo/*::*/; +} + +fn op(foo: Bar, key: &[u8], upd: Fn(Option<&memcache::Item>, Baz) -> Result) -> MapResult {} diff --git a/tests/target/pattern-condense-wildcards.rs b/tests/target/pattern-condense-wildcards.rs new file mode 100644 index 000000000000..a85a16004ad2 --- /dev/null +++ b/tests/target/pattern-condense-wildcards.rs @@ -0,0 +1,12 @@ +// rustfmt-normalize_comments: true +// rustfmt-condense_wildcard_suffixes: true + +fn main() { + match x { + Butt(..) => "hah", + Tup(_) => "nah", + Quad(_, _, x, _) => " also no rewrite", + Quad(x, ..) => "condense me pls", + Weird(x, _, _, /* don't condense before */ ..) => "pls work", + } +} diff --git a/tests/target/pattern.rs b/tests/target/pattern.rs new file mode 100644 index 000000000000..576018ac623f --- /dev/null +++ b/tests/target/pattern.rs @@ -0,0 +1,98 @@ +// rustfmt-normalize_comments: true +#![feature(exclusive_range_pattern)] +use core::u8::MAX; + +fn main() { + let z = match x { + "pat1" => 1, + (ref x, ref mut y /* comment */) => 2, + }; + + if let ::CONST = ident { + do_smth(); + } + + let Some(ref xyz /* comment! */) = opt; + + if let None = opt2 { + panic!("oh noes"); + } + + let foo @ bar(f) = 42; + let a::foo(..) = 42; + let [] = 42; + let [a, b, c] = 42; + let [a, b, c] = 42; + let [a, b, c, d, e, f, g] = 42; + let foo {} = 42; + let foo { .. } = 42; + let foo { x, y: ref foo, .. } = 42; + let foo { + x, + yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy: ref foo, + .. + } = 42; + let foo { + x, + yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy: ref foo, + } = 42; + let foo { + x, + yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy: ref foo, + .. + }; + let foo { + x, + yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy: ref foo, + }; + + match b"12" { + [0, 1..MAX] => {} + _ => {} + } +} + +impl<'a, 'b> ResolveGeneratedContentFragmentMutator<'a, 'b> { + fn mutate_fragment(&mut self, fragment: &mut Fragment) { + match **info { + GeneratedContentInfo::ContentItem(ContentItem::Counter( + ref counter_name, + counter_style, + )) => {} + } + } +} + +fn issue_1319() { + if let (Event { .. }, ..) = ev_state {} +} + +fn issue_1874() { + if let Some(()) = x { + y + } +} + +fn combine_patterns() { + let x = match y { + Some(Some(Foo { + z: Bar(..), + a: Bar(..), + b: Bar(..), + })) => z, + _ => return, + }; +} + +fn slice_patterns() { + match b"123" { + [0, ..] => {} + [0, foo] => {} + _ => {} + } +} + +fn issue3728() { + let foo = |(c,)| c; + foo((1,)); +} diff --git a/tests/target/preserves_carriage_return_for_unix.rs b/tests/target/preserves_carriage_return_for_unix.rs new file mode 100644 index 000000000000..e5e0b2865989 --- /dev/null +++ b/tests/target/preserves_carriage_return_for_unix.rs @@ -0,0 +1,2 @@ +// rustfmt-newline_style: Unix +// Foo Bar diff --git a/tests/target/preserves_carriage_return_for_windows.rs b/tests/target/preserves_carriage_return_for_windows.rs new file mode 100644 index 000000000000..1085360ee59e --- /dev/null +++ b/tests/target/preserves_carriage_return_for_windows.rs @@ -0,0 +1,2 @@ +// rustfmt-newline_style: Windows +// Foo Bar diff --git a/tests/target/pub-restricted.rs b/tests/target/pub-restricted.rs new file mode 100644 index 000000000000..0e178ef10136 --- /dev/null +++ b/tests/target/pub-restricted.rs @@ -0,0 +1,51 @@ +pub(super) enum WriteState { + WriteId { + id: U64Writer, + size: U64Writer, + payload: Option>, + }, + WriteSize { + size: U64Writer, + payload: Option>, + }, + WriteData(Writer), +} + +pub(crate) enum WriteState { + WriteId { + id: U64Writer, + size: U64Writer, + payload: Option>, + }, + WriteSize { + size: U64Writer, + payload: Option>, + }, + WriteData(Writer), +} + +pub(in global::path::to::some_mod) enum WriteState { + WriteId { + id: U64Writer, + size: U64Writer, + payload: Option>, + }, + WriteSize { + size: U64Writer, + payload: Option>, + }, + WriteData(Writer), +} + +pub(in local::path::to::some_mod) enum WriteState { + WriteId { + id: U64Writer, + size: U64Writer, + payload: Option>, + }, + WriteSize { + size: U64Writer, + payload: Option>, + }, + WriteData(Writer), +} diff --git a/tests/target/raw_identifiers.rs b/tests/target/raw_identifiers.rs new file mode 100644 index 000000000000..6ab0fdf053bc --- /dev/null +++ b/tests/target/raw_identifiers.rs @@ -0,0 +1,66 @@ +#![feature(custom_attribute)] +#![feature(raw_identifiers)] +#![feature(extern_types)] +#![allow(invalid_type_param_default)] +#![allow(unused_attributes)] + +use r#foo as r#alias_foo; + +// https://github.com/rust-lang/rustfmt/issues/3837 +pub(crate) static r#break: &'static str = "foo"; + +fn main() { + #[r#attr] + r#foo::r#bar(); + + let r#local = r#Struct { r#field: () }; + r#local.r#field = 1; + r#foo.r#barr(); + let r#async = r#foo(r#local); + r#macro!(); + + if let r#sub_pat @ r#Foo(_) = r#Foo(3) {} + + match r#async { + r#Foo | r#Bar => r#foo(), + } +} + +fn r#bar<'a, r#T>(r#x: &'a r#T) {} + +mod r#foo { + pub fn r#bar() {} +} + +enum r#Foo { + r#Bar {}, +} + +struct r#Struct { + r#field: r#FieldType, +} + +trait r#Trait { + type r#Type; +} + +impl r#Trait for r#Impl { + type r#Type = r#u32; + fn r#xxx(r#fjio: r#u32) {} +} + +extern "C" { + type r#ccc; + static r#static_val: u32; +} + +macro_rules! r#macro { + () => {}; +} + +macro_rules! foo { + ($x:expr) => { + let r#catch = $x + 1; + println!("{}", r#catch); + }; +} diff --git a/tests/target/remove_blank_lines.rs b/tests/target/remove_blank_lines.rs new file mode 100644 index 000000000000..de74c81ef57f --- /dev/null +++ b/tests/target/remove_blank_lines.rs @@ -0,0 +1,28 @@ +fn main() { + let x = 1; + + let y = 2; + + println!("x + y = {}", x + y); +} + +fn foo() { + #![attribute] + + let x = 1; + + // comment +} +// comment after item + +// comment before item +fn bar() { + let x = 1; + // comment after statement + + // comment before statement + let y = 2; + let z = 3; + + println!("x + y + z = {}", x + y + z); +} diff --git a/tests/target/reorder-impl-items.rs b/tests/target/reorder-impl-items.rs new file mode 100644 index 000000000000..16efff55b066 --- /dev/null +++ b/tests/target/reorder-impl-items.rs @@ -0,0 +1,15 @@ +// rustfmt-reorder_impl_items: true + +// The ordering of the following impl items should be idempotent. +impl<'a> Command<'a> { + pub fn send_to(&self, w: &mut io::Write) -> io::Result<()> { + match self { + &Command::Data(ref c) => c.send_to(w), + &Command::Vrfy(ref c) => c.send_to(w), + } + } + + pub fn parse(arg: &[u8]) -> Result { + nom_to_result(command(arg)) + } +} diff --git a/tests/target/should_not_format_string_when_format_strings_is_not_set.rs b/tests/target/should_not_format_string_when_format_strings_is_not_set.rs new file mode 100644 index 000000000000..efb755d4aea8 --- /dev/null +++ b/tests/target/should_not_format_string_when_format_strings_is_not_set.rs @@ -0,0 +1,16 @@ +// format_strings is false by default. + +println!( + "DirEntry {{ \ + binary_name: {:<64}, \ + context_id: {:>2}, \ + file_size: {:>6}, \ + offset: 0x {:>08X}, \ + actual_crc: 0x{:>08X} \ + }}", + dir_entry.binary_name, + dir_entry.context_id, + dir_entry.file_size, + dir_entry.offset, + dir_entry.actual_crc +); diff --git a/tests/target/single-line-if-else.rs b/tests/target/single-line-if-else.rs new file mode 100644 index 000000000000..98fd793cba2d --- /dev/null +++ b/tests/target/single-line-if-else.rs @@ -0,0 +1,58 @@ +// Format if-else expressions on a single line, when possible. + +fn main() { + let a = if 1 > 2 { unreachable!() } else { 10 }; + + let a = if x { + 1 + } else if y { + 2 + } else { + 3 + }; + + let b = if cond() { + 5 + } else { + // Brief comment. + 10 + }; + + let c = if cond() { + statement(); + + 5 + } else { + 10 + }; + + let d = if let Some(val) = turbo { + "cool" + } else { + "beans" + }; + + if cond() { + statement(); + } else { + other_statement(); + } + + if true { + do_something() + } + + let x = if veeeeeeeeery_loooooong_condition() { + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + } else { + bbbbbbbbbb + }; + + let x = if veeeeeeeeery_loooooong_condition() { + aaaaaaaaaaaaaaaaaaaaaaaaa + } else { + bbbbbbbbbb + }; + + funk(if test() { 1 } else { 2 }, arg2); +} diff --git a/tests/target/single-line-macro/v1.rs b/tests/target/single-line-macro/v1.rs new file mode 100644 index 000000000000..a3aa631ed4af --- /dev/null +++ b/tests/target/single-line-macro/v1.rs @@ -0,0 +1,10 @@ +// rustfmt-version: One + +// #2652 +// Preserve trailing comma inside macro, even if it looks an array. +macro_rules! bar { + ($m:ident) => { + $m!([a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z,]); + $m!([a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z]); + }; +} diff --git a/tests/target/single-line-macro/v2.rs b/tests/target/single-line-macro/v2.rs new file mode 100644 index 000000000000..9c6bcf33ad53 --- /dev/null +++ b/tests/target/single-line-macro/v2.rs @@ -0,0 +1,14 @@ +// rustfmt-version: Two + +// #2652 +// Preserve trailing comma inside macro, even if it looks an array. +macro_rules! bar { + ($m:ident) => { + $m!([ + a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, + ]); + $m!([ + a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z + ]); + }; +} diff --git a/tests/target/skip.rs b/tests/target/skip.rs new file mode 100644 index 000000000000..6c9737a3377e --- /dev/null +++ b/tests/target/skip.rs @@ -0,0 +1,87 @@ +// Test the skip attribute works + +#[rustfmt::skip] +fn foo() { badly; formatted; stuff +; } + +#[rustfmt::skip] +trait Foo +{ +fn foo( +); +} + +impl LateLintPass for UsedUnderscoreBinding { + #[cfg_attr(rustfmt, rustfmt::skip)] + fn check_expr() { // comment + } +} + +fn issue1346() { + #[cfg_attr(rustfmt, rustfmt::skip)] + Box::new(self.inner.call(req).then(move |result| { + match result { + Ok(resp) => Box::new(future::done(Ok(resp))), + Err(e) => { + try_error!(clo_stderr, "{}", e); + Box::new(future::err(e)) + } + } + })) +} + +fn skip_on_statements() { + // Outside block + #[rustfmt::skip] + { + foo; bar; + // junk + } + + { + // Inside block + #![rustfmt::skip] + foo; bar; + // junk + } + + // Semi + #[cfg_attr(rustfmt, rustfmt::skip)] + foo( + 1, 2, 3, 4, + 1, 2, + 1, 2, 3, + ); + + // Local + #[cfg_attr(rustfmt, rustfmt::skip)] + let x = foo( a, b , c); + + // Item + #[cfg_attr(rustfmt, rustfmt::skip)] + use foobar; + + // Mac + #[cfg_attr(rustfmt, rustfmt::skip)] + vec![ + 1, 2, 3, 4, + 1, 2, 3, 4, + 1, 2, 3, 4, + 1, 2, 3, + 1, + 1, 2, + 1, + ]; + + // Expr + #[cfg_attr(rustfmt, rustfmt::skip)] + foo( a, b , c) +} + +// Check that the skip attribute applies to other attributes. +#[rustfmt::skip] +#[cfg +( a , b +)] +fn +main() {} diff --git a/tests/target/skip/foo.rs b/tests/target/skip/foo.rs new file mode 100644 index 000000000000..776658f8fe57 --- /dev/null +++ b/tests/target/skip/foo.rs @@ -0,0 +1,5 @@ +#![rustfmt::skip] + +fn +foo() +{} diff --git a/tests/target/skip/main.rs b/tests/target/skip/main.rs new file mode 100644 index 000000000000..2d33bef92519 --- /dev/null +++ b/tests/target/skip/main.rs @@ -0,0 +1,5 @@ +mod foo; + +fn main() { + println!("Hello, world!"); +} diff --git a/tests/target/skip/preserve_trailing_comment.rs b/tests/target/skip/preserve_trailing_comment.rs new file mode 100644 index 000000000000..f85de33257cc --- /dev/null +++ b/tests/target/skip/preserve_trailing_comment.rs @@ -0,0 +1,7 @@ +#![rustfmt::skip] + +fn main() { + println!("Hello, world!"); +} + +// Trailing Comment diff --git a/tests/target/skip_macro_invocations/all.rs b/tests/target/skip_macro_invocations/all.rs new file mode 100644 index 000000000000..d0437ee10fd1 --- /dev/null +++ b/tests/target/skip_macro_invocations/all.rs @@ -0,0 +1,11 @@ +// rustfmt-skip_macro_invocations: ["*"] + +// Should skip this invocation +items!( + const _: u8 = 0; +); + +// Should skip this invocation +renamed_items!( + const _: u8 = 0; +); diff --git a/tests/target/skip_macro_invocations/all_and_name.rs b/tests/target/skip_macro_invocations/all_and_name.rs new file mode 100644 index 000000000000..1f6722344fe3 --- /dev/null +++ b/tests/target/skip_macro_invocations/all_and_name.rs @@ -0,0 +1,11 @@ +// rustfmt-skip_macro_invocations: ["*","items"] + +// Should skip this invocation +items!( + const _: u8 = 0; +); + +// Should also skip this invocation, as the wildcard covers it +renamed_items!( + const _: u8 = 0; +); diff --git a/tests/target/skip_macro_invocations/empty.rs b/tests/target/skip_macro_invocations/empty.rs new file mode 100644 index 000000000000..4a398cc59c6e --- /dev/null +++ b/tests/target/skip_macro_invocations/empty.rs @@ -0,0 +1,11 @@ +// rustfmt-skip_macro_invocations: [] + +// Should not skip this invocation +items!( + const _: u8 = 0; +); + +// Should not skip this invocation +renamed_items!( + const _: u8 = 0; +); diff --git a/tests/target/skip_macro_invocations/name.rs b/tests/target/skip_macro_invocations/name.rs new file mode 100644 index 000000000000..c4d577269c6f --- /dev/null +++ b/tests/target/skip_macro_invocations/name.rs @@ -0,0 +1,11 @@ +// rustfmt-skip_macro_invocations: ["items"] + +// Should skip this invocation +items!( + const _: u8 = 0; +); + +// Should not skip this invocation +renamed_items!( + const _: u8 = 0; +); diff --git a/tests/target/skip_macro_invocations/name_unknown.rs b/tests/target/skip_macro_invocations/name_unknown.rs new file mode 100644 index 000000000000..7ab1440395c3 --- /dev/null +++ b/tests/target/skip_macro_invocations/name_unknown.rs @@ -0,0 +1,6 @@ +// rustfmt-skip_macro_invocations: ["unknown"] + +// Should not skip this invocation +items!( + const _: u8 = 0; +); diff --git a/tests/target/skip_macro_invocations/names.rs b/tests/target/skip_macro_invocations/names.rs new file mode 100644 index 000000000000..c6b41ff93d79 --- /dev/null +++ b/tests/target/skip_macro_invocations/names.rs @@ -0,0 +1,16 @@ +// rustfmt-skip_macro_invocations: ["foo","bar"] + +// Should skip this invocation +foo!( + const _: u8 = 0; +); + +// Should skip this invocation +bar!( + const _: u8 = 0; +); + +// Should not skip this invocation +baz!( + const _: u8 = 0; +); diff --git a/tests/target/skip_macro_invocations/path_qualified_invocation_mismatch.rs b/tests/target/skip_macro_invocations/path_qualified_invocation_mismatch.rs new file mode 100644 index 000000000000..6e372c726952 --- /dev/null +++ b/tests/target/skip_macro_invocations/path_qualified_invocation_mismatch.rs @@ -0,0 +1,6 @@ +// rustfmt-skip_macro_invocations: ["items"] + +// Should not skip this invocation +self::items!( + const _: u8 = 0; +); diff --git a/tests/target/skip_macro_invocations/path_qualified_match.rs b/tests/target/skip_macro_invocations/path_qualified_match.rs new file mode 100644 index 000000000000..9398918a9e11 --- /dev/null +++ b/tests/target/skip_macro_invocations/path_qualified_match.rs @@ -0,0 +1,6 @@ +// rustfmt-skip_macro_invocations: ["self::items"] + +// Should skip this invocation +self::items!( + const _: u8 = 0; +); diff --git a/tests/target/skip_macro_invocations/path_qualified_name_mismatch.rs b/tests/target/skip_macro_invocations/path_qualified_name_mismatch.rs new file mode 100644 index 000000000000..aa57a2a655c4 --- /dev/null +++ b/tests/target/skip_macro_invocations/path_qualified_name_mismatch.rs @@ -0,0 +1,6 @@ +// rustfmt-skip_macro_invocations: ["self::items"] + +// Should not skip this invocation +items!( + const _: u8 = 0; +); diff --git a/tests/target/skip_macro_invocations/use_alias_examples.rs b/tests/target/skip_macro_invocations/use_alias_examples.rs new file mode 100644 index 000000000000..799dd8c08af1 --- /dev/null +++ b/tests/target/skip_macro_invocations/use_alias_examples.rs @@ -0,0 +1,32 @@ +// rustfmt-skip_macro_invocations: ["aaa","ccc"] + +// These tests demonstrate a realistic use case with use aliases. +// The use statements should not impact functionality in any way. + +use crate::{aaa, bbb, ddd}; + +// No use alias, invocation in list +// Should skip this invocation +aaa!( + const _: u8 = 0; +); + +// Use alias, invocation in list +// Should skip this invocation +use crate::bbb as ccc; +ccc!( + const _: u8 = 0; +); + +// Use alias, invocation not in list +// Should not skip this invocation +use crate::ddd as eee; +eee!( + const _: u8 = 0; +); + +// No use alias, invocation not in list +// Should not skip this invocation +fff!( + const _: u8 = 0; +); diff --git a/tests/target/skip_mod.rs b/tests/target/skip_mod.rs new file mode 100644 index 000000000000..d770ab349f4e --- /dev/null +++ b/tests/target/skip_mod.rs @@ -0,0 +1,3 @@ +#![rustfmt::skip] +use a :: b +; diff --git a/tests/target/soft-wrapping.rs b/tests/target/soft-wrapping.rs new file mode 100644 index 000000000000..5b4c6d9e85a5 --- /dev/null +++ b/tests/target/soft-wrapping.rs @@ -0,0 +1,15 @@ +// rustfmt-wrap_comments: true +// rustfmt-max_width: 80 +// Soft wrapping for comments. + +// #535, soft wrapping for comments +// Compare the lowest `f32` of both inputs for greater than or equal. The +// lowest 32 bits of the result will be `0xffffffff` if `a.extract(0)` is +// ggreater than or equal `b.extract(0)`, or `0` otherwise. The upper 96 bits +// off the result are the upper 96 bits of `a`. + +/// Compares the lowest `f32` of both inputs for greater than or equal. The +/// lowest 32 bits of the result will be `0xffffffff` if `a.extract(0)` is +/// greater than or equal `b.extract(0)`, or `0` otherwise. The upper 96 bits +/// off the result are the upper 96 bits of `a`. +fn foo() {} diff --git a/tests/target/space-not-before-newline.rs b/tests/target/space-not-before-newline.rs new file mode 100644 index 000000000000..9d75b726aa0a --- /dev/null +++ b/tests/target/space-not-before-newline.rs @@ -0,0 +1,8 @@ +struct Foo { + a: (), + // spaces ^^^ to be removed +} +enum Foo { + Bar, + // spaces ^^^ to be removed +} diff --git a/tests/target/spaces-around-ranges.rs b/tests/target/spaces-around-ranges.rs new file mode 100644 index 000000000000..b53e5b58b8cd --- /dev/null +++ b/tests/target/spaces-around-ranges.rs @@ -0,0 +1,15 @@ +// rustfmt-spaces_around_ranges: true + +fn bar(v: &[u8]) {} + +fn foo() { + let a = vec![0; 20]; + for j in 0 ..= 20 { + for i in 0 .. 3 { + bar(a[i .. j]); + bar(a[i ..]); + bar(a[.. j]); + bar(a[..= (j + 1)]); + } + } +} diff --git a/tests/target/statements.rs b/tests/target/statements.rs new file mode 100644 index 000000000000..c1e7dc464c2a --- /dev/null +++ b/tests/target/statements.rs @@ -0,0 +1,42 @@ +// FIXME(calebcartwright) - Hopefully one day we can +// elide these redundant semis like we do in other contexts. +fn redundant_item_semis() { + impl Foo { + fn get(&self) -> usize { + 5 + } + }; + + impl Bar { + fn get(&self) -> usize { + 5 + } + } /*asdfsf*/ + ; + + impl Baz { + fn get(&self) -> usize { + 5 + } + } /*asdfsf*/ + + // why would someone do this + ; + + impl Qux { + fn get(&self) -> usize { + 5 + } + } + + // why + ; + + impl Lorem { + fn get(&self) -> usize { + 5 + } + } + // oh why + ; +} diff --git a/tests/target/static.rs b/tests/target/static.rs new file mode 100644 index 000000000000..5daccf3e7f74 --- /dev/null +++ b/tests/target/static.rs @@ -0,0 +1,27 @@ +const FILE_GENERIC_READ: DWORD = + STANDARD_RIGHTS_READ | FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE; + +static boolnames: &'static [&'static str] = &[ + "bw", "am", "xsb", "xhp", "xenl", "eo", "gn", "hc", "km", "hs", "in", "db", "da", "mir", + "msgr", "os", "eslok", "xt", "hz", "ul", "xon", "nxon", "mc5i", "chts", "nrrmc", "npc", + "ndscr", "ccc", "bce", "hls", "xhpa", "crxm", "daisy", "xvpa", "sam", "cpix", "lpix", "OTbs", + "OTns", "OTnc", "OTMT", "OTNL", "OTpt", "OTxr", +]; + +static mut name: SomeType = + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa; + +pub static count: u8 = 10; + +pub const test: &Type = &val; + +impl Color { + pub const WHITE: u32 = 10; +} + +// #1391 +pub const XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX: NTSTATUS = + 0 as usize; + +pub const XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX: + Yyyyyyyyyyyyyyyyyyyyyyyyyyyy = 1; diff --git a/tests/target/string-lit-2.rs b/tests/target/string-lit-2.rs new file mode 100644 index 000000000000..6b95e25a052b --- /dev/null +++ b/tests/target/string-lit-2.rs @@ -0,0 +1,25 @@ +fn main() -> &'static str { + let too_many_lines = "Hello"; + let leave_me = "sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss\ + s + jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj"; +} + +fn issue_1237() { + let msg = "eedadn\n\ + drvtee\n\ + eandsr\n\ + raavrd\n\ + atevrs\n\ + tsrnev\n\ + sdttsa\n\ + rasrtv\n\ + nssdts\n\ + ntnada\n\ + svetve\n\ + tesnvt\n\ + vntsnd\n\ + vrdear\n\ + dvrsen\n\ + enarar"; +} diff --git a/tests/target/string-lit-custom.rs b/tests/target/string-lit-custom.rs new file mode 100644 index 000000000000..89639b8ebd3f --- /dev/null +++ b/tests/target/string-lit-custom.rs @@ -0,0 +1,20 @@ +fn main() { + let expected = "; ModuleID = \'foo\' + +; Function Attrs: nounwind +declare void @llvm.memset.p0i8.i32(i8* nocapture, i8, i32, i32, i1) #0 + +declare i32 @write(i32, i8*, i32) + +declare i32 @putchar(i32) + +declare i32 @getchar() + +define i32 @main() { +entry: + ret i32 0 +} + +attributes #0 = { nounwind } +"; +} diff --git a/tests/target/string-lit.rs b/tests/target/string-lit.rs new file mode 100644 index 000000000000..2d33061074de --- /dev/null +++ b/tests/target/string-lit.rs @@ -0,0 +1,63 @@ +// rustfmt-format_strings: true +// Long string literals + +fn main() -> &'static str { + let str = "AAAAAAAAAAAAAAaAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAaAA \ + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaAa"; + let str = "AAAAAAAAAAAAAAaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaAa"; + let str = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + + let too_many_lines = "Hello"; + + // Make sure we don't break after an escape character. + let odd_length_name = + "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"; + let even_length_name = + "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"; + + let really_long_variable_name = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + + let raw_string = r#"Do +not +remove +formatting"#; + + filename.replace(" ", "\\"); + + let xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx = + funktion("yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"); + + let unicode = "a̐éö̲\r\n"; + let unicode2 = "Löwe 老虎 Léopard"; + let unicode3 = "中华Việt Nam"; + let unicode4 = "☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃"; + + "stuffin'" +} + +fn issue682() { + let a = "hello \\ o/"; + let b = a.replace("\\ ", "\\"); +} + +fn issue716() { + println!( + "forall x. mult(e(), x) = x /\\ + forall x. mult(x, x) = e()" + ); +} + +fn issue_1282() { + { + match foo { + Permission::AndroidPermissionAccessLocationExtraCommands => { + "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" + } + } + } +} + +// #1987 +#[link_args = "-s NO_FILESYSTEM=1 -s NO_EXIT_RUNTIME=1 -s EXPORTED_RUNTIME_METHODS=[\"_malloc\"] \ + -s NO_DYNAMIC_EXECUTION=1 -s ELIMINATE_DUPLICATE_FUNCTIONS=1 -s EVAL_CTORS=1"] +extern "C" {} diff --git a/tests/target/string_punctuation.rs b/tests/target/string_punctuation.rs new file mode 100644 index 000000000000..0b8ec1b7f393 --- /dev/null +++ b/tests/target/string_punctuation.rs @@ -0,0 +1,24 @@ +// rustfmt-format_strings: true + +fn main() { + println!( + "ThisIsAReallyLongStringWithNoSpaces.It_should_prefer_to_break_onpunctuation:\ + Likethisssssssssssss" + ); + format!("{}__{}__{}ItShouldOnlyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyNoticeSemicolonsPeriodsColonsAndCommasAndResortToMid-CharBreaksAfterPunctuation{}{}",x,y,z,a,b); + println!( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaalhijalfhiigjapdighjapdigjapdighdapighapdighpaidhg;\ + adopgihadoguaadbadgad,qeoihapethae8t0aet8haetadbjtaeg;\ + ooeouthaoeutgadlgajduabgoiuadogabudogubaodugbadgadgadga;adoughaoeugbaouea" + ); + println!( + "sentuhaesnuthaesnutheasunteahusnaethuseantuihaesntdiastnidaetnuhaideuhsenathe。\ + WeShouldSupportNonAsciiPunctuations§\ + ensuhatheasunteahsuneathusneathuasnuhaesnuhaesnuaethusnaetuheasnuth" + ); + println!( + "ThisIsASampleOfCJKString.祇園精舍の鐘の声、諸行無常の響きあり。娑羅双樹の花の色、\ + 盛者必衰の理をあらはす。奢れる人も久しからず、ただ春の夜の夢のごとし。\ + 猛き者もつひにはほろびぬ、ひとへに風の前の塵に同じ。" + ); +} diff --git a/tests/target/struct-field-attributes.rs b/tests/target/struct-field-attributes.rs new file mode 100644 index 000000000000..0f461b98bc73 --- /dev/null +++ b/tests/target/struct-field-attributes.rs @@ -0,0 +1,62 @@ +// #1535 +#![feature(struct_field_attributes)] + +struct Foo { + bar: u64, + + #[cfg(test)] + qux: u64, +} + +fn do_something() -> Foo { + Foo { + bar: 0, + + #[cfg(test)] + qux: 1, + } +} + +fn main() { + do_something(); +} + +// #1462 +struct Foo { + foo: usize, + #[cfg(feature = "include-bar")] + bar: usize, +} + +fn new_foo() -> Foo { + Foo { + foo: 0, + #[cfg(feature = "include-bar")] + bar: 0, + } +} + +// #2044 +pub enum State { + Closure( + #[cfg_attr( + feature = "serde_derive", + serde(state_with = "::serialization::closure") + )] + GcPtr, + ), +} + +struct Fields( + #[cfg_attr( + feature = "serde_derive", + serde(state_with = "::base::serialization::shared") + )] + Arc>, +); + +// #2309 +pub struct A { + #[doc = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"] + pub foos: Vec, +} diff --git a/tests/target/struct_field_doc_comment.rs b/tests/target/struct_field_doc_comment.rs new file mode 100644 index 000000000000..ebb01a668f4c --- /dev/null +++ b/tests/target/struct_field_doc_comment.rs @@ -0,0 +1,69 @@ +// #5215 +struct MyTuple( + /// Doc Comments + /* TODO note to add more to Doc Comments */ + u32, + /// Doc Comments + // TODO note + u64, +); + +struct MyTuple( + #[cfg(unix)] // some comment + u64, + #[cfg(not(unix))] /*block comment */ u32, +); + +struct MyTuple( + #[cfg(unix)] + // some comment + u64, + #[cfg(not(unix))] + /*block comment */ + u32, +); + +struct MyTuple( + #[cfg(unix)] // some comment + pub u64, + #[cfg(not(unix))] /*block comment */ pub(crate) u32, +); + +struct MyTuple( + /// Doc Comments + /* TODO note to add more to Doc Comments */ + pub u32, + /// Doc Comments + // TODO note + pub(crate) u64, +); + +struct MyStruct { + #[cfg(unix)] // some comment + a: u64, + #[cfg(not(unix))] /*block comment */ b: u32, +} + +struct MyStruct { + #[cfg(unix)] // some comment + pub a: u64, + #[cfg(not(unix))] /*block comment */ pub(crate) b: u32, +} + +struct MyStruct { + /// Doc Comments + /* TODO note to add more to Doc Comments */ + a: u32, + /// Doc Comments + // TODO note + b: u64, +} + +struct MyStruct { + /// Doc Comments + /* TODO note to add more to Doc Comments */ + pub a: u32, + /// Doc Comments + // TODO note + pub(crate) b: u64, +} diff --git a/tests/target/struct_lits.rs b/tests/target/struct_lits.rs new file mode 100644 index 000000000000..d3bc364c3504 --- /dev/null +++ b/tests/target/struct_lits.rs @@ -0,0 +1,190 @@ +// rustfmt-normalize_comments: true +// rustfmt-wrap_comments: true +// Struct literal expressions. + +fn main() { + let x = Bar; + + // Comment + let y = Foo { a: x }; + + Foo { + a: foo(), // comment + // comment + b: bar(), + ..something + }; + + Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { a: f(), b: b() }; + + Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { + a: f(), + b: b(), + }; + + Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { + // Comment + a: foo(), // Comment + // Comment + b: bar(), // Comment + }; + + Foo { a: Bar, b: f() }; + + Quux { + x: if cond { + bar(); + }, + y: baz(), + }; + + A { + // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit + // amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. + // Donec et mollis dolor. + first: item(), + // Praesent et diam eget libero egestas mattis sit amet vitae augue. + // Nam tincidunt congue enim, ut porta lorem lacinia consectetur. + second: Item, + }; + + Some(Data::MethodCallData(MethodCallData { + span: sub_span.unwrap(), + scope: self.enclosing_scope(id), + ref_id: def_id, + decl_id: Some(decl_id), + })); + + Diagram { + // o This graph demonstrates how + // / \ significant whitespace is + // o o preserved. + // /|\ \ + // o o o o + graph: G, + } +} + +fn matcher() { + TagTerminatedByteMatcher { + matcher: ByteMatcher { + pattern: b" { + memb: T, + } + let foo = Foo:: { memb: 10 }; +} + +fn issue201() { + let s = S { a: 0, ..b }; +} + +fn issue201_2() { + let s = S { a: S2 { ..c }, ..b }; +} + +fn issue278() { + let s = S { + a: 0, + // + b: 0, + }; + let s1 = S { + a: 0, + // foo + // + // bar + b: 0, + }; +} + +fn struct_exprs() { + Foo { a: 1, b: f(2) }; + Foo { + a: 1, + b: f(2), + ..g(3) + }; + LoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooongStruct { + ..base + }; + IntrinsicISizesContribution { + content_intrinsic_sizes: IntrinsicISizes { + minimum_inline_size: 0, + }, + }; +} + +fn issue123() { + Foo { a: b, c: d, e: f }; + + Foo { + a: bb, + c: dd, + e: ff, + }; + + Foo { + a: ddddddddddddddddddddd, + b: cccccccccccccccccccccccccccccccccccccc, + }; +} + +fn issue491() { + Foo { + guard: None, + arm: 0, // Comment + }; + + Foo { + arm: 0, // Comment + }; + + Foo { + a: aaaaaaaaaa, + b: bbbbbbbb, + c: cccccccccc, + d: dddddddddd, // a comment + e: eeeeeeeee, + }; +} + +fn issue698() { + Record { + ffffffffffffffffffffffffffields: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + }; + Record { + ffffffffffffffffffffffffffields: + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + } +} + +fn issue835() { + MyStruct {}; + MyStruct { /* a comment */ }; + MyStruct { + // Another comment + }; + MyStruct {} +} + +fn field_init_shorthand() { + MyStruct { x, y, z }; + MyStruct { x, y, z, ..base }; + Foo { + aaaaaaaaaa, + bbbbbbbb, + cccccccccc, + dddddddddd, // a comment + eeeeeeeee, + }; + Record { + ffffffffffffffffffffffffffieldsaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + }; +} diff --git a/tests/target/struct_lits_multiline.rs b/tests/target/struct_lits_multiline.rs new file mode 100644 index 000000000000..b29aafd05484 --- /dev/null +++ b/tests/target/struct_lits_multiline.rs @@ -0,0 +1,117 @@ +// rustfmt-normalize_comments: true +// rustfmt-wrap_comments: true +// rustfmt-struct_lit_single_line: false + +// Struct literal expressions. + +fn main() { + let x = Bar; + + // Comment + let y = Foo { + a: x, + }; + + Foo { + a: foo(), // comment + // comment + b: bar(), + ..something + }; + + Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { + a: foo(), + b: bar(), + }; + + Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { + a: foo(), + b: bar(), + }; + + Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { + // Comment + a: foo(), // Comment + // Comment + b: bar(), // Comment + }; + + Foo { + a: Bar, + b: foo(), + }; + + Quux { + x: if cond { + bar(); + }, + y: baz(), + }; + + A { + // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit + // amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. + // Donec et mollis dolor. + first: item(), + // Praesent et diam eget libero egestas mattis sit amet vitae augue. + // Nam tincidunt congue enim, ut porta lorem lacinia consectetur. + second: Item, + }; + + Some(Data::MethodCallData(MethodCallData { + span: sub_span.unwrap(), + scope: self.enclosing_scope(id), + ref_id: def_id, + decl_id: Some(decl_id), + })); + + Diagram { + // o This graph demonstrates how + // / \ significant whitespace is + // o o preserved. + // /|\ \ + // o o o o + graph: G, + } +} + +fn matcher() { + TagTerminatedByteMatcher { + matcher: ByteMatcher { + pattern: b" { + memb: T, + } + let foo = Foo:: { + memb: 10, + }; +} + +fn issue201() { + let s = S { + a: 0, + ..b + }; +} + +fn issue201_2() { + let s = S { + a: S2 { + ..c + }, + ..b + }; +} + +fn issue491() { + Foo { + guard: None, + arm: 0, // Comment + }; +} diff --git a/tests/target/struct_lits_visual.rs b/tests/target/struct_lits_visual.rs new file mode 100644 index 000000000000..a9627fb90f51 --- /dev/null +++ b/tests/target/struct_lits_visual.rs @@ -0,0 +1,49 @@ +// rustfmt-normalize_comments: true +// rustfmt-wrap_comments: true +// rustfmt-indent_style: Visual + +// Struct literal expressions. + +fn main() { + let x = Bar; + + // Comment + let y = Foo { a: x }; + + Foo { a: foo(), // comment + // comment + b: bar(), + ..something }; + + Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { a: f(), b: b() }; + + Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { // Comment + a: foo(), /* Comment */ + // Comment + b: bar() /* Comment */ }; + + Foo { a: Bar, b: f() }; + + Quux { x: if cond { + bar(); + }, + y: baz() }; + + Baz { x: yxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, + z: zzzzz /* test */ }; + + A { // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit + // amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante + // hendrerit. Donec et mollis dolor. + first: item(), + // Praesent et diam eget libero egestas mattis sit amet vitae augue. + // Nam tincidunt congue enim, ut porta lorem lacinia consectetur. + second: Item }; + + Diagram { // o This graph demonstrates how + // / \ significant whitespace is + // o o preserved. + // /|\ \ + // o o o o + graph: G } +} diff --git a/tests/target/struct_lits_visual_multiline.rs b/tests/target/struct_lits_visual_multiline.rs new file mode 100644 index 000000000000..3f43ef0c981d --- /dev/null +++ b/tests/target/struct_lits_visual_multiline.rs @@ -0,0 +1,49 @@ +// rustfmt-normalize_comments: true +// rustfmt-wrap_comments: true +// rustfmt-indent_style: Visual +// rustfmt-struct_lit_single_line: false + +// Struct literal expressions. + +fn main() { + let x = Bar; + + // Comment + let y = Foo { a: x }; + + Foo { a: foo(), // comment + // comment + b: bar(), + ..something }; + + Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { a: foo(), + b: bar() }; + + Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { // Comment + a: foo(), /* Comment */ + // Comment + b: bar() /* Comment */ }; + + Foo { a: Bar, + b: foo() }; + + Quux { x: if cond { + bar(); + }, + y: baz() }; + + A { // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit + // amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante + // hendrerit. Donec et mollis dolor. + first: item(), + // Praesent et diam eget libero egestas mattis sit amet vitae augue. + // Nam tincidunt congue enim, ut porta lorem lacinia consectetur. + second: Item }; + + Diagram { // o This graph demonstrates how + // / \ significant whitespace is + // o o preserved. + // /|\ \ + // o o o o + graph: G } +} diff --git a/tests/target/struct_tuple_visual.rs b/tests/target/struct_tuple_visual.rs new file mode 100644 index 000000000000..f95f3fe4fd39 --- /dev/null +++ b/tests/target/struct_tuple_visual.rs @@ -0,0 +1,36 @@ +// rustfmt-normalize_comments: true +// rustfmt-wrap_comments: true +// rustfmt-indent_style: Visual +fn foo() { + Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo(f(), b()); + + Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo(// Comment + foo(), /* Comment */ + // Comment + bar() /* Comment */); + + Foo(Bar, f()); + + Quux(if cond { + bar(); + }, + baz()); + + Baz(xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, + zzzzz /* test */); + + A(// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit + // amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante + // hendrerit. Donec et mollis dolor. + item(), + // Praesent et diam eget libero egestas mattis sit amet vitae augue. + // Nam tincidunt congue enim, ut porta lorem lacinia consectetur. + Item); + + Diagram(// o This graph demonstrates how + // / \ significant whitespace is + // o o preserved. + // /|\ \ + // o o o o + G) +} diff --git a/tests/target/structs.rs b/tests/target/structs.rs new file mode 100644 index 000000000000..4948e37a5a3d --- /dev/null +++ b/tests/target/structs.rs @@ -0,0 +1,358 @@ +// rustfmt-normalize_comments: true +// rustfmt-wrap_comments: true + +/// A Doc comment +#[AnAttribute] +pub struct Foo { + #[rustfmt::skip] + f : SomeType, // Comment beside a field + f: SomeType, // Comment beside a field + // Comment on a field + #[AnAttribute] + g: SomeOtherType, + /// A doc comment on a field + h: AThirdType, + pub i: TypeForPublicField, +} + +// Destructuring +fn foo() { + S { x: 5, .. }; + Struct { .. } = Struct { a: 1, b: 4 }; + Struct { a, .. } = Struct { a: 1, b: 2, c: 3 }; + TupleStruct(a, .., b) = TupleStruct(1, 2); + TupleStruct(..) = TupleStruct(3, 4); + TupleStruct( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + .., + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, + ) = TupleStruct(1, 2); +} + +// #1095 +struct S { + t: T, +} + +// #1029 +pub struct Foo { + #[doc(hidden)] + // This will NOT get deleted! + bar: String, // hi +} + +// #1029 +struct X { + // `x` is an important number. + #[allow(unused)] // TODO: use + x: u32, +} + +// #410 +#[allow(missing_docs)] +pub struct Writebatch { + #[allow(dead_code)] // only used for holding the internal pointer + writebatch: RawWritebatch, + marker: PhantomData, +} + +struct Bar; + +struct NewType(Type, OtherType); + +struct NewInt( + pub i32, + SomeType, // inline comment + T, // sup +); + +struct Qux< + 'a, + N: Clone + 'a, + E: Clone + 'a, + G: Labeller<'a, N, E> + GraphWalk<'a, N, E>, + W: Write + Copy, +>( + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, // Comment + BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB, + #[AnAttr] + // Comment + /// Testdoc + G, + pub W, +); + +struct Tuple( + // Comment 1 + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, + // Comment 2 + BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB, +); + +// With a where-clause and generics. +pub struct Foo<'a, Y: Baz> +where + X: Whatever, +{ + f: SomeType, // Comment beside a field +} + +struct Baz { + a: A, // Comment A + b: B, // Comment B + c: C, // Comment C +} + +struct Baz { + a: A, // Comment A + + b: B, // Comment B + + c: C, // Comment C +} + +struct Baz { + a: A, + + b: B, + c: C, + + d: D, +} + +struct Baz { + // Comment A + a: A, + + // Comment B + b: B, + // Comment C + c: C, +} + +// Will this be a one-liner? +struct Tuple( + A, // Comment + B, +); + +pub struct State time::Timespec> { + now: F, +} + +pub struct State ()> { + now: F, +} + +pub struct State { + now: F, +} + +struct Palette { + /// A map of indices in the palette to a count of pixels in approximately + /// that color + foo: i32, +} + +// Splitting a single line comment into a block previously had a misalignment +// when the field had attributes +struct FieldsWithAttributes { + // Pre Comment + #[rustfmt::skip] pub host:String, /* Post comment BBBBBBBBBBBBBB BBBBBBBBBBBBBBBB + * BBBBBBBBBBBBBBBB BBBBBBBBBBBBBBBBB BBBBBBBBBBB */ + // Another pre comment + #[attr1] + #[attr2] + pub id: usize, /* CCCCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCC + * CCCCCCCCCCCCCCCCCC CCCCCCCCCCCCCC CCCCCCCCCCCC */ +} + +struct Deep { + deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeep: + node::Handle>, Type, NodeType>, +} + +struct Foo(T); +struct Foo(T) +where + T: Copy, + T: Eq; +struct Foo( + TTTTTTTTTTTTTTTTT, + UUUUUUUUUUUUUUUUUUUUUUUU, + TTTTTTTTTTTTTTTTTTT, + UUUUUUUUUUUUUUUUUUU, +); +struct Foo( + TTTTTTTTTTTTTTTTTT, + UUUUUUUUUUUUUUUUUUUUUUUU, + TTTTTTTTTTTTTTTTTTT, +) +where + T: PartialEq; +struct Foo( + TTTTTTTTTTTTTTTTT, + UUUUUUUUUUUUUUUUUUUUUUUU, + TTTTTTTTTTTTTTTTTTTTT, +) +where + T: PartialEq; +struct Foo( + TTTTTTTTTTTTTTTTT, + UUUUUUUUUUUUUUUUUUUUUUUU, + TTTTTTTTTTTTTTTTTTT, + UUUUUUUUUUUUUUUUUUU, +) +where + T: PartialEq; +struct Foo( + TTTTTTTTTTTTTTTTT, // Foo + UUUUUUUUUUUUUUUUUUUUUUUU, // Bar + // Baz + TTTTTTTTTTTTTTTTTTT, + // Qux (FIXME #572 - doc comment) + UUUUUUUUUUUUUUUUUUU, +); + +mod m { + struct X + where + T: Sized, + { + a: T, + } +} + +struct Foo( + TTTTTTTTTTTTTTTTTTT, + /// Qux + UUUUUUUUUUUUUUUUUUU, +); + +struct Issue677 { + pub ptr: *const libc::c_void, + pub trace: fn(obj: *const libc::c_void, tracer: *mut JSTracer), +} + +struct Foo {} +struct Foo {} +struct Foo { + // comment +} +struct Foo { + // trailing space -> +} +struct Foo { + // comment +} +struct Foo( + // comment +); + +struct LongStruct { + a: A, + the_quick_brown_fox_jumps_over_the_lazy_dog: + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, +} + +struct Deep { + deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeep: + node::Handle>, Type, NodeType>, +} + +struct Foo(String); + +// #1364 +fn foo() { + convex_shape.set_point(0, &Vector2f { x: 400.0, y: 100.0 }); + convex_shape.set_point(1, &Vector2f { x: 500.0, y: 70.0 }); + convex_shape.set_point(2, &Vector2f { x: 450.0, y: 100.0 }); + convex_shape.set_point(3, &Vector2f { x: 580.0, y: 150.0 }); +} + +// Vertical alignment +struct Foo { + aaaaa: u32, // a + + b: u32, // b + cc: u32, // cc + + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx: u32, // 1 + yy: u32, // comment2 + zzz: u32, // comment3 + + aaaaaa: u32, // comment4 + bb: u32, // comment5 + // separate + dd: u32, // comment7 + c: u32, // comment6 + + aaaaaaa: u32, /* multi + * line + * comment + */ + b: u32, // hi + + do_not_push_this_comment1: u32, // comment1 + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx: u32, // 2 + please_do_not_push_this_comment3: u32, // comment3 + + do_not_push_this_comment1: u32, // comment1 + // separate + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx: u32, // 2 + please_do_not_push_this_comment3: u32, // comment3 + + do_not_push_this_comment1: u32, // comment1 + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx: u32, // 2 + // separate + please_do_not_push_this_comment3: u32, // comment3 +} + +// structs with long identifier +struct Loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong +{} +struct Looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong +{} +struct Loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong +{} +struct Loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong +{ + x: i32, +} + +// structs with visibility, do not duplicate visibility (#2110). +pub(self) struct Foo {} +pub(super) struct Foo {} +pub(crate) struct Foo {} +pub(self) struct Foo(); +pub(super) struct Foo(); +pub(crate) struct Foo(); + +// #2125 +pub struct ReadinessCheckRegistry( + Mutex, Box ReadinessCheck + Sync + Send>>>, +); + +// #2144 unit struct with generics +struct MyBox; +struct MyBoxx +where + T: ?Sized, + S: Clone; + +// #2208 +struct Test { + /// foo + #[serde(default)] + pub join: Vec, + #[serde(default)] + pub tls: bool, +} + +// #2818 +struct Paren((i32)) +where + i32: Trait; +struct Parens((i32, i32)) +where + i32: Trait; diff --git a/tests/target/trailing-comma-never.rs b/tests/target/trailing-comma-never.rs new file mode 100644 index 000000000000..ea199f5ff294 --- /dev/null +++ b/tests/target/trailing-comma-never.rs @@ -0,0 +1,35 @@ +// rustfmt-trailing_comma: Never + +enum X { + A, + B +} + +enum Y { + A, + B +} + +enum TupX { + A(u32), + B(i32, u16) +} + +enum TupY { + A(u32), + B(i32, u16) +} + +enum StructX { + A { s: u16 }, + B { u: u32, i: i32 } +} + +enum StructY { + A { s: u16 }, + B { u: u32, i: i32 } +} + +static XXX: [i8; 64] = [ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 +]; diff --git a/tests/target/trailing_commas.rs b/tests/target/trailing_commas.rs new file mode 100644 index 000000000000..06f0a13b10ce --- /dev/null +++ b/tests/target/trailing_commas.rs @@ -0,0 +1,78 @@ +// rustfmt-match_block_trailing_comma: true +// rustfmt-trailing_comma: Always + +fn main() { + match foo { + x => {}, + y => { + foo(); + }, + _ => x, + } +} + +fn f(x: T, y: S,) -> T +where + T: P, + S: Q, +{ + x +} + +impl Trait for T +where + T: P, +{ + fn f(x: T,) -> T + where + T: Q + R, + { + x + } +} + +struct Pair +where + T: P, + S: P + Q, +{ + a: T, + b: S, +} + +struct TupPair(S, T,) +where + T: P, + S: P + Q; + +enum E +where + S: P, + T: P, +{ + A { a: T, }, +} + +type Double +where + T: P, + T: Q, += Pair; + +extern "C" { + fn f(x: T, y: S,) -> T + where + T: P, + S: Q; +} + +trait Q +where + T: P, + S: R, +{ + fn f(self, x: T, y: S, z: U,) -> Self + where + U: P, + V: P; +} diff --git a/tests/target/trailing_comments/hard_tabs.rs b/tests/target/trailing_comments/hard_tabs.rs new file mode 100644 index 000000000000..35e72f1affdb --- /dev/null +++ b/tests/target/trailing_comments/hard_tabs.rs @@ -0,0 +1,30 @@ +// rustfmt-version: Two +// rustfmt-wrap_comments: true +// rustfmt-hard_tabs: true + +impl Foo { + fn foo() { + bar(); // comment 1 + // comment 2 + // comment 3 + baz(); + } +} + +fn lorem_ipsum() { + let f = bar(); // Donec consequat mi. Quisque vitae dolor. Integer lobortis. Maecenas id nulla. Lorem. + // Id turpis. Nam posuere lectus vitae nibh. Etiam tortor orci, sagittis + // malesuada, rhoncus quis, hendrerit eget, libero. Quisque commodo nulla at + // nunc. Mauris consequat, enim vitae venenatis sollicitudin, dolor orci + // bibendum enim, a sagittis nulla nunc quis elit. Phasellus augue. Nunc + // suscipit, magna tincidunt lacinia faucibus, lacus tellus ornare purus, a + // pulvinar lacus orci eget nibh. Maecenas sed nibh non lacus tempor faucibus. + // In hac habitasse platea dictumst. Vivamus a orci at nulla tristique + // condimentum. Donec arcu quam, dictum accumsan, convallis accumsan, cursus sit + // amet, ipsum. In pharetra sagittis nunc. + let b = baz(); + + let normalized = self.ctfont.all_traits().normalized_weight(); // [-1.0, 1.0] + // TODO(emilio): It may make sense to make this range [.01, 10.0], to align + // with css-fonts-4's range of [1, 1000]. +} diff --git a/tests/target/trailing_comments/soft_tabs.rs b/tests/target/trailing_comments/soft_tabs.rs new file mode 100644 index 000000000000..eba943042ad0 --- /dev/null +++ b/tests/target/trailing_comments/soft_tabs.rs @@ -0,0 +1,30 @@ +// rustfmt-version: Two +// rustfmt-wrap_comments: true + +pub const IFF_MULTICAST: ::c_int = 0x0000000800; // Supports multicast +// Multicast using broadcst. add. + +pub const SQ_CRETAB: u16 = 0x000e; // CREATE TABLE +pub const SQ_DRPTAB: u16 = 0x000f; // DROP TABLE +pub const SQ_CREIDX: u16 = 0x0010; // CREATE INDEX +//const SQ_DRPIDX: u16 = 0x0011; // DROP INDEX +//const SQ_GRANT: u16 = 0x0012; // GRANT +//const SQ_REVOKE: u16 = 0x0013; // REVOKE + +fn foo() { + let f = bar(); // Donec consequat mi. Quisque vitae dolor. Integer lobortis. Maecenas id nulla. Lorem. + // Id turpis. Nam posuere lectus vitae nibh. Etiam tortor orci, sagittis + // malesuada, rhoncus quis, hendrerit eget, libero. Quisque commodo nulla at + // nunc. Mauris consequat, enim vitae venenatis sollicitudin, dolor orci + // bibendum enim, a sagittis nulla nunc quis elit. Phasellus augue. Nunc + // suscipit, magna tincidunt lacinia faucibus, lacus tellus ornare purus, a + // pulvinar lacus orci eget nibh. Maecenas sed nibh non lacus tempor faucibus. + // In hac habitasse platea dictumst. Vivamus a orci at nulla tristique + // condimentum. Donec arcu quam, dictum accumsan, convallis accumsan, cursus sit + // amet, ipsum. In pharetra sagittis nunc. + let b = baz(); + + let normalized = self.ctfont.all_traits().normalized_weight(); // [-1.0, 1.0] + // TODO(emilio): It may make sense to make this range [.01, 10.0], to align + // with css-fonts-4's range of [1, 1000]. +} diff --git a/tests/target/trait.rs b/tests/target/trait.rs new file mode 100644 index 000000000000..7f067991b267 --- /dev/null +++ b/tests/target/trait.rs @@ -0,0 +1,220 @@ +// Test traits + +trait Foo { + fn bar(x: i32) -> Baz { + Baz::new() + } + + fn baz(a: AAAAAAAAAAAAAAAAAAAAAA, b: BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB) -> RetType; + + fn foo( + a: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, // Another comment + b: BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB, + ) -> RetType; // Some comment + + fn baz(&mut self) -> i32; + + fn increment(&mut self, x: i32); + + fn read(&mut self, x: BufReader /* Used to be MemReader */) + where + R: Read; +} + +pub trait WriteMessage { + fn write_message(&mut self, &FrontendMessage) -> io::Result<()>; +} + +trait Runnable { + fn handler(self: &Runnable); +} + +trait TraitWithExpr { + fn fn_with_expr(x: [i32; 1]); +} + +trait Test { + fn read_struct(&mut self, s_name: &str, len: usize, f: F) -> Result + where + F: FnOnce(&mut Self) -> Result; +} + +trait T {} + +trait Foo { + type Bar: Baz; + type Inner: Foo = Box; +} + +trait ConstCheck: Foo +where + T: Baz, +{ + const J: i32; +} + +trait Tttttttttttttttttttttttttttttttttttttttttttttttttttttttttt +where + T: Foo, +{ +} + +trait Ttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt +where + T: Foo, +{ +} + +trait FooBar: Tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt +where + J: Bar, +{ + fn test(); +} + +trait WhereList +where + T: Foo, + J: Bar, +{ +} + +trait X /* comment */ {} +trait Y // comment +{ +} + +// #2055 +pub trait Foo: +// A and C +A + C +// and B + + B +{} + +// #2158 +trait Foo { + type ItRev = > as UntypedTimeSeries>::IterRev; + type IteRev = + > as UntypedTimeSeries>::IterRev; +} + +// #2331 +trait MyTrait< + AAAAAAAAAAAAAAAAAAAA, + BBBBBBBBBBBBBBBBBBBB, + CCCCCCCCCCCCCCCCCCCC, + DDDDDDDDDDDDDDDDDDDD, +> +{ + fn foo() {} +} + +// Trait aliases +trait FooBar = Foo + Bar; +trait FooBar = Foo + Bar; +pub trait FooBar = Foo + Bar; +pub trait FooBar = Foo + Bar; +trait AAAAAAAAAAAAAAAAAA = BBBBBBBBBBBBBBBBBBB + CCCCCCCCCCCCCCCCCCCCCCCCCCCCC + DDDDDDDDDDDDDDDDDD; +pub trait AAAAAAAAAAAAAAAAAA = + BBBBBBBBBBBBBBBBBBB + CCCCCCCCCCCCCCCCCCCCCCCCCCCCC + DDDDDDDDDDDDDDDDDD; +trait AAAAAAAAAAAAAAAAAAA = + BBBBBBBBBBBBBBBBBBB + CCCCCCCCCCCCCCCCCCCCCCCCCCCCC + DDDDDDDDDDDDDDDDDD; +trait AAAAAAAAAAAAAAAAAA = + BBBBBBBBBBBBBBBBBBB + CCCCCCCCCCCCCCCCCCCCCCCCCCCCC + DDDDDDDDDDDDDDDDDDD; +trait AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA = + FooBar; +trait AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA< + A, + B, + C, + D, + E, +> = FooBar; +#[rustfmt::skip] +trait FooBar = Foo + + Bar; + +// #2637 +auto trait Example {} +pub auto trait PubExample {} +pub unsafe auto trait PubUnsafeExample {} + +// #3006 +trait Foo<'a> { + type Bar<'a>; +} + +impl<'a> Foo<'a> for i32 { + type Bar<'a> = i32; +} + +// #3092 +pub mod test { + pub trait ATraitWithALooongName {} + pub trait ATrait: + ATraitWithALooongName + + ATraitWithALooongName + + ATraitWithALooongName + + ATraitWithALooongName + { + } +} + +// Trait aliases with where clauses. +trait A = where for<'b> &'b Self: Send; + +trait B = where for<'b> &'b Self: Send + Clone + Copy + SomeTrait + AAAAAAAA + BBBBBBB + CCCCCCCCCC; +trait B = + where for<'b> &'b Self: Send + Clone + Copy + SomeTrait + AAAAAAAA + BBBBBBB + CCCCCCCCCCC; +trait B = where + for<'b> &'b Self: + Send + Clone + Copy + SomeTrait + AAAAAAAA + BBBBBBB + CCCCCCCCCCCCCCCCCCCCCCC; +trait B = where + for<'b> &'b Self: Send + + Clone + + Copy + + SomeTrait + + AAAAAAAA + + BBBBBBB + + CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC; + +trait B = where + for<'b> &'b Self: Send + + Clone + + Copy + + SomeTrait + + AAAAAAAA + + BBBBBBB + + CCCCCCCCC + + DDDDDDD + + DDDDDDDD + + DDDDDDDDD + + EEEEEEE; + +trait A<'a, 'b, 'c> = Debug + Foo where for<'b> &'b Self: Send; + +trait B<'a, 'b, 'c> = Debug + Foo +where + for<'b> &'b Self: Send + Clone + Copy + SomeTrait + AAAAAAAA + BBBBBBB + CCCCCCCCC + DDDDDDD; + +trait B<'a, 'b, 'c, T> = Debug<'a, T> +where + for<'b> &'b Self: Send + + Clone + + Copy + + SomeTrait + + AAAAAAAA + + BBBBBBB + + CCCCCCCCC + + DDDDDDD + + DDDDDDDD + + DDDDDDDDD + + EEEEEEE; + +trait Visible { + pub const C: i32; + pub type T; + pub fn f(); + pub fn g() {} +} diff --git a/tests/target/try-conversion.rs b/tests/target/try-conversion.rs new file mode 100644 index 000000000000..04992a0a0f6c --- /dev/null +++ b/tests/target/try-conversion.rs @@ -0,0 +1,28 @@ +// rustfmt-use_try_shorthand: true + +fn main() { + let x = some_expr()?; + + let y = a + .very + .loooooooooooooooooooooooooooooooooooooong() + .chain() + .inside() + .weeeeeeeeeeeeeee()? + .test() + .0 + .x; +} + +fn test() { + a? +} + +fn issue1291() { + fs::create_dir_all(&gitfiledir).chain_err(|| { + format!( + "failed to create the {} submodule directory for the workarea", + name + ) + })?; +} diff --git a/tests/target/try_block.rs b/tests/target/try_block.rs new file mode 100644 index 000000000000..19a3f3e14876 --- /dev/null +++ b/tests/target/try_block.rs @@ -0,0 +1,29 @@ +// rustfmt-edition: 2018 + +fn main() -> Result<(), !> { + let _x: Option<_> = try { 4 }; + + try {} +} + +fn baz() -> Option { + if (1 == 1) { + return try { 5 }; + } + + // test + let x: Option<()> = try { + // try blocks are great + }; + + let y: Option = try { 6 }; // comment + + let x: Option = try { + baz()?; + baz()?; + baz()?; + 7 + }; + + return None; +} diff --git a/tests/target/tuple.rs b/tests/target/tuple.rs new file mode 100644 index 000000000000..24fcf8cfd7cf --- /dev/null +++ b/tests/target/tuple.rs @@ -0,0 +1,100 @@ +// Test tuple literals + +fn foo() { + let a = (a, a, a, a, a); + let aaaaaaaaaaaaaaaa = ( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + aaaaaaaaaaaaaa, + aaaaaaaaaaaaaa, + ); + let aaaaaaaaaaaaaaaaaaaaaa = ( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + aaaaaaaaaaaaaaaaaaaaaaaaa, + aaaa, + ); + let a = (a,); + + let b = ( + // This is a comment + b, // Comment + b, /* Trailing comment */ + ); + + // #1063 + foo(x.0 .0); +} + +fn a() { + (( + aaaaaaaa, + aaaaaaaaaaaaa, + aaaaaaaaaaaaaaaaa, + aaaaaaaaaaaaaa, + aaaaaaaaaaaaaaaa, + aaaaaaaaaaaaaa, + ),) +} + +fn b() { + ( + ( + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, + ), + bbbbbbbbbbbbbbbbbb, + ) +} + +fn issue550() { + self.visitor.visit_volume( + self.level.sector_id(sector), + ( + floor_y, + if is_sky_flat(ceil_tex) { + from_wad_height(self.height_range.1) + } else { + ceil_y + }, + ), + ); +} + +fn issue775() { + if indent { + let a = mk_object(&[ + ("a".to_string(), Boolean(true)), + ( + "b".to_string(), + Array(vec![ + mk_object(&[("c".to_string(), String("\x0c\r".to_string()))]), + mk_object(&[("d".to_string(), String("".to_string()))]), + ]), + ), + ]); + } +} + +fn issue1725() { + bench_antialiased_lines!( + bench_draw_antialiased_line_segment_diagonal, + (10, 10), + (450, 450) + ); + bench_antialiased_lines!( + bench_draw_antialiased_line_segment_shallow, + (10, 10), + (450, 80) + ); +} + +fn issue_4355() { + let _ = ((1,),).0 .0; +} + +// https://github.com/rust-lang/rustfmt/issues/4410 +impl Drop for LockGuard { + fn drop(&mut self) { + LockMap::unlock(&self.0 .0, &self.0 .1); + } +} diff --git a/tests/target/tuple_v2.rs b/tests/target/tuple_v2.rs new file mode 100644 index 000000000000..ba653291c2f5 --- /dev/null +++ b/tests/target/tuple_v2.rs @@ -0,0 +1,5 @@ +// rustfmt-version: Two + +fn issue_4355() { + let _ = ((1,),).0.0; +} diff --git a/tests/target/type.rs b/tests/target/type.rs new file mode 100644 index 000000000000..c789ecb055a7 --- /dev/null +++ b/tests/target/type.rs @@ -0,0 +1,175 @@ +// rustfmt-normalize_comments: true +fn types() { + let x: [Vec<_>] = []; + let y: *mut [SomeType; konst_funk()] = expr(); + let z: (/* #digits */ usize, /* exp */ i16) = funk(); + let z: (usize /* #digits */, i16 /* exp */) = funk(); +} + +struct F { + f: extern "C" fn(x: u8, ... /* comment */), + g: extern "C" fn(x: u8, /* comment */ ...), + h: extern "C" fn(x: u8, ...), + i: extern "C" fn( + x: u8, + // comment 4 + y: String, // comment 3 + z: Foo, + // comment + ... // comment 2 + ), +} + +fn issue_1006(def_id_to_string: for<'a, 'b> unsafe fn(TyCtxt<'b, 'tcx, 'tcx>, DefId) -> String) {} + +fn impl_trait_fn_1() -> impl Fn(i32) -> Option {} + +fn impl_trait_fn_2() -> impl Future {} + +fn issue_1234() { + do_parse!(name: take_while1!(is_token) >> (Header)) +} + +// #2510 +impl CombineTypes { + pub fn pop_callback( + &self, + query_id: Uuid, + ) -> Option<( + ProjectId, + Box () + Sync + Send>, + )> { + self.query_callbacks()(&query_id) + } +} + +// #2859 +pub fn do_something<'a, T: Trait1 + Trait2 + 'a>( + &fooo: u32, +) -> impl Future< + Item = ( + impl Future + 'a, + impl Future + 'a, + impl Future + 'a, + ), + Error = SomeError, +> + 'a { +} + +pub fn do_something<'a, T: Trait1 + Trait2 + 'a>( + &fooo: u32, +) -> impl Future< + Item = ( + impl Future + 'a, + impl Future + 'a, + impl Future + 'a, + ), + Error = SomeError, +> + Future< + Item = ( + impl Future + 'a, + impl Future + 'a, + impl Future + 'a, + ), + Error = SomeError, +> + Future< + Item = ( + impl Future + 'a, + impl Future + 'a, + impl Future + 'a, + ), + Error = SomeError, +> + 'a + 'b + 'c { +} + +// #3051 +token![impl]; +token![impl]; + +// #3060 +macro_rules! foo { + ($foo_api: ty) => { + type Target = ($foo_api) + 'static; + }; +} + +type Target = (FooAPI) + 'static; + +// #3137 +fn foo(t: T) +where + T: (FnOnce() -> ()) + Clone, + U: (FnOnce() -> ()) + 'static, +{ +} + +// #3117 +fn issue3117() { + { + { + { + { + { + { + { + { + let opt: &mut Option = + unsafe { &mut *self.future.get() }; + } + } + } + } + } + } + } + } +} + +// #3139 +fn issue3139() { + assert_eq!( + to_json_value(&None::).unwrap(), + json!( { "test": None :: } ) + ); +} + +// #3180 +fn foo( + a: SomeLongComplexType, + b: SomeOtherLongComplexType, +) -> Box> { +} + +type MyFn = fn( + a: SomeLongComplexType, + b: SomeOtherLongComplexType, +) -> Box>; + +// Const bound + +trait T: ~const Super {} + +const fn not_quite_const() -> i32 { + ::CONST +} + +struct S(std::marker::PhantomData); + +impl ~const T {} + +fn apit(_: impl ~const T) {} + +fn rpit() -> impl ~const T { + S +} + +pub struct Foo(T); +impl Foo { + fn new(t: T) -> Self { + Self(t) + } +} + +// #4357 +type T = typeof(1); +impl T for .. {} diff --git a/tests/target/type_alias.rs b/tests/target/type_alias.rs new file mode 100644 index 000000000000..862f9ecbeec6 --- /dev/null +++ b/tests/target/type_alias.rs @@ -0,0 +1,76 @@ +// rustfmt-normalize_comments: true + +type PrivateTest<'a, I> = ( + Box + 'a>, + Box + 'a>, +); + +pub type PublicTest<'a, I, O> = Result< + Vec, + Box + 'a>, + Box + 'a>, +>; + +pub type LongGenericListTest< + 'a, + 'b, + 'c, + 'd, + LONGPARAMETERNAME, + LONGPARAMETERNAME, + LONGPARAMETERNAME, + A, + B, + C, +> = Option>; + +pub type Exactly100CharsTest<'a, 'b, 'c, 'd, LONGPARAMETERNAME, LONGPARAMETERNAME, A, B> = Vec; + +pub type Exactly101CharsTest<'a, 'b, 'c, 'd, LONGPARAMETERNAME, LONGPARAMETERNAME, A, B> = + Vec; + +pub type Exactly100CharsToEqualTest<'a, 'b, 'c, 'd, LONGPARAMETERNAME, LONGPARAMETERNAME, A, B, C> = + Vec; + +pub type GenericsFitButNotEqualTest< + 'a, + 'b, + 'c, + 'd, + LONGPARAMETERNAME, + LONGPARAMETERNAME, + A1, + B, + C, +> = Vec; + +pub type CommentTest< + // Lifetime + 'a, + // Type + T, +> = (); + +pub type WithWhereClause +where + T: Clone, + LONGPARAMETERNAME: Clone + Eq + OtherTrait, += Option; + +pub type Exactly100CharstoEqualWhereTest +where + T: Clone + Ord + Eq + SomeOtherTrait, += Option; + +pub type Exactly101CharstoEqualWhereTest +where + T: Clone + Ord + Eq + SomeOtherTrait, += Option; + +type RegisterPlugin = unsafe fn(pt: *const c_char, plugin: *mut c_void, data: *mut CallbackData); + +// #1683 +pub type Between = + super::operators::Between, AsExpr>>; +pub type NotBetween = + super::operators::NotBetween, AsExpr>>; diff --git a/tests/target/unicode.rs b/tests/target/unicode.rs new file mode 100644 index 000000000000..34a4f4634796 --- /dev/null +++ b/tests/target/unicode.rs @@ -0,0 +1,30 @@ +// rustfmt-wrap_comments: true + +fn foo() { + let s = "this line goes to 100: ͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶͶ"; + let s = 42; + + // a comment of length 80, with the starting sigil: ҘҘҘҘҘҘҘҘҘҘ ҘҘҘҘҘҘҘҘҘҘҘҘҘҘ + let s = 42; +} + +pub fn bar(config: &Config) { + let csv = RefCell::new(create_csv(config, "foo")); + { + let mut csv = csv.borrow_mut(); + for (i1, i2, i3) in iproduct!(0..2, 0..3, 0..3) { + csv.write_field(format!("γ[{}.{}.{}]", i1, i2, i3)).unwrap(); + csv.write_field(format!("d[{}.{}.{}]", i1, i2, i3)).unwrap(); + csv.write_field(format!("i[{}.{}.{}]", i1, i2, i3)).unwrap(); + } + csv.write_record(None::<&[u8]>).unwrap(); + } +} + +// The NotUnicode line is below 100 wrt chars but over it wrt String::len +fn baz() { + let our_error_b = result_b_from_func.or_else(|e| match e { + NotPresent => Err(e).chain_err(|| "env var wasn't provided"), + NotUnicode(_) => Err(e).chain_err(|| "env var was very very very bork文字化ã"), + }); +} diff --git a/tests/target/unindent_if_else_cond_comment.rs b/tests/target/unindent_if_else_cond_comment.rs new file mode 100644 index 000000000000..98621b1eed15 --- /dev/null +++ b/tests/target/unindent_if_else_cond_comment.rs @@ -0,0 +1,27 @@ +// Comments on else block. See #1575. + +fn example() { + // `if` comment + if x { + foo(); + // `else if` comment + } else if y { + foo(); + // Comment on `else if`. + // Comment on `else if`. + } else if z { + bar(); + /* + * Multi line comment on `else if` + */ + } else if xx { + bar(); + /* Single line comment on `else if` */ + } else if yy { + foo(); + // `else` comment + } else { + foo(); + // Comment at the end of `else` block + }; +} diff --git a/tests/target/unions.rs b/tests/target/unions.rs new file mode 100644 index 000000000000..8ed16b269c23 --- /dev/null +++ b/tests/target/unions.rs @@ -0,0 +1,198 @@ +// rustfmt-normalize_comments: true +// rustfmt-wrap_comments: true + +/// A Doc comment +#[AnAttribute] +pub union Foo { + #[rustfmt::skip] + f : SomeType, // Comment beside a field + f: SomeType, // Comment beside a field + // Comment on a field + #[AnAttribute] + g: SomeOtherType, + /// A doc comment on a field + h: AThirdType, + pub i: TypeForPublicField, +} + +// #1029 +pub union Foo { + #[doc(hidden)] + // This will NOT get deleted! + bar: String, // hi +} + +// #1029 +union X { + // `x` is an important number. + #[allow(unused)] // TODO: use + x: u32, +} + +// #410 +#[allow(missing_docs)] +pub union Writebatch { + #[allow(dead_code)] // only used for holding the internal pointer + writebatch: RawWritebatch, + marker: PhantomData, +} + +// With a where-clause and generics. +pub union Foo<'a, Y: Baz> +where + X: Whatever, +{ + f: SomeType, // Comment beside a field +} + +union Baz { + a: A, // Comment A + b: B, // Comment B + c: C, // Comment C +} + +union Baz { + a: A, // Comment A + + b: B, // Comment B + + c: C, // Comment C +} + +union Baz { + a: A, + + b: B, + c: C, + + d: D, +} + +union Baz { + // Comment A + a: A, + + // Comment B + b: B, + // Comment C + c: C, +} + +pub union State time::Timespec> { + now: F, +} + +pub union State ()> { + now: F, +} + +pub union State { + now: F, +} + +union Palette { + /// A map of indices in the palette to a count of pixels in approximately + /// that color + foo: i32, +} + +// Splitting a single line comment into a block previously had a misalignment +// when the field had attributes +union FieldsWithAttributes { + // Pre Comment + #[rustfmt::skip] pub host:String, /* Post comment BBBBBBBBBBBBBB BBBBBBBBBBBBBBBB + * BBBBBBBBBBBBBBBB BBBBBBBBBBBBBBBBB BBBBBBBBBBB */ + // Another pre comment + #[attr1] + #[attr2] + pub id: usize, /* CCCCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCC + * CCCCCCCCCCCCCCCCCC CCCCCCCCCCCCCC CCCCCCCCCCCC */ +} + +union Deep { + deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeep: + node::Handle>, Type, NodeType>, +} + +mod m { + union X + where + T: Sized, + { + a: T, + } +} + +union Issue677 { + pub ptr: *const libc::c_void, + pub trace: fn(obj: *const libc::c_void, tracer: *mut JSTracer), +} + +union Foo {} +union Foo {} +union Foo { + // comment +} +union Foo { + // trailing space -> +} +union Foo { + // comment +} + +union LongUnion { + a: A, + the_quick_brown_fox_jumps_over_the_lazy_dog: + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, +} + +union Deep { + deeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeep: + node::Handle>, Type, NodeType>, +} + +// #1364 +fn foo() { + convex_shape.set_point(0, &Vector2f { x: 400.0, y: 100.0 }); + convex_shape.set_point(1, &Vector2f { x: 500.0, y: 70.0 }); + convex_shape.set_point(2, &Vector2f { x: 450.0, y: 100.0 }); + convex_shape.set_point(3, &Vector2f { x: 580.0, y: 150.0 }); +} + +// Vertical alignment +union Foo { + aaaaa: u32, // a + + b: u32, // b + cc: u32, // cc + + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx: u32, // 1 + yy: u32, // comment2 + zzz: u32, // comment3 + + aaaaaa: u32, // comment4 + bb: u32, // comment5 + // separate + dd: u32, // comment7 + c: u32, // comment6 + + aaaaaaa: u32, /* multi + * line + * comment + */ + b: u32, // hi + + do_not_push_this_comment1: u32, // comment1 + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx: u32, // 2 + please_do_not_push_this_comment3: u32, // comment3 + + do_not_push_this_comment1: u32, // comment1 + // separate + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx: u32, // 2 + please_do_not_push_this_comment3: u32, // comment3 + + do_not_push_this_comment1: u32, // comment1 + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx: u32, // 2 + // separate + please_do_not_push_this_comment3: u32, // comment3 +} diff --git a/tests/target/unsafe-mod.rs b/tests/target/unsafe-mod.rs new file mode 100644 index 000000000000..05ba2f54faf8 --- /dev/null +++ b/tests/target/unsafe-mod.rs @@ -0,0 +1,7 @@ +// These are supported by rustc syntactically but not semantically. + +#[cfg(any())] +unsafe mod m {} + +#[cfg(any())] +unsafe extern "C++" {} diff --git a/tests/target/visibility.rs b/tests/target/visibility.rs new file mode 100644 index 000000000000..ca078422c135 --- /dev/null +++ b/tests/target/visibility.rs @@ -0,0 +1,8 @@ +// #2398 +pub mod outer_mod { + pub mod inner_mod { + pub(in outer_mod) fn outer_mod_visible_fn() {} + pub(super) fn super_mod_visible_fn() {} + pub(self) fn inner_mod_visible_fn() {} + } +} diff --git a/tests/target/visual-fn-type.rs b/tests/target/visual-fn-type.rs new file mode 100644 index 000000000000..052acde0209b --- /dev/null +++ b/tests/target/visual-fn-type.rs @@ -0,0 +1,9 @@ +// rustfmt-indent_style: Visual +type CNodeSetAtts = unsafe extern "C" fn(node: *const RsvgNode, + node_impl: *const RsvgCNodeImpl, + handle: *const RsvgHandle, + pbag: *const PropertyBag); +type CNodeDraw = unsafe extern "C" fn(node: *const RsvgNode, + node_impl: *const RsvgCNodeImpl, + draw_ctx: *const RsvgDrawingCtx, + dominate: i32); diff --git a/tests/target/where-clause-rfc.rs b/tests/target/where-clause-rfc.rs new file mode 100644 index 000000000000..9c43e91d3707 --- /dev/null +++ b/tests/target/where-clause-rfc.rs @@ -0,0 +1,156 @@ +fn reflow_list_node_with_rule(node: &CompoundNode, rule: &Rule, args: &[Arg], shape: &Shape) +where + T: FOo, + U: Bar, +{ + let mut effects = HashMap::new(); +} + +fn reflow_list_node_with_rule(node: &CompoundNode, rule: &Rule, args: &[Arg], shape: &Shape) +where + T: FOo, +{ + let mut effects = HashMap::new(); +} + +fn reflow_list_node_with_rule( + node: &CompoundNode, + rule: &Rule, + args: &[Arg], + shape: &Shape, + shape: &Shape, +) where + T: FOo, + U: Bar, +{ + let mut effects = HashMap::new(); +} + +fn reflow_list_node_with_rule( + node: &CompoundNode, + rule: &Rule, + args: &[Arg], + shape: &Shape, + shape: &Shape, +) where + T: FOo, +{ + let mut effects = HashMap::new(); +} + +fn reflow_list_node_with_rule( + node: &CompoundNode, + rule: &Rule, + args: &[Arg], + shape: &Shape, +) -> Option +where + T: FOo, + U: Bar, +{ + let mut effects = HashMap::new(); +} + +fn reflow_list_node_with_rule( + node: &CompoundNode, + rule: &Rule, + args: &[Arg], + shape: &Shape, +) -> Option +where + T: FOo, +{ + let mut effects = HashMap::new(); +} + +pub trait Test { + fn very_long_method_name(self, f: F) -> MyVeryLongReturnType + where + F: FnMut(Self::Item) -> bool; + + fn exactly_100_chars1(self, f: F) -> MyVeryLongReturnType + where + F: FnMut(Self::Item) -> bool; +} + +fn very_long_function_name(very_long_argument: F) -> MyVeryLongReturnType +where + F: FnMut(Self::Item) -> bool, +{ +} + +struct VeryLongTupleStructName(LongLongTypename, LongLongTypename, i32, i32) +where + A: LongTrait; + +struct Exactly100CharsToSemicolon(LongLongTypename, i32, i32) +where + A: LongTrait1234; + +struct AlwaysOnNextLine +where + A: LongTrait, +{ + x: i32, +} + +pub trait SomeTrait +where + T: Something + + Sync + + Send + + Display + + Debug + + Copy + + Hash + + Debug + + Display + + Write + + Read + + FromStr, +{ +} + +// #2020 +impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { + fn elaborate_bounds(&mut self, bounds: &[ty::PolyTraitRef<'tcx>], mut mk_cand: F) + where + F: for<'b> FnMut( + &mut ProbeContext<'b, 'gcx, 'tcx>, + ty::PolyTraitRef<'tcx>, + ty::AssociatedItem, + ), + { + // ... + } +} + +// #2497 +fn handle_update<'a, Tab, Conn, R, C>( + executor: &Executor>>, + change_set: &'a C, +) -> ExecutionResult +where + &'a C: Identifiable + AsChangeset + HasTable
, + <&'a C as AsChangeset>::Changeset: QueryFragment, + Tab: Table + HasTable
, + Tab::PrimaryKey: EqAll<<&'a C as Identifiable>::Id>, + Tab::FromClause: QueryFragment, + Tab: FindDsl<<&'a C as Identifiable>::Id>, + Find::Id>: IntoUpdateTarget
, + ::Id> as IntoUpdateTarget>::WhereClause: + QueryFragment, + Tab::Query: FilterDsl<::Id>>::Output>, + Filter::Id>>::Output>: LimitDsl, + Limit::Id>>::Output>>: + QueryDsl + + BoxedDsl< + 'a, + Conn::Backend, + Output = BoxedSelectStatement<'a, R::SqlType, Tab, Conn::Backend>, + >, + R: LoadingHandler + + GraphQLType, +{ + unimplemented!() +} diff --git a/tests/target/where-clause.rs b/tests/target/where-clause.rs new file mode 100644 index 000000000000..eb2f8d5e6e81 --- /dev/null +++ b/tests/target/where-clause.rs @@ -0,0 +1,107 @@ +// rustfmt-indent_style: Visual + +fn reflow_list_node_with_rule(node: &CompoundNode, rule: &Rule, args: &[Arg], shape: &Shape) + where T: FOo, + U: Bar +{ + let mut effects = HashMap::new(); +} + +fn reflow_list_node_with_rule(node: &CompoundNode, rule: &Rule, args: &[Arg], shape: &Shape) + where T: FOo +{ + let mut effects = HashMap::new(); +} + +fn reflow_list_node_with_rule(node: &CompoundNode, + rule: &Rule, + args: &[Arg], + shape: &Shape, + shape: &Shape) + where T: FOo, + U: Bar +{ + let mut effects = HashMap::new(); +} + +fn reflow_list_node_with_rule(node: &CompoundNode, + rule: &Rule, + args: &[Arg], + shape: &Shape, + shape: &Shape) + where T: FOo +{ + let mut effects = HashMap::new(); +} + +fn reflow_list_node_with_rule(node: &CompoundNode, + rule: &Rule, + args: &[Arg], + shape: &Shape) + -> Option + where T: FOo, + U: Bar +{ + let mut effects = HashMap::new(); +} + +fn reflow_list_node_with_rule(node: &CompoundNode, + rule: &Rule, + args: &[Arg], + shape: &Shape) + -> Option + where T: FOo +{ + let mut effects = HashMap::new(); +} + +pub trait Test { + fn very_long_method_name(self, f: F) -> MyVeryLongReturnType + where F: FnMut(Self::Item) -> bool; + + fn exactly_100_chars1(self, f: F) -> MyVeryLongReturnType + where F: FnMut(Self::Item) -> bool; +} + +fn very_long_function_name(very_long_argument: F) -> MyVeryLongReturnType + where F: FnMut(Self::Item) -> bool +{ +} + +struct VeryLongTupleStructName(LongLongTypename, LongLongTypename, i32, i32) + where A: LongTrait; + +struct Exactly100CharsToSemicolon(LongLongTypename, i32, i32) where A: LongTrait1234; + +struct AlwaysOnNextLine + where A: LongTrait +{ + x: i32, +} + +pub trait SomeTrait + where T: Something + + Sync + + Send + + Display + + Debug + + Copy + + Hash + + Debug + + Display + + Write + + Read + + FromStr +{ +} + +// #2020 +impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { + fn elaborate_bounds(&mut self, bounds: &[ty::PolyTraitRef<'tcx>], mut mk_cand: F) + where F: for<'b> FnMut(&mut ProbeContext<'b, 'gcx, 'tcx>, + ty::PolyTraitRef<'tcx>, + ty::AssociatedItem) + { + // ... + } +} diff --git a/tests/target/width-heuristics.rs b/tests/target/width-heuristics.rs new file mode 100644 index 000000000000..e177a2152e81 --- /dev/null +++ b/tests/target/width-heuristics.rs @@ -0,0 +1,24 @@ +// rustfmt-max_width: 120 + +// elems on multiple lines for max_width 100, but same line for max_width 120 +fn foo(e: Enum) { + match e { + Enum::Var { elem1, elem2, elem3 } => { + return; + } + } +} + +// elems not on same line for either max_width 100 or 120 +fn bar(e: Enum) { + match e { + Enum::Var { + elem1, + elem2, + elem3, + elem4, + } => { + return; + } + } +} diff --git a/tests/target/wrap_comments_should_not_imply_format_doc_comments.rs b/tests/target/wrap_comments_should_not_imply_format_doc_comments.rs new file mode 100644 index 000000000000..6ccecc7e0bbe --- /dev/null +++ b/tests/target/wrap_comments_should_not_imply_format_doc_comments.rs @@ -0,0 +1,16 @@ +// rustfmt-wrap_comments: true + +/// Foo +/// +/// # Example +/// ``` +/// # #![cfg_attr(not(dox), feature(cfg_target_feature, target_feature, stdsimd))] +/// # #![cfg_attr(not(dox), no_std)] +/// fn foo() { } +/// ``` +fn foo() {} + +/// A long comment for wrapping +/// This is a long long long long long long long long long long long long long +/// long long long long long long long sentence. +fn bar() {} diff --git a/tests/writemode/source/fn-single-line.rs b/tests/writemode/source/fn-single-line.rs new file mode 100644 index 000000000000..ab1e13e17a78 --- /dev/null +++ b/tests/writemode/source/fn-single-line.rs @@ -0,0 +1,80 @@ +// rustfmt-fn_single_line: true +// rustfmt-emit_mode: checkstyle +// Test single-line functions. + +fn foo_expr() { + 1 +} + +fn foo_stmt() { + foo(); +} + +fn foo_decl_local() { + let z = 5; + } + +fn foo_decl_item(x: &mut i32) { + x = 3; +} + + fn empty() { + +} + +fn foo_return() -> String { + "yay" +} + +fn foo_where() -> T where T: Sync { + let x = 2; +} + +fn fooblock() { + { + "inner-block" + } +} + +fn fooblock2(x: i32) { + let z = match x { + _ => 2, + }; +} + +fn comment() { + // this is a test comment + 1 +} + +fn comment2() { + // multi-line comment + let z = 2; + 1 +} + +fn only_comment() { + // Keep this here +} + +fn aaaaaaaaaaaaaaaaa_looooooooooooooooooooooong_name() { + let z = "aaaaaaawwwwwwwwwwwwwwwwwwwwwwwwwwww"; +} + +fn lots_of_space () { + 1 +} + +fn mac() -> Vec { vec![] } + +trait CoolTypes { + fn dummy(&self) { + } +} + +trait CoolerTypes { fn dummy(&self) { +} +} + +fn Foo() where T: Bar { +} diff --git a/tests/writemode/source/json.rs b/tests/writemode/source/json.rs new file mode 100644 index 000000000000..89dcf694183b --- /dev/null +++ b/tests/writemode/source/json.rs @@ -0,0 +1,80 @@ +// rustfmt-fn_single_line: true +// rustfmt-emit_mode: json +// Test single-line functions. + +fn foo_expr() { + 1 +} + +fn foo_stmt() { + foo(); +} + +fn foo_decl_local() { + let z = 5; + } + +fn foo_decl_item(x: &mut i32) { + x = 3; +} + + fn empty() { + +} + +fn foo_return() -> String { + "yay" +} + +fn foo_where() -> T where T: Sync { + let x = 2; +} + +fn fooblock() { + { + "inner-block" + } +} + +fn fooblock2(x: i32) { + let z = match x { + _ => 2, + }; +} + +fn comment() { + // this is a test comment + 1 +} + +fn comment2() { + // multi-line comment + let z = 2; + 1 +} + +fn only_comment() { + // Keep this here +} + +fn aaaaaaaaaaaaaaaaa_looooooooooooooooooooooong_name() { + let z = "aaaaaaawwwwwwwwwwwwwwwwwwwwwwwwwwww"; +} + +fn lots_of_space () { + 1 +} + +fn mac() -> Vec { vec![] } + +trait CoolTypes { + fn dummy(&self) { + } +} + +trait CoolerTypes { fn dummy(&self) { +} +} + +fn Foo() where T: Bar { +} diff --git a/tests/writemode/source/modified.rs b/tests/writemode/source/modified.rs new file mode 100644 index 000000000000..948beb348dbb --- /dev/null +++ b/tests/writemode/source/modified.rs @@ -0,0 +1,14 @@ +// rustfmt-write_mode: modified +// Test "modified" output + +fn +blah +() +{ } + + +#[cfg +( a , b +)] +fn +main() {} diff --git a/tests/writemode/source/stdin.rs b/tests/writemode/source/stdin.rs new file mode 100644 index 000000000000..06f8a0c288d7 --- /dev/null +++ b/tests/writemode/source/stdin.rs @@ -0,0 +1,6 @@ + +fn + some( ) +{ +} +fn main () {} diff --git a/tests/writemode/target/checkstyle.xml b/tests/writemode/target/checkstyle.xml new file mode 100644 index 000000000000..05bc3a2525d5 --- /dev/null +++ b/tests/writemode/target/checkstyle.xml @@ -0,0 +1,2 @@ + + diff --git a/tests/writemode/target/modified.txt b/tests/writemode/target/modified.txt new file mode 100644 index 000000000000..5c0539a665e6 --- /dev/null +++ b/tests/writemode/target/modified.txt @@ -0,0 +1,5 @@ +4 4 1 +fn blah() {} +10 5 2 +#[cfg(a, b)] +fn main() {} diff --git a/tests/writemode/target/output.json b/tests/writemode/target/output.json new file mode 100644 index 000000000000..d8b5467ee91c --- /dev/null +++ b/tests/writemode/target/output.json @@ -0,0 +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}\n","expected":"fn foo_expr() { 1 }\n"},{"original_begin_line":9,"original_end_line":11,"expected_begin_line":7,"expected_end_line":7,"original":"fn foo_stmt() {\n foo();\n}\n","expected":"fn foo_stmt() { foo(); }\n"},{"original_begin_line":13,"original_end_line":15,"expected_begin_line":9,"expected_end_line":9,"original":"fn foo_decl_local() {\n let z = 5;\n }\n","expected":"fn foo_decl_local() { let z = 5; }\n"},{"original_begin_line":17,"original_end_line":19,"expected_begin_line":11,"expected_end_line":11,"original":"fn foo_decl_item(x: &mut i32) {\n x = 3;\n}\n","expected":"fn foo_decl_item(x: &mut i32) { x = 3; }\n"},{"original_begin_line":21,"original_end_line":21,"expected_begin_line":13,"expected_end_line":13,"original":" fn empty() {\n","expected":"fn empty() {}\n"},{"original_begin_line":23,"original_end_line":23,"expected_begin_line":15,"expected_end_line":15,"original":"}\n","expected":"fn foo_return() -> String { \"yay\" }\n"},{"original_begin_line":25,"original_end_line":29,"expected_begin_line":17,"expected_end_line":20,"original":"fn foo_return() -> String {\n \"yay\"\n}\n\nfn foo_where() -> T where T: Sync {\n","expected":"fn foo_where() -> T\nwhere\n T: Sync,\n{\n"},{"original_begin_line":64,"original_end_line":66,"expected_begin_line":55,"expected_end_line":55,"original":"fn lots_of_space () {\n 1 \n}\n","expected":"fn lots_of_space() { 1 }\n"},{"original_begin_line":71,"original_end_line":72,"expected_begin_line":60,"expected_end_line":60,"original":" fn dummy(&self) {\n }\n","expected":" fn dummy(&self) {}\n"},{"original_begin_line":75,"original_end_line":75,"expected_begin_line":63,"expected_end_line":64,"original":"trait CoolerTypes { fn dummy(&self) { \n","expected":"trait CoolerTypes {\n fn dummy(&self) {}\n"},{"original_begin_line":77,"original_end_line":77,"expected_begin_line":66,"expected_end_line":66,"original":"}\n","expected":""},{"original_begin_line":79,"original_end_line":79,"expected_begin_line":67,"expected_end_line":70,"original":"fn Foo() where T: Bar {\n","expected":"fn Foo()\nwhere\n T: Bar,\n{\n"}]}] diff --git a/tests/writemode/target/stdin.json b/tests/writemode/target/stdin.json new file mode 100644 index 000000000000..dbf2c4863229 --- /dev/null +++ b/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/tests/writemode/target/stdin.xml b/tests/writemode/target/stdin.xml new file mode 100644 index 000000000000..a7301bbc553c --- /dev/null +++ b/tests/writemode/target/stdin.xml @@ -0,0 +1,2 @@ + + diff --git a/triagebot.toml b/triagebot.toml new file mode 100644 index 000000000000..fa0824ac53c0 --- /dev/null +++ b/triagebot.toml @@ -0,0 +1 @@ +[assign] From 5ed7f74d103ff6032088ea43dc50cba6c590e216 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Thu, 6 Jul 2023 05:21:33 +0000 Subject: [PATCH 301/401] Actually use `tracing` for logging --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 2bd17e9982bb..b9ab3ffd989a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,7 +8,7 @@ #[macro_use] extern crate lazy_static; #[macro_use] -extern crate log; +extern crate tracing; // N.B. these crates are loaded from the sysroot, so they need extern crate. extern crate rustc_ast; From 89500fab0ed52c3260288068453dad908a5314e1 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Mon, 10 Jul 2023 09:31:59 -0400 Subject: [PATCH 302/401] Add `LD_LIBRARY_PATH` in `check_diff.sh` There were some upstream changes made a while back that requires this to be set when building rustfmt from source like we do in the `check_diff.sh` script. See issue 5675 for more details. --- ci/check_diff.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ci/check_diff.sh b/ci/check_diff.sh index 062c2dd8673a..6f30e8ba9cb4 100755 --- a/ci/check_diff.sh +++ b/ci/check_diff.sh @@ -1,5 +1,8 @@ #!/bin/bash +# https://github.com/rust-lang/rustfmt/issues/5675 +export LD_LIBRARY_PATH=$(rustc --print sysroot)/lib:$LD_LIBRARY_PATH + function print_usage() { echo "usage check_diff REMOTE_REPO FEATURE_BRANCH [COMMIT_HASH] [OPTIONAL_RUSTFMT_CONFIGS]" } From e5c212b56fb7c4f91d369faa3981488e09002cf1 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Mon, 10 Jul 2023 10:03:51 -0400 Subject: [PATCH 303/401] Improve error discovery in `check_diff.sh` The `set -e` option is used to immediately exit if any command exits with a non zero exit status. This will help us catch errors in the script, for example, needing the `LD_LIBRARY_PATH` to be set. --- ci/check_diff.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ci/check_diff.sh b/ci/check_diff.sh index 6f30e8ba9cb4..a685c2e108e6 100755 --- a/ci/check_diff.sh +++ b/ci/check_diff.sh @@ -1,5 +1,7 @@ #!/bin/bash +set -e + # https://github.com/rust-lang/rustfmt/issues/5675 export LD_LIBRARY_PATH=$(rustc --print sysroot)/lib:$LD_LIBRARY_PATH @@ -143,9 +145,15 @@ function check_repo() { init_submodules $SUBMODULES fi + + # rustfmt --check returns 1 if a diff was found + # Also check_diff returns 1 if there was a diff between master rustfmt and the feature branch + # so we want to ignore the exit status check + set +e check_diff $REPO_NAME # append the status of running `check_diff` to the STATUSES array STATUSES+=($?) + set -e echo "removing tmp_dir $tmp_dir" rm -rf $tmp_dir From d698bf4e1b2033245d39af85bb5876500c3fbfde Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Mon, 10 Jul 2023 11:07:49 -0400 Subject: [PATCH 304/401] use the `branch_name` as the default for the optional commit hash There was an issue with the script when passing optional rustfmt configs without specifying a commit hash. Because these optional values are passed via positional arguments the configs ($4) would be used in place of the commit hash ($3). Now that we set a default value for the optional commit hash we avoid this problem. --- .github/workflows/check_diff.yml | 2 +- ci/check_diff.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/check_diff.yml b/.github/workflows/check_diff.yml index 8bfb5834519c..2f2beb769159 100644 --- a/.github/workflows/check_diff.yml +++ b/.github/workflows/check_diff.yml @@ -30,4 +30,4 @@ jobs: rustup target add x86_64-unknown-linux-gnu - name: check diff - run: bash ${GITHUB_WORKSPACE}/ci/check_diff.sh ${{ github.event.inputs.clone_url }} ${{ github.event.inputs.branch_name }} ${{ github.event.inputs.commit_hash }} ${{ github.event.inputs.rustfmt_configs }} + run: bash ${GITHUB_WORKSPACE}/ci/check_diff.sh ${{ github.event.inputs.clone_url }} ${{ github.event.inputs.branch_name }} ${{ github.event.inputs.commit_hash || github.event.inputs.branch_name }} ${{ github.event.inputs.rustfmt_configs }} diff --git a/ci/check_diff.sh b/ci/check_diff.sh index a685c2e108e6..50c58b1f4925 100755 --- a/ci/check_diff.sh +++ b/ci/check_diff.sh @@ -115,7 +115,7 @@ function compile_rustfmt() { git fetch feature $FEATURE_BRANCH cargo build --release --bin rustfmt && cp target/release/rustfmt $1/rustfmt - if [ -z "$OPTIONAL_COMMIT_HASH" ]; then + if [ -z "$OPTIONAL_COMMIT_HASH" ] || [ "$FEATURE_BRANCH" = "$OPTIONAL_COMMIT_HASH" ]; then git switch $FEATURE_BRANCH else git switch $OPTIONAL_COMMIT_HASH --detach From e9dfb6f2179db598cb20052a945550919f401aec Mon Sep 17 00:00:00 2001 From: xxchan Date: Mon, 17 Jul 2023 12:08:28 +0200 Subject: [PATCH 305/401] fix link in CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 34fcd9a81c11..22f183c04a3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ - When formatting doc comments with `wrap_comments = true` rustfmt will no longer wrap markdown tables [#4210](https://github.com/rust-lang/rustfmt/issues/4210) - Properly handle wrapping comments that include a numbered list in markdown [#5416](https://github.com/rust-lang/rustfmt/issues/5416) -- Properly handle markdown sublists that utilize a `+` [#4041](https://github.com/rust-lang/rustfmt/issues/4210) +- Properly handle markdown sublists that utilize a `+` [#4041](https://github.com/rust-lang/rustfmt/issues/4041) - rustfmt will no longer use shorthand initialization when rewriting a tuple struct even when `use_field_init_shorthand = true` as this leads to code that could no longer compile. Take the following struct as an example `struct MyStruct(u64);`. rustfmt will no longer format `MyStruct { 0: 0 }` as `MyStruct { 0 }` [#5488](https://github.com/rust-lang/rustfmt/issues/5488) - rustfmt no longer panics when formatting an empty code block in a doc comment with `format_code_in_doc_comments = true` [#5234](https://github.com/rust-lang/rustfmt/issues/5234). For example: From e0e633ef14e85fac498ec68bf68253e8473c23c5 Mon Sep 17 00:00:00 2001 From: Jordan McQueen Date: Tue, 18 Jul 2023 07:51:51 +0900 Subject: [PATCH 306/401] Use matches!() macro to improve readability (#5830) * Use matches!() macro to improve readability 1. Use `matches!()` macro in `is_line_comment` and `is_block_comment` to improve readability. 2. Very sightly improve the wording of the doc comment for these two functions. * Update wording on doc comment on is_line_comment() --- src/comment.rs | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/comment.rs b/src/comment.rs index 85918ecc1164..c241e12c56a9 100644 --- a/src/comment.rs +++ b/src/comment.rs @@ -58,25 +58,23 @@ fn custom_opener(s: &str) -> &str { } impl<'a> CommentStyle<'a> { - /// Returns `true` if the commenting style covers a line only. + /// Returns `true` if the commenting style cannot span multiple lines. pub(crate) fn is_line_comment(&self) -> bool { - match *self { + matches!( + self, CommentStyle::DoubleSlash - | CommentStyle::TripleSlash - | CommentStyle::Doc - | CommentStyle::Custom(_) => true, - _ => false, - } + | CommentStyle::TripleSlash + | CommentStyle::Doc + | CommentStyle::Custom(_) + ) } - /// Returns `true` if the commenting style can span over multiple lines. + /// Returns `true` if the commenting style can span multiple lines. pub(crate) fn is_block_comment(&self) -> bool { - match *self { - CommentStyle::SingleBullet | CommentStyle::DoubleBullet | CommentStyle::Exclamation => { - true - } - _ => false, - } + matches!( + self, + CommentStyle::SingleBullet | CommentStyle::DoubleBullet | CommentStyle::Exclamation + ) } /// Returns `true` if the commenting style is for documentation. From 184296754238c78e97fa469cf2b6983f5d0b513c Mon Sep 17 00:00:00 2001 From: fee1-dead Date: Wed, 19 Jul 2023 01:48:01 +0000 Subject: [PATCH 307/401] automatically add pr-not-reviewed to new PRs (#5843) --- triagebot.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/triagebot.toml b/triagebot.toml index fa0824ac53c0..b8691192e24f 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -1 +1,4 @@ +[autolabel."pr-not-reviewed"] +new_pr = true + [assign] From b944a32e5cd153344ec93827821d8607c3988a09 Mon Sep 17 00:00:00 2001 From: tdanniels Date: Tue, 18 Jul 2023 18:48:57 -0700 Subject: [PATCH 308/401] Prevent ICE when formatting an empty-ish macro arm (#5833) Fixes 5730 Previously rustfmt was attempting to slice a string with an invalid range (`start > end`), leading to the ICE. When formatting a macro transcriber snippet consisting of a lone semicolon, the snippet was being formatted into the empty string, leading the enclosing `fn main() {\n}` added by `format_code_block` to be formatted into `fn main() {}`. However, rustfmt was assuming that the enclosing function string's length had been left unchanged. This was leading to an invalid range being constructed when attempting to trim off the enclosing function. The fix is to just clamp the range's start to be less than or equal to the range's end, since if `end < start` there's nothing to iterate over anyway. --- src/lib.rs | 9 ++++++++- tests/source/issue_5730.rs | 3 +++ tests/target/issue_5730.rs | 3 +++ 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 tests/source/issue_5730.rs create mode 100644 tests/target/issue_5730.rs diff --git a/src/lib.rs b/src/lib.rs index b9ab3ffd989a..8800e200fa40 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,6 +28,7 @@ extern crate thin_vec; extern crate rustc_driver; use std::cell::RefCell; +use std::cmp::min; use std::collections::HashMap; use std::fmt; use std::io::{self, Write}; @@ -385,9 +386,15 @@ fn format_code_block( .snippet .rfind('}') .unwrap_or_else(|| formatted.snippet.len()); + + // It's possible that `block_len < FN_MAIN_PREFIX.len()`. This can happen if the code block was + // formatted into the empty string, leading to the enclosing `fn main() {\n}` being formatted + // into `fn main() {}`. In this case no unindentation is done. + let block_start = min(FN_MAIN_PREFIX.len(), block_len); + let mut is_indented = true; let indent_str = Indent::from_width(config, config.tab_spaces()).to_string(config); - for (kind, ref line) in LineClasses::new(&formatted.snippet[FN_MAIN_PREFIX.len()..block_len]) { + for (kind, ref line) in LineClasses::new(&formatted.snippet[block_start..block_len]) { if !is_first { result.push('\n'); } else { diff --git a/tests/source/issue_5730.rs b/tests/source/issue_5730.rs new file mode 100644 index 000000000000..9a3f4f0d07aa --- /dev/null +++ b/tests/source/issue_5730.rs @@ -0,0 +1,3 @@ +macro_rules! statement { + () => {;}; +} diff --git a/tests/target/issue_5730.rs b/tests/target/issue_5730.rs new file mode 100644 index 000000000000..7922fdcc90fe --- /dev/null +++ b/tests/target/issue_5730.rs @@ -0,0 +1,3 @@ +macro_rules! statement { + () => {}; +} From c6d39a22590d7ad3a77a0332c039a498cbda1312 Mon Sep 17 00:00:00 2001 From: xxchan Date: Wed, 19 Jul 2023 20:22:24 +0200 Subject: [PATCH 309/401] doc: fix instruction about running Rustfmt from source code (#5838) Update docs to include an example of running `rustfmt` built from src, and show how users can set the `RUSTFMT` environment variable to test `cargo-fmt` using the `rustfmt` they built from src. --- Contributing.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Contributing.md b/Contributing.md index b986a887c92f..69a2c76369f9 100644 --- a/Contributing.md +++ b/Contributing.md @@ -95,10 +95,18 @@ wish there weren't. You can leave `FIXME`s, preferably with an issue number. You may want to run a version of rustfmt from source code as part of a test or hacking on the rustfmt codebase. It's strongly discouraged to install a version -of rustfmt from source. Instead, run it using `cargo run`, and `--manifest-path`. +of rustfmt from source. + +To run `rustfmt` on a file: ``` -cargo run --bin cargo-fmt -- --manifest-path path/to/project/you/want2test/Cargo.toml +cargo run --bin rustfmt -- path/to/file.rs +``` + +If you want to test modified `cargo-fmt`, or run `rustfmt` on the whole project (You may need to build rustfmt first): + +``` +RUSTFMT="./target/debug/rustfmt" cargo run --bin cargo-fmt -- --manifest-path path/to/project/you/want2test/Cargo.toml ``` ### Version-gate formatting changes From 2db13f448c889fa543e9b8060e865afe4d427bc4 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 19 Jul 2023 16:50:00 -0400 Subject: [PATCH 310/401] Support non-lifetime binders --- src/closures.rs | 4 ++-- src/types.rs | 19 ++++++++----------- tests/source/issue_5721.rs | 8 ++++++++ tests/source/non-lifetime-binders.rs | 10 ++++++++++ tests/target/issue_5721.rs | 10 ++++++++++ tests/target/non-lifetime-binders.rs | 10 ++++++++++ 6 files changed, 48 insertions(+), 13 deletions(-) create mode 100644 tests/source/issue_5721.rs create mode 100644 tests/source/non-lifetime-binders.rs create mode 100644 tests/target/issue_5721.rs create mode 100644 tests/target/non-lifetime-binders.rs diff --git a/src/closures.rs b/src/closures.rs index c95e9a97b43d..cf1287529197 100644 --- a/src/closures.rs +++ b/src/closures.rs @@ -12,7 +12,7 @@ use crate::overflow::OverflowableItem; use crate::rewrite::{Rewrite, RewriteContext}; use crate::shape::Shape; use crate::source_map::SpanUtils; -use crate::types::rewrite_lifetime_param; +use crate::types::rewrite_bound_params; use crate::utils::{last_line_width, left_most_sub_expr, stmt_expr, NodeIdExt}; // This module is pretty messy because of the rules around closures and blocks: @@ -246,7 +246,7 @@ fn rewrite_closure_fn_decl( "for<> ".to_owned() } ast::ClosureBinder::For { generic_params, .. } => { - let lifetime_str = rewrite_lifetime_param(context, shape, generic_params)?; + let lifetime_str = rewrite_bound_params(context, shape, generic_params)?; format!("for<{lifetime_str}> ") } ast::ClosureBinder::NotPresent => "".to_owned(), diff --git a/src/types.rs b/src/types.rs index 18a08f17ba02..bb236a386e4e 100644 --- a/src/types.rs +++ b/src/types.rs @@ -426,10 +426,10 @@ impl Rewrite for ast::WherePredicate { }) => { let type_str = bounded_ty.rewrite(context, shape)?; let colon = type_bound_colon(context).trim_end(); - let lhs = if let Some(lifetime_str) = - rewrite_lifetime_param(context, shape, bound_generic_params) + let lhs = if let Some(binder_str) = + rewrite_bound_params(context, shape, bound_generic_params) { - format!("for<{}> {}{}", lifetime_str, type_str, colon) + format!("for<{}> {}{}", binder_str, type_str, colon) } else { format!("{}{}", type_str, colon) }; @@ -657,8 +657,7 @@ impl Rewrite for ast::GenericParam { impl Rewrite for ast::PolyTraitRef { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { - if let Some(lifetime_str) = - rewrite_lifetime_param(context, shape, &self.bound_generic_params) + if let Some(lifetime_str) = rewrite_bound_params(context, shape, &self.bound_generic_params) { // 6 is "for<> ".len() let extra_offset = lifetime_str.len() + 6; @@ -881,8 +880,7 @@ fn rewrite_bare_fn( let mut result = String::with_capacity(128); - if let Some(ref lifetime_str) = rewrite_lifetime_param(context, shape, &bare_fn.generic_params) - { + if let Some(ref lifetime_str) = rewrite_bound_params(context, shape, &bare_fn.generic_params) { result.push_str("for<"); // 6 = "for<> ".len(), 4 = "for<". // This doesn't work out so nicely for multiline situation with lots of @@ -1122,16 +1120,15 @@ pub(crate) fn can_be_overflowed_type( } } -/// Returns `None` if there is no `LifetimeDef` in the given generic parameters. -pub(crate) fn rewrite_lifetime_param( +/// Returns `None` if there is no `GenericParam` in the list +pub(crate) fn rewrite_bound_params( context: &RewriteContext<'_>, shape: Shape, generic_params: &[ast::GenericParam], ) -> Option { let result = generic_params .iter() - .filter(|p| matches!(p.kind, ast::GenericParamKind::Lifetime)) - .map(|lt| lt.rewrite(context, shape)) + .map(|param| param.rewrite(context, shape)) .collect::>>()? .join(", "); if result.is_empty() { diff --git a/tests/source/issue_5721.rs b/tests/source/issue_5721.rs new file mode 100644 index 000000000000..e5ae9612c982 --- /dev/null +++ b/tests/source/issue_5721.rs @@ -0,0 +1,8 @@ +#![feature(non_lifetime_binders)] +#![allow(incomplete_features)] + +trait Other {} + +trait Trait +where + for U: Other {} diff --git a/tests/source/non-lifetime-binders.rs b/tests/source/non-lifetime-binders.rs new file mode 100644 index 000000000000..c26393c8f895 --- /dev/null +++ b/tests/source/non-lifetime-binders.rs @@ -0,0 +1,10 @@ +fn main() where for<'a, T: Sized + 'a, const C: usize> [&'a T; C]: Sized { + let x = for + || {}; + + let y: dyn + for Into; + + let z: for + fn(T); +} diff --git a/tests/target/issue_5721.rs b/tests/target/issue_5721.rs new file mode 100644 index 000000000000..d073b09cac2b --- /dev/null +++ b/tests/target/issue_5721.rs @@ -0,0 +1,10 @@ +#![feature(non_lifetime_binders)] +#![allow(incomplete_features)] + +trait Other {} + +trait Trait +where + for U: Other, +{ +} diff --git a/tests/target/non-lifetime-binders.rs b/tests/target/non-lifetime-binders.rs new file mode 100644 index 000000000000..ca6941a0c00b --- /dev/null +++ b/tests/target/non-lifetime-binders.rs @@ -0,0 +1,10 @@ +fn main() +where + for<'a, T: Sized + 'a, const C: usize> [&'a T; C]: Sized, +{ + let x = for || {}; + + let y: dyn for Into; + + let z: for fn(T); +} From a9ae746267e8ad4c2f4aaa7c2cf16d88d7886192 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Tue, 18 Jul 2023 17:31:22 +0000 Subject: [PATCH 311/401] misc code cleanup --- src/items.rs | 358 +++++++++++++++++++++++++-------------------------- 1 file changed, 178 insertions(+), 180 deletions(-) diff --git a/src/items.rs b/src/items.rs index d5bc38303e00..fd4e08ed403b 100644 --- a/src/items.rs +++ b/src/items.rs @@ -306,22 +306,20 @@ impl<'a> FnSig<'a> { defaultness: ast::Defaultness, ) -> FnSig<'a> { match *fn_kind { - visit::FnKind::Fn(fn_ctxt, _, fn_sig, vis, generics, _) => match fn_ctxt { - visit::FnCtxt::Assoc(..) => { - let mut fn_sig = FnSig::from_method_sig(fn_sig, generics, vis); - fn_sig.defaultness = defaultness; - fn_sig - } - _ => FnSig { - decl, - generics, - ext: fn_sig.header.ext, - constness: fn_sig.header.constness, - is_async: Cow::Borrowed(&fn_sig.header.asyncness), - defaultness, - unsafety: fn_sig.header.unsafety, - visibility: vis, - }, + visit::FnKind::Fn(visit::FnCtxt::Assoc(..), _, fn_sig, vis, generics, _) => { + let mut fn_sig = FnSig::from_method_sig(fn_sig, generics, vis); + fn_sig.defaultness = defaultness; + fn_sig + } + visit::FnKind::Fn(_, _, fn_sig, vis, generics, _) => FnSig { + decl, + generics, + ext: fn_sig.header.ext, + constness: fn_sig.header.constness, + is_async: Cow::Borrowed(&fn_sig.header.asyncness), + defaultness, + unsafety: fn_sig.header.unsafety, + visibility: vis, }, _ => unreachable!(), } @@ -1118,172 +1116,172 @@ pub(crate) fn format_trait( item: &ast::Item, offset: Indent, ) -> Option { - if let ast::ItemKind::Trait(trait_kind) = &item.kind { - let ast::Trait { - is_auto, - unsafety, - ref generics, - ref bounds, - ref items, - } = **trait_kind; - let mut result = String::with_capacity(128); - let header = format!( - "{}{}{}trait ", - format_visibility(context, &item.vis), - format_unsafety(unsafety), - format_auto(is_auto), - ); - result.push_str(&header); - - let body_lo = context.snippet_provider.span_after(item.span, "{"); - - let shape = Shape::indented(offset, context.config).offset_left(result.len())?; - let generics_str = - rewrite_generics(context, rewrite_ident(context, item.ident), generics, shape)?; - result.push_str(&generics_str); - - // FIXME(#2055): rustfmt fails to format when there are comments between trait bounds. - if !bounds.is_empty() { - let ident_hi = context - .snippet_provider - .span_after(item.span, item.ident.as_str()); - let bound_hi = bounds.last().unwrap().span().hi(); - let snippet = context.snippet(mk_sp(ident_hi, bound_hi)); - if contains_comment(snippet) { - return None; - } - - result = rewrite_assign_rhs_with( - context, - result + ":", - bounds, - shape, - &RhsAssignKind::Bounds, - RhsTactics::ForceNextLineWithoutIndent, - )?; - } - - // Rewrite where-clause. - if !generics.where_clause.predicates.is_empty() { - let where_on_new_line = context.config.indent_style() != IndentStyle::Block; - - let where_budget = context.budget(last_line_width(&result)); - let pos_before_where = if bounds.is_empty() { - generics.where_clause.span.lo() - } else { - bounds[bounds.len() - 1].span().hi() - }; - let option = WhereClauseOption::snuggled(&generics_str); - let where_clause_str = rewrite_where_clause( - context, - &generics.where_clause.predicates, - generics.where_clause.span, - context.config.brace_style(), - Shape::legacy(where_budget, offset.block_only()), - where_on_new_line, - "{", - None, - pos_before_where, - option, - )?; - // If the where-clause cannot fit on the same line, - // put the where-clause on a new line - if !where_clause_str.contains('\n') - && last_line_width(&result) + where_clause_str.len() + offset.width() - > context.config.comment_width() - { - let width = offset.block_indent + context.config.tab_spaces() - 1; - let where_indent = Indent::new(0, width); - result.push_str(&where_indent.to_string_with_newline(context.config)); - } - result.push_str(&where_clause_str); - } else { - let item_snippet = context.snippet(item.span); - if let Some(lo) = item_snippet.find('/') { - // 1 = `{` - let comment_hi = if generics.params.len() > 0 { - generics.span.lo() - BytePos(1) - } else { - body_lo - BytePos(1) - }; - let comment_lo = item.span.lo() + BytePos(lo as u32); - if comment_lo < comment_hi { - match recover_missing_comment_in_span( - mk_sp(comment_lo, comment_hi), - Shape::indented(offset, context.config), - context, - last_line_width(&result), - ) { - Some(ref missing_comment) if !missing_comment.is_empty() => { - result.push_str(missing_comment); - } - _ => (), - } - } - } - } - - let block_span = mk_sp(generics.where_clause.span.hi(), item.span.hi()); - let snippet = context.snippet(block_span); - let open_pos = snippet.find_uncommented("{")? + 1; - - match context.config.brace_style() { - _ if last_line_contains_single_line_comment(&result) - || last_line_width(&result) + 2 > context.budget(offset.width()) => - { - result.push_str(&offset.to_string_with_newline(context.config)); - } - _ if context.config.empty_item_single_line() - && items.is_empty() - && !result.contains('\n') - && !contains_comment(&snippet[open_pos..]) => - { - result.push_str(" {}"); - return Some(result); - } - BraceStyle::AlwaysNextLine => { - result.push_str(&offset.to_string_with_newline(context.config)); - } - BraceStyle::PreferSameLine => result.push(' '), - BraceStyle::SameLineWhere => { - if result.contains('\n') - || (!generics.where_clause.predicates.is_empty() && !items.is_empty()) - { - result.push_str(&offset.to_string_with_newline(context.config)); - } else { - result.push(' '); - } - } - } - result.push('{'); - - let outer_indent_str = offset.block_only().to_string_with_newline(context.config); - - if !items.is_empty() || contains_comment(&snippet[open_pos..]) { - let mut visitor = FmtVisitor::from_context(context); - visitor.block_indent = offset.block_only().block_indent(context.config); - visitor.last_pos = block_span.lo() + BytePos(open_pos as u32); - - for item in items { - visitor.visit_trait_item(item); - } - - visitor.format_missing(item.span.hi() - BytePos(1)); - - let inner_indent_str = visitor.block_indent.to_string_with_newline(context.config); - - result.push_str(&inner_indent_str); - result.push_str(visitor.buffer.trim()); - result.push_str(&outer_indent_str); - } else if result.contains('\n') { - result.push_str(&outer_indent_str); - } - - result.push('}'); - Some(result) - } else { + let ast::ItemKind::Trait(trait_kind) = &item.kind else { unreachable!(); + }; + let ast::Trait { + is_auto, + unsafety, + ref generics, + ref bounds, + ref items, + } = **trait_kind; + + let mut result = String::with_capacity(128); + let header = format!( + "{}{}{}trait ", + format_visibility(context, &item.vis), + format_unsafety(unsafety), + format_auto(is_auto), + ); + result.push_str(&header); + + let body_lo = context.snippet_provider.span_after(item.span, "{"); + + let shape = Shape::indented(offset, context.config).offset_left(result.len())?; + let generics_str = + rewrite_generics(context, rewrite_ident(context, item.ident), generics, shape)?; + result.push_str(&generics_str); + + // FIXME(#2055): rustfmt fails to format when there are comments between trait bounds. + if !bounds.is_empty() { + let ident_hi = context + .snippet_provider + .span_after(item.span, item.ident.as_str()); + let bound_hi = bounds.last().unwrap().span().hi(); + let snippet = context.snippet(mk_sp(ident_hi, bound_hi)); + if contains_comment(snippet) { + return None; + } + + result = rewrite_assign_rhs_with( + context, + result + ":", + bounds, + shape, + &RhsAssignKind::Bounds, + RhsTactics::ForceNextLineWithoutIndent, + )?; } + + // Rewrite where-clause. + if !generics.where_clause.predicates.is_empty() { + let where_on_new_line = context.config.indent_style() != IndentStyle::Block; + + let where_budget = context.budget(last_line_width(&result)); + let pos_before_where = if bounds.is_empty() { + generics.where_clause.span.lo() + } else { + bounds[bounds.len() - 1].span().hi() + }; + let option = WhereClauseOption::snuggled(&generics_str); + let where_clause_str = rewrite_where_clause( + context, + &generics.where_clause.predicates, + generics.where_clause.span, + context.config.brace_style(), + Shape::legacy(where_budget, offset.block_only()), + where_on_new_line, + "{", + None, + pos_before_where, + option, + )?; + // If the where-clause cannot fit on the same line, + // put the where-clause on a new line + if !where_clause_str.contains('\n') + && last_line_width(&result) + where_clause_str.len() + offset.width() + > context.config.comment_width() + { + let width = offset.block_indent + context.config.tab_spaces() - 1; + let where_indent = Indent::new(0, width); + result.push_str(&where_indent.to_string_with_newline(context.config)); + } + result.push_str(&where_clause_str); + } else { + let item_snippet = context.snippet(item.span); + if let Some(lo) = item_snippet.find('/') { + // 1 = `{` + let comment_hi = if generics.params.len() > 0 { + generics.span.lo() - BytePos(1) + } else { + body_lo - BytePos(1) + }; + let comment_lo = item.span.lo() + BytePos(lo as u32); + if comment_lo < comment_hi { + match recover_missing_comment_in_span( + mk_sp(comment_lo, comment_hi), + Shape::indented(offset, context.config), + context, + last_line_width(&result), + ) { + Some(ref missing_comment) if !missing_comment.is_empty() => { + result.push_str(missing_comment); + } + _ => (), + } + } + } + } + + let block_span = mk_sp(generics.where_clause.span.hi(), item.span.hi()); + let snippet = context.snippet(block_span); + let open_pos = snippet.find_uncommented("{")? + 1; + + match context.config.brace_style() { + _ if last_line_contains_single_line_comment(&result) + || last_line_width(&result) + 2 > context.budget(offset.width()) => + { + result.push_str(&offset.to_string_with_newline(context.config)); + } + _ if context.config.empty_item_single_line() + && items.is_empty() + && !result.contains('\n') + && !contains_comment(&snippet[open_pos..]) => + { + result.push_str(" {}"); + return Some(result); + } + BraceStyle::AlwaysNextLine => { + result.push_str(&offset.to_string_with_newline(context.config)); + } + BraceStyle::PreferSameLine => result.push(' '), + BraceStyle::SameLineWhere => { + if result.contains('\n') + || (!generics.where_clause.predicates.is_empty() && !items.is_empty()) + { + result.push_str(&offset.to_string_with_newline(context.config)); + } else { + result.push(' '); + } + } + } + result.push('{'); + + let outer_indent_str = offset.block_only().to_string_with_newline(context.config); + + if !items.is_empty() || contains_comment(&snippet[open_pos..]) { + let mut visitor = FmtVisitor::from_context(context); + visitor.block_indent = offset.block_only().block_indent(context.config); + visitor.last_pos = block_span.lo() + BytePos(open_pos as u32); + + for item in items { + visitor.visit_trait_item(item); + } + + visitor.format_missing(item.span.hi() - BytePos(1)); + + let inner_indent_str = visitor.block_indent.to_string_with_newline(context.config); + + result.push_str(&inner_indent_str); + result.push_str(visitor.buffer.trim()); + result.push_str(&outer_indent_str); + } else if result.contains('\n') { + result.push_str(&outer_indent_str); + } + + result.push('}'); + Some(result) } pub(crate) struct TraitAliasBounds<'a> { From cdfa2f86b729b0bbba8661bca075a5974cec83bf Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Fri, 30 Sep 2022 12:41:44 -0400 Subject: [PATCH 312/401] Handle `dyn*` syntax when rewriting `ast::TyKind::TraitObject` Resolves 5542 Prior to rust-lang/rust#101212 the `ast::TraitObjectSyntax` enum only had two variants `Dyn` and `None`. The PR that introduced the `dyn*` syntax added a new variant `DynStar`, but did not update the formatting rules to account for the new variant. Now the new `DynStar` variant is properly handled and is no longer removed by rustfmt. --- src/types.rs | 14 ++++++-------- tests/target/issue_5542.rs | 10 ++++++++++ 2 files changed, 16 insertions(+), 8 deletions(-) create mode 100644 tests/target/issue_5542.rs diff --git a/src/types.rs b/src/types.rs index bb236a386e4e..aef85598f06f 100644 --- a/src/types.rs +++ b/src/types.rs @@ -683,9 +683,11 @@ impl Rewrite for ast::Ty { match self.kind { ast::TyKind::TraitObject(ref bounds, tobj_syntax) => { // we have to consider 'dyn' keyword is used or not!!! - let is_dyn = tobj_syntax == ast::TraitObjectSyntax::Dyn; - // 4 is length of 'dyn ' - let shape = if is_dyn { shape.offset_left(4)? } else { shape }; + let (shape, prefix) = match tobj_syntax { + ast::TraitObjectSyntax::Dyn => (shape.offset_left(4)?, "dyn "), + ast::TraitObjectSyntax::DynStar => (shape.offset_left(5)?, "dyn* "), + ast::TraitObjectSyntax::None => (shape, ""), + }; let mut res = bounds.rewrite(context, shape)?; // We may have falsely removed a trailing `+` inside macro call. if context.inside_macro() && bounds.len() == 1 { @@ -693,11 +695,7 @@ impl Rewrite for ast::Ty { res.push('+'); } } - if is_dyn { - Some(format!("dyn {}", res)) - } else { - Some(res) - } + Some(format!("{}{}", prefix, res)) } ast::TyKind::Ptr(ref mt) => { let prefix = match mt.mutbl { diff --git a/tests/target/issue_5542.rs b/tests/target/issue_5542.rs new file mode 100644 index 000000000000..730bb7b681ae --- /dev/null +++ b/tests/target/issue_5542.rs @@ -0,0 +1,10 @@ +#![feature(dyn_star)] +#![allow(incomplete_features)] + +use core::fmt::Debug; + +fn main() { + let i = 42; + let dyn_i = i as dyn* Debug; + dbg!(dyn_i); +} From f122a3328d6e32bb8a8f4f3a5cc88e5696b6e43b Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 25 Jul 2023 13:25:38 +0000 Subject: [PATCH 313/401] Use builder pattern instead of lots of arguments for `EmitterWriter::new` --- src/parse/session.rs | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/parse/session.rs b/src/parse/session.rs index aa75b477473d..945e3e42fddf 100644 --- a/src/parse/session.rs +++ b/src/parse/session.rs @@ -4,7 +4,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use rustc_data_structures::sync::{Lrc, Send}; use rustc_errors::emitter::{Emitter, EmitterWriter}; use rustc_errors::translation::Translate; -use rustc_errors::{ColorConfig, Diagnostic, Handler, Level as DiagnosticLevel, TerminalUrl}; +use rustc_errors::{ColorConfig, Diagnostic, Handler, Level as DiagnosticLevel}; use rustc_session::parse::ParseSess as RawParseSess; use rustc_span::{ source_map::{FilePathMapping, SourceMap}, @@ -139,18 +139,7 @@ fn default_handler( rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), false, ); - Box::new(EmitterWriter::stderr( - emit_color, - Some(source_map.clone()), - None, - fallback_bundle, - false, - false, - None, - false, - false, - TerminalUrl::No, - )) + Box::new(EmitterWriter::stderr(emit_color, fallback_bundle).sm(Some(source_map.clone()))) }; Handler::with_emitter(Box::new(SilentOnIgnoredFilesEmitter { has_non_ignorable_parser_errors: false, From a72613be50f8afa39c2d46f732252463dc9cc14f Mon Sep 17 00:00:00 2001 From: Catherine <114838443+Centri3@users.noreply.github.com> Date: Tue, 18 Jul 2023 09:41:56 -0500 Subject: [PATCH 314/401] Add parenthesis around closure method call --- src/chains.rs | 25 ++++++++++++++++++++----- tests/source/issue-4808.rs | 13 +++++++++++++ tests/target/issue-4808.rs | 13 +++++++++++++ 3 files changed, 46 insertions(+), 5 deletions(-) create mode 100644 tests/source/issue-4808.rs create mode 100644 tests/target/issue-4808.rs diff --git a/src/chains.rs b/src/chains.rs index bf09d817ed1b..d4876389c861 100644 --- a/src/chains.rs +++ b/src/chains.rs @@ -253,11 +253,7 @@ impl ChainItemKind { return ( ChainItemKind::Parent { expr: expr.clone(), - parens: is_method_call_receiver - && matches!( - &expr.kind, - ast::ExprKind::Lit(lit) if crate::expr::lit_ends_in_dot(lit) - ), + parens: is_method_call_receiver && should_add_parens(expr), }, expr.span, ); @@ -982,3 +978,22 @@ fn trim_tries(s: &str) -> String { } result } + +/// Whether a method call's receiver needs parenthesis, like +/// ```rust,ignore +/// || .. .method(); +/// || 1.. .method(); +/// 1. .method(); +/// ``` +/// Which all need parenthesis or a space before `.method()`. +fn should_add_parens(expr: &ast::Expr) -> bool { + match expr.kind { + ast::ExprKind::Lit(ref lit) => crate::expr::lit_ends_in_dot(lit), + ast::ExprKind::Closure(ref cl) => match cl.body.kind { + ast::ExprKind::Range(_, _, ast::RangeLimits::HalfOpen) => true, + ast::ExprKind::Lit(ref lit) => crate::expr::lit_ends_in_dot(lit), + _ => false, + }, + _ => false, + } +} diff --git a/tests/source/issue-4808.rs b/tests/source/issue-4808.rs new file mode 100644 index 000000000000..93076edcdcef --- /dev/null +++ b/tests/source/issue-4808.rs @@ -0,0 +1,13 @@ +trait Trait { + fn method(&self) {} +} + +impl T, T> Trait for F {} + +impl Trait for f32 {} + +fn main() { + || 10. .method(); + || .. .method(); + || 1.. .method(); +} diff --git a/tests/target/issue-4808.rs b/tests/target/issue-4808.rs new file mode 100644 index 000000000000..cdef53a1bfca --- /dev/null +++ b/tests/target/issue-4808.rs @@ -0,0 +1,13 @@ +trait Trait { + fn method(&self) {} +} + +impl T, T> Trait for F {} + +impl Trait for f32 {} + +fn main() { + || (10.).method(); + (|| ..).method(); + (|| 1..).method(); +} From e42bc9ec66f32782fa167712218c230d72c86297 Mon Sep 17 00:00:00 2001 From: Mu001999 Date: Thu, 3 Aug 2023 00:13:41 +0800 Subject: [PATCH 315/401] Fix rustfmt dep --- src/parse/macros/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parse/macros/mod.rs b/src/parse/macros/mod.rs index 67f3985926e2..7a802f7a88e0 100644 --- a/src/parse/macros/mod.rs +++ b/src/parse/macros/mod.rs @@ -56,7 +56,7 @@ fn parse_macro_arg<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option { ); parse_macro_arg!( Pat, - |parser: &mut rustc_parse::parser::Parser<'b>| parser.parse_pat_no_top_alt(None), + |parser: &mut rustc_parse::parser::Parser<'b>| parser.parse_pat_no_top_alt(None, None), |x: ptr::P| Some(x) ); // `parse_item` returns `Option>`. From d165d4ad3840ae14ddd3f62597b5a2a01adc1e27 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 2 Aug 2023 09:56:26 +1000 Subject: [PATCH 316/401] Remove `MacDelimiter`. It's the same as `Delimiter`, minus the `Invisible` variant. I'm generally in favour of using types to make impossible states unrepresentable, but this one feels very low-value, and the conversions between the two types are annoying and confusing. Look at the change in `src/tools/rustfmt/src/expr.rs` for an example: the old code converted from `MacDelimiter` to `Delimiter` and back again, for no good reason. This suggests the author was confused about the types. --- src/expr.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/expr.rs b/src/expr.rs index 5b1b4fbd491c..739afb4e0ac0 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -1382,12 +1382,8 @@ pub(crate) fn can_be_overflowed_expr( || (context.use_block_indent() && args_len == 1) } ast::ExprKind::MacCall(ref mac) => { - match ( - rustc_ast::ast::MacDelimiter::from_token(mac.args.delim.to_token()), - context.config.overflow_delimited_expr(), - ) { - (Some(ast::MacDelimiter::Bracket), true) - | (Some(ast::MacDelimiter::Brace), true) => true, + match (mac.args.delim, context.config.overflow_delimited_expr()) { + (Delimiter::Bracket, true) | (Delimiter::Brace, true) => true, _ => context.use_block_indent() && args_len == 1, } } From e83a7cadd23c5b5530f7c7f53ef5004c9fd8fe79 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Thu, 3 Aug 2023 21:43:17 +0200 Subject: [PATCH 317/401] Improve spans for indexing expressions Indexing is similar to method calls in having an arbitrary left-hand-side and then something on the right, which is the main part of the expression. Method calls already have a span for that right part, but indexing does not. This means that long method chains that use indexing have really bad spans, especially when the indexing panics and that span in coverted into a panic location. This does the same thing as method calls for the AST and HIR, storing an extra span which is then put into the `fn_span` field in THIR. --- src/expr.rs | 4 ++-- src/matches.rs | 2 +- src/utils.rs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/expr.rs b/src/expr.rs index 739afb4e0ac0..c3c07f310bf6 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -256,7 +256,7 @@ pub(crate) fn format_expr( shape, SeparatorPlace::Back, ), - ast::ExprKind::Index(ref expr, ref index) => { + ast::ExprKind::Index(ref expr, ref index, _) => { rewrite_index(&**expr, &**index, context, shape) } ast::ExprKind::Repeat(ref expr, ref repeats) => rewrite_pair( @@ -1342,7 +1342,7 @@ pub(crate) fn is_simple_expr(expr: &ast::Expr) -> bool { | ast::ExprKind::Field(ref expr, _) | ast::ExprKind::Try(ref expr) | ast::ExprKind::Unary(_, ref expr) => is_simple_expr(expr), - ast::ExprKind::Index(ref lhs, ref rhs) => is_simple_expr(lhs) && is_simple_expr(rhs), + ast::ExprKind::Index(ref lhs, ref rhs, _) => is_simple_expr(lhs) && is_simple_expr(rhs), ast::ExprKind::Repeat(ref lhs, ref rhs) => { is_simple_expr(lhs) && is_simple_expr(&*rhs.value) } diff --git a/src/matches.rs b/src/matches.rs index aac5e59b8603..4c37116f120b 100644 --- a/src/matches.rs +++ b/src/matches.rs @@ -594,7 +594,7 @@ fn can_flatten_block_around_this(body: &ast::Expr) -> bool { ast::ExprKind::AddrOf(_, _, ref expr) | ast::ExprKind::Try(ref expr) | ast::ExprKind::Unary(_, ref expr) - | ast::ExprKind::Index(ref expr, _) + | ast::ExprKind::Index(ref expr, _, _) | ast::ExprKind::Cast(ref expr, _) => can_flatten_block_around_this(expr), _ => false, } diff --git a/src/utils.rs b/src/utils.rs index 890a05b8c825..4fc5a9b68967 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -441,7 +441,7 @@ pub(crate) fn left_most_sub_expr(e: &ast::Expr) -> &ast::Expr { | ast::ExprKind::Assign(ref e, _, _) | ast::ExprKind::AssignOp(_, ref e, _) | ast::ExprKind::Field(ref e, _) - | ast::ExprKind::Index(ref e, _) + | ast::ExprKind::Index(ref e, _, _) | ast::ExprKind::Range(Some(ref e), _, _) | ast::ExprKind::Try(ref e) => left_most_sub_expr(e), _ => e, @@ -479,7 +479,7 @@ pub(crate) fn is_block_expr(context: &RewriteContext<'_>, expr: &ast::Expr, repr | ast::ExprKind::Match(..) => repr.contains('\n'), ast::ExprKind::Paren(ref expr) | ast::ExprKind::Binary(_, _, ref expr) - | ast::ExprKind::Index(_, ref expr) + | ast::ExprKind::Index(_, ref expr, _) | ast::ExprKind::Unary(_, ref expr) | ast::ExprKind::Try(ref expr) | ast::ExprKind::Yield(Some(ref expr)) => is_block_expr(context, expr, repr), From 36af403e6b4968b622975888a22adab36f75b64e Mon Sep 17 00:00:00 2001 From: Richard Date: Sat, 12 Aug 2023 17:05:04 -0500 Subject: [PATCH 318/401] improve the --file-lines help (#5846) * improve the file-lines example * fix help order --- src/bin/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/main.rs b/src/bin/main.rs index 2ff67d27e1b0..97d31852d635 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -422,7 +422,7 @@ are 1-based and inclusive of both end points. Specifying an empty array will result in no files being formatted. For example, ``` -rustfmt --file-lines '[ +rustfmt src/lib.rs src/foo.rs --file-lines '[ {{\"file\":\"src/lib.rs\",\"range\":[7,13]}}, {{\"file\":\"src/lib.rs\",\"range\":[21,29]}}, {{\"file\":\"src/foo.rs\",\"range\":[10,11]}}, From 9f58224123f8185edca08035b74d741fbc0620fb Mon Sep 17 00:00:00 2001 From: Charles Lew Date: Sat, 29 Jul 2023 09:10:09 +0800 Subject: [PATCH 319/401] Update Unicode data to 15.0 --- Cargo.lock | 24 ++++++++++++------------ Cargo.toml | 2 +- src/string.rs | 4 ++-- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e867f2cb840c..bcac61ef3428 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -588,9 +588,9 @@ dependencies = [ "toml", "tracing", "tracing-subscriber", + "unicode-properties", "unicode-segmentation", "unicode-width", - "unicode_categories", ] [[package]] @@ -852,22 +852,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[package]] -name = "unicode-segmentation" -version = "1.9.0" +name = "unicode-properties" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" +checksum = "c7f91c8b21fbbaa18853c3d0801c78f4fc94cdb976699bb03e832e75f7fd22f0" + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" [[package]] name = "unicode-width" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" - -[[package]] -name = "unicode_categories" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" [[package]] name = "utf8parse" diff --git a/Cargo.toml b/Cargo.toml index f4c4bab37cb5..5a800e95a0dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,7 +54,7 @@ tracing = "0.1.37" tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } unicode-segmentation = "1.9" unicode-width = "0.1" -unicode_categories = "0.1" +unicode-properties = { version = "0.1", default-features = false, features = ["general-category"] } rustfmt-config_proc_macro = { version = "0.3", path = "config_proc_macro" } diff --git a/src/string.rs b/src/string.rs index 78b72a50cb2f..cb666fff6951 100644 --- a/src/string.rs +++ b/src/string.rs @@ -1,7 +1,7 @@ // Format string literals. use regex::Regex; -use unicode_categories::UnicodeCategories; +use unicode_properties::{GeneralCategory, UnicodeGeneralCategory}; use unicode_segmentation::UnicodeSegmentation; use crate::config::Config; @@ -366,7 +366,7 @@ fn is_whitespace(grapheme: &str) -> bool { fn is_punctuation(grapheme: &str) -> bool { grapheme .chars() - .all(UnicodeCategories::is_punctuation_other) + .all(|c| c.general_category() == GeneralCategory::OtherPunctuation) } fn graphemes_width(graphemes: &[&str]) -> usize { From 0d4c1431f58ca10300a323cb4a3b7e472da11551 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Fri, 11 Aug 2023 09:02:04 -0400 Subject: [PATCH 320/401] Improve formatting of empty macro_rules! definitions Fixes 5882 --- src/macros.rs | 24 ++++++++++++++++++++++++ tests/source/issue_5882.rs | 7 +++++++ tests/target/issue_5882.rs | 7 +++++++ 3 files changed, 38 insertions(+) create mode 100644 tests/source/issue_5882.rs create mode 100644 tests/target/issue_5882.rs diff --git a/src/macros.rs b/src/macros.rs index e9a298a27693..88d26358b872 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -379,6 +379,23 @@ fn handle_vec_semi( } } +fn rewrite_empty_macro_def_body( + context: &RewriteContext<'_>, + span: Span, + shape: Shape, +) -> Option { + // Create an empty, dummy `ast::Block` representing an empty macro body + let block = ast::Block { + stmts: vec![].into(), + id: rustc_ast::node_id::DUMMY_NODE_ID, + rules: ast::BlockCheckMode::Default, + span: span, + tokens: None, + could_be_bare_literal: false, + }; + block.rewrite(context, shape) +} + pub(crate) fn rewrite_macro_def( context: &RewriteContext<'_>, shape: Shape, @@ -419,6 +436,13 @@ pub(crate) fn rewrite_macro_def( shape }; + if parsed_def.branches.len() == 0 { + let lo = context.snippet_provider.span_before(span, "{"); + result += " "; + result += &rewrite_empty_macro_def_body(context, span.with_lo(lo), shape)?; + return Some(result); + } + let branch_items = itemize_list( context.snippet_provider, parsed_def.branches.iter(), diff --git a/tests/source/issue_5882.rs b/tests/source/issue_5882.rs new file mode 100644 index 000000000000..e36f9965438b --- /dev/null +++ b/tests/source/issue_5882.rs @@ -0,0 +1,7 @@ +macro_rules!foo{} +macro_rules!bar{/*comment*/} +macro_rules!baz{//comment +} +macro_rules!foobar{ +//comment +} diff --git a/tests/target/issue_5882.rs b/tests/target/issue_5882.rs new file mode 100644 index 000000000000..565fb434a6a7 --- /dev/null +++ b/tests/target/issue_5882.rs @@ -0,0 +1,7 @@ +macro_rules! foo {} +macro_rules! bar { /*comment*/ } +macro_rules! baz { //comment +} +macro_rules! foobar { + //comment +} From 177ef66947e0faef7bac3eaeaa294fc7ed3413d8 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Wed, 2 Aug 2023 09:32:13 -0400 Subject: [PATCH 321/401] Fix building rustfmt with `--features generic-simd` Bumping bytecount from `0.6.2` -> `0.6.3` allows rustfmt to properly build when using the `generic-simd` feature. --- Cargo.lock | 8 ++++---- Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bcac61ef3428..2cefec09589c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -90,9 +90,9 @@ dependencies = [ [[package]] name = "bytecount" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72feb31ffc86498dacdbd0fcebb56138e7177a8cc5cea4516031d15ae85a742e" +checksum = "2c676a478f63e9fa2dd5368a42f28bba0d6c560b775f38583c8bbaa7fcd67c9c" dependencies = [ "packed_simd_2", ] @@ -477,9 +477,9 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "packed_simd_2" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "defdcfef86dcc44ad208f71d9ff4ce28df6537a4e0d6b0e8e845cb8ca10059a6" +checksum = "a1914cd452d8fccd6f9db48147b29fd4ae05bea9dc5d9ad578509f72415de282" dependencies = [ "cfg-if", "libm", diff --git a/Cargo.toml b/Cargo.toml index 5a800e95a0dd..b53b455a16d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ generic-simd = ["bytecount/generic-simd"] [dependencies] annotate-snippets = { version = "0.9", features = ["color"] } anyhow = "1.0" -bytecount = "0.6" +bytecount = "0.6.3" cargo_metadata = "0.15.4" clap = { version = "4.2.1", features = ["derive"] } diff = "0.1" From 641d4f5898fd3d5a8a620d0e313d6eb7fb2e5cac Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Wed, 2 Aug 2023 10:10:15 -0400 Subject: [PATCH 322/401] Build nightly rustfmt using `--all-features` in CI Previously we were only building rustfmt with default features in CI. We recently received a report that rustfmt was unable to compile with the `generic-simd` feature, which is not enabled by default. To prevent a similar situation in the future we'll start build nightly rustfmt with all features enabled. --- ci/build_and_test.bat | 6 +++++- ci/build_and_test.sh | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/ci/build_and_test.bat b/ci/build_and_test.bat index 69dae1fff7b4..16608a4aaa73 100755 --- a/ci/build_and_test.bat +++ b/ci/build_and_test.bat @@ -6,7 +6,11 @@ rustc -Vv || exit /b 1 cargo -V || exit /b 1 :: Build and test main crate -cargo build --locked || exit /b 1 +if "%CFG_RELEASE_CHANNEL%"=="nightly" ( + cargo build --locked --all-features || exit /b 1 +) else ( + cargo build --locked || exit /b 1 +) cargo test || exit /b 1 :: Build and test other crates diff --git a/ci/build_and_test.sh b/ci/build_and_test.sh index 94991853263c..207da362fd65 100755 --- a/ci/build_and_test.sh +++ b/ci/build_and_test.sh @@ -10,7 +10,11 @@ rustc -Vv cargo -V # Build and test main crate -cargo build --locked +if [ "$CFG_RELEASE_CHANNEL" == "nightly" ]; then + cargo build --locked --all-features +else + cargo build --locked +fi cargo test # Build and test other crates From d8aeabaee103bc1788ee237c6bdcd4c345923963 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Thu, 26 Jan 2023 13:50:02 -0500 Subject: [PATCH 323/401] include block label length when calculating pat_shape of a match arm Previously we alwasy assumed the match arm pattern would have `shape.width` - 5 characters of space to work with. Now if we're formatting a block expression with a label we'll take the label into account. --- src/matches.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/matches.rs b/src/matches.rs index aac5e59b8603..fe9e7836ba71 100644 --- a/src/matches.rs +++ b/src/matches.rs @@ -246,8 +246,18 @@ fn rewrite_match_arm( }; // Patterns - // 5 = ` => {` - let pat_shape = shape.sub_width(5)?.offset_left(pipe_offset)?; + let pat_shape = match &arm.body.kind { + ast::ExprKind::Block(_, Some(label)) => { + // Some block with a label ` => 'label: {` + // 7 = ` => : {` + let label_len = label.ident.as_str().len(); + shape.sub_width(7 + label_len)?.offset_left(pipe_offset)? + } + _ => { + // 5 = ` => {` + shape.sub_width(5)?.offset_left(pipe_offset)? + } + }; let pats_str = arm.pat.rewrite(context, pat_shape)?; // Guard From e86c2ba5459679cf264fd2f037e246f1924686ac Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Thu, 26 Jan 2023 13:50:18 -0500 Subject: [PATCH 324/401] Don't flatten blocks that have labels --- src/matches.rs | 5 +++-- tests/target/issue_5676.rs | 8 ++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 tests/target/issue_5676.rs diff --git a/src/matches.rs b/src/matches.rs index fe9e7836ba71..a7677c3a35cd 100644 --- a/src/matches.rs +++ b/src/matches.rs @@ -306,8 +306,9 @@ fn block_can_be_flattened<'a>( expr: &'a ast::Expr, ) -> Option<&'a ast::Block> { match expr.kind { - ast::ExprKind::Block(ref block, _) - if !is_unsafe_block(block) + ast::ExprKind::Block(ref block, label) + if label.is_none() + && !is_unsafe_block(block) && !context.inside_macro() && is_simple_block(context, block, Some(&expr.attrs)) && !stmt_is_expr_mac(&block.stmts[0]) => diff --git a/tests/target/issue_5676.rs b/tests/target/issue_5676.rs new file mode 100644 index 000000000000..258771105453 --- /dev/null +++ b/tests/target/issue_5676.rs @@ -0,0 +1,8 @@ +fn main() { + match true { + true => 'a: { + break 'a; + } + _ => (), + } +} From b069aac44ddfdb70d55d9ae40695be44515e5bb0 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Sun, 13 Aug 2023 21:14:31 +0200 Subject: [PATCH 325/401] Inline format arguments for easier reading (#5881) * Inline format arguments for easier reading Code becomes shorter and often easier to read when format args are inlined. Note that I skipped the mixed cases to make it more straightforward (could be done separatelly). Also, there are two FIXME comments - for some reasons inlining makes format string exceed 100 char line width and crash. ``` cargo clippy --workspace --allow-dirty --fix --benches --tests --bins -- -A clippy::all -W clippy::uninlined_format_args ``` * address feedback --- src/attr.rs | 8 ++--- src/attr/doc_comment.rs | 6 ++-- src/bin/main.rs | 19 +++++------ src/cargo-fmt/main.rs | 9 +++-- src/chains.rs | 2 +- src/closures.rs | 7 ++-- src/comment.rs | 35 ++++++------------- src/config/config_type.rs | 10 +++--- src/config/file_lines.rs | 2 +- src/config/macro_names.rs | 2 +- src/config/mod.rs | 15 ++++---- src/config/options.rs | 2 +- src/emitter.rs | 2 +- src/emitter/checkstyle.rs | 4 +-- src/emitter/checkstyle/xml.rs | 2 +- src/emitter/diff.rs | 6 ++-- src/emitter/json.rs | 6 ++-- src/emitter/stdout.rs | 4 +-- src/expr.rs | 57 +++++++++++++++---------------- src/format-diff/main.rs | 8 ++--- src/formatting.rs | 2 +- src/git-rustfmt/main.rs | 4 +-- src/imports.rs | 20 +++++------ src/items.rs | 40 +++++++++++----------- src/macros.rs | 32 ++++++++--------- src/matches.rs | 6 ++-- src/pairs.rs | 4 +-- src/parse/session.rs | 3 +- src/patterns.rs | 8 ++--- src/rustfmt_diff.rs | 17 +++++---- src/skip.rs | 2 +- src/source_file.rs | 2 +- src/test/configuration_snippet.rs | 10 +++--- src/test/mod.rs | 22 +++++------- src/types.rs | 16 ++++----- src/utils.rs | 4 +-- tests/cargo-fmt/main.rs | 2 +- tests/rustfmt/main.rs | 6 ++-- 38 files changed, 183 insertions(+), 223 deletions(-) diff --git a/src/attr.rs b/src/attr.rs index 22e45082a9f7..4d83547d664e 100644 --- a/src/attr.rs +++ b/src/attr.rs @@ -308,7 +308,7 @@ impl Rewrite for ast::MetaItem { // See #2479 for example. let value = rewrite_literal(context, lit.as_token_lit(), lit.span, lit_shape) .unwrap_or_else(|| context.snippet(lit.span).to_owned()); - format!("{} = {}", path, value) + format!("{path} = {value}") } }) } @@ -342,7 +342,7 @@ impl Rewrite for ast::Attribute { let literal_str = literal.as_str(); let doc_comment_formatter = DocCommentFormatter::new(literal_str, comment_style); - let doc_comment = format!("{}", doc_comment_formatter); + let doc_comment = format!("{doc_comment_formatter}"); return rewrite_doc_comment( &doc_comment, shape.comment(context.config), @@ -406,9 +406,9 @@ impl Rewrite for [ast::Attribute] { 0, )?; let comment = if comment.is_empty() { - format!("\n{}", mlb) + format!("\n{mlb}") } else { - format!("{}{}\n{}", mla, comment, mlb) + format!("{mla}{comment}\n{mlb}") }; result.push_str(&comment); result.push_str(&shape.indent.to_string(context.config)); diff --git a/src/attr/doc_comment.rs b/src/attr/doc_comment.rs index 25c8158df8c5..f55201839b5b 100644 --- a/src/attr/doc_comment.rs +++ b/src/attr/doc_comment.rs @@ -20,15 +20,15 @@ impl Display for DocCommentFormatter<'_> { // Handle `#[doc = ""]`. if lines.peek().is_none() { - return write!(formatter, "{}", opener); + return write!(formatter, "{opener}"); } while let Some(line) = lines.next() { let is_last_line = lines.peek().is_none(); if is_last_line { - write!(formatter, "{}{}", opener, line)?; + write!(formatter, "{opener}{line}")?; } else { - writeln!(formatter, "{}{}", opener, line)?; + writeln!(formatter, "{opener}{line}")?; } } Ok(()) diff --git a/src/bin/main.rs b/src/bin/main.rs index 97d31852d635..6f5640836563 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -38,7 +38,7 @@ fn main() { let exit_code = match execute(&opts) { Ok(code) => code, Err(e) => { - eprintln!("{:#}", e); + eprintln!("{e:#}"); 1 } }; @@ -284,7 +284,7 @@ fn format_string(input: String, options: GetOptsOptions) -> Result { for f in config.file_lines().files() { match *f { FileName::Stdin => {} - _ => eprintln!("Warning: Extra file listed in file_lines option '{}'", f), + _ => eprintln!("Warning: Extra file listed in file_lines option '{f}'"), } } @@ -380,7 +380,7 @@ fn format_and_emit_report(session: &mut Session<'_, T>, input: Input) } } Err(msg) => { - eprintln!("Error writing files: {}", msg); + eprintln!("Error writing files: {msg}"); session.add_operational_error(); } } @@ -403,12 +403,9 @@ fn print_usage_to_stdout(opts: &Options, reason: &str) { let sep = if reason.is_empty() { String::new() } else { - format!("{}\n\n", reason) + format!("{reason}\n\n") }; - let msg = format!( - "{}Format Rust code\n\nusage: rustfmt [options] ...", - sep - ); + let msg = format!("{sep}Format Rust code\n\nusage: rustfmt [options] ..."); println!("{}", opts.usage(&msg)); } @@ -442,7 +439,7 @@ fn print_version() { include_str!(concat!(env!("OUT_DIR"), "/commit-info.txt")) ); - println!("rustfmt {}", version_info); + println!("rustfmt {version_info}"); } fn determine_operation(matches: &Matches) -> Result { @@ -647,9 +644,9 @@ impl GetOptsOptions { match *f { FileName::Real(ref f) if files.contains(f) => {} FileName::Real(_) => { - eprintln!("Warning: Extra file listed in file_lines option '{}'", f) + eprintln!("Warning: Extra file listed in file_lines option '{f}'") } - FileName::Stdin => eprintln!("Warning: Not a file '{}'", f), + FileName::Stdin => eprintln!("Warning: Not a file '{f}'"), } } } diff --git a/src/cargo-fmt/main.rs b/src/cargo-fmt/main.rs index bc9745275f20..3d399c12978a 100644 --- a/src/cargo-fmt/main.rs +++ b/src/cargo-fmt/main.rs @@ -200,14 +200,13 @@ fn convert_message_format_to_rustfmt_args( } "human" => Ok(()), _ => Err(format!( - "invalid --message-format value: {}. Allowed values are: short|json|human", - message_format + "invalid --message-format value: {message_format}. Allowed values are: short|json|human" )), } } fn print_usage_to_stderr(reason: &str) { - eprintln!("{}", reason); + eprintln!("{reason}"); let app = Opts::command(); app.after_help("") .write_help(&mut io::stderr()) @@ -460,7 +459,7 @@ fn get_targets_with_hitlist( let package = workspace_hitlist.iter().next().unwrap(); Err(io::Error::new( io::ErrorKind::InvalidInput, - format!("package `{}` is not a member of the workspace", package), + format!("package `{package}` is not a member of the workspace"), )) } } @@ -498,7 +497,7 @@ fn run_rustfmt( if verbosity == Verbosity::Verbose { print!("rustfmt"); - print!(" --edition {}", edition); + print!(" --edition {edition}"); fmt_args.iter().for_each(|f| print!(" {}", f)); files.iter().for_each(|f| print!(" {}", f.display())); println!(); diff --git a/src/chains.rs b/src/chains.rs index d4876389c861..ea23690caed5 100644 --- a/src/chains.rs +++ b/src/chains.rs @@ -296,7 +296,7 @@ impl Rewrite for ChainItem { rewrite_comment(comment, false, shape, context.config)? } }; - Some(format!("{}{}", rewrite, "?".repeat(self.tries))) + Some(format!("{rewrite}{}", "?".repeat(self.tries))) } } diff --git a/src/closures.rs b/src/closures.rs index cf1287529197..a09146e95924 100644 --- a/src/closures.rs +++ b/src/closures.rs @@ -175,7 +175,7 @@ fn rewrite_closure_with_block( shape, false, )?; - Some(format!("{} {}", prefix, block)) + Some(format!("{prefix} {block}")) } // Rewrite closure with a single expression without wrapping its body with block. @@ -310,10 +310,7 @@ fn rewrite_closure_fn_decl( .tactic(tactic) .preserve_newline(true); let list_str = write_list(&item_vec, &fmt)?; - let mut prefix = format!( - "{}{}{}{}{}|{}|", - binder, const_, immovable, is_async, mover, list_str - ); + let mut prefix = format!("{binder}{const_}{immovable}{is_async}{mover}|{list_str}|"); if !ret_str.is_empty() { if prefix.contains('\n') { diff --git a/src/comment.rs b/src/comment.rs index c241e12c56a9..a000d110daab 100644 --- a/src/comment.rs +++ b/src/comment.rs @@ -621,7 +621,7 @@ impl<'a> CommentRewrite<'a> { is_prev_line_multi_line: false, code_block_attr: None, item_block: None, - comment_line_separator: format!("{}{}", indent_str, line_start), + comment_line_separator: format!("{indent_str}{line_start}"), max_width, indent_str, fmt_indent: shape.indent, @@ -951,7 +951,7 @@ const RUSTFMT_CUSTOM_COMMENT_PREFIX: &str = "//#### "; fn hide_sharp_behind_comment(s: &str) -> Cow<'_, str> { let s_trimmed = s.trim(); if s_trimmed.starts_with("# ") || s_trimmed == "#" { - Cow::from(format!("{}{}", RUSTFMT_CUSTOM_COMMENT_PREFIX, s)) + Cow::from(format!("{RUSTFMT_CUSTOM_COMMENT_PREFIX}{s}")) } else { Cow::from(s) } @@ -1035,7 +1035,7 @@ pub(crate) fn recover_missing_comment_in_span( } else { Cow::from(" ") }; - Some(format!("{}{}", sep, missing_comment)) + Some(format!("{sep}{missing_comment}")) } } @@ -1832,8 +1832,7 @@ fn remove_comment_header(comment: &str) -> &str { } else { assert!( comment.starts_with("/*"), - "string '{}' is not a comment", - comment + "string '{comment}' is not a comment" ); &comment[2..comment.len() - 2] } @@ -2069,26 +2068,13 @@ fn main() { expected_line_start: &str, ) { let block = ItemizedBlock::new(test_input).unwrap(); - assert_eq!(1, block.lines.len(), "test_input: {:?}", test_input); - assert_eq!( - expected_line, &block.lines[0], - "test_input: {:?}", - test_input - ); - assert_eq!( - expected_indent, block.indent, - "test_input: {:?}", - test_input - ); - assert_eq!( - expected_opener, &block.opener, - "test_input: {:?}", - test_input - ); + assert_eq!(1, block.lines.len(), "test_input: {test_input:?}"); + assert_eq!(expected_line, &block.lines[0], "test_input: {test_input:?}"); + assert_eq!(expected_indent, block.indent, "test_input: {test_input:?}"); + assert_eq!(expected_opener, &block.opener, "test_input: {test_input:?}"); assert_eq!( expected_line_start, &block.line_start, - "test_input: {:?}", - test_input + "test_input: {test_input:?}" ); } @@ -2145,8 +2131,7 @@ fn main() { let maybe_block = ItemizedBlock::new(line); assert!( maybe_block.is_none(), - "The following line shouldn't be classified as a list item: {}", - line + "The following line shouldn't be classified as a list item: {line}" ); } } diff --git a/src/config/config_type.rs b/src/config/config_type.rs index c836b4bbb789..feb452d7235f 100644 --- a/src/config/config_type.rs +++ b/src/config/config_type.rs @@ -500,18 +500,16 @@ where // Stable with an unstable option (false, false, _) => { eprintln!( - "Warning: can't set `{} = {:?}`, unstable features are only \ - available in nightly channel.", - option_name, option_value + "Warning: can't set `{option_name} = {option_value:?}`, unstable features are only \ + available in nightly channel." ); false } // Stable with a stable option, but an unstable variant (false, true, false) => { eprintln!( - "Warning: can't set `{} = {:?}`, unstable variants are only \ - available in nightly channel.", - option_name, option_value + "Warning: can't set `{option_name} = {option_value:?}`, unstable variants are only \ + available in nightly channel." ); false } diff --git a/src/config/file_lines.rs b/src/config/file_lines.rs index e4e51a3f3b40..e33fe9bb2836 100644 --- a/src/config/file_lines.rs +++ b/src/config/file_lines.rs @@ -162,7 +162,7 @@ impl fmt::Display for FileLines { None => write!(f, "None")?, Some(map) => { for (file_name, ranges) in map.iter() { - write!(f, "{}: ", file_name)?; + write!(f, "{file_name}: ")?; write!(f, "{}\n", ranges.iter().format(", "))?; } } diff --git a/src/config/macro_names.rs b/src/config/macro_names.rs index 61658f0a212d..edfe925c2b3a 100644 --- a/src/config/macro_names.rs +++ b/src/config/macro_names.rs @@ -123,6 +123,6 @@ mod test { #[test] fn macro_names_display() { let macro_names = MacroSelectors::from_str(r#"["foo", "*", "bar"]"#).unwrap(); - assert_eq!(format!("{}", macro_names), "foo, *, bar"); + assert_eq!(format!("{macro_names}"), "foo, *, bar"); } } diff --git a/src/config/mod.rs b/src/config/mod.rs index 6f41b299e87d..7538b26522d8 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -216,8 +216,8 @@ impl Config { let required_version = self.required_version(); if version != required_version { println!( - "Error: rustfmt version ({}) doesn't match the required version ({})", - version, required_version, + "Error: rustfmt version ({version}) doesn't match the required version \ +({required_version})" ); return false; } @@ -310,20 +310,20 @@ impl Config { .ok_or_else(|| String::from("Parsed config was not table"))?; for key in table.keys() { if !Config::is_valid_name(key) { - let msg = &format!("Warning: Unknown configuration option `{}`\n", key); + let msg = &format!("Warning: Unknown configuration option `{key}`\n"); err.push_str(msg) } } match parsed.try_into() { Ok(parsed_config) => { if !err.is_empty() { - eprint!("{}", err); + eprint!("{err}"); } Ok(Config::default().fill_from_parsed_config(parsed_config, dir)) } Err(e) => { err.push_str("Error: Decoding config file failed:\n"); - err.push_str(format!("{}\n", e).as_str()); + err.push_str(format!("{e}\n").as_str()); err.push_str("Please check your config file."); Err(err) } @@ -563,10 +563,7 @@ mod test { let toml = used_options.to_toml().unwrap(); assert_eq!( toml, - format!( - "merge_derives = {}\nskip_children = {}\n", - merge_derives, skip_children, - ) + format!("merge_derives = {merge_derives}\nskip_children = {skip_children}\n",) ); } diff --git a/src/config/options.rs b/src/config/options.rs index 3aa1a4de99d6..e37f4027e4a6 100644 --- a/src/config/options.rs +++ b/src/config/options.rs @@ -243,7 +243,7 @@ pub struct WidthHeuristics { impl fmt::Display for WidthHeuristics { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", self) + write!(f, "{self:?}") } } diff --git a/src/emitter.rs b/src/emitter.rs index dc2c99a301e3..9c335314d75e 100644 --- a/src/emitter.rs +++ b/src/emitter.rs @@ -47,6 +47,6 @@ pub(crate) trait Emitter { fn ensure_real_path(filename: &FileName) -> &Path { match *filename { FileName::Real(ref path) => path, - _ => panic!("cannot format `{}` and emit to files", filename), + _ => panic!("cannot format `{filename}` and emit to files"), } } diff --git a/src/emitter/checkstyle.rs b/src/emitter/checkstyle.rs index 545b259979d9..56d6a0ed6819 100644 --- a/src/emitter/checkstyle.rs +++ b/src/emitter/checkstyle.rs @@ -43,7 +43,7 @@ pub(crate) fn output_checkstyle_file( where T: Write, { - write!(writer, r#""#, filename)?; + write!(writer, r#""#)?; for mismatch in diff { let begin_line = mismatch.line_number; let mut current_line; @@ -82,7 +82,7 @@ mod tests { ); assert_eq!( &writer[..], - format!(r#""#, file_name).as_bytes() + format!(r#""#).as_bytes() ); } diff --git a/src/emitter/checkstyle/xml.rs b/src/emitter/checkstyle/xml.rs index f251aabe8785..d1d9af708578 100644 --- a/src/emitter/checkstyle/xml.rs +++ b/src/emitter/checkstyle/xml.rs @@ -13,7 +13,7 @@ impl<'a> Display for XmlEscaped<'a> { '"' => write!(formatter, """), '\'' => write!(formatter, "'"), '&' => write!(formatter, "&"), - _ => write!(formatter, "{}", char), + _ => write!(formatter, "{char}"), }?; } diff --git a/src/emitter/diff.rs b/src/emitter/diff.rs index 5e1f13446560..764cd136e019 100644 --- a/src/emitter/diff.rs +++ b/src/emitter/diff.rs @@ -28,7 +28,7 @@ impl Emitter for DiffEmitter { if has_diff { if self.config.print_misformatted_file_names() { - writeln!(output, "{}", filename)?; + writeln!(output, "{filename}")?; } else { print_diff( mismatch, @@ -40,7 +40,7 @@ impl Emitter for DiffEmitter { // This occurs when the only difference between the original and formatted values // is the newline style. This happens because The make_diff function compares the // original and formatted values line by line, independent of line endings. - writeln!(output, "Incorrect newline style in {}", filename)?; + writeln!(output, "Incorrect newline style in {filename}")?; return Ok(EmitterResult { has_diff: true }); } @@ -110,7 +110,7 @@ mod tests { assert_eq!( String::from_utf8(writer).unwrap(), - format!("{}\n{}\n", bin_file, lib_file), + format!("{bin_file}\n{lib_file}\n"), ) } diff --git a/src/emitter/json.rs b/src/emitter/json.rs index c7f68d4675a6..5594196bed9e 100644 --- a/src/emitter/json.rs +++ b/src/emitter/json.rs @@ -96,7 +96,7 @@ impl JsonEmitter { }); } self.mismatched_files.push(MismatchedFile { - name: format!("{}", filename), + name: format!("{filename}"), mismatches, }); Ok(()) @@ -281,7 +281,7 @@ mod tests { }]) .unwrap(); assert_eq!(result.has_diff, true); - assert_eq!(&writer[..], format!("{}\n", exp_json).as_bytes()); + assert_eq!(&writer[..], format!("{exp_json}\n").as_bytes()); } #[test] @@ -341,6 +341,6 @@ mod tests { }; let exp_json = to_json_string(&vec![exp_bin, exp_lib]).unwrap(); - assert_eq!(&writer[..], format!("{}\n", exp_json).as_bytes()); + assert_eq!(&writer[..], format!("{exp_json}\n").as_bytes()); } } diff --git a/src/emitter/stdout.rs b/src/emitter/stdout.rs index 9fddd515e492..0bbc7332dfe5 100644 --- a/src/emitter/stdout.rs +++ b/src/emitter/stdout.rs @@ -24,9 +24,9 @@ impl Emitter for StdoutEmitter { }: FormattedFile<'_>, ) -> Result { if self.verbosity != Verbosity::Quiet { - writeln!(output, "{}:\n", filename)?; + writeln!(output, "{filename}:\n")?; } - write!(output, "{}", formatted_text)?; + write!(output, "{formatted_text}")?; Ok(EmitterResult::default()) } } diff --git a/src/expr.rs b/src/expr.rs index 9a605d6427e8..450f1476db08 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -187,7 +187,7 @@ pub(crate) fn format_expr( Some(label) => format!(" {}", label.ident), None => String::new(), }; - Some(format!("continue{}", id_str)) + Some(format!("continue{id_str}")) } ast::ExprKind::Break(ref opt_label, ref opt_expr) => { let id_str = match *opt_label { @@ -196,9 +196,9 @@ pub(crate) fn format_expr( }; if let Some(ref expr) = *opt_expr { - rewrite_unary_prefix(context, &format!("break{} ", id_str), &**expr, shape) + rewrite_unary_prefix(context, &format!("break{id_str} "), &**expr, shape) } else { - Some(format!("break{}", id_str)) + Some(format!("break{id_str}")) } } ast::ExprKind::Yield(ref opt_expr) => { @@ -309,7 +309,7 @@ pub(crate) fn format_expr( match (lhs.as_ref().map(|x| &**x), rhs.as_ref().map(|x| &**x)) { (Some(lhs), Some(rhs)) => { let sp_delim = if context.config.spaces_around_ranges() { - format!(" {} ", delim) + format!(" {delim} ") } else { default_sp_delim(Some(lhs), Some(rhs)) }; @@ -324,7 +324,7 @@ pub(crate) fn format_expr( } (None, Some(rhs)) => { let sp_delim = if context.config.spaces_around_ranges() { - format!("{} ", delim) + format!("{delim} ") } else { default_sp_delim(None, Some(rhs)) }; @@ -332,7 +332,7 @@ pub(crate) fn format_expr( } (Some(lhs), None) => { let sp_delim = if context.config.spaces_around_ranges() { - format!(" {}", delim) + format!(" {delim}") } else { default_sp_delim(Some(lhs), None) }; @@ -375,7 +375,7 @@ pub(crate) fn format_expr( }; if let rw @ Some(_) = rewrite_single_line_block( context, - format!("{}{}", "async ", mover).as_str(), + format!("async {mover}").as_str(), block, Some(&expr.attrs), None, @@ -386,9 +386,7 @@ pub(crate) fn format_expr( // 6 = `async ` let budget = shape.width.saturating_sub(6); Some(format!( - "{}{}{}", - "async ", - mover, + "async {mover}{}", rewrite_block( block, Some(&expr.attrs), @@ -460,7 +458,7 @@ fn rewrite_empty_block( } if !block_contains_comment(context, block) && shape.width >= 2 { - return Some(format!("{}{}{{}}", prefix, label_str)); + return Some(format!("{prefix}{label_str}{{}}")); } // If a block contains only a single-line comment, then leave it on one line. @@ -473,7 +471,7 @@ fn rewrite_empty_block( && !comment_str.starts_with("//") && comment_str.len() + 4 <= shape.width { - return Some(format!("{}{}{{ {} }}", prefix, label_str, comment_str)); + return Some(format!("{prefix}{label_str}{{ {comment_str} }}")); } } @@ -520,7 +518,7 @@ fn rewrite_single_line_block( let expr_shape = shape.offset_left(last_line_width(prefix))?; let expr_str = block_expr.rewrite(context, expr_shape)?; let label_str = rewrite_label(label); - let result = format!("{}{}{{ {} }}", prefix, label_str, expr_str); + let result = format!("{prefix}{label_str}{{ {expr_str} }}"); if result.len() <= shape.width && !result.contains('\n') { return Some(result); } @@ -1100,7 +1098,7 @@ impl<'a> Rewrite for ControlFlow<'a> { result? }; - let mut result = format!("{}{}", cond_str, block_str); + let mut result = format!("{cond_str}{block_str}"); if let Some(else_block) = self.else_block { let shape = Shape::indented(shape.indent, context.config); @@ -1160,8 +1158,7 @@ fn rewrite_label(opt_label: Option) -> Cow<'static, str> { fn extract_comment(span: Span, context: &RewriteContext<'_>, shape: Shape) -> Option { match rewrite_missing_comment(span, shape, context) { Some(ref comment) if !comment.is_empty() => Some(format!( - "{indent}{}{indent}", - comment, + "{indent}{comment}{indent}", indent = shape.indent.to_string_with_newline(context.config) )), _ => None, @@ -1478,7 +1475,7 @@ pub(crate) fn rewrite_paren( let subexpr_str = subexpr.rewrite(context, sub_shape)?; let fits_single_line = !pre_comment.contains("//") && !post_comment.contains("//"); if fits_single_line { - Some(format!("({}{}{})", pre_comment, subexpr_str, post_comment)) + Some(format!("({pre_comment}{subexpr_str}{post_comment})")) } else { rewrite_paren_in_multi_line(context, subexpr, shape, pre_span, post_span) } @@ -1542,7 +1539,7 @@ fn rewrite_index( // Return if index fits in a single line. match orig_index_rw { Some(ref index_str) if !index_str.contains('\n') => { - return Some(format!("{}[{}]", expr_str, index_str)); + return Some(format!("{expr_str}[{index_str}]")); } _ => (), } @@ -1565,7 +1562,7 @@ fn rewrite_index( indent.to_string_with_newline(context.config), new_index_str, )), - (Some(ref index_str), _) => Some(format!("{}[{}]", expr_str, index_str)), + (Some(ref index_str), _) => Some(format!("{expr_str}[{index_str}]")), _ => None, } } @@ -1597,9 +1594,9 @@ fn rewrite_struct_lit<'a>( let path_str = rewrite_path(context, PathContext::Expr, qself, path, path_shape)?; let has_base_or_rest = match struct_rest { - ast::StructRest::None if fields.is_empty() => return Some(format!("{} {{}}", path_str)), + ast::StructRest::None if fields.is_empty() => return Some(format!("{path_str} {{}}")), ast::StructRest::Rest(_) if fields.is_empty() => { - return Some(format!("{} {{ .. }}", path_str)); + return Some(format!("{path_str} {{ .. }}")); } ast::StructRest::Rest(_) | ast::StructRest::Base(_) => true, _ => false, @@ -1690,7 +1687,7 @@ fn rewrite_struct_lit<'a>( let fields_str = wrap_struct_field(context, attrs, &fields_str, shape, v_shape, one_line_width)?; - Some(format!("{} {{{}}}", path_str, fields_str)) + Some(format!("{path_str} {{{fields_str}}}")) // FIXME if context.config.indent_style() == Visual, but we run out // of space, we should fall back to BlockIndent. @@ -1720,7 +1717,7 @@ pub(crate) fn wrap_struct_field( )) } else { // One liner or visual indent. - Some(format!(" {} ", fields_str)) + Some(format!(" {fields_str} ")) } } else { Some(format!( @@ -1769,7 +1766,7 @@ pub(crate) fn rewrite_field( { Some(attrs_str + name) } - Some(e) => Some(format!("{}{}{}{}", attrs_str, name, separator, e)), + Some(e) => Some(format!("{attrs_str}{name}{separator}{e}")), None => { let expr_offset = shape.indent.block_indent(context.config); let expr = field @@ -1834,7 +1831,7 @@ fn rewrite_tuple_in_visual_indent_style<'a, T: 'a + IntoOverflowableItem<'a>>( .ends_with_newline(false); let list_str = write_list(&item_vec, &fmt)?; - Some(format!("({})", list_str)) + Some(format!("({list_str})")) } pub(crate) fn rewrite_tuple<'a, T: 'a + IntoOverflowableItem<'a>>( @@ -2076,7 +2073,7 @@ fn choose_rhs( Some(ref new_str) if !new_str.contains('\n') && unicode_str_width(new_str) <= shape.width => { - Some(format!(" {}", new_str)) + Some(format!(" {new_str}")) } _ => { // Expression did not fit on the same line as the identifier. @@ -2093,21 +2090,21 @@ fn choose_rhs( (Some(ref orig_rhs), Some(ref new_rhs)) if !filtered_str_fits(&new_rhs, context.config.max_width(), new_shape) => { - Some(format!("{}{}", before_space_str, orig_rhs)) + Some(format!("{before_space_str}{orig_rhs}")) } (Some(ref orig_rhs), Some(ref new_rhs)) if prefer_next_line(orig_rhs, new_rhs, rhs_tactics) => { - Some(format!("{}{}", new_indent_str, new_rhs)) + Some(format!("{new_indent_str}{new_rhs}")) } - (None, Some(ref new_rhs)) => Some(format!("{}{}", new_indent_str, new_rhs)), + (None, Some(ref new_rhs)) => Some(format!("{new_indent_str}{new_rhs}")), (None, None) if rhs_tactics == RhsTactics::AllowOverflow => { let shape = shape.infinite_width(); expr.rewrite(context, shape) .map(|s| format!("{}{}", before_space_str, s)) } (None, None) => None, - (Some(orig_rhs), _) => Some(format!("{}{}", before_space_str, orig_rhs)), + (Some(orig_rhs), _) => Some(format!("{before_space_str}{orig_rhs}")), } } } diff --git a/src/format-diff/main.rs b/src/format-diff/main.rs index db4a21ba09e9..61e2cb711a51 100644 --- a/src/format-diff/main.rs +++ b/src/format-diff/main.rs @@ -69,7 +69,7 @@ fn main() { .init(); let opts = Opts::parse(); if let Err(e) = run(opts) { - println!("{}", e); + println!("{e}"); Opts::command() .print_help() .expect("cannot write to stdout"); @@ -113,7 +113,7 @@ fn run_rustfmt(files: &HashSet, ranges: &[Range]) -> Result<(), FormatDi if !exit_status.success() { return Err(FormatDiffError::IoError(io::Error::new( io::ErrorKind::Other, - format!("rustfmt failed with {}", exit_status), + format!("rustfmt failed with {exit_status}"), ))); } Ok(()) @@ -129,12 +129,12 @@ fn scan_diff( where R: io::Read, { - let diff_pattern = format!(r"^\+\+\+\s(?:.*?/){{{}}}(\S*)", skip_prefix); + let diff_pattern = format!(r"^\+\+\+\s(?:.*?/){{{skip_prefix}}}(\S*)"); let diff_pattern = Regex::new(&diff_pattern).unwrap(); let lines_pattern = Regex::new(r"^@@.*\+(\d+)(,(\d+))?").unwrap(); - let file_filter = Regex::new(&format!("^{}$", file_filter))?; + let file_filter = Regex::new(&format!("^{file_filter}$"))?; let mut current_file = None; diff --git a/src/formatting.rs b/src/formatting.rs index 1f4ad6960e20..cd57a025b672 100644 --- a/src/formatting.rs +++ b/src/formatting.rs @@ -296,7 +296,7 @@ impl<'b, T: Write + 'b> FormatHandler for Session<'b, T> { Ok(ref result) if result.has_diff => report.add_diff(), Err(e) => { // Create a new error with path_str to help users see which files failed - let err_msg = format!("{}: {}", path, e); + let err_msg = format!("{path}: {e}"); return Err(io::Error::new(e.kind(), err_msg).into()); } _ => {} diff --git a/src/git-rustfmt/main.rs b/src/git-rustfmt/main.rs index efeda53e7ec6..3059d917c6b9 100644 --- a/src/git-rustfmt/main.rs +++ b/src/git-rustfmt/main.rs @@ -43,7 +43,7 @@ fn git_diff(commits: &str) -> String { let mut cmd = Command::new("git"); cmd.arg("diff"); if commits != "0" { - cmd.arg(format!("HEAD~{}", commits)); + cmd.arg(format!("HEAD~{commits}")); } let output = cmd.output().expect("Couldn't execute `git diff`"); String::from_utf8_lossy(&output.stdout).into_owned() @@ -108,7 +108,7 @@ fn check_uncommitted() { if !uncommitted.is_empty() { println!("Found untracked changes:"); for f in &uncommitted { - println!(" {}", f); + println!(" {f}"); } println!("Commit your work, or run with `-u`."); println!("Exiting."); diff --git a/src/imports.rs b/src/imports.rs index 339e5cef5af9..6f0050647dce 100644 --- a/src/imports.rs +++ b/src/imports.rs @@ -191,7 +191,7 @@ impl UseSegment { "crate" => UseSegmentKind::Crate(None), _ => { let mod_sep = if modsep { "::" } else { "" }; - UseSegmentKind::Ident(format!("{}{}", mod_sep, name), None) + UseSegmentKind::Ident(format!("{mod_sep}{name}"), None) } }; @@ -295,8 +295,8 @@ impl fmt::Display for UseSegmentKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { UseSegmentKind::Glob => write!(f, "*"), - UseSegmentKind::Ident(ref s, Some(ref alias)) => write!(f, "{} as {}", s, alias), - UseSegmentKind::Ident(ref s, None) => write!(f, "{}", s), + UseSegmentKind::Ident(ref s, Some(ref alias)) => write!(f, "{s} as {alias}"), + UseSegmentKind::Ident(ref s, None) => write!(f, "{s}"), UseSegmentKind::Slf(..) => write!(f, "self"), UseSegmentKind::Super(..) => write!(f, "super"), UseSegmentKind::Crate(..) => write!(f, "crate"), @@ -306,7 +306,7 @@ impl fmt::Display for UseSegmentKind { if i != 0 { write!(f, ", ")?; } - write!(f, "{}", item)?; + write!(f, "{item}")?; } write!(f, "}}") } @@ -319,7 +319,7 @@ impl fmt::Display for UseTree { if i != 0 { write!(f, "::")?; } - write!(f, "{}", segment)?; + write!(f, "{segment}")?; } Ok(()) } @@ -1042,7 +1042,7 @@ fn rewrite_nested_use_tree( shape.indent.to_string(context.config) ) } else { - format!("{{{}}}", list_str) + format!("{{{list_str}}}") }; Some(result) @@ -1052,14 +1052,14 @@ impl Rewrite for UseSegment { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { Some(match self.kind { UseSegmentKind::Ident(ref ident, Some(ref rename)) => { - format!("{} as {}", ident, rename) + format!("{ident} as {rename}") } UseSegmentKind::Ident(ref ident, None) => ident.clone(), - UseSegmentKind::Slf(Some(ref rename)) => format!("self as {}", rename), + UseSegmentKind::Slf(Some(ref rename)) => format!("self as {rename}"), UseSegmentKind::Slf(None) => "self".to_owned(), - UseSegmentKind::Super(Some(ref rename)) => format!("super as {}", rename), + UseSegmentKind::Super(Some(ref rename)) => format!("super as {rename}"), UseSegmentKind::Super(None) => "super".to_owned(), - UseSegmentKind::Crate(Some(ref rename)) => format!("crate as {}", rename), + UseSegmentKind::Crate(Some(ref rename)) => format!("crate as {rename}"), UseSegmentKind::Crate(None) => "crate".to_owned(), UseSegmentKind::Glob => "*".to_owned(), UseSegmentKind::List(ref use_tree_list) => rewrite_nested_use_tree( diff --git a/src/items.rs b/src/items.rs index fd4e08ed403b..a72646ef8975 100644 --- a/src/items.rs +++ b/src/items.rs @@ -470,7 +470,7 @@ impl<'a> FmtVisitor<'a> { && self.block_indent.width() + fn_str.len() + 3 <= self.config.max_width() && !last_line_contains_single_line_comment(fn_str) { - return Some(format!("{} {{}}", fn_str)); + return Some(format!("{fn_str} {{}}")); } if !self.config.fn_single_line() || !is_simple_block_stmt(&context, block, None) { @@ -482,7 +482,7 @@ impl<'a> FmtVisitor<'a> { let width = self.block_indent.width() + fn_str.len() + res.len() + 5; if !res.contains('\n') && width <= self.config.max_width() { - Some(format!("{} {{ {} }}", fn_str, res)) + Some(format!("{fn_str} {{ {res} }}")) } else { None } @@ -664,7 +664,7 @@ impl<'a> FmtVisitor<'a> { }; let variant_body = if let Some(ref expr) = field.disr_expr { - let lhs = format!("{:1$} =", variant_body, pad_discrim_ident_to); + let lhs = format!("{variant_body:pad_discrim_ident_to$} ="); let ex = &*expr.value; rewrite_assign_rhs_with( &context, @@ -827,7 +827,7 @@ pub(crate) fn format_impl( if generics.where_clause.predicates.len() == 1 { result.push(','); } - result.push_str(&format!("{}{{{}}}", sep, sep)); + result.push_str(&format!("{sep}{{{sep}}}")); } else { result.push_str(" {}"); } @@ -1018,7 +1018,7 @@ fn rewrite_trait_ref( let shape = Shape::indented(offset + used_space, context.config); if let Some(trait_ref_str) = trait_ref.rewrite(context, shape) { if !trait_ref_str.contains('\n') { - return Some(format!(" {}{}", polarity_str, trait_ref_str)); + return Some(format!(" {polarity_str}{trait_ref_str}")); } } // We could not make enough space for trait_ref, so put it on new line. @@ -1320,7 +1320,7 @@ impl<'a> Rewrite for TraitAliasBounds<'a> { shape.indent.to_string_with_newline(context.config) }; - Some(format!("{}{}{}", generic_bounds_str, space, where_str)) + Some(format!("{generic_bounds_str}{space}{where_str}")) } } @@ -1337,7 +1337,7 @@ pub(crate) fn format_trait_alias( let g_shape = shape.offset_left(6)?.sub_width(2)?; let generics_str = rewrite_generics(context, alias, generics, g_shape)?; let vis_str = format_visibility(context, vis); - let lhs = format!("{}trait {} =", vis_str, generics_str); + let lhs = format!("{vis_str}trait {generics_str} ="); // 1 = ";" let trait_alias_bounds = TraitAliasBounds { generic_bounds, @@ -1374,7 +1374,7 @@ fn format_unit_struct( } else { String::new() }; - Some(format!("{}{};", header_str, generics_str)) + Some(format!("{header_str}{generics_str};")) } pub(crate) fn format_struct_struct( @@ -1464,7 +1464,7 @@ pub(crate) fn format_struct_struct( && items_str.len() <= one_line_budget && !last_line_contains_single_line_comment(&items_str) { - Some(format!("{} {} }}", result, items_str)) + Some(format!("{result} {items_str} }}")) } else { Some(format!( "{}\n{}{}\n{}}}", @@ -1694,7 +1694,7 @@ pub(crate) fn rewrite_type_alias<'a, 'b>( rewrite_ty(rw_info, Some(bounds), ty_opt, vis) }?; match defaultness { - ast::Defaultness::Default(..) => Some(format!("default {}", result)), + ast::Defaultness::Default(..) => Some(format!("default {result}")), _ => Some(result), } } @@ -1801,14 +1801,14 @@ fn rewrite_ty( true, )? } - _ => format!("{}=", result), + _ => format!("{result}="), }; // 1 = `;` let shape = Shape::indented(indent, context.config).sub_width(1)?; rewrite_assign_rhs(context, lhs, &*ty, &RhsAssignKind::Ty, shape).map(|s| s + ";") } else { - Some(format!("{};", result)) + Some(format!("{result};")) } } @@ -2017,7 +2017,7 @@ fn rewrite_static( let expr_lo = expr.span.lo(); let comments_span = mk_sp(comments_lo, expr_lo); - let lhs = format!("{}{} =", prefix, ty_str); + let lhs = format!("{prefix}{ty_str} ="); // 1 = ; let remaining_width = context.budget(offset.block_indent + 1); @@ -2034,7 +2034,7 @@ fn rewrite_static( .and_then(|res| recover_comment_removed(res, static_parts.span, context)) .map(|s| if s.ends_with(';') { s } else { s + ";" }) } else { - Some(format!("{}{};", prefix, ty_str)) + Some(format!("{prefix}{ty_str};")) } } @@ -2227,7 +2227,7 @@ fn rewrite_explicit_self( Some(combine_strs_with_missing_comments( context, param_attrs, - &format!("&{} {}self", lifetime_str, mut_str), + &format!("&{lifetime_str} {mut_str}self"), span, shape, !has_multiple_attr_lines, @@ -2236,7 +2236,7 @@ fn rewrite_explicit_self( None => Some(combine_strs_with_missing_comments( context, param_attrs, - &format!("&{}self", mut_str), + &format!("&{mut_str}self"), span, shape, !has_multiple_attr_lines, @@ -2906,7 +2906,7 @@ fn rewrite_where_clause_rfc_style( clause_shape.indent.to_string_with_newline(context.config) }; - Some(format!("{}{}{}", where_keyword, clause_sep, preds_str)) + Some(format!("{where_keyword}{clause_sep}{preds_str}")) } /// Rewrite `where` and comment around it. @@ -2946,8 +2946,8 @@ fn rewrite_where_keyword( let newline_before_where = comment_separator(&comment_before, shape); let newline_after_where = comment_separator(&comment_after, clause_shape); let result = format!( - "{}{}{}where{}{}", - starting_newline, comment_before, newline_before_where, newline_after_where, comment_after + "{starting_newline}{comment_before}{newline_before_where}where\ +{newline_after_where}{comment_after}" ); let allow_single_line = where_clause_option.allow_single_line && comment_before.is_empty() @@ -3102,7 +3102,7 @@ fn rewrite_where_clause( preds_str )) } else { - Some(format!(" where {}", preds_str)) + Some(format!(" where {preds_str}")) } } diff --git a/src/macros.rs b/src/macros.rs index 88d26358b872..b6a49536d175 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -103,7 +103,7 @@ fn rewrite_macro_name( format!("{}!", pprust::path_to_string(path)) }; match extra_ident { - Some(ident) if ident.name != kw::Empty => format!("{} {}", name, ident), + Some(ident) if ident.name != kw::Empty => format!("{name} {ident}"), _ => name, } } @@ -214,14 +214,14 @@ fn rewrite_macro_inner( if ts.is_empty() && !has_comment { return match style { Delimiter::Parenthesis if position == MacroPosition::Item => { - Some(format!("{}();", macro_name)) + Some(format!("{macro_name}();")) } Delimiter::Bracket if position == MacroPosition::Item => { - Some(format!("{}[];", macro_name)) + Some(format!("{macro_name}[];")) } - Delimiter::Parenthesis => Some(format!("{}()", macro_name)), - Delimiter::Bracket => Some(format!("{}[]", macro_name)), - Delimiter::Brace => Some(format!("{} {{}}", macro_name)), + Delimiter::Parenthesis => Some(format!("{macro_name}()")), + Delimiter::Bracket => Some(format!("{macro_name}[]")), + Delimiter::Brace => Some(format!("{macro_name} {{}}")), _ => unreachable!(), }; } @@ -321,7 +321,7 @@ fn rewrite_macro_inner( _ => "", }; - Some(format!("{}{}", rewrite, comma)) + Some(format!("{rewrite}{comma}")) } } Delimiter::Brace => { @@ -330,8 +330,8 @@ fn rewrite_macro_inner( // anything in between the braces (for now). let snippet = context.snippet(mac.span()).trim_start_matches(|c| c != '{'); match trim_left_preserve_layout(snippet, shape.indent, context.config) { - Some(macro_body) => Some(format!("{} {}", macro_name, macro_body)), - None => Some(format!("{} {}", macro_name, snippet)), + Some(macro_body) => Some(format!("{macro_name} {macro_body}")), + None => Some(format!("{macro_name} {snippet}")), } } _ => unreachable!(), @@ -362,7 +362,7 @@ fn handle_vec_semi( && lhs.len() + rhs.len() + total_overhead <= shape.width { // macro_name(lhs; rhs) or macro_name[lhs; rhs] - Some(format!("{}{}{}; {}{}", macro_name, left, lhs, rhs, right)) + Some(format!("{macro_name}{left}{lhs}; {rhs}{right}")) } else { // macro_name(\nlhs;\nrhs\n) or macro_name[\nlhs;\nrhs\n] Some(format!( @@ -596,8 +596,8 @@ fn delim_token_to_str( .block_indent(context.config) .to_string_with_newline(context.config); ( - format!("{}{}", lhs, nested_indent_str), - format!("{}{}", indent_str, rhs), + format!("{lhs}{nested_indent_str}"), + format!("{indent_str}{rhs}"), ) } else { (lhs.to_owned(), rhs.to_owned()) @@ -654,7 +654,7 @@ impl MacroArgKind { }; match *self { - MacroArgKind::MetaVariable(ty, ref name) => Some(format!("${}:{}", name, ty)), + MacroArgKind::MetaVariable(ty, ref name) => Some(format!("${name}:{ty}")), MacroArgKind::Repeat(delim_tok, ref args, ref another, ref tok) => { let (lhs, inner, rhs) = rewrite_delimited_inner(delim_tok, args)?; let another = another @@ -663,14 +663,14 @@ impl MacroArgKind { .unwrap_or_else(|| "".to_owned()); let repeat_tok = pprust::token_to_string(tok); - Some(format!("${}{}{}{}{}", lhs, inner, rhs, another, repeat_tok)) + Some(format!("${lhs}{inner}{rhs}{another}{repeat_tok}")) } MacroArgKind::Delimited(delim_tok, ref args) => { rewrite_delimited_inner(delim_tok, args) .map(|(lhs, inner, rhs)| format!("{}{}{}", lhs, inner, rhs)) } - MacroArgKind::Separator(ref sep, ref prefix) => Some(format!("{}{} ", prefix, sep)), - MacroArgKind::Other(ref inner, ref prefix) => Some(format!("{}{}", prefix, inner)), + MacroArgKind::Separator(ref sep, ref prefix) => Some(format!("{prefix}{sep} ")), + MacroArgKind::Other(ref inner, ref prefix) => Some(format!("{prefix}{inner}")), } } } diff --git a/src/matches.rs b/src/matches.rs index a7677c3a35cd..27a9c1d31309 100644 --- a/src/matches.rs +++ b/src/matches.rs @@ -124,7 +124,7 @@ pub(crate) fn rewrite_match( if arms.is_empty() { let snippet = context.snippet(mk_sp(open_brace_pos, span.hi() - BytePos(1))); if snippet.trim().is_empty() { - Some(format!("match {} {{}}", cond_str)) + Some(format!("match {cond_str} {{}}")) } else { // Empty match with comments or inner attributes? We are not going to bother, sorry ;) Some(context.snippet(span).to_owned()) @@ -274,7 +274,7 @@ fn rewrite_match_arm( let lhs_str = combine_strs_with_missing_comments( context, &attrs_str, - &format!("{}{}{}", pipe_str, pats_str, guard_str), + &format!("{pipe_str}{pats_str}{guard_str}"), missing_span, shape, false, @@ -543,7 +543,7 @@ fn rewrite_guard( if let Some(cond_shape) = cond_shape { if let Some(cond_str) = guard.rewrite(context, cond_shape) { if !cond_str.contains('\n') || pattern_width <= context.config.tab_spaces() { - return Some(format!(" if {}", cond_str)); + return Some(format!(" if {cond_str}")); } } } diff --git a/src/pairs.rs b/src/pairs.rs index d135da7e3591..96f023b3b0ed 100644 --- a/src/pairs.rs +++ b/src/pairs.rs @@ -234,8 +234,8 @@ where let rhs_result = rhs.rewrite(context, rhs_shape)?; let indent_str = rhs_shape.indent.to_string_with_newline(context.config); let infix_with_sep = match separator_place { - SeparatorPlace::Back => format!("{}{}", infix, indent_str), - SeparatorPlace::Front => format!("{}{}", indent_str, infix), + SeparatorPlace::Back => format!("{infix}{indent_str}"), + SeparatorPlace::Front => format!("{indent_str}{infix}"), }; Some(format!( "{}{}{}{}", diff --git a/src/parse/session.rs b/src/parse/session.rs index 81b5015dde33..2edb830a5731 100644 --- a/src/parse/session.rs +++ b/src/parse/session.rs @@ -331,8 +331,7 @@ impl LineRangeUtils for ParseSess { debug_assert_eq!( lo.sf.name, hi.sf.name, - "span crossed file boundary: lo: {:?}, hi: {:?}", - lo, hi + "span crossed file boundary: lo: {lo:?}, hi: {hi:?}" ); // in case the span starts with a newline, the line range is off by 1 without the diff --git a/src/patterns.rs b/src/patterns.rs index 3f335172590e..33f3b4b8a21a 100644 --- a/src/patterns.rs +++ b/src/patterns.rs @@ -208,7 +208,7 @@ impl Rewrite for Pat { None => "", Some(_) => " ", }; - format!("{}{}{}", lhs_spacing, infix, rhs_spacing) + format!("{lhs_spacing}{infix}{rhs_spacing}") } else { infix.to_owned() }; @@ -283,7 +283,7 @@ fn rewrite_struct_pat( let path_str = rewrite_path(context, PathContext::Expr, qself, path, path_shape)?; if fields.is_empty() && !ellipsis { - return Some(format!("{} {{}}", path_str)); + return Some(format!("{path_str} {{}}")); } let (ellipsis_str, terminator) = if ellipsis { (", ..", "..") } else { ("", "}") }; @@ -344,7 +344,7 @@ fn rewrite_struct_pat( // ast::Pat doesn't have attrs so use &[] let fields_str = wrap_struct_field(context, &[], &fields_str, shape, v_shape, one_line_width)?; - Some(format!("{} {{{}}}", path_str, fields_str)) + Some(format!("{path_str} {{{fields_str}}}")) } impl Rewrite for PatField { @@ -376,7 +376,7 @@ impl Rewrite for PatField { let id_str = rewrite_ident(context, self.ident); let one_line_width = id_str.len() + 2 + pat_str.len(); let pat_and_id_str = if one_line_width <= shape.width { - format!("{}: {}", id_str, pat_str) + format!("{id_str}: {pat_str}") } else { format!( "{}:\n{}{}", diff --git a/src/rustfmt_diff.rs b/src/rustfmt_diff.rs index 1724a0f87bf7..c98834521852 100644 --- a/src/rustfmt_diff.rs +++ b/src/rustfmt_diff.rs @@ -95,7 +95,7 @@ impl fmt::Display for ModifiedLines { )?; for line in &chunk.lines { - writeln!(f, "{}", line)?; + writeln!(f, "{line}")?; } } @@ -166,12 +166,12 @@ impl OutputWriter { if let Some(color) = color { t.fg(color).unwrap(); } - writeln!(t, "{}", msg).unwrap(); + writeln!(t, "{msg}").unwrap(); if color.is_some() { t.reset().unwrap(); } } - None => println!("{}", msg), + None => println!("{msg}"), } } } @@ -265,16 +265,15 @@ where for line in mismatch.lines { match line { DiffLine::Context(ref str) => { - writer.writeln(&format!(" {}{}", str, line_terminator), None) + writer.writeln(&format!(" {str}{line_terminator}"), None) } DiffLine::Expected(ref str) => writer.writeln( - &format!("+{}{}", str, line_terminator), + &format!("+{str}{line_terminator}"), Some(term::color::GREEN), ), - DiffLine::Resulting(ref str) => writer.writeln( - &format!("-{}{}", str, line_terminator), - Some(term::color::RED), - ), + DiffLine::Resulting(ref str) => { + writer.writeln(&format!("-{str}{line_terminator}"), Some(term::color::RED)) + } } } } diff --git a/src/skip.rs b/src/skip.rs index 68f85b2ade48..d733f7068fd8 100644 --- a/src/skip.rs +++ b/src/skip.rs @@ -105,7 +105,7 @@ pub(crate) fn is_skip_attr(segments: &[ast::PathSegment]) -> bool { fn get_skip_names(kind: &str, attrs: &[ast::Attribute]) -> Vec { let mut skip_names = vec![]; - let path = format!("{}::{}::{}", RUSTFMT, SKIP, kind); + let path = format!("{RUSTFMT}::{SKIP}::{kind}"); for attr in attrs { // rustc_ast::ast::Path is implemented partialEq // but it is designed for segments.len() == 1 diff --git a/src/source_file.rs b/src/source_file.rs index 56d4ab400383..958f9b0154f7 100644 --- a/src/source_file.rs +++ b/src/source_file.rs @@ -62,7 +62,7 @@ where fn ensure_real_path(filename: &FileName) -> &Path { match *filename { FileName::Real(ref path) => path, - _ => panic!("cannot format `{}` and emit to files", filename), + _ => panic!("cannot format `{filename}` and emit to files"), } } diff --git a/src/test/configuration_snippet.rs b/src/test/configuration_snippet.rs index c70b3c5facd5..80b61c88a001 100644 --- a/src/test/configuration_snippet.rs +++ b/src/test/configuration_snippet.rs @@ -233,13 +233,11 @@ impl ConfigCodeBlock { Some(ConfigurationSection::ConfigName(name)) => { assert!( Config::is_valid_name(&name), - "an unknown configuration option was found: {}", - name + "an unknown configuration option was found: {name}" ); assert!( hash_set.remove(&name), - "multiple configuration guides found for option {}", - name + "multiple configuration guides found for option {name}" ); code_block.set_config_name(Some(name)); } @@ -266,7 +264,7 @@ fn configuration_snippet_tests() { // Display results. println!("Ran {} configurations tests.", blocks.len()); - assert_eq!(failures, 0, "{} configurations tests failed", failures); + assert_eq!(failures, 0, "{failures} configurations tests failed"); } // Read Configurations.md and build a `Vec` of `ConfigCodeBlock` structs with one @@ -289,7 +287,7 @@ fn get_code_blocks() -> Vec { for name in hash_set { if !Config::is_hidden_option(&name) { - panic!("{} does not have a configuration guide", name); + panic!("{name} does not have a configuration guide"); } } diff --git a/src/test/mod.rs b/src/test/mod.rs index f88ab3f194b6..c8137c1bd284 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -203,8 +203,8 @@ fn coverage_tests() { let files = get_test_files(Path::new("tests/coverage/source"), true); let (_reports, count, fails) = check_files(files, &None); - println!("Ran {} tests in coverage mode.", count); - assert_eq!(fails, 0, "{} tests failed", fails); + println!("Ran {count} tests in coverage mode."); + assert_eq!(fails, 0, "{fails} tests failed"); } #[test] @@ -396,8 +396,8 @@ fn self_tests() { let mut warnings = 0; // Display results. - println!("Ran {} self tests.", count); - assert_eq!(fails, 0, "{} self tests failed", fails); + println!("Ran {count} self tests."); + assert_eq!(fails, 0, "{fails} self tests failed"); for format_report in reports { println!( @@ -407,11 +407,7 @@ fn self_tests() { warnings += format_report.warning_count(); } - assert_eq!( - warnings, 0, - "Rustfmt's code generated {} warnings", - warnings - ); + assert_eq!(warnings, 0, "Rustfmt's code generated {warnings} warnings"); } #[test] @@ -606,7 +602,7 @@ fn stdin_handles_mod_inner_ignore_attr() { fn format_lines_errors_are_reported() { init_log(); let long_identifier = String::from_utf8(vec![b'a'; 239]).unwrap(); - let input = Input::Text(format!("fn {}() {{}}", long_identifier)); + let input = Input::Text(format!("fn {long_identifier}() {{}}")); let mut config = Config::default(); config.set().error_on_line_overflow(true); let mut session = Session::::new(config, None); @@ -618,7 +614,7 @@ fn format_lines_errors_are_reported() { fn format_lines_errors_are_reported_with_tabs() { init_log(); let long_identifier = String::from_utf8(vec![b'a'; 97]).unwrap(); - let input = Input::Text(format!("fn a() {{\n\t{}\n}}", long_identifier)); + let input = Input::Text(format!("fn a() {{\n\t{long_identifier}\n}}")); let mut config = Config::default(); config.set().error_on_line_overflow(true); config.set().hard_tabs(true); @@ -829,11 +825,11 @@ fn handle_result( for (file_name, fmt_text) in result { // If file is in tests/source, compare to file with same name in tests/target. let target = get_target(&file_name, target); - let open_error = format!("couldn't open target {:?}", target); + let open_error = format!("couldn't open target {target:?}"); let mut f = fs::File::open(&target).expect(&open_error); let mut text = String::new(); - let read_error = format!("failed reading target {:?}", target); + let read_error = format!("failed reading target {target:?}"); f.read_to_string(&mut text).expect(&read_error); // Ignore LF and CRLF difference for Windows. diff --git a/src/types.rs b/src/types.rs index aef85598f06f..8be474d5bcad 100644 --- a/src/types.rs +++ b/src/types.rs @@ -301,7 +301,7 @@ where let output = match *output { FnRetTy::Ty(ref ty) => { let type_str = ty.rewrite(context, ty_shape)?; - format!(" -> {}", type_str) + format!(" -> {type_str}") } FnRetTy::Default(..) => String::new(), }; @@ -373,7 +373,7 @@ where || !context.use_block_indent() || is_inputs_empty { - format!("({})", list_str) + format!("({list_str})") } else { format!( "({}{}{})", @@ -383,7 +383,7 @@ where ) }; if output.is_empty() || last_line_width(&args) + first_line_width(&output) <= shape.width { - Some(format!("{}{}", args, output)) + Some(format!("{args}{output}")) } else { Some(format!( "{}\n{}{}", @@ -429,9 +429,9 @@ impl Rewrite for ast::WherePredicate { let lhs = if let Some(binder_str) = rewrite_bound_params(context, shape, bound_generic_params) { - format!("for<{}> {}{}", binder_str, type_str, colon) + format!("for<{binder_str}> {type_str}{colon}") } else { - format!("{}{}", type_str, colon) + format!("{type_str}{colon}") }; rewrite_assign_rhs(context, lhs, bounds, &RhsAssignKind::Bounds, shape)? @@ -665,7 +665,7 @@ impl Rewrite for ast::PolyTraitRef { .trait_ref .rewrite(context, shape.offset_left(extra_offset)?)?; - Some(format!("for<{}> {}", lifetime_str, path_str)) + Some(format!("for<{lifetime_str}> {path_str}")) } else { self.trait_ref.rewrite(context, shape) } @@ -695,7 +695,7 @@ impl Rewrite for ast::Ty { res.push('+'); } } - Some(format!("{}{}", prefix, res)) + Some(format!("{prefix}{res}")) } ast::TyKind::Ptr(ref mt) => { let prefix = match mt.mutbl { @@ -791,7 +791,7 @@ impl Rewrite for ast::Ty { if let Some(sh) = shape.sub_width(2) { if let Some(ref s) = ty.rewrite(context, sh) { if !s.contains('\n') { - return Some(format!("({})", s)); + return Some(format!("({s})")); } } } diff --git a/src/utils.rs b/src/utils.rs index b8a44d4bade3..d1cb197cb511 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -69,7 +69,7 @@ pub(crate) fn format_visibility( let path = segments_iter.collect::>().join("::"); let in_str = if is_keyword(&path) { "" } else { "in " }; - Cow::from(format!("pub({}{}) ", in_str, path)) + Cow::from(format!("pub({in_str}{path}) ")) } } } @@ -147,7 +147,7 @@ pub(crate) fn format_extern( } else if abi == "C" && !explicit_abi { Cow::from("extern ") } else { - Cow::from(format!(r#"extern "{}" "#, abi)) + Cow::from(format!(r#"extern "{abi}" "#)) } } diff --git a/tests/cargo-fmt/main.rs b/tests/cargo-fmt/main.rs index 701c36fadeaf..63573bf341b1 100644 --- a/tests/cargo-fmt/main.rs +++ b/tests/cargo-fmt/main.rs @@ -26,7 +26,7 @@ fn cargo_fmt(args: &[&str]) -> (String, String) { String::from_utf8(output.stdout).expect("utf-8"), String::from_utf8(output.stderr).expect("utf-8"), ), - Err(e) => panic!("failed to run `{:?} {:?}`: {}", cmd, args, e), + Err(e) => panic!("failed to run `{cmd:?} {args:?}`: {e}"), } } diff --git a/tests/rustfmt/main.rs b/tests/rustfmt/main.rs index 4936a7174636..7dcf7c8416ee 100644 --- a/tests/rustfmt/main.rs +++ b/tests/rustfmt/main.rs @@ -27,7 +27,7 @@ fn rustfmt(args: &[&str]) -> (String, String) { String::from_utf8(output.stdout).expect("utf-8"), String::from_utf8(output.stderr).expect("utf-8"), ), - Err(e) => panic!("failed to run `{:?} {:?}`: {}", cmd, args, e), + Err(e) => panic!("failed to run `{cmd:?} {args:?}`: {e}"), } } @@ -71,9 +71,7 @@ fn print_config() { ]); assert!( Path::new("minimal-config").exists(), - "stdout:\n{}\nstderr:\n{}", - stdout, - stderr + "stdout:\n{stdout}\nstderr:\n{stderr}" ); remove_file("minimal-config").unwrap(); } From 4b01e62943866588e4cf2be8605f0124195b2c55 Mon Sep 17 00:00:00 2001 From: fee1-dead Date: Mon, 14 Aug 2023 20:19:23 +0800 Subject: [PATCH 326/401] refactor ABI formatting (#5845) fixes 5701 Whenever we see an `extern "Rust"` on a function, we don't strip it from the function. If there's any future desire to have rustfmt remove an explicit "Rust" ABI, as it historically did prior to this change, then we can consider updating the rustfmt config surface to support that scenario --- src/items.rs | 2 -- src/types.rs | 1 - src/utils.rs | 29 ++++++++++++----------------- tests/target/extern-rust.rs | 1 + 4 files changed, 13 insertions(+), 20 deletions(-) create mode 100644 tests/target/extern-rust.rs diff --git a/src/items.rs b/src/items.rs index a72646ef8975..1f4a14211b58 100644 --- a/src/items.rs +++ b/src/items.rs @@ -248,7 +248,6 @@ impl<'a> Item<'a> { abi: format_extern( ast::Extern::from_abi(fm.abi, DUMMY_SP), config.force_explicit_abi(), - true, ), vis: None, body: fm @@ -336,7 +335,6 @@ impl<'a> FnSig<'a> { result.push_str(&format_extern( self.ext, context.config.force_explicit_abi(), - false, )); result } diff --git a/src/types.rs b/src/types.rs index 8be474d5bcad..f2e229d74772 100644 --- a/src/types.rs +++ b/src/types.rs @@ -892,7 +892,6 @@ fn rewrite_bare_fn( result.push_str(&format_extern( bare_fn.ext, context.config.force_explicit_abi(), - false, )); result.push_str("fn"); diff --git a/src/utils.rs b/src/utils.rs index d1cb197cb511..ae7c50b4ca2f 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -131,23 +131,18 @@ pub(crate) fn format_mutability(mutability: ast::Mutability) -> &'static str { } #[inline] -pub(crate) fn format_extern( - ext: ast::Extern, - explicit_abi: bool, - is_mod: bool, -) -> Cow<'static, str> { - let abi = match ext { - ast::Extern::None => "Rust".to_owned(), - ast::Extern::Implicit(_) => "C".to_owned(), - ast::Extern::Explicit(abi, _) => abi.symbol_unescaped.to_string(), - }; - - if abi == "Rust" && !is_mod { - Cow::from("") - } else if abi == "C" && !explicit_abi { - Cow::from("extern ") - } else { - Cow::from(format!(r#"extern "{abi}" "#)) +pub(crate) fn format_extern(ext: ast::Extern, explicit_abi: bool) -> Cow<'static, str> { + match ext { + ast::Extern::None => Cow::from(""), + ast::Extern::Implicit(_) if explicit_abi => Cow::from("extern \"C\" "), + ast::Extern::Implicit(_) => Cow::from("extern "), + // turn `extern "C"` into `extern` when `explicit_abi` is set to false + ast::Extern::Explicit(abi, _) if abi.symbol_unescaped == sym::C && !explicit_abi => { + Cow::from("extern ") + } + ast::Extern::Explicit(abi, _) => { + Cow::from(format!(r#"extern "{}" "#, abi.symbol_unescaped)) + } } } diff --git a/tests/target/extern-rust.rs b/tests/target/extern-rust.rs new file mode 100644 index 000000000000..32824c912038 --- /dev/null +++ b/tests/target/extern-rust.rs @@ -0,0 +1 @@ +extern "Rust" fn uwu() {} From a57d57b16a2411e9081a083f25b25c7d76e2c2ff Mon Sep 17 00:00:00 2001 From: Kevin Ji <1146876+kevinji@users.noreply.github.com> Date: Sat, 29 Jul 2023 22:46:17 -0700 Subject: [PATCH 327/401] Remove newlines in where clauses for v2 Fixes #5655. --- src/items.rs | 4 +++- tests/source/issue-5655/one.rs | 9 +++++++++ tests/source/issue-5655/two.rs | 9 +++++++++ tests/target/issue-5655/one.rs | 9 +++++++++ tests/target/issue-5655/two.rs | 8 ++++++++ 5 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 tests/source/issue-5655/one.rs create mode 100644 tests/source/issue-5655/two.rs create mode 100644 tests/target/issue-5655/one.rs create mode 100644 tests/target/issue-5655/two.rs diff --git a/src/items.rs b/src/items.rs index 1f4a14211b58..4d82e192b7da 100644 --- a/src/items.rs +++ b/src/items.rs @@ -2996,10 +2996,12 @@ fn rewrite_bounds_on_where_clause( DefinitiveListTactic::Vertical }; + let preserve_newline = context.config.version() == Version::One; + let fmt = ListFormatting::new(shape, context.config) .tactic(shape_tactic) .trailing_separator(comma_tactic) - .preserve_newline(true); + .preserve_newline(preserve_newline); write_list(&items.collect::>(), &fmt) } diff --git a/tests/source/issue-5655/one.rs b/tests/source/issue-5655/one.rs new file mode 100644 index 000000000000..1758ec56f8b7 --- /dev/null +++ b/tests/source/issue-5655/one.rs @@ -0,0 +1,9 @@ +// rustfmt-version: One + +fn foo(_: T) +where + T: std::fmt::Debug, + + T: std::fmt::Display, +{ +} diff --git a/tests/source/issue-5655/two.rs b/tests/source/issue-5655/two.rs new file mode 100644 index 000000000000..e37ebbea8af4 --- /dev/null +++ b/tests/source/issue-5655/two.rs @@ -0,0 +1,9 @@ +// rustfmt-version: Two + +fn foo(_: T) +where + T: std::fmt::Debug, + + T: std::fmt::Display, +{ +} diff --git a/tests/target/issue-5655/one.rs b/tests/target/issue-5655/one.rs new file mode 100644 index 000000000000..1758ec56f8b7 --- /dev/null +++ b/tests/target/issue-5655/one.rs @@ -0,0 +1,9 @@ +// rustfmt-version: One + +fn foo(_: T) +where + T: std::fmt::Debug, + + T: std::fmt::Display, +{ +} diff --git a/tests/target/issue-5655/two.rs b/tests/target/issue-5655/two.rs new file mode 100644 index 000000000000..14fbc3d1321c --- /dev/null +++ b/tests/target/issue-5655/two.rs @@ -0,0 +1,8 @@ +// rustfmt-version: Two + +fn foo(_: T) +where + T: std::fmt::Debug, + T: std::fmt::Display, +{ +} From e480739e56b19c8f580e8fdc6a2f83cde784126c Mon Sep 17 00:00:00 2001 From: tdanniels Date: Thu, 17 Aug 2023 12:21:53 -0700 Subject: [PATCH 328/401] Prevent ICE when formatting item-only `vec!{}` (#5879) * Prevent ICE when formatting item-only `vec!{}` Fixes 5735 Attempting to format invocations of macros which are considered "forced bracket macros" (currently only `vec!`), but are invoked with braces instead of brackets, and contain only items in their token trees, currently triggers an ICE in rustfmt. This is because the function that handles formatting macro invocations containing only items, `rewrite_macro_with_items`, assumes that the forced delimiter style of the macro being formatted is the same as the delimiter style in the macro's source text when attempting to locate the span after the macro's opening delimiter. This leads to the construction of an invalid span, triggering the ICE. The fix here is to pass the old delimiter style to `rewrite_macro_with_items` as well, so that it can successfully locate the span. --- src/macros.rs | 28 +++++++++++++++++++--------- tests/source/issue_5735.rs | 6 ++++++ tests/target/issue_5735.rs | 6 ++++++ 3 files changed, 31 insertions(+), 9 deletions(-) create mode 100644 tests/source/issue_5735.rs create mode 100644 tests/target/issue_5735.rs diff --git a/src/macros.rs b/src/macros.rs index b6a49536d175..8047ab036870 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -255,6 +255,7 @@ fn rewrite_macro_inner( ¯o_name, shape, style, + original_style, position, mac.span(), ); @@ -295,20 +296,19 @@ fn rewrite_macro_inner( // If we are rewriting `vec!` macro or other special macros, // then we can rewrite this as a usual array literal. // Otherwise, we must preserve the original existence of trailing comma. - let macro_name = ¯o_name.as_str(); let mut force_trailing_comma = if trailing_comma { Some(SeparatorTactic::Always) } else { Some(SeparatorTactic::Never) }; - if FORCED_BRACKET_MACROS.contains(macro_name) && !is_nested_macro { + if is_forced_bracket && !is_nested_macro { context.leave_macro(); if context.use_block_indent() { force_trailing_comma = Some(SeparatorTactic::Vertical); }; } let rewrite = rewrite_array( - macro_name, + ¯o_name, arg_vec.iter(), mac.span(), context, @@ -1402,15 +1402,19 @@ fn rewrite_macro_with_items( macro_name: &str, shape: Shape, style: Delimiter, + original_style: Delimiter, position: MacroPosition, span: Span, ) -> Option { - let (opener, closer) = match style { - Delimiter::Parenthesis => ("(", ")"), - Delimiter::Bracket => ("[", "]"), - Delimiter::Brace => (" {", "}"), - _ => return None, + let style_to_delims = |style| match style { + Delimiter::Parenthesis => Some(("(", ")")), + Delimiter::Bracket => Some(("[", "]")), + Delimiter::Brace => Some((" {", "}")), + _ => None, }; + + let (opener, closer) = style_to_delims(style)?; + let (original_opener, _) = style_to_delims(original_style)?; let trailing_semicolon = match style { Delimiter::Parenthesis | Delimiter::Bracket if position == MacroPosition::Item => ";", _ => "", @@ -1418,7 +1422,13 @@ fn rewrite_macro_with_items( let mut visitor = FmtVisitor::from_context(context); visitor.block_indent = shape.indent.block_indent(context.config); - visitor.last_pos = context.snippet_provider.span_after(span, opener.trim()); + + // The current opener may be different from the original opener. This can happen + // if our macro is a forced bracket macro originally written with non-bracket + // delimiters. We need to use the original opener to locate the span after it. + visitor.last_pos = context + .snippet_provider + .span_after(span, original_opener.trim()); for item in items { let item = match item { MacroArg::Item(item) => item, diff --git a/tests/source/issue_5735.rs b/tests/source/issue_5735.rs new file mode 100644 index 000000000000..7708d028bf50 --- /dev/null +++ b/tests/source/issue_5735.rs @@ -0,0 +1,6 @@ +fn find_errors(mut self) { + let errors: Vec<> = vec!{ + #[debug_format = "A({})"] + struct A {} + }; +} diff --git a/tests/target/issue_5735.rs b/tests/target/issue_5735.rs new file mode 100644 index 000000000000..2d1376303f55 --- /dev/null +++ b/tests/target/issue_5735.rs @@ -0,0 +1,6 @@ +fn find_errors(mut self) { + let errors: Vec = vec![ + #[debug_format = "A({})"] + struct A {} + ]; +} From 16db2a4be2488dac816a55f3f1c014d5a36cd88e Mon Sep 17 00:00:00 2001 From: Kevin Ji <1146876+kevinji@users.noreply.github.com> Date: Thu, 17 Aug 2023 22:38:21 -0700 Subject: [PATCH 329/401] Use `OR` operator in Cargo.toml `license` field The use of `/` is deprecated, per the Cargo reference: https://doc.rust-lang.org/cargo/reference/manifest.html#the-license-and-license-file-fields --- Cargo.toml | 2 +- config_proc_macro/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b53b455a16d9..051c60fca11a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ version = "1.6.0" description = "Tool to find and fix Rust formatting issues" repository = "https://github.com/rust-lang/rustfmt" readme = "README.md" -license = "Apache-2.0/MIT" +license = "Apache-2.0 OR MIT" build = "build.rs" categories = ["development-tools"] edition = "2021" diff --git a/config_proc_macro/Cargo.toml b/config_proc_macro/Cargo.toml index 34e8c237f556..eda8a7fce81c 100644 --- a/config_proc_macro/Cargo.toml +++ b/config_proc_macro/Cargo.toml @@ -3,7 +3,7 @@ name = "rustfmt-config_proc_macro" version = "0.3.0" edition = "2018" description = "A collection of procedural macros for rustfmt" -license = "Apache-2.0/MIT" +license = "Apache-2.0 OR MIT" categories = ["development-tools::procedural-macro-helpers"] repository = "https://github.com/rust-lang/rustfmt" From cef311730279fcda3981f25103e1c84783856148 Mon Sep 17 00:00:00 2001 From: Frank King Date: Wed, 23 Aug 2023 20:53:47 +0800 Subject: [PATCH 330/401] Parse unnamed fields and anonymous structs or unions Anonymous structs or unions are only allowed in struct field definitions. Co-authored-by: carbotaniuman <41451839+carbotaniuman@users.noreply.github.com> --- src/types.rs | 2 ++ tests/target/anonymous-types.rs | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 tests/target/anonymous-types.rs diff --git a/src/types.rs b/src/types.rs index 18a08f17ba02..5e8edd8f8bf4 100644 --- a/src/types.rs +++ b/src/types.rs @@ -819,6 +819,8 @@ impl Rewrite for ast::Ty { ast::TyKind::Tup(ref items) => { rewrite_tuple(context, items.iter(), self.span, shape, items.len() == 1) } + ast::TyKind::AnonStruct(_) => Some(context.snippet(self.span).to_owned()), + ast::TyKind::AnonUnion(_) => Some(context.snippet(self.span).to_owned()), ast::TyKind::Path(ref q_self, ref path) => { rewrite_path(context, PathContext::Type, q_self, path, shape) } diff --git a/tests/target/anonymous-types.rs b/tests/target/anonymous-types.rs new file mode 100644 index 000000000000..8e08c314ed1f --- /dev/null +++ b/tests/target/anonymous-types.rs @@ -0,0 +1,19 @@ +// Test for issue 85480 +// Pretty print anonymous struct and union types + +// pp-exact +// pretty-compare-only + +struct Foo { + _: union { + _: struct { + a: u8, + b: u16, + }, + c: u32, + }, + d: u64, + e: f32, +} + +fn main() {} From 0439486ff7d931c81dca904c7e1fd687415c814d Mon Sep 17 00:00:00 2001 From: Collin Styles Date: Sat, 26 Aug 2023 11:01:08 -0700 Subject: [PATCH 331/401] Fix link in CHANGELOG.md (#5894) --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 22f183c04a3e..8d41c76fa773 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ - Support for formatting let-else statements [#5690] - New config option, `single_line_let_else_max_width`, that allows users to configure the maximum length of single line `let-else` statements. `let-else` statements that otherwise meet the requirements to be formatted on a single line will have their divergent`else` block formatted over multiple lines if they exceed this length [#5684] -[#5690]: https://github.com/rust-lang/rustfmt/pulls/5690 +[#5690]: https://github.com/rust-lang/rustfmt/pull/5690 [#5684]: https://github.com/rust-lang/rustfmt/issues/5684 ## [1.5.3] 2023-06-20 From df2471bf4ceb3ef124d2117e37196e1c18c9a700 Mon Sep 17 00:00:00 2001 From: Viktor Lazarev Date: Sun, 27 Aug 2023 21:46:09 +0200 Subject: [PATCH 332/401] Fix typos in index.html (#5896) --- docs/index.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/index.html b/docs/index.html index 4fa932d4c762..ee0339bc50da 100644 --- a/docs/index.html +++ b/docs/index.html @@ -87,7 +87,7 @@