提交 a2ecbf87 编写于 作者: B bors

Auto merge of #98040 - calebcartwright:sync-rustfmt, r=calebcartwright

Sync rustfmt subtree
......@@ -72,7 +72,14 @@ name = "annotate-snippets"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d78ea013094e5ea606b1c05fe35f1dd7ea1eb1ea259908d040b25bd5ec677ee5"
[[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",
]
......@@ -614,6 +621,7 @@ checksum = "6d76c22c9b9b215eeb8d016ad3a90417bd13cb24cf8142756e6472445876cab7"
dependencies = [
"atty",
"bitflags",
"clap_derive",
"indexmap",
"lazy_static",
"os_str_bytes",
......@@ -631,6 +639,19 @@ dependencies = [
"clap 3.1.1",
]
[[package]]
name = "clap_derive"
version = "3.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25320346e922cffe59c0bbc5410c8d8784509efb321488971081313cb1e1a33c"
dependencies = [
"heck 0.4.0",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "clippy"
version = "0.1.63"
......@@ -1099,11 +1120,10 @@ dependencies = [
[[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",
]
......@@ -1219,19 +1239,6 @@ dependencies = [
"termcolor",
]
[[package]]
name = "env_logger"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3"
dependencies = [
"atty",
"humantime 2.0.1",
"log",
"regex",
"termcolor",
]
[[package]]
name = "env_logger"
version = "0.9.0"
......@@ -1708,6 +1715,12 @@ dependencies = [
"unicode-segmentation",
]
[[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"
......@@ -3290,7 +3303,7 @@ dependencies = [
"difference",
"env_logger 0.9.0",
"futures 0.3.19",
"heck",
"heck 0.3.1",
"home",
"itertools",
"jsonrpc-core",
......@@ -3500,6 +3513,7 @@ version = "1.0.0"
dependencies = [
"bstr",
"byteorder",
"clap 3.1.1",
"crossbeam-utils",
"libc",
"libz-sys",
......@@ -3834,7 +3848,7 @@ dependencies = [
name = "rustc_errors"
version = "0.0.0"
dependencies = [
"annotate-snippets",
"annotate-snippets 0.8.0",
"atty",
"rustc_data_structures",
"rustc_error_messages",
......@@ -4085,7 +4099,7 @@ dependencies = [
name = "rustc_macros"
version = "0.1.0"
dependencies = [
"annotate-snippets",
"annotate-snippets 0.8.0",
"fluent-bundle",
"fluent-syntax",
"proc-macro2",
......@@ -4687,16 +4701,17 @@ dependencies = [
[[package]]
name = "rustfmt-nightly"
version = "1.4.38"
version = "1.5.0"
dependencies = [
"annotate-snippets",
"annotate-snippets 0.9.1",
"anyhow",
"bytecount",
"cargo_metadata",
"clap 3.1.1",
"derive-new",
"diff",
"dirs",
"env_logger 0.8.4",
"env_logger 0.9.0",
"getopts",
"ignore",
"itertools",
......@@ -4707,8 +4722,7 @@ dependencies = [
"rustfmt-config_proc_macro",
"serde",
"serde_json",
"structopt",
"term 0.6.1",
"term",
"thiserror",
"toml",
"unicode-segmentation",
......@@ -5096,30 +5110,6 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "structopt"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40b9788f4202aa75c240ecc9c15c65185e6a39ccdeb0fd5d008b98825464c87c"
dependencies = [
"clap 2.34.0",
"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",
]
[[package]]
name = "strum"
version = "0.18.0"
......@@ -5132,7 +5122,7 @@ version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87c85aa3f8ea653bfd3ddf25f7ee357ee4d204731f6aa9ad04002306f6e2774c"
dependencies = [
"heck",
"heck 0.3.1",
"proc-macro2",
"quote",
"syn",
......@@ -5212,16 +5202,6 @@ dependencies = [
"utf-8",
]
[[package]]
name = "term"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0863a3345e70f61d613eab32ee046ccd1bcc5f9105fe402c61fcd0c13eeb8b5"
dependencies = [
"dirs",
"winapi",
]
[[package]]
name = "term"
version = "0.7.0"
......@@ -5276,7 +5256,7 @@ dependencies = [
"getopts",
"libc",
"num_cpus",
"term 0.7.0",
"term",
]
[[package]]
......@@ -5705,9 +5685,9 @@ dependencies = [
[[package]]
name = "unicode-segmentation"
version = "1.6.0"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99"
[[package]]
name = "unicode-width"
......
......@@ -73,6 +73,7 @@ features = [
[dependencies]
bstr = { version = "0.2.13", features = ["default"] }
byteorder = { version = "1", features = ['default', 'std'] }
clap = { version = "3.1.1", features = ["lazy_static", "derive", "clap_derive"]}
curl-sys = { version = "0.4.13", features = ["http2", "libnghttp2-sys"], optional = true }
crossbeam-utils = { version = "0.8.0", features = ["nightly"] }
libc = { version = "0.2.79", features = ["align"] }
......
......@@ -69,7 +69,7 @@ jobs:
steps:
- name: checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
# Run build
- name: install rustup
......
......@@ -26,7 +26,7 @@ jobs:
steps:
- name: checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
# Run build
- name: install rustup
......
......@@ -23,7 +23,7 @@ jobs:
steps:
- name: checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
# Run build
- name: install rustup
......
......@@ -11,7 +11,7 @@ jobs:
name: rustdoc check
steps:
- name: checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: install rustup
run: |
......
......@@ -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
......
......@@ -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
......
......@@ -2,9 +2,82 @@
## [Unreleased]
### Fixed
## [1.5.0] 2022-06-13
### 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)
### 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 `<Struct as Trait>::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 `<stdin>` 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<const N: usize = { 1048576 }> {}`) [#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)
- 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)
### 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)** - *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
......
此差异已折叠。
[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"
......@@ -33,30 +33,31 @@ rustfmt-format-diff = []
generic-simd = ["bytecount/generic-simd"]
[dependencies]
itertools = "0.10.1"
toml = "0.5"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
unicode-segmentation = "1.0.0"
regex = "1.0"
term = "0.6"
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"
log = "0.4.14"
env_logger = "0.8"
dirs = "4.0"
env_logger = "0.9"
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"
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"
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`
......
......@@ -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 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
......@@ -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`
......@@ -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.
......@@ -1705,6 +1685,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.
......@@ -2061,12 +2043,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.
......@@ -2162,35 +2148,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.
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
......
[toolchain]
channel = "nightly-2022-03-27"
channel = "nightly-2022-06-06"
components = ["rustc-dev"]
......@@ -10,59 +10,63 @@
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;
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<String>,
/// Specify path to Cargo.toml
#[structopt(long = "manifest-path", value_name = "manifest-path")]
#[clap(long = "manifest-path", value_name = "manifest-path")]
manifest_path: Option<String>,
/// 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<String>,
/// Options passed to rustfmt
// 'raw = true' to make `--` explicit.
#[structopt(name = "rustfmt_options", raw(true))]
#[clap(name = "rustfmt_options", raw(true))]
rustfmt_options: Vec<String>,
/// 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 +91,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 +208,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");
......
......@@ -6,7 +6,7 @@
#[test]
fn default_options() {
let empty: Vec<String> = 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()
);
}
......@@ -796,7 +796,7 @@ fn handle_line(
// 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
......
......@@ -61,9 +61,6 @@ fn doc_hint() -> String {
#[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<Regex>,
// 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 @@ pub fn $i(&mut self, value: $ty) {
| "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 @@ fn fill_from_parsed_config(mut self, parsed: PartialConfig, dir: &Path) -> Confi
}
)+
self.set_heuristics();
self.set_license_template();
self.set_ignore(dir);
self.set_merge_imports();
self
......@@ -247,7 +242,6 @@ pub fn override_value(&mut self, key: &str, val: &str)
| "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 @@ fn set_heuristics(&mut self) {
};
}
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(&lt_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 @@ pub fn is_default(&self, key: &str) -> bool {
impl Default for Config {
fn default() -> Config {
Config {
license_template: None,
$(
$i: (Cell::new(false), false, $def, $stb),
)+
......
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<io::Error> for LicenseError {
fn from(err: io::Error) -> LicenseError {
LicenseError::IO(err)
}
}
impl From<regex::Error> 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 <LICENSE-APACHE or
/// // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
/// // <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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 <LICENSE\-APACHE or
/// // http://www\.apache\.org/licenses/LICENSE\-2\.0> or the MIT license
/// // <LICENSE\-MIT or http://opensource\.org/licenses/MIT>, at your
/// // option\. This file may not be copied, modified, or distributed
/// // except according to those terms\.
/// "
/// );
/// ```
pub(crate) fn parse(template: &str) -> Result<String, LicenseError> {
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(&regex::escape(&parser.buffer));
Ok(parser.parsed)
}
fn trans_from_lit(&mut self, chr: char) -> ParsingState {
match chr {
'{' => {
self.parsed.push_str(&regex::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<Regex, LicenseError> {
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(&lt_str)?;
Ok(Regex::new(&lt_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"
);
}
}
......@@ -5,7 +5,6 @@
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 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 @@
"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";
......@@ -164,10 +160,6 @@
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,
"Skip formatting the specified files and directories";
......@@ -418,8 +410,6 @@ mod mock {
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,
......@@ -524,31 +514,6 @@ fn test_print_docs_include_unstable() {
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!(
......@@ -570,7 +535,6 @@ fn test_dump_default_config() {
comment_width = 80
normalize_comments = false
normalize_doc_attributes = false
license_template_path = ""
format_strings = false
format_macro_matchers = false
format_macro_bodies = true
......@@ -625,8 +589,6 @@ fn test_dump_default_config() {
hide_parse_errors = false
error_on_line_overflow = false
error_on_unformatted = false
report_todo = "Never"
report_fixme = "Never"
ignore = []
emit_mode = "Files"
make_backup = false
......
use std::borrow::Cow;
use std::cmp::min;
use std::collections::HashMap;
use itertools::Itertools;
use rustc_ast::token::{Delimiter, LitKind};
......@@ -22,7 +23,7 @@
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<String> {
// 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<String> {
skip_out_of_file_lines_range!(context, expr.span);
......
......@@ -19,8 +19,7 @@
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<String> = 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 @@ 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()
);
}
......@@ -254,8 +255,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()
);
}
......@@ -263,8 +264,8 @@ fn unexpected_flag() {
#[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 @@ fn overridden_option() {
#[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()
);
}
......
......@@ -142,10 +142,9 @@ 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,
ErrorKind::BadIssue(_) | ErrorKind::DeprecatedAttr => AnnotationType::Warning,
ErrorKind::DeprecatedAttr => AnnotationType::Warning,
}
}
......@@ -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;
......@@ -11,7 +12,6 @@
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;
......@@ -39,12 +39,10 @@ pub(crate) fn format_input_inner(
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();
......@@ -93,6 +91,13 @@ fn should_skip_module<T: FormatHandler>(
false
}
fn echo_back_stdin(input: &str) -> Result<FormatReport, ErrorKind> {
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<T: FormatHandler>(
input: Input,
......@@ -135,7 +140,8 @@ fn format_project<T: FormatHandler>(
.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::<Vec<_>>();
......@@ -145,6 +151,14 @@ fn format_project<T: FormatHandler>(
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)?;
}
......@@ -189,6 +203,7 @@ fn format_file(
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;
......@@ -329,10 +344,8 @@ pub(crate) fn format_len(&self) -> (usize, usize) {
ErrorKind::LineOverflow(found, max) => (max, found - max),
ErrorKind::TrailingWhitespace
| ErrorKind::DeprecatedAttr
| ErrorKind::BadIssue(_)
| ErrorKind::BadAttr
| ErrorKind::LostComment
| ErrorKind::LicenseCheck => {
| ErrorKind::LostComment => {
let trailing_ws_start = self
.line_buffer
.rfind(|c: char| !c.is_whitespace())
......@@ -364,7 +377,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).
......@@ -460,7 +473,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 {
......@@ -480,11 +492,9 @@ struct FormatLines<'a> {
cur_line: usize,
newline_count: usize,
errors: Vec<FormattingError>,
issue_seeker: BadIssueSeeker,
line_buffer: String,
current_line_contains_string_literal: bool,
format_line: bool,
allow_issue_seek: bool,
config: &'a Config,
}
......@@ -494,7 +504,6 @@ fn new(
skipped_range: &'a [(usize, usize)],
config: &'a Config,
) -> FormatLines<'a> {
let issue_seeker = BadIssueSeeker::new(config.report_todo(), config.report_fixme());
FormatLines {
name,
skipped_range,
......@@ -503,8 +512,6 @@ fn new(
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),
......@@ -512,20 +519,6 @@ fn new(
}
}
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()) {
......@@ -533,13 +526,6 @@ fn iterate(&mut self, text: &mut String) {
continue;
}
if self.allow_issue_seek && self.format_line {
// Add warnings for bad todos/ 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 {
......
......@@ -2,6 +2,10 @@
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},
......@@ -10,6 +14,7 @@
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,
......@@ -86,7 +91,7 @@ pub(crate) fn format_import(&mut self, item: &ast::Item, tree: &ast::UseTree) {
// 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<String>),
Slf(Option<String>),
......@@ -180,17 +185,36 @@ fn from_path_segment(
}
})
}
fn contains_comment(&self) -> bool {
if let UseSegment::List(list) = self {
list.iter().any(|subtree| subtree.contains_comment())
} else {
false
}
}
}
pub(crate) fn merge_use_trees(use_trees: Vec<UseTree>, merge_by: SharedPrefix) -> Vec<UseTree> {
pub(crate) fn normalize_use_trees_with_granularity(
use_trees: Vec<UseTree>,
import_granularity: ImportGranularity,
) -> Vec<UseTree> {
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() {
if use_tree.contains_comment() || use_tree.attrs.is_some() {
result.push(use_tree);
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,11 +232,17 @@ pub(crate) fn merge_use_trees(use_trees: Vec<UseTree>, merge_by: SharedPrefix) -
result
}
pub(crate) fn flatten_use_trees(use_trees: Vec<UseTree>) -> Vec<UseTree> {
fn flatten_use_trees(
use_trees: Vec<UseTree>,
import_granularity: ImportGranularity,
) -> Vec<UseTree> {
// 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(UseTree::flatten)
.flat_map(|tree| tree.flatten(import_granularity))
.map(UseTree::nest_trailing_self)
.unique()
.collect()
}
......@@ -541,6 +571,10 @@ 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) {
(
......@@ -567,6 +601,7 @@ 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
......@@ -581,8 +616,8 @@ fn share_prefix(&self, other: &UseTree, shared_prefix: SharedPrefix) -> bool {
}
}
fn flatten(self) -> Vec<UseTree> {
if self.path.is_empty() {
fn flatten(self, import_granularity: ImportGranularity) -> Vec<UseTree> {
if self.path.is_empty() || self.contains_comment() {
return vec![self];
}
match self.path.clone().last().unwrap() {
......@@ -595,7 +630,7 @@ fn flatten(self) -> Vec<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 +638,11 @@ fn flatten(self) -> Vec<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,
},
});
}
}
......@@ -748,6 +787,12 @@ struct SimilarTree<'a> {
trees.sort();
}
impl Hash for UseTree {
fn hash<H: Hasher>(&self, state: &mut H) {
self.path.hash(state);
}
}
impl PartialOrd for UseSegment {
fn partial_cmp(&self, other: &UseSegment) -> Option<Ordering> {
Some(self.cmp(other))
......@@ -951,7 +996,7 @@ fn rewrite(&self, context: &RewriteContext<'_>, mut shape: Shape) -> Option<Stri
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub(crate) enum SharedPrefix {
enum SharedPrefix {
Crate,
Module,
One,
......@@ -1106,7 +1151,10 @@ fn parse_list(&mut self) -> Vec<UseTree> {
macro_rules! test_merge {
($by:ident, [$($input:expr),* $(,)*], [$($output:expr),* $(,)*]) => {
assert_eq!(
merge_use_trees(parse_use_trees!($($input,)*), SharedPrefix::$by),
normalize_use_trees_with_granularity(
parse_use_trees!($($input,)*),
ImportGranularity::$by,
),
parse_use_trees!($($output,)*),
);
}
......@@ -1215,12 +1263,18 @@ fn test_use_tree_merge_one() {
#[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 +1288,13 @@ fn test_flatten_use_trees() {
#[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",
......
// 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;
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
// irrelevant outside the issues module
fn is_enabled(report_tactic: ReportTactic) -> bool {
report_tactic != ReportTactic::Never
}
#[derive(Clone, Copy)]
enum Seeking {
Issue { todo_idx: usize, fixme_idx: usize },
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: IssueType,
// 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 {
IssueType::Todo => "TODO",
IssueType::Fixme => "FIXME",
};
let details = if self.missing_number {
" without issue number"
} else {
""
};
write!(fmt, "{}{}", msg, details)
}
}
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
enum IssueType {
Todo,
Fixme,
}
enum IssueClassification {
Good,
Bad(Issue),
None,
}
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 {
BadIssueSeeker {
state: Seeking::Issue {
todo_idx: 0,
fixme_idx: 0,
},
report_todo,
report_fixme,
}
}
pub(crate) fn is_disabled(&self) -> bool {
!is_enabled(self.report_todo) && !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<Issue> {
match self.state {
Seeking::Issue {
todo_idx,
fixme_idx,
} => {
self.state = self.inspect_issue(c, todo_idx, fixme_idx);
}
Seeking::Number { issue, part } => {
let result = self.inspect_number(c, issue, part);
if let IssueClassification::None = result {
return None;
}
self.state = Seeking::Issue {
todo_idx: 0,
fixme_idx: 0,
};
if let IssueClassification::Bad(issue) = result {
return Some(issue);
}
}
}
None
}
fn inspect_issue(&mut self, c: char, mut todo_idx: usize, 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] {
// 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,
};
}
todo_idx = 0;
} else {
todo_idx = 0;
fixme_idx = 0;
}
}
Seeking::Issue {
todo_idx,
fixme_idx,
}
}
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(ReportTactic::Unnumbered, ReportTactic::Unnumbered);
assert_eq!(
Some(failing_pos),
text.find(|c| seeker.inspect(c).is_some())
);
}
fn check_pass(text: &str) {
let mut seeker = BadIssueSeeker::new(ReportTactic::Unnumbered, 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);
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");
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);
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) mixed case\n",
ReportTactic::Never,
ReportTactic::Always,
));
assert!(!is_bad_issue(
"bad FIXME\n",
ReportTactic::Always,
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 expected = Some(Issue {
issue_type: IssueType::Fixme,
missing_number: true,
});
assert_eq!(
expected,
"Test. FIXME: bad, bad, not good"
.chars()
.map(|c| seeker.inspect(c))
.find(Option::is_some)
.unwrap()
);
}
......@@ -1770,7 +1770,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);
}
}
......
......@@ -40,7 +40,6 @@
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;
......@@ -70,7 +69,6 @@
pub(crate) mod formatting;
mod ignore_path;
mod imports;
mod issues;
mod items;
mod lists;
mod macros;
......@@ -111,12 +109,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,
/// Used deprecated skip attribute.
#[error("`rustfmt_skip` is deprecated; use `rustfmt::skip`")]
DeprecatedAttr,
......@@ -237,11 +229,7 @@ fn track_errors(&self, new_errors: &[FormattingError]) {
ErrorKind::LostComment => {
errs.has_unformatted_code_errors = true;
}
ErrorKind::BadIssue(_)
| ErrorKind::LicenseCheck
| ErrorKind::DeprecatedAttr
| ErrorKind::BadAttr
| ErrorKind::VersionMismatch => {
ErrorKind::DeprecatedAttr | ErrorKind::BadAttr | ErrorKind::VersionMismatch => {
errs.has_check_errors = true;
}
_ => {}
......
......@@ -32,7 +32,7 @@
/// 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 @@
("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 @@ pub(crate) fn can_be_overflowed(&self, context: &RewriteContext<'_>, len: usize)
}
}
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)
{
......
......@@ -170,7 +170,7 @@ pub(crate) fn new(config: &Config) -> Result<ParseSess, ErrorKind> {
/// * `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,
......
......@@ -11,8 +11,8 @@
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::{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,15 +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 = 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 = normalize_use_trees_with_granularity(
normalized_items,
context.config.imports_granularity(),
);
let mut regrouped_items = match context.config.group_imports() {
GroupImportsTactic::Preserve | GroupImportsTactic::One => {
......
......@@ -12,6 +12,7 @@
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 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String>
}
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub(crate) struct QueryId {
pub(crate) shape: Shape,
pub(crate) span: Span,
}
// We use Option<HashMap> 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<Cell<Option<FxHashMap<QueryId, Option<String>>>>>;
#[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<Cell<bool>>,
// Force block indent style even if we are using visual indent style.
pub(crate) use_block: Cell<bool>,
......
......@@ -4,7 +4,7 @@
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 @@ fn sub(self, rhs: usize) -> 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.
......
......@@ -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 @@ fn issue343() {
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::<Vec<&str>>();
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.";
......
......@@ -9,7 +9,7 @@
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;
......@@ -24,7 +24,7 @@
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 @@ fn is_subpath<P>(path: &Path, subpath: &P) -> bool
}
fn is_file_skip(path: &Path) -> bool {
SKIP_FILE_WHITE_LIST
FILE_SKIP_LIST
.iter()
.any(|file_path| is_subpath(path, file_path))
}
......@@ -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();
......@@ -688,9 +712,6 @@ fn read_config(filename: &Path) -> Config {
}
}
// Don't generate warnings for to-do items.
config.set().report_todo(ReportTactic::Never);
config
}
......@@ -957,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(),
......
......@@ -17,7 +17,7 @@
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 @@ pub(crate) fn end_pos(&self) -> BytePos {
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,
......@@ -758,6 +759,7 @@ pub(crate) fn from_context(ctx: &'a RewriteContext<'_>) -> 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);
......@@ -769,10 +771,12 @@ pub(crate) fn from_parse_sess(
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(),
......@@ -995,6 +999,7 @@ pub(crate) fn get_context(&self) -> RewriteContext<'_> {
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),
......
unstable_features = true
license_template_path = ""
......@@ -6,7 +6,5 @@ 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
// rustfmt-license_template_path: tests/license-template/lt.txt
// Copyright {\d+} The rustfmt developers.
......@@ -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)]
......
// 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;
......@@ -35,3 +35,31 @@
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;
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};
// 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};
// 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};
// 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;
......@@ -17,3 +17,31 @@
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;
// 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};
......@@ -58,3 +58,31 @@
};
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;
// 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};
// 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`
}
// rustfmt-config: issue-3802.toml
fn main() {
println!("Hello world!");
}
// rustfmt-license_template_path: tests/license-template/lt.txt
// Copyright 2019 The rustfmt developers.
fn main() {
println!("Hello world!");
}
// #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,
}
// 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;
......@@ -26,3 +26,34 @@
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,
};
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};
// rustfmt-imports_granularity: Item
// rustfmt-reorder_imports: false
// rustfmt-group_imports: StdExternalCrate
use crate::lexer;
use crate::lexer::tokens::TokenData;
use crate::lexer::{self};
// rustfmt-imports_granularity: Item
use crate::lexer;
use crate::lexer::tokens::TokenData;
use crate::lexer::{self};
// 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,
};
......@@ -17,6 +17,39 @@
#[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,
};
// 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};
......@@ -68,12 +68,42 @@
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,
};
// 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;
// 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`
}
// rustfmt-format_code_in_doc_comments:true
struct Foo {
// a: i32,
//
// b: i32,
}
struct Foo {
a: i32,
//
// b: i32,
}
// 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<u8>;
#[no_mangle]
#[cfg(not(miri))] // inline comment
#[non_exhaustive] // inline comment
enum Foo {
Bar,
Baz,
}
#[no_mangle]
#[cfg(not(miri))] /* inline comment */
struct Foo<A> {
x: A,
}
#[cfg(not(miri))] // inline comment
union Foo<A, B> {
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<u8>;
#[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<A> {
x: A,
}
#[cfg(not(miri))] // inline comment
union Foo<A, B> {
#[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) => {};
}
}
// rustfmt-config: issue-3802.toml
fn main() {
println!("Hello world!");
}
// rustfmt-license_template_path: tests/license-template/lt.txt
// Copyright 2019 The rustfmt developers.
fn main() {
println!("Hello world!");
}
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") }) }) }) }) }) }) }) }) }) }) });
}
}
}
}
}
}
// #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,
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册