]> git.lizzy.rs Git - rust.git/commitdiff
Merge commit '5ff7b632a95bac6955611d85040859128902c580' into sync-rustfmt-subtree
authorCaleb Cartwright <caleb.cartwright@outlook.com>
Wed, 30 Mar 2022 04:17:30 +0000 (23:17 -0500)
committerCaleb Cartwright <caleb.cartwright@outlook.com>
Wed, 30 Mar 2022 04:17:30 +0000 (23:17 -0500)
125 files changed:
1  2 
src/tools/rustfmt/.github/workflows/linux.yml
src/tools/rustfmt/CHANGELOG.md
src/tools/rustfmt/Cargo.lock
src/tools/rustfmt/Configurations.md
src/tools/rustfmt/config_proc_macro/src/utils.rs
src/tools/rustfmt/rust-toolchain
src/tools/rustfmt/src/attr.rs
src/tools/rustfmt/src/bin/main.rs
src/tools/rustfmt/src/cargo-fmt/main.rs
src/tools/rustfmt/src/comment.rs
src/tools/rustfmt/src/config/file_lines.rs
src/tools/rustfmt/src/config/mod.rs
src/tools/rustfmt/src/emitter/checkstyle.rs
src/tools/rustfmt/src/emitter/diff.rs
src/tools/rustfmt/src/emitter/json.rs
src/tools/rustfmt/src/format_report_formatter.rs
src/tools/rustfmt/src/ignore_path.rs
src/tools/rustfmt/src/imports.rs
src/tools/rustfmt/src/items.rs
src/tools/rustfmt/src/lists.rs
src/tools/rustfmt/src/macros.rs
src/tools/rustfmt/src/modules.rs
src/tools/rustfmt/src/overflow.rs
src/tools/rustfmt/src/parse/session.rs
src/tools/rustfmt/src/spanned.rs
src/tools/rustfmt/src/string.rs
src/tools/rustfmt/src/test/configuration_snippet.rs
src/tools/rustfmt/src/test/mod.rs
src/tools/rustfmt/src/test/mod_resolver.rs
src/tools/rustfmt/src/types.rs
src/tools/rustfmt/src/utils.rs
src/tools/rustfmt/src/vertical.rs
src/tools/rustfmt/tests/cargo-fmt/main.rs
src/tools/rustfmt/tests/mod-resolver/issue-5167/src/a.rs
src/tools/rustfmt/tests/mod-resolver/issue-5167/src/a/mod.rs
src/tools/rustfmt/tests/mod-resolver/issue-5167/src/lib.rs
src/tools/rustfmt/tests/mod-resolver/issue-5198/a.rs
src/tools/rustfmt/tests/mod-resolver/issue-5198/lib.rs
src/tools/rustfmt/tests/mod-resolver/issue-5198/lib/b.rs
src/tools/rustfmt/tests/mod-resolver/issue-5198/lib/c/d.rs
src/tools/rustfmt/tests/mod-resolver/issue-5198/lib/c/d/explanation.txt
src/tools/rustfmt/tests/mod-resolver/issue-5198/lib/c/d/f.rs
src/tools/rustfmt/tests/mod-resolver/issue-5198/lib/c/d/g/mod.rs
src/tools/rustfmt/tests/mod-resolver/issue-5198/lib/c/e.rs
src/tools/rustfmt/tests/mod-resolver/issue-5198/lib/c/mod.rs
src/tools/rustfmt/tests/mod-resolver/issue-5198/lib/explanation.txt
src/tools/rustfmt/tests/mod-resolver/module-not-found/bad_path_attribute/lib.rs
src/tools/rustfmt/tests/mod-resolver/module-not-found/relative_module/a.rs
src/tools/rustfmt/tests/mod-resolver/module-not-found/relative_module/lib.rs
src/tools/rustfmt/tests/mod-resolver/module-not-found/sibling_module/lib.rs
src/tools/rustfmt/tests/mod-resolver/test-submodule-issue-5119/Cargo.toml
src/tools/rustfmt/tests/mod-resolver/test-submodule-issue-5119/src/lib.rs
src/tools/rustfmt/tests/mod-resolver/test-submodule-issue-5119/tests/test1.rs
src/tools/rustfmt/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub1.rs
src/tools/rustfmt/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub2.rs
src/tools/rustfmt/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/mod.rs
src/tools/rustfmt/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/sub4.rs
src/tools/rustfmt/tests/rustfmt/main.rs
src/tools/rustfmt/tests/source/5131_crate.rs
src/tools/rustfmt/tests/source/5131_module.rs
src/tools/rustfmt/tests/source/5131_one.rs
src/tools/rustfmt/tests/source/configs/short_array_element_width_threshold/10.rs
src/tools/rustfmt/tests/source/configs/short_array_element_width_threshold/20.rs
src/tools/rustfmt/tests/source/configs/short_array_element_width_threshold/greater_than_max_width.rs
src/tools/rustfmt/tests/source/extern.rs
src/tools/rustfmt/tests/source/imports_granularity_module.rs
src/tools/rustfmt/tests/source/issue-4036/one.rs
src/tools/rustfmt/tests/source/issue-4036/three.rs
src/tools/rustfmt/tests/source/issue-4036/two.rs
src/tools/rustfmt/tests/source/issue-4791/buggy.rs
src/tools/rustfmt/tests/source/issue-4791/trailing_comma.rs
src/tools/rustfmt/tests/source/issue-5023.rs
src/tools/rustfmt/tests/source/issue-5042/multi-line_comment_with_trailing_comma.rs
src/tools/rustfmt/tests/source/issue-5042/multi-line_comment_without_trailing_comma.rs
src/tools/rustfmt/tests/source/issue-5042/single-line_comment_with_trailing_comma.rs
src/tools/rustfmt/tests/source/issue-5042/single-line_comment_without_trailing_comma.rs
src/tools/rustfmt/tests/source/issue-5157/indented_itemized_markdown_blockquote.rs
src/tools/rustfmt/tests/source/issue-5157/nested_itemized_markdown_blockquote.rs
src/tools/rustfmt/tests/source/issue-5157/support_itemized_markdown_blockquote.rs
src/tools/rustfmt/tests/source/issue-5238/markdown_header_wrap_comments_false.rs
src/tools/rustfmt/tests/source/issue-5238/markdown_header_wrap_comments_true.rs
src/tools/rustfmt/tests/source/issue-5270/merge_derives_true.rs
src/tools/rustfmt/tests/source/issue_4854.rs
src/tools/rustfmt/tests/target/5131_crate.rs
src/tools/rustfmt/tests/target/5131_module.rs
src/tools/rustfmt/tests/target/5131_one.rs
src/tools/rustfmt/tests/target/comments-in-lists/format-doc-comments.rs
src/tools/rustfmt/tests/target/comments-in-lists/wrap-comments-false.rs
src/tools/rustfmt/tests/target/comments-in-lists/wrap-comments-not-normalized.rs
src/tools/rustfmt/tests/target/comments-in-lists/wrap-comments-true.rs
src/tools/rustfmt/tests/target/configs/short_array_element_width_threshold/10.rs
src/tools/rustfmt/tests/target/configs/short_array_element_width_threshold/20.rs
src/tools/rustfmt/tests/target/configs/short_array_element_width_threshold/greater_than_max_width.rs
src/tools/rustfmt/tests/target/doc-of-generic-item.rs
src/tools/rustfmt/tests/target/extern.rs
src/tools/rustfmt/tests/target/imports_granularity_module.rs
src/tools/rustfmt/tests/target/issue-4036/one.rs
src/tools/rustfmt/tests/target/issue-4036/three.rs
src/tools/rustfmt/tests/target/issue-4036/two.rs
src/tools/rustfmt/tests/target/issue-4791/buggy.rs
src/tools/rustfmt/tests/target/issue-4791/issue_4928.rs
src/tools/rustfmt/tests/target/issue-4791/no_trailing_comma.rs
src/tools/rustfmt/tests/target/issue-4791/trailing_comma.rs
src/tools/rustfmt/tests/target/issue-5023.rs
src/tools/rustfmt/tests/target/issue-5042/multi-line_comment_with_trailing_comma.rs
src/tools/rustfmt/tests/target/issue-5042/multi-line_comment_without_trailing_comma.rs
src/tools/rustfmt/tests/target/issue-5042/single-line_comment_with_trailing_comma.rs
src/tools/rustfmt/tests/target/issue-5042/single-line_comment_without_trailing_comma.rs
src/tools/rustfmt/tests/target/issue-5125/attributes_in_formal_fuction_parameter.rs
src/tools/rustfmt/tests/target/issue-5125/long_parameter_in_different_positions.rs
src/tools/rustfmt/tests/target/issue-5125/minimum_example.rs
src/tools/rustfmt/tests/target/issue-5125/with_leading_and_inline_comments.rs
src/tools/rustfmt/tests/target/issue-5157/indented_itemized_markdown_blockquote.rs
src/tools/rustfmt/tests/target/issue-5157/nested_itemized_markdown_blockquote.rs
src/tools/rustfmt/tests/target/issue-5157/support_itemized_markdown_blockquote.rs
src/tools/rustfmt/tests/target/issue-5238/markdown_header_wrap_comments_false.rs
src/tools/rustfmt/tests/target/issue-5238/markdown_header_wrap_comments_true.rs
src/tools/rustfmt/tests/target/issue-5270/merge_derives_false.rs
src/tools/rustfmt/tests/target/issue-5270/merge_derives_true.rs
src/tools/rustfmt/tests/target/issue_4854.rs
src/tools/rustfmt/tests/target/issue_5273.rs
src/tools/rustfmt/tests/writemode/source/stdin.rs
src/tools/rustfmt/tests/writemode/target/output.json
src/tools/rustfmt/tests/writemode/target/stdin.json
src/tools/rustfmt/tests/writemode/target/stdin.xml

index db497941642122c107bb3335a7520bcd1761647d,0000000000000000000000000000000000000000..45f63b83c056217722dc91908ed2e89c52f4c559
mode 100644,000000..100644
--- /dev/null
@@@ -1,45 -1,0 +1,49 @@@
 +name: linux
 +on:
 +  push:
 +    branches:
 +      - master
 +  pull_request:
 +
 +jobs:
 +  test:
 +    runs-on: ubuntu-latest
 +    name: (${{ matrix.target }}, ${{ matrix.cfg_release_channel }})
 +    env:
 +      CFG_RELEASE_CHANNEL: ${{ matrix.cfg_release_channel }}
 +    strategy:
 +      # https://help.github.com/en/actions/getting-started-with-github-actions/about-github-actions#usage-limits
 +      # There's a limit of 60 concurrent jobs across all repos in the rust-lang organization.
 +      # In order to prevent overusing too much of that 60 limit, we throttle the
 +      # number of rustfmt jobs that will run concurrently.
 +      max-parallel: 1
 +      fail-fast: false
 +      matrix:
 +        target: [
 +          x86_64-unknown-linux-gnu,
 +        ]
 +        cfg_release_channel: [nightly, stable]
 +
 +    steps:
 +    - name: checkout
 +      uses: actions/checkout@v2
 +
 +      # Run build
 +    - name: install rustup
 +      run: |
 +        curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > rustup-init.sh
 +        sh rustup-init.sh -y --default-toolchain none
 +        rustup target add ${{ matrix.target }}
 +
 +    - name: build
 +      run: |
 +        rustc -Vv
 +        cargo -V
 +        cargo build
++      env:
++        RUSTFLAGS: '-D warnings'
 +
 +    - name: test
 +      run: cargo test
++      env:
++        RUSTFLAGS: '-D warnings'
index b59438dc4fe78a11483f61b2c69b875a006c2f16,0000000000000000000000000000000000000000..5b2336085835650beead28a5bdf5756a30059bce
mode 100644,000000..100644
--- /dev/null
@@@ -1,1235 -1,0 +1,1240 @@@
 +# Changelog
 +
 +## [Unreleased]
 +
++### Fixed
++
++- Fixes issue where wrapped strings would be incorrectly indented in macro defs when `format_strings` was enabled [#4036](https://github.com/rust-lang/rustfmt/issues/4036)
++
 +## [1.4.38] 2021-10-20
 +
 +### Changed
 +
 +- Switched from `rustc-ap-*` crates to `rustc_private` for consumption model of rustc internals
 +- `annotate-snippets` updated to v0.8 [PR #4762](https://github.com/rust-lang/rustfmt/pull/4762)
 +- Greatly improved the performance of `cargo fmt` in large workspaces utilizing the `--all` flag by updating to a newer version of `cargo_metadata` that leverages updated `cargo` output from v1.51+ [PR #4997](https://github.com/rust-lang/rustfmt/pull/4997)
 +- Improved formatting of long slice patterns [#4530](https://github.com/rust-lang/rustfmt/issues/4530)
 +  - **Note you must have `version = Two` in your configuration to take advantage of the new formatting**
 +- Stabilized `match_block_trailing_comma` configuration option [#3380](https://github.com/rust-lang/rustfmt/issues/3380) - [https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#match_block_trailing_comma](https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#match_block_trailing_comma)
 +- Stabilized `disable_all_formatting` configuration option [#5026](https://github.com/rust-lang/rustfmt/pull/5026) - [https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#disable_all_formatting](https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#disable_all_formatting)
 +- Various improvements to the configuration documentation website [https://rust-lang.github.io/rustfmt/?version=v1.4.38]([https://rust-lang.github.io/rustfmt/?version=v1.4.38])
 +- Addressed various clippy and rustc warnings
 +
 +
 +### Fixed
 +
 +- Resolved issue where specious whitespace would be inserted when a block style comment was terminated within string literal processing [#4312](https://github.com/rust-lang/rustfmt/issues/4312)
 +- Nested out-of-line mods are again parsed and formatted [#4874](https://github.com/rust-lang/rustfmt/issues/4874)
 +- Accepts `2021` for edition value from rustfmt command line [PR #4847](https://github.com/rust-lang/rustfmt/pull/4847)
 +- Unstable command line options are no longer displayed in `--help` text on stable [PR #4798](https://github.com/rust-lang/rustfmt/issues/4798)
 +- Stopped panicking on patterns in match arms which start with non-ascii characters [#4868](https://github.com/rust-lang/rustfmt/issues/4868)
 +- Stopped stripping defaults on const params [#4816](https://github.com/rust-lang/rustfmt/issues/4816)
 +- Fixed issue with dropped content with GAT aliases with self bounds in impls [#4911](https://github.com/rust-lang/rustfmt/issues/4911)
 +- Stopped removing generic args on associated type constraints [#4943](https://github.com/rust-lang/rustfmt/issues/4943)
 +- Stopped dropping visibility on certain trait and impl items [#4960](https://github.com/rust-lang/rustfmt/issues/4960)
 +- Fixed dropping of qualified paths in struct patterns [#4908](https://github.com/rust-lang/rustfmt/issues/4908) and [#5005](https://github.com/rust-lang/rustfmt/issues/5005)
 +- Fixed bug in line width calculation that was causing specious formatting of certain patterns [#4031](https://github.com/rust-lang/rustfmt/issues/4031)
 +  - **Note that this bug fix may cause observable formatting changes in cases where code had been formatted with prior versions of rustfmt that contained the bug**
 +- Fixed bug where rustfmt would drop parameter attributes if they were too long in certain cases [#4579](https://github.com/rust-lang/rustfmt/issues/4579)
 +- Resolved idempotency issue with extern body elements [#4963](https://github.com/rust-lang/rustfmt/issues/4963)
 +- rustfmt will now handle doc-style comments on function parameters, since they could appear with certain macro usage patterns even though it's generally invalid syntax [#4936](https://github.com/rust-lang/rustfmt/issues/4936)
 +- Fixed bug in `match_block_trailing_comma` where commas were not added to the blocks of bodies whose arm had a guard that did not fit on the same line as the pattern [#4998](https://github.com/rust-lang/rustfmt/pull/4998)
 +- Fixed bug in cases where derive attributes started with a block style comment [#4984](https://github.com/rust-lang/rustfmt/issues/4984)
 +- Fixed issue where the struct rest could be lost when `struct_field_align_threshold` was enabled [#4926](https://github.com/rust-lang/rustfmt/issues/4926)
 +- Handles cases where certain control flow type expressions have comments between patterns/keywords and the pattern ident contains the keyword [#5009](https://github.com/rust-lang/rustfmt/issues/5009)
 +- Handles tuple structs that have explicit visibilities and start with a block style comment [#5011](https://github.com/rust-lang/rustfmt/issues/5011)
 +- Handles leading line-style comments in certain types of macro calls [#4615](https://github.com/rust-lang/rustfmt/issues/4615)
 +
 +
 +### Added
 +- Granular width heuristic options made available for user control [PR #4782](https://github.com/rust-lang/rustfmt/pull/4782). This includes the following:
 +  - [`array_width`](https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#array_width)
 +  - [`attr_fn_like_width`](https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#attr_fn_like_width)
 +  - [`chain_width`](https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#chain_width)
 +  - [`fn_call_width`](https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#fn_call_width)
 +  - [`single_line_if_else_max_width`](https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#single_line_if_else_max_width)
 +  - [`struct_lit_width`](https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#struct_lit_width)
 +  - [`struct_variant_width`](https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#struct_variant_width)
 +
 +Note this hit the rustup distributions prior to the v1.4.38 release as part of an out-of-cycle updates, but is listed in this version because the feature was not in the other v1.4.37 releases. See also the `use_small_heuristics` section on the configuration site for more information
 +[https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#use_small_heuristics](https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#use_small_heuristics)
 +
 +- New `One` variant added to `imports_granularity` configuration option which can be used to reformat all imports into a single use statement [#4669](https://github.com/rust-lang/rustfmt/issues/4669)
 +- rustfmt will now skip files that are annotated with `@generated` at the top of the file [#3958](https://github.com/rust-lang/rustfmt/issues/3958)
++  if `format_generated_files` option is set to `false` (by default `@generated` files are formatted)
 +- New configuration option `hex_literal_case` that allows user to control the casing utilized for hex literals [PR #4903](https://github.com/rust-lang/rustfmt/pull/4903)
 +
 +See the section on the configuration site for more information
 +https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#hex_literal_case
 +
 +- `cargo fmt` now directly supports the `--check` flag, which means it's now possible to run `cargo fmt --check` instead of the more verbose `cargo fmt -- --check` [#3888](https://github.com/rust-lang/rustfmt/issues/3888)
 +
 +### Install/Download Options
 +- **rustup (nightly)** - *pending*
 +- **GitHub Release Binaries** - [Release v1.4.38](https://github.com/rust-lang/rustfmt/releases/tag/v1.4.38)
 +- **Build from source** - [Tag v1.4.38](https://github.com/rust-lang/rustfmt/tree/v1.4.38), see instructions for how to [install rustfmt from source][install-from-source]
 +
 +## [1.4.37] 2021-04-03
 +
 +### Changed
 +
 +- `rustc-ap-*` crates updated to v712.0.0
 +
 +### Fixed
 +- Resolve idempotence issue related to indentation of macro defs that contain or-patterns with inner comments ([#4603](https://github.com/rust-lang/rustfmt/issues/4603))
 +- Addressed various clippy and rustc warnings
 +
 +### Install/Download Options
 +- **crates.io package** - *pending*
 +- **rustup (nightly)** - *pending*
 +- **GitHub Release Binaries** - [Release v1.4.37](https://github.com/rust-lang/rustfmt/releases/tag/v1.4.37)
 +- **Build from source** - [Tag v1.4.37](https://github.com/rust-lang/rustfmt/tree/v1.4.37), see instructions for how to [install rustfmt from source][install-from-source]
 +
 +## [1.4.36] 2021-02-07
 +
 +### Changed
 +
 +- `rustc-ap-*` crates updated to v705.0.0
 +
 +### Install/Download Options
 +- **crates.io package** - *pending*
 +- **rustup (nightly)** - *pending*
 +- **GitHub Release Binaries** - [Release v1.4.36](https://github.com/rust-lang/rustfmt/releases/tag/v1.4.36)
 +- **Build from source** - [Tag v1.4.36](https://github.com/rust-lang/rustfmt/tree/v1.4.36), see instructions for how to [install rustfmt from source][install-from-source]
 +
 +## [1.4.35] 2021-02-03
 +
 +### Changed
 +
 +- `rustc-ap-*` crates updated to v702.0.0
 +
 +### Install/Download Options
 +- **crates.io package** - *pending*
 +- **rustup (nightly)** - *n/a (superseded by [v1.4.36](#1436-2021-02-07))
 +- **GitHub Release Binaries** - [Release v1.4.35](https://github.com/rust-lang/rustfmt/releases/tag/v1.4.35)
 +- **Build from source** - [Tag v1.4.35](https://github.com/rust-lang/rustfmt/tree/v1.4.35), see instructions for how to [install rustfmt from source][install-from-source]
 +
 +## [1.4.34] 2021-01-28
 +
 +### Fixed
 +- Don't insert trailing comma on (base-less) rest in struct literals within macros ([#4675](https://github.com/rust-lang/rustfmt/issues/4675))
 +
 +### Install/Download Options
 +- **crates.io package** - *pending*
 +- **rustup (nightly)** - Starting in `2021-01-31`
 +- **GitHub Release Binaries** - [Release v1.4.34](https://github.com/rust-lang/rustfmt/releases/tag/v1.4.34)
 +- **Build from source** - [Tag v1.4.34](https://github.com/rust-lang/rustfmt/tree/v1.4.34), see instructions for how to [install rustfmt from source][install-from-source]
 +
 +## [1.4.33] 2021-01-27
 +
 +### Changed
 +- `merge_imports` configuration has been deprecated in favor of the new `imports_granularity` option. Any existing usage of `merge_imports` will be automatically mapped to the corresponding value on `imports_granularity` with a warning message printed to encourage users to update their config files.
 +
 +### Added
 +- New `imports_granularity` option has been added which succeeds `merge_imports`. This new option supports several additional variants which allow users to merge imports at different levels (crate or module), and even flatten imports to have a single use statement per item. ([PR #4634](https://github.com/rust-lang/rustfmt/pull/4634), [PR #4639](https://github.com/rust-lang/rustfmt/pull/4639))
 +
 +See the section on the configuration site for more information
 +https://rust-lang.github.io/rustfmt/?version=v1.4.33&search=#imports_granularity
 +
 +### Fixed
 +- Fix erroneous removal of `const` keyword on const trait impl ([#4084](https://github.com/rust-lang/rustfmt/issues/4084))
 +- Fix incorrect span usage wit const generics in supertraits ([#4204](https://github.com/rust-lang/rustfmt/issues/4204))
 +- Use correct span for const generic params ([#4263](https://github.com/rust-lang/rustfmt/issues/4263))
 +- Correct span on const generics to include type bounds ([#4310](https://github.com/rust-lang/rustfmt/issues/4310))
 +- Idempotence issue on blocks containing only empty statements ([#4627](https://github.com/rust-lang/rustfmt/issues/4627) and [#3868](https://github.com/rust-lang/rustfmt/issues/3868))
 +- Fix issue with semicolon placement on required functions that have a trailing comment that ends in a line-style comment before the semicolon ([#4646](https://github.com/rust-lang/rustfmt/issues/4646))
 +- Avoid shared interned cfg_if symbol since rustfmt can re-initialize the rustc_ast globals on multiple inputs ([#4656](https://github.com/rust-lang/rustfmt/issues/4656))
 +
 +### Install/Download Options
 +- **crates.io package** - *pending*
 +- **rustup (nightly)** - n/a (superseded by [v1.4.34](#1434-2021-01-28))
 +- **GitHub Release Binaries** - [Release v1.4.33](https://github.com/rust-lang/rustfmt/releases/tag/v1.4.33)
 +- **Build from source** - [Tag v1.4.33](https://github.com/rust-lang/rustfmt/tree/v1.4.33), see instructions for how to [install rustfmt from source][install-from-source]
 +
 +## [1.4.32] 2021-01-16
 +
 +### Fixed
 +- Indentation now correct on first bound in cases where the generic bounds are multiline formatted and the first bound itself is multiline formatted ([#4636](https://github.com/rust-lang/rustfmt/issues/4636))
 +
 +### Install/Download Options
 +- **crates.io package** - *pending*
 +- **rustup (nightly)** - Starting in `2021-01-18`
 +- **GitHub Release Binaries** - [Release v1.4.32](https://github.com/rust-lang/rustfmt/releases/tag/v1.4.32)
 +- **Build from source** - [Tag v1.4.32](https://github.com/rust-lang/rustfmt/tree/v1.4.32), see instructions for how to [install rustfmt from source][install-from-source]
 +
 +## [1.4.31] 2021-01-09
 +
 +### Changed
 +
 +- `rustc-ap-*` crates updated to v697.0.0
 +
 +### Added
 +- Support for 2021 Edition [#4618](https://github.com/rust-lang/rustfmt/pull/4618))
 +
 +### Install/Download Options
 +- **crates.io package** - *pending*
 +- **rustup (nightly)** - Starting in `2021-01-16`
 +- **GitHub Release Binaries** - [Release v1.4.31](https://github.com/rust-lang/rustfmt/releases/tag/v1.4.31)
 +- **Build from source** - [Tag v1.4.31](https://github.com/rust-lang/rustfmt/tree/v1.4.31), see instructions for how to [install rustfmt from source][install-from-source]
 +
 +## [1.4.30] 2020-12-20
 +
 +### Fixed
 +- Last character in derive no longer erroneously stripped when `indent_style` is overridden to `Visual`. ([#4584](https://github.com/rust-lang/rustfmt/issues/4584))
 +- Brace wrapping of closure bodies maintained in cases where the closure has an explicit return type and the body consists of a single expression statement. ([#4577](https://github.com/rust-lang/rustfmt/issues/4577))
 +- No more panics on invalid code with `err` and `typeof` types ([#4357](https://github.com/rust-lang/rustfmt/issues/4357), [#4586](https://github.com/rust-lang/rustfmt/issues/4586))
 +
 +### Install/Download Options
 +- **crates.io package** - *pending*
 +- **rustup (nightly)** - Starting in `2020-12-25`
 +- **GitHub Release Binaries** - [Release v1.4.30](https://github.com/rust-lang/rustfmt/releases/tag/v1.4.30)
 +- **Build from source** - [Tag v1.4.30](https://github.com/rust-lang/rustfmt/tree/v1.4.30), see instructions for how to [install rustfmt from source][install-from-source]
 +
 +## [1.4.29] 2020-12-04
 +
 +### Fixed
 +- Negative polarity on non-trait impl now preserved. ([#4566](https://github.com/rust-lang/rustfmt/issues/4566))
 +
 +### Install/Download Options
 +- **crates.io package** - *pending*
 +- **rustup (nightly)** - Starting in `2020-12-07`
 +- **GitHub Release Binaries** - [Release v1.4.29](https://github.com/rust-lang/rustfmt/releases/tag/v1.4.29)
 +- **Build from source** - [Tag v1.4.29](https://github.com/rust-lang/rustfmt/tree/v1.4.29), see instructions for how to [install rustfmt from source][install-from-source]
 +
 +## [1.4.28] 2020-11-29
 +
 +### Changed
 +
 +- `rustc-ap-*` crates updated to v691.0.0
 +- In the event of an invalid inner attribute on a `cfg_if` condition, rustfmt will now attempt to continue and format the imported modules. Previously rustfmt would emit the parser error about an inner attribute being invalid in this position, but for rustfmt's purposes the invalid attribute doesn't prevent nor impact module formatting.
 +
 +### Added
 +
 +- [`group_imports`][group-imports-config-docs] - a new configuration option that allows users to control the strategy used for grouping imports ([#4107](https://github.com/rust-lang/rustfmt/issues/4107))
 +
 +[group-imports-config-docs]: https://github.com/rust-lang/rustfmt/blob/v1.4.28/Configurations.md#group_imports
 +
 +### Fixed
 +- Formatting of malformed derived attributes is no longer butchered. ([#3898](https://github.com/rust-lang/rustfmt/issues/3898), [#4029](https://github.com/rust-lang/rustfmt/issues/4029), [#4115](https://github.com/rust-lang/rustfmt/issues/4115), [#4545](https://github.com/rust-lang/rustfmt/issues/4545))
 +- Correct indentation used in macro branches when `hard_tabs` is enabled. ([#4152](https://github.com/rust-lang/rustfmt/issues/4152))
 +- Comments between the visibility modifier and item name are no longer dropped. ([#2781](https://github.com/rust-lang/rustfmt/issues/2781))
 +- Comments preceding the assignment operator in type aliases are no longer dropped. ([#4244](https://github.com/rust-lang/rustfmt/issues/4244))
 +- Comments between {`&` operator, lifetime, `mut` kw, type} are no longer dropped. ([#4245](https://github.com/rust-lang/rustfmt/issues/4245))
 +- Comments between type bounds are no longer dropped. ([#4243](https://github.com/rust-lang/rustfmt/issues/4243))
 +- Function headers are no longer dropped on foreign function items. ([#4288](https://github.com/rust-lang/rustfmt/issues/4288))
 +- Foreign function blocks are no longer dropped. ([#4313](https://github.com/rust-lang/rustfmt/issues/4313))
 +- `where_single_line` is no longer incorrectly applied to multiline function signatures that have no `where` clause. ([#4547](https://github.com/rust-lang/rustfmt/issues/4547))
 +- `matches!` expressions with multiple patterns and a destructure pattern are now able to be formatted. ([#4512](https://github.com/rust-lang/rustfmt/issues/4512))
 +
 +### Install/Download Options
 +- **crates.io package** - *pending*
 +- **rustup (nightly)** - n/a (superseded by [v1.4.29](#1429-2020-12-04))
 +- **GitHub Release Binaries** - [Release v1.4.28](https://github.com/rust-lang/rustfmt/releases/tag/v1.4.28)
 +- **Build from source** - [Tag v1.4.28](https://github.com/rust-lang/rustfmt/tree/v1.4.28), see instructions for how to [install rustfmt from source][install-from-source]
 +
 +## [1.4.27] 2020-11-16
 +
 +### Fixed
 +
 +- Leading comments in an extern block are no longer dropped (a bug that exists in v1.4.26). ([#4528](https://github.com/rust-lang/rustfmt/issues/4528))
 +
 +### Install/Download Options
 +- **crates.io package** - *pending*
 +- **rustup (nightly)** - Starting in `2020-11-18`
 +- **GitHub Release Binaries** - [Release v1.4.27](https://github.com/rust-lang/rustfmt/releases/tag/v1.4.27)
 +- **Build from source** - [Tag v1.4.27](https://github.com/rust-lang/rustfmt/tree/v1.4.27), see instructions for how to [install rustfmt from source][install-from-source]
 +
 +## [1.4.26] 2020-11-14
 +
 +### Changed
 +
 +- Original comment indentation for trailing comments within an `if` is now taken into account when determining the indentation level to use for the trailing comment in formatted code. This does not modify any existing code formatted with rustfmt; it simply gives the programmer discretion to specify whether the comment is associated to the `else` block, or if the trailing comment is just a member of the `if` block. ([#1575](https://github.com/rust-lang/rustfmt/issues/1575), [#4120](https://github.com/rust-lang/rustfmt/issues/4120), [#4506](https://github.com/rust-lang/rustfmt/issues/4506))
 +
 +In this example the `// else comment` refers to the `else`:
 +```rust
 +// if comment
 +if cond {
 +    "if"
 +// else comment
 +} else {
 +    "else"
 +}
 +```
 +
 +Whereas in this case the `// continue` comments are members of their respective blocks and do not refer to the `else` below.
 +```rust
 +if toks.eat_token(Token::Word("modify"))? && toks.eat_token(Token::Word("labels"))? {
 +    if toks.eat_token(Token::Colon)? {
 +        // ate the token
 +    } else if toks.eat_token(Token::Word("to"))? {
 +        // optionally eat the colon after to, e.g.:
 +        // @rustbot modify labels to: -S-waiting-on-author, +S-waiting-on-review
 +        toks.eat_token(Token::Colon)?;
 +    } else {
 +        // It's okay if there's no to or colon, we can just eat labels
 +        // afterwards.
 +    }
 +    1 + 2;
 +    // continue
 +} else if toks.eat_token(Token::Word("label"))? {
 +    // continue
 +} else {
 +    return Ok(None);
 +}
 +```
 +
 +### Fixed
 +- Formatting of empty blocks with attributes which only contained comments is no longer butchered.([#4475](https://github.com/rust-lang/rustfmt/issues/4475), [#4467](https://github.com/rust-lang/rustfmt/issues/4467), [#4452](https://github.com/rust-lang/rustfmt/issues/4452#issuecomment-705886282), [#4522](https://github.com/rust-lang/rustfmt/issues/4522))
 +- Indentation of trailing comments in non-empty extern blocks is now correct. ([#4120](https://github.com/rust-lang/rustfmt/issues/4120#issuecomment-696491872))
 +
 +### Install/Download Options
 +- **crates.io package** - *pending*
 +- **rustup (nightly)** - Starting in `2020-11-16`
 +- **GitHub Release Binaries** - [Release v1.4.26](https://github.com/rust-lang/rustfmt/releases/tag/v1.4.26)
 +- **Build from source** - [Tag v1.4.26](https://github.com/rust-lang/rustfmt/tree/v1.4.26), see instructions for how to [install rustfmt from source][install-from-source]
 +
 +## [1.4.25] 2020-11-10
 +
 +### Changed
 +
 +- Semicolons are no longer automatically inserted on trailing expressions in macro definition arms ([#4507](https://github.com/rust-lang/rustfmt/pull/4507)). This gives the programmer control and discretion over whether there should be semicolons in these scenarios so that potential expansion issues can be avoided.
 +
 +### Install/Download Options
 +- **crates.io package** - *pending*
 +- **rustup (nightly)** - Starting in `2020-11-14`
 +- **GitHub Release Binaries** - [Release v1.4.25](https://github.com/rust-lang/rustfmt/releases/tag/v1.4.25)
 +- **Build from source** - [Tag v1.4.25](https://github.com/rust-lang/rustfmt/tree/v1.4.25), see instructions for how to [install rustfmt from source][install-from-source]
 +
 +## [1.4.24] 2020-11-05
 +
 +### Changed
 +
 +- Block wrapped match arm bodies containing a single macro call expression are no longer flattened ([#4496](https://github.com/rust-lang/rustfmt/pull/4496)). This allows programmer discretion so that the block wrapping can be preserved in cases where needed to prevent issues in expansion, such as with trailing semicolons, and aligns with updated [Style Guide guidance](https://github.com/rust-dev-tools/fmt-rfcs/blob/master/guide/expressions.md#macro-call-expressions) for such scenarios.
 +
 +### Fixed
 +- Remove useless `deprecated` attribute on a trait impl block in the rustfmt lib, as these now trigger errors ([rust-lang/rust/#78626](https://github.com/rust-lang/rust/pull/78626))
 +
 +### Install/Download Options
 +- **crates.io package** - *pending*
 +- **rustup (nightly)** - Starting in `2020-11-09`
 +- **GitHub Release Binaries** - [Release v1.4.24](https://github.com/rust-lang/rustfmt/releases/tag/v1.4.24)
 +- **Build from source** - [Tag v1.4.24](https://github.com/rust-lang/rustfmt/tree/v1.4.24), see instructions for how to [install rustfmt from source][install-from-source]
 +
 +## [1.4.23] 2020-10-30
 +
 +### Changed
 +
 +- Update `rustc-ap-*` crates to v686.0.0
 +
 +### Added
 +- Initial support for formatting new ConstBlock syntax ([#4478](https://github.com/rust-lang/rustfmt/pull/4478))
 +
 +### Fixed
 +- Handling of unclosed delimiter-only parsing errors in input files ([#4466](https://github.com/rust-lang/rustfmt/issues/4466))
 +- Misc. minor parser bugs ([#4418](https://github.com/rust-lang/rustfmt/issues/4418) and [#4431](https://github.com/rust-lang/rustfmt/issues/4431))
 +- Panic on nested tuple access ([#4355](https://github.com/rust-lang/rustfmt/issues/4355))
 +- Unable to disable license template path via cli override ([#4487](https://github.com/rust-lang/rustfmt/issues/4487))
 +- Preserve comments in empty statements [#4018](https://github.com/rust-lang/rustfmt/issues/4018))
 +- Indentation on skipped code [#4398](https://github.com/rust-lang/rustfmt/issues/4398))
 +
 +### Install/Download Options
 +- **crates.io package** - *pending*
 +- **rustup (nightly)** - n/a (superseded by [v1.4.24](#1424-2020-11-05))
 +- **GitHub Release Binaries** - [Release v1.4.23](https://github.com/rust-lang/rustfmt/releases/tag/v1.4.23)
 +- **Build from source** - [Tag v1.4.23](https://github.com/rust-lang/rustfmt/tree/v1.4.23), see instructions for how to [install rustfmt from source][install-from-source]
 +
 +
 +
 +## [1.4.22] 2020-10-04
 +
 +### Changed
 +
 +- Update `rustc-ap-*` crates to v679.0.0
 +- Add config option to allow control of leading match arm pipes
 +- Support `RUSTFMT` environment variable in `cargo fmt` to run specified `rustfmt` instance
 +
 +### Fixed
 +
 +- Fix preservation of type aliases within extern blocks
 +
 +
 +## [1.4.9] 2019-10-07
 +
 +### Changed
 +
 +- Update `rustc-ap-*` crates to 606.0.0.
 +
 +### Fixed
 +
 +- Fix aligning comments of different group
 +- Fix flattening imports with a single `self`.
 +- Fix removing attributes on function parameters.
 +- Fix removing `impl` keyword from opaque type.
 +
 +## [1.4.8] 2019-09-08
 +
 +### Changed
 +
 +- Update `rustc-ap-*` crates to 583.0.0.
 +
 +## [1.4.7] 2019-09-06
 +
 +### Added
 +
 +- Add `--config` command line option.
 +
 +### Changed
 +
 +- Update `rustc-ap-*` crates to 581.0.0.
 +- rustfmt now do not warn against trailing whitespaces inside macro calls.
 +
 +### Fixed
 +
 +- Fix `merge_imports` generating invalid code.
 +- Fix removing discriminant values on enum variants.
 +- Fix modules defined inside `cfg_if!` not being formatted.
 +- Fix minor formatting issues.
 +
 +## [1.4.6] 2019-08-28
 +
 +### Added
 +
 +- Add `--message-format` command line option to `cargo-fmt`.
 +- Add `-l,--files-with-diff` command line option to `rustfmt`.
 +- Add `json` emit mode.
 +
 +### Fixed
 +
 +- Fix removing attributes on struct pattern's fields.
 +- Fix non-idempotent formatting of match arm.
 +- Fix `merge_imports` generating invalid code.
 +- Fix imports with `#![macro_use]` getting reordered with `reorder_imports`.
 +- Fix calculation of line numbers in checkstyle output.
 +- Fix poor formatting of complex fn type.
 +
 +## [1.4.5] 2019-08-13
 +
 +### Fixed
 +
 +- Fix generating invalid code when formatting an impl block with const generics inside a where clause.
 +- Fix adding a trailing space after a `dyn` keyword which is used as a macro argument by itself.
 +
 +## [1.4.4] 2019-08-06
 +
 +### Fixed
 +
 +- Fix `cargo fmt` incorrectly formatting crates that is not part of the workspace or the path dependencies.
 +- Fix removing a trailing comma from a tuple pattern.
 +
 +## [1.4.3] 2019-08-02
 +
 +### Changed
 +
 +- Update `rustc-ap-*` crates to 546.0.0.
 +
 +### Fixed
 +
 +- Fix an underscore pattern getting removed.
 +
 +## [1.4.2] 2019-07-31
 +
 +### Changed
 +
 +- Explicitly require the version of `rustfmt-config_proc_macro` to be 0.1.2 or later.
 +
 +## [1.4.1] 2019-07-30
 +
 +### Changed
 +
 +- Update `rustc-ap-*` crates to 542.0.0.
 +
 +## [1.4.0] 2019-07-29
 +
 +### Added
 +
 +- Add new attribute `rustfmt::skip::attributes` to prevent rustfmt
 +from formatting an attribute #3665
 +
 +### Changed
 +
 +- Update `rustc-ap-*` crates to 541.0.0.
 +- Remove multiple semicolons.
 +
 +## [1.3.3] 2019-07-15
 +
 +### Added
 +
 +- Add `--manifest-path` support to `cargo fmt` (#3683).
 +
 +### Fixed
 +
 +- Fix `cargo fmt -- --help` printing nothing (#3620).
 +- Fix inserting an extra comma (#3677).
 +- Fix incorrect handling of CRLF with `file-lines` (#3684).
 +- Fix `print-config=minimal` option (#3687).
 +
 +## [1.3.2] 2019-07-06
 +
 +### Fixed
 +
 +- Fix rustfmt crashing when `await!` macro call is used in a method chain.
 +- Fix rustfmt not recognizing a package whose name differs from its directory's name.
 +
 +## [1.3.1] 2019-06-30
 +
 +### Added
 +
 +- Implement the `Display` trait on the types of `Config`.
 +
 +### Changed
 +
 +- `ignore` configuration option now only supports paths separated by `/`. Windows-style paths are not supported.
 +- Running `cargo fmt` in a sub-directory of a project is now supported.
 +
 +### Fixed
 +
 +- Fix bugs that may cause rustfmt to crash.
 +
 +## [1.3.0] 2019-06-09
 +
 +### Added
 +
 +- Format modules defined inside `cfg_if` macro calls #3600
 +
 +### Changed
 +
 +- Change option `format_doc_comment` to `format_code_in_doc_comment`.
 +- `use_small_heuristics` changed to be an enum and stabilised. Configuration
 +  options are now ready for 1.0.
 +- Stabilise `fn_args_density` configuration option and rename it to `fn_args_layout` #3581
 +- Update `rustc-ap-*` crates to 486.0.0
 +- Ignore sub-modules when skip-children is used #3607
 +- Removed bitrig support #3608
 +
 +### Fixed
 +
 +- `wrap_comments` should not imply `format_doc_comments` #3535
 +- Incorrect handling of const generics #3555
 +- Add the handling for `vec!` with paren inside macro #3576
 +- Format trait aliases with where clauses #3586
 +- Catch panics from the parser while rewriting macro calls #3589
 +- Fix erasing inner attributes in struct #3593
 +- Inline the attribute with its item even with the `macro_use` attribute or when `reorder_imports` is disabled #3598
 +- Fix the bug add unwanted code to impl #3602
 +
 +## [1.2.2] 2019-04-24
 +
 +### Fixed
 +
 +- Fix processing of `ignore` paths #3522
 +- Attempt to format attributes if only they exist #3523
 +
 +## [1.2.1] 2019-04-18
 +
 +### Added
 +
 +- Add `--print-config current` CLI option b473e65
 +- Create GitHub [page](https://rust-lang.github.io/rustfmt/) for Configuration.md #3485
 +
 +### Fixed
 +
 +- Keep comment appearing between parameter's name and its type #3491
 +- Do not delete semicolon after macro call with square brackets #3500
 +- Fix `--version` CLI option #3506
 +- Fix duplication of attributes on a match arm's body #3510
 +- Avoid overflowing item with attributes #3511
 +
 +## [1.2.0] 2019-03-27
 +
 +### Added
 +
 +- Add new attribute `rustfmt::skip::macros` to prevent rustfmt from formatting a macro #3454
 +
 +### Changed
 +
 +- Discard error report in silent_emitter #3466
 +
 +### Fixed
 +
 +- Fix bad performance on deeply nested binary expressions #3467
 +- Use BTreeMap to guarantee consistent ordering b4d4b57
 +
 +## [1.1.1] 2019-03-21
 +
 +### Fixed
 +
 +- Avoid panic on macro inside deeply nested block c9479de
 +- Fix line numbering in missed spans and handle file_lines in edge cases cdd08da
 +- Fix formatting of async blocks 1fa06ec
 +- Avoid duplication on the presence of spaces between macro name and `!` #3464
 +
 +## [1.1.0] 2019-03-17
 +
 +### Added
 +
 +- Add `inline_attribute_width` configuration option to write an item and its attribute on the same line if their combined width is below a threshold #3409
 +- Support `const` generics f0c861b
 +- Support path clarity module #3448
 +
 +### Changed
 +
 +- Align loop and while formatting 7d9a2ef
 +- Support `EmitMode::ModifiedLines` with stdin input #3424
 +- Update `rustc-ap-*` crates to 407.0.0
 +- Remove trailing whitespaces in missing spans 2d5bc69
 +
 +### Fixed
 +
 +- Do not remove comment in the case of no arg 8e3ef3e
 +- Fix `Ident of macro+ident gets duplicated` error 40ff078
 +- Format the if expression at the end of the block in a single line 5f3dfe6
 +
 +## [1.0.3] 2019-02-14
 +
 +### Added
 +
 +- Point unstable options to tracking issues 412dcc7
 +
 +### Changed
 +
 +- Update `rustc-ap-*` crates to 373.0.0
 +
 +## [1.0.2] 2019-02-12
 +
 +### Added
 +
 +- Add a [section](https://github.com/rust-lang/rustfmt/blob/ae331be/Contributing.md#version-gate-formatting-changes) to the Contributing.md file about version-gating formatting changes 36e2cb0
 +- Allow specifying package with `-p` CLI option a8d2591
 +- Support `rustfmt::skip` on imports #3289
 +- Support global `rustfmt.toml` to be written in user config directory #3280
 +- Format visibility on trait alias 96a3df3
 +
 +### Changed
 +
 +- Do not modify original source code inside macro call #3260
 +- Recognize strings inside comments in order to avoid indenting them baa62c6
 +- Use Unicode-standard char width to wrap comments or strings a01990c
 +- Change new line point in the case of no args #3294
 +- Use the same formatting rule between functions and macros #3298
 +- Update rustc-ap-rustc_target to 366.0.0, rustc-ap-syntax to 366.0.0, and rustc-ap-syntax_pos to 366.0.0
 +
 +### Fixed
 +
 +- rewrite_comment: fix block fallback when failing to rewrite an itemized block ab7f4e1
 +- Catch possible tokenizer panics #3240
 +- Fix macro indentation on Windows #3266
 +- Fix shape when formatting return or break expr on statement position #3259
 +- rewrite_comment: fix block fallback when failing to rewrite an itemized block
 +- Keep leading double-colon to respect the 2018 edition of rust's paths a2bfc02
 +- Fix glob and nested global imports 2125ad2
 +- Do not force trailing comma when using mixed layout #3306
 +- Prioritize `single_line_fn` and `empty_item_single_line` over `brace_style` #3308
 +- Fix `internal error: left behind trailing whitespace` with long lines c2534f5
 +- Fix attribute duplication #3325
 +- Fix formatting of strings within a macro 813aa79
 +- Handle a macro argument with a single keyword 9a7ea6a
 +
 +## [1.0.1] 2018-12-09
 +
 +### Added
 +
 +- Add a `version` option 378994b
 +
 +### Changed
 +
 +- End expressions like return/continue/break with a semicolon #3223
 +- Update rustc-ap-rustc_target to 306.0.0, rustc-ap-syntax to 306.0.0, and rustc-ap-syntax_pos to 306.0.0
 +
 +### Fixed
 +
 +- Allow to run a rustfmt command from cargo-fmt even when there is no target a2da636
 +- Fix `un-closed delimiter` errors when formatting break labels 40174e9
 +
 +## [1.0.0] 2018-11-19
 +
 +### Changed
 +
 +- Preserve possibly one whitespace for brace macros 1a3bc79
 +- Prefer to break arguments over putting output type on the next line 1dd54e6
 +
 +## [0.99.9] 2018-11-15
 +
 +### Changed
 +
 +- Update rustc-ap-rustc_target to 297.0.0, rustc-ap-syntax to 297.0.0, to rustc-ap-syntax_pos to 297.0.0
 +- Don't align comments on `extern crate`s dd7add7
 +
 +## [0.99.8] 2018-11-14
 +
 +### Added
 +
 +- Add `overflow_delimited_expr` config option to more aggressively allow overflow #3175
 +
 +### Fixed
 +
 +- Fix the logic for retaining a comment before the arrow in a match #3181
 +- Do not wrap comments in doctest to avoid failing doctest runs #3183
 +- Fix comment rewriting that was wrapping code into a line comment #3188
 +- Fix formatting of unit-struct with `where`-clause #3200
 +
 +## [0.99.7] 2018-11-07
 +
 +### Changed
 +
 +- Force a newline after the `if` condition if there is a different indentation level #3109
 +- Use correct width when formatting type on local statement #3126
 +- Treat crates non-alphabetically when ordering 799005f
 +- Fix formatting of code that is annotated with rustfmt::skip #3113
 +- Stabilize `edition` configuration option 9c3ae2d
 +- cargo-fmt: detect Rust edition in use #3129
 +- Trim the indentation on macros which heuristically appear to use block-style indentation #3178
 +
 +### Fixed
 +
 +- Do not remove path disambiugator inside macro #3142
 +- Improve handling of Windows newlines #3141
 +- Fix alignment of a struct's fields (`struct_field_align_threshold` option) with the Visual `indent_style` #3165
 +- Fix a bug in formatting markdown lists within comments #3172
 +
 +## [0.99.6] 2018-10-18
 +
 +### Added
 +
 +- Add `enum_discrim_align_threshold` option to vertically align enum discriminants cc22869
 +- Add `println!`-like heuristic to the `fail` attribute #3067
 +- Handle itemized items inside comments #3083
 +- Add `format_doc_comments` configuration option to control the formatting of code snippets inside comments #3089
 +
 +### Changed
 +
 +- Makes brace behavior consistent with empty bodies for traits and impls 2727d41
 +- Consider a multi-lined array as a block-like expression #3969
 +- Improve formatting of strings #3073
 +- Get rid of extra commas in Visual struct literal formatting #3077
 +- Update rustc-ap-rustc_target to 274.0.0, rustc-ap-syntax to 274.0.0, and rustc-ap-syntax_pos to 274.0.0
 +- Format macro calls with item-like arguments #3080
 +- Avoid control flow expressions conditions to go multi line ef59b34
 +- Simplify multi-lining binop expressions #3101
 +
 +### Fixed
 +
 +- Do not format a code block in documentation if it is annotated with ignore or text 2bcc3a9
 +- Fix inconsistent overflow behavior in Visual style #3078
 +- Fix corner cases of the string formatting implementation #3083
 +- Do not add parens around lifetimes 0ac68c9
 +- Catch parser panic in format_snippet 8c4e92a
 +
 +## [0.99.5] 2018-09-25
 +
 +### Added
 +
 +- Handle leading module separator for 2018 Edition #2952
 +- Add configuration option `normalize_doc_attributes`: convert doc attributes to comments #3002
 +
 +### Changed
 +
 +- Accept 2015 and 2018 instead of Edition2015 and Edition2018 for edition option eec7436
 +- Support platforms without a timer 46e2a2e
 +- Update rustc-ap-rustc_target to 263.0.0, rustc-ap-syntax to 263.0.0, and rustc-ap-syntax_pos to 263.0.0
 +
 +### Fixed
 +
 +- Format of attributes with commas #2971
 +- Fix optional arg condensing #2972
 +- Improve formatting of long function parameters #2981
 +- Fix formatting of raw string literals #2983
 +- Handle chain with try operators with spaces #2986
 +- Use correct shape in Visual tuple rewriting #2987
 +- Impove formatting of arguments with `visual_style = "Visual"` option #2988
 +- Change `print_diff` to output the correct line number 992b179
 +- Propagate errors about failing to rewrite a macro 6f318e3
 +- Handle formatting of long function signature #3010
 +- Fix indent computation of a macro with braces c3edf6d
 +- Format generics on associated types #3035
 +- Incorrect indentation of multiline block match expression #3042
 +- Fix bug in import where two consecutive module separators were possible 98a0ef2
 +- Prevent right-shifting of block comments with bare lines 5fdb6db
 +
 +## [0.99.4] 2018-08-27
 +
 +### Added
 +
 +- Handle formatting of underscore imports #2951
 +- Handle formatting of try blocks #2965
 +
 +### Changed
 +
 +- Update rustc-ap-rustc_target to 237.0.0, rustc-ap-syntax to 237.0.0, and rustc-ap-syntax_pos to 237.0.0 ca19c9a
 +- Consider `dev` channel as nightly for unstable features #2948
 +
 +### Fixed
 +
 +- Fix formatting of patterns with ellipsis # 2942
 +
 +## [0.99.3] 2018-08-23
 +
 +### Added
 +
 +- Use path attribute when searching for modules #2901
 +- Expose FileLines JSON representation to allow external libraries to use the file_lines option #2915
 +
 +### Changed
 +
 +- Replace '--conifig-help' with '--config=help' cb10e06
 +- Improve formatting of slice patterns #2912
 +
 +### Fixed
 +
 +- Format chains with comment #2899
 +- Fix indentation of formatted macro body #2920
 +- Fix indentation of block comments f23e6aa
 +
 +## [0.99.2] 2018-08-07
 +
 +### Changed
 +
 +- Update rustc-ap-rustc_target to 218.0.0, rustc-ap-syntax to 218.0.0, and rustc-ap-syntax_pos to 218.0.0 5c9a2b6
 +- Combine function-like attributes #2900
 +
 +### Fixed
 +
 +- Explicitly handle semicolon after the item in statement position d96e3ca
 +- Fix parsing '#'-hiding of rustdoc 2eca09e
 +
 +## [0.99.1] 2018-08-04
 +
 +### Fixed
 +
 +- fix use statements ordering when a number is present 1928ae7
 +
 +## [0.99.0] 2018-08-03
 +
 +- 1.0 RC release
 +
 +### Changed
 +
 +- Clarification in README.md 30fe66b
 +
 +## [0.9.0] 2018-08-01
 +
 +### Added
 +
 +- Handle raw identifiers 3027c21
 +- Format async closure 60ce411
 +- Add max_width option for all heuristics c2ae39e
 +- Add config option `format_macro_matchers` to format the metavariable matching patterns in macros 79c5ee8
 +- Add config option `format_macro_bodies` to format the bodies of macros 79c5ee8
 +- Format exitential type fc307ff
 +- Support raw identifiers in struct expressions f121b1a
 +- Format Async block and async function 0b25f60
 +
 +### Changed
 +
 +- Update rustc-ap-rustc_target to 211.0.0, rustc-ap-syntax to 211.0.0, and rustc-ap-syntax_pos to 211.0.0
 +- Put each nested import on its own line while putting non-nested imports on the same line as much as possible 42ab258
 +- Respect `empty_item_single_line` config option when formatting empty impls. Put the `where` on its own line to improve readability #2771
 +- Strip leading `|` in match arm patterns 1d4b988
 +- Apply short function call heuristic to attributes 3abebf9
 +- Indent a match guard if the pattern is multiline be4d37d
 +- Change default newline style to `Native` 9d8f381
 +- Improve formatting of series of binop expressions a4cdb68
 +- Trigger an internal error if we skip formatting due to a lost comment b085113
 +- Refactor chain formatting #2838
 +
 +### Fixed
 +
 +- Do not insert spaces around braces with empty body or multiple lines 2f65852
 +- Allow using mixed layout with comments #2766
 +- Handle break labels #2726
 +- fix rewrite_string when a line feed is present 472a2ed
 +- Fix an anomaly with comments and array literals b28a0cd
 +- Check for comments after the `=>` in a match arm 6899471
 +
 +## [0.8.0,0.8.1,0.8.2] 2018-05-28
 +
 +### Added
 +
 +- Use scoped attributes for skip attribute https://github.com/rust-lang/rustfmt/pull/2703
 +
 +### Changed
 +
 +- Comment options `wrap_comments` and `normalize_comments` are reverted back to unstable 416bc4c
 +- Stabilise `reorder_imports` and `reorder_modules` options 7b6d2b4
 +- Remove `spaces_within_parens_and_brackets` option d726492
 +- Stabilise shorthand options: `use_try_shorthand`, `use_field_init_shorthand`, and `force_explicit_abi` 8afe367
 +- Stabilise `remove_nested_parens` and set default to true a70f716
 +- Unstabilise `unstable_features` dd9c15a
 +- Remove `remove_blank_lines_at_start_or_end_of_block` option 2ee8b0e
 +- Update rustc-ap-syntax to 146.0.0 and rustc-ap-rustc_target to 146.0.0 2c275a2
 +- Audit the public API #2639
 +
 +### Fixed
 +
 +- Handle code block in doc comment without rust prefix f1974e2
 +
 +## [0.7.0] 2018-05-14
 +
 +### Added
 +
 +- Add integration tests against crates in the rust-lang-nursery c79f39a
 +
 +### Changed
 +
 +- Update rustc-ap-syntax to 128.0.0 and ustc-ap-rustc_target to 128.0.0 195395f
 +- Put operands on its own line when each fits in a single line f8439ce
 +- Improve CLI options 55ac062 1869888 798bffb 4d9de48 eca7796 8396da1 5d9f5aa
 +
 +### Fixed
 +
 +- Use correct line width for list attribute 61a401a
 +- Avoid flip-flopping impl items when reordering them 37c216c
 +- Formatting breaks short lines when max_width is less than 100 9b36156
 +- Fix variant "Mixed" of imports_layout option 8c8676c
 +- Improve handling of long lines f885039
 +- Fix up lines exceeding max width 51c07f4
 +- Fix handling of modules in non_modrs_mods style cf573e8
 +- Do not duplicate attributes on use items e59ceaf
 +- Do not insert an extra brace in macros with native newlines 4c9ef93
 +
 +## [0.6.1] 2018-05-01
 +
 +### Changed
 +
 +- Change the default value of imports_indent to IndentStyle::Block https://github.com/rust-lang/rustfmt/pull/2662
 +
 +### Fixed
 +
 +- Handle formatting of auto traits 5b5a72c
 +- Use consistent formatting for empty enum and struct https://github.com/rust-lang/rustfmt/pull/2656
 +
 +## [0.6.0] 2018-04-20
 +
 +### Changed
 +
 +- Improve public API 8669004
 +
 +## [0.5.0] 2018-04-20
 +
 +### Added
 +
 +- Add `verbose-diff` CLI option 5194984
 +
 +### Changed
 +
 +- Update rustc-ap-syntax to 103.0.0 dd807e2
 +- Refactor to make a sensible public API ca610d3
 +
 +### Fixed
 +
 +- Add spaces between consecutive `..` `..=` 61d29eb
 +
 +## [0.4.2] 2018-04-12
 +
 +### Added
 +
 +- Handle binary operators and lifetimes 0fd174d
 +- Add reorder_impl_items config option 94f5a05
 +- Add `--unstable-features` CLI option to list unstable options from the `--help` output 8208f8a
 +- Add merge_imports config option 5dd203e
 +
 +### Changed
 +
 +- Format macro arguments with vertical layout ec71459
 +- Reorder imports by default 164cf7d
 +- Do not collapse block around expr with condition on match arm 5b9b7d5
 +- Use vertical layout for complex attributes c77708f
 +- Format array using heuristics for function calls 98c6f7b
 +- Implement stable ordering for impl items with the the following item priority: type, const, macro, then method fa80ddf
 +- Reorder imports by default 164cf7d
 +- Group `extern crate` by default 3a138a2
 +- Make `error_on_line_overflow` false by default f146711
 +- Merge imports with the same prefix into a single nested import 1954513
 +- Squash the various 'reorder imports' option into one 911395a
 +
 +### Fixed
 +
 +- Print version is missing the channel ca6fc67
 +- Do not add the beginning vert to the match arm 1e1d9d4
 +- Follow indent style config when formatting attributes efd295a
 +- Do not insert newline when item is empty a8022f3
 +- Do not indent or unindent inside string literal ec1907b
 +
 +## [0.4.1] 2018-03-16
 +
 +### Added
 +
 +- Add `ignore` configuration option.
 +- Add `license_template_path` configuration option.
 +- Format `lazy_static!`.
 +
 +### Fixed
 +
 +- Fix formatting bugs.
 +- Fix setting `reorder_modules` removing inline modules.
 +- Format attributes on block expressions.
 +- Support `dyn trait` syntax.
 +- Support multiple patterns in `if let` and `while let`.
 +- Support a pattern with parentheses.
 +
 +## [0.4.0] 2018-03-02
 +
 +### Changed
 +
 +- Do not print verbose outputs when formatting with stdin.
 +- Preserve trailing whitespaces in doc comments.
 +- Scale the values of width heuristics by `max_width`.
 +
 +### Fixed
 +
 +- Do not reorder items with `#[macro_use]`.
 +- Fix formatting bugs.
 +- Support the beginning `|` on a match arm.
 +
 +## [0.3.8] 2018-02-04
 +
 +### Added
 +
 +- Format (or at least try to format) `macro_rules!`.
 +
 +## [0.3.7] 2018-02-01
 +
 +### Added
 +
 +- Add `use_field_init_shorthand` config option.
 +- Add `reorder_modules` configuration option.
 +
 +## [0.3.6] 2018-01-18
 +
 +### Fixed
 +
 +- Fix panicking on formatting certain macros (#2371).
 +
 +## [0.3.5] 2018-01-15
 +
 +### Changed
 +
 +- Format code block in comments when `wrap_comments` is set to `true`.
 +- Remove `same_line_attributes` configuration option.
 +- Rename `git-fmt` to `git-rustfmt`.
 +
 +### Fixed
 +
 +- Rustup to `rustc 1.25.0-nightly (e6072a7b3 2018-01-13)`.
 +- Fix formatting bugs.
 +
 +## [0.3.4] 2017-12-23
 +
 +### Added
 +
 +- Add `--version` flag to `cargo-fmt`, allow `cargo fmt --version`.
 +
 +### Fixed
 +
 +- Rustup to `rustc 1.24.0-nightly (5165ee9e2 2017-12-22)`.
 +
 +## [0.3.3] 2017-12-22
 +
 +### Added
 +
 +- Format trait aliases.
 +
 +### Changed
 +
 +- `cargo fmt` will format every workspace member.
 +
 +### Fixed
 +
 +- Rustup to `rustc 1.24.0-nightly (250b49205 2017-12-21)`
 +- Fix formatting bugs.
 +
 +## [0.3.2] 2017-12-15
 +
 +### Changed
 +
 +- Warn when unknown configuration option is used.
 +
 +### Fixed
 +
 +- Rustup to `rustc 1.24.0-nightly (0077d128d 2017-12-14)`.
 +
 +## [0.3.1] 2017-12-11
 +
 +### Added
 +
 +- Add `error_on_unformatted` configuration option.
 +- Add `--error-on-unformatted` command line option.
 +
 +### Changed
 +
 +- Do not report formatting errors on comments or strings by default.
 +- Rename `error_on_line_overflow_comments` to `error_on_unformatted`.
 +
 +### Fixed
 +
 +- Fix formatting bugs.
 +- Fix adding a trailing whitespace inside code block when `wrap_comments = true`.
 +
 +## [0.3.0] 2017-12-11
 +
 +### Added
 +
 +- Support nested imports.
 +
 +### Changed
 +
 +- Do not report errors on skipped items.
 +- Do not format code block inside comments when `wrap_comments = true`.
 +- Keep vertical spaces between items within range.
 +- Format `format!` and its variants using compressed style.
 +- Format `write!` and its variants using compressed style.
 +- Format **simple** array using compressed style.
 +
 +### Fixed
 +
 +- Fix `rustfmt --package package_name` not working properly.
 +- Fix formatting bugs.
 +
 +## [0.2.17] 2017-12-03
 +
 +### Added
 +
 +- Add `blank_lines_lower_bound` and `blank_lines_upper_bound` configuration options.
 +
 +### Changed
 +
 +- Combine configuration options related to width heuristic into `width_heuristic`.
 +- If the match arm's body is `if` expression, force to use block.
 +
 +### Fixed
 +
 +- Fix `cargo fmt --all` being trapped in an infinite loop.
 +- Fix many formatting bugs.
 +
 +### Removed
 +
 +- Remove legacy configuration options.
 +
 +## [0.2.16] 2017-11-21
 +
 +### Added
 +
 +- Remove empty lines at the beginning of the file.
 +- Soft wrapping on doc comments.
 +
 +### Changed
 +
 +- Break before `|` when using multiple lines for match arm patterns.
 +- Combine `control_style`, `where_style` and `*_indent` config options into `indent_style`.
 +- Combine `item_brace_style` and `fn_brace_style` config options into `brace_style`.
 +- Combine config options related spacing around colons into `space_before_colon` and `space_after_colon`.
 +
 +### Fixed
 +
 +- Fix many bugs.
 +
 +## [0.2.15] 2017-11-08
 +
 +### Added
 +
 +- Add git-fmt tool
 +- `where_single_line` configuration option.
 +
 +### Changed
 +
 +- Rename `chain_one_line_max` to `chain_width`.
 +- Change the suffix of indent-related configuration options to `_indent`.
 +
 +## [0.2.14] 2017-11-06
 +
 +### Fixed
 +
 +- Rustup to the latest nightly.
 +
 +## [0.2.13] 2017-10-30
 +
 +### Fixed
 +
 +- Rustup to the latest nightly.
 +
 +## [0.2.12] 2017-10-29
 +
 +### Fixed
 +
 +- Fix a bug that `cargo fmt` hangs forever.
 +
 +## [0.2.11] 2017-10-29
 +
 +### Fixed
 +
 +- Fix a bug that `cargo fmt` crashes.
 +
 +## [0.2.10] 2017-10-28
 +
 +## [0.2.9] 2017-10-16
 +
 +## [0.2.8] 2017-09-28
 +
 +## [0.2.7] 2017-09-21
 +
 +### Added
 +
 +- `binop_separator` configuration option (#1964).
 +
 +### Changed
 +
 +- Use horizontal layout for function call with a single argument.
 +
 +### Fixed
 +
 +- Fix panicking when calling `cargo fmt --all` (#1963).
 +- Refactorings & faster rustfmt.
 +
 +## [0.2.6] 2017-09-14
 +
 +### Fixed
 +
 +- Fix a performance issue with nested block (#1940).
 +- Refactorings & faster rustfmt.
 +
 +## [0.2.5] 2017-08-31
 +
 +### Added
 +
 +- Format and preserve attributes on statements (#1933).
 +
 +### Fixed
 +
 +- Use getters to access `Span` fields (#1899).
 +
 +## [0.2.4] 2017-08-30
 +
 +### Added
 +
 +- Add support for `Yield` (#1928).
 +
 +## [0.2.3] 2017-08-30
 +
 +### Added
 +
 +- `multiline_closure_forces_block` configuration option (#1898).
 +- `multiline_match_arm_forces_block` configuration option (#1898).
 +- `merge_derives` configuration option (#1910).
 +- `struct_remove_empty_braces` configuration option (#1930).
 +- Various refactorings.
 +
 +### Changed
 +
 +- Put single-lined block comments on the same line with list-like structure's item (#1923).
 +- Preserve blank line between doc comment and attribute (#1925).
 +- Put the opening and the closing braces of enum and struct on the same line, even when `item_brace_style = "AlwaysNextLine"` (#1930).
 +
 +### Fixed
 +
 +- Format attributes on `ast::ForeignItem` and take max width into account (#1916).
 +- Ignore empty lines when calculating the shortest indent width inside macro with braces (#1918).
 +- Handle tabs properly inside macro with braces (#1918).
 +- Fix a typo in `compute_budgets_for_args()` (#1924).
 +- Recover comment between keyword (`impl` and `trait`) and `{` which used to get removed (#1925).
 +
 +
 +[install-from-source]: https://github.com/rust-lang/rustfmt#installing-from-source
index 2ef83ddd1ae6c1b6e89361f47705a00304bb1c0f,0000000000000000000000000000000000000000..b932e15ef7461ce0e9794ddcab0d478b75889344
mode 100644,000000..100644
--- /dev/null
@@@ -1,735 -1,0 +1,735 @@@
- version = "0.9.0"
 +# This file is automatically @generated by Cargo.
 +# It is not intended for manual editing.
 +version = 3
 +
 +[[package]]
 +name = "aho-corasick"
 +version = "0.7.6"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d"
 +dependencies = [
 + "memchr",
 +]
 +
 +[[package]]
 +name = "annotate-snippets"
 +version = "0.8.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "d78ea013094e5ea606b1c05fe35f1dd7ea1eb1ea259908d040b25bd5ec677ee5"
 +dependencies = [
 + "yansi-term",
 +]
 +
 +[[package]]
 +name = "ansi_term"
 +version = "0.11.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
 +dependencies = [
 + "winapi",
 +]
 +
 +[[package]]
 +name = "anyhow"
 +version = "1.0.25"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "9267dff192e68f3399525901e709a48c1d3982c9c072fa32f2127a0cb0babf14"
 +
 +[[package]]
 +name = "atty"
 +version = "0.2.13"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90"
 +dependencies = [
 + "libc",
 + "winapi",
 +]
 +
 +[[package]]
 +name = "autocfg"
 +version = "1.0.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
 +
 +[[package]]
 +name = "bitflags"
 +version = "1.2.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
 +
 +[[package]]
 +name = "bstr"
 +version = "0.2.8"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "8d6c2c5b58ab920a4f5aeaaca34b4488074e8cc7596af94e6f8c6ff247c60245"
 +dependencies = [
 + "memchr",
 +]
 +
 +[[package]]
 +name = "bytecount"
 +version = "0.6.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "72feb31ffc86498dacdbd0fcebb56138e7177a8cc5cea4516031d15ae85a742e"
 +dependencies = [
 + "packed_simd_2",
 +]
 +
 +[[package]]
 +name = "camino"
 +version = "1.0.5"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "52d74260d9bf6944e2208aa46841b4b8f0d7ffc0849a06837b2f510337f86b2b"
 +dependencies = [
 + "serde",
 +]
 +
 +[[package]]
 +name = "cargo-platform"
 +version = "0.1.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "0226944a63d1bf35a3b5f948dd7c59e263db83695c9e8bffc4037de02e30f1d7"
 +dependencies = [
 + "serde",
 +]
 +
 +[[package]]
 +name = "cargo_metadata"
 +version = "0.14.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "c297bd3135f558552f99a0daa180876984ea2c4ffa7470314540dff8c654109a"
 +dependencies = [
 + "camino",
 + "cargo-platform",
 + "semver",
 + "serde",
 + "serde_json",
 +]
 +
 +[[package]]
 +name = "cfg-if"
 +version = "0.1.10"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
 +
 +[[package]]
 +name = "cfg-if"
 +version = "1.0.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 +
 +[[package]]
 +name = "clap"
 +version = "2.33.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
 +dependencies = [
 + "ansi_term",
 + "atty",
 + "bitflags",
 + "strsim",
 + "textwrap",
 + "unicode-width",
 + "vec_map",
 +]
 +
 +[[package]]
 +name = "crossbeam-utils"
 +version = "0.8.3"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49"
 +dependencies = [
 + "autocfg",
 + "cfg-if 1.0.0",
 + "lazy_static",
 +]
 +
 +[[package]]
 +name = "derive-new"
 +version = "0.5.8"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "71f31892cd5c62e414316f2963c5689242c43d8e7bbcaaeca97e5e28c95d91d9"
 +dependencies = [
 + "proc-macro2",
 + "quote",
 + "syn",
 +]
 +
 +[[package]]
 +name = "diff"
 +version = "0.1.11"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "3c2b69f912779fbb121ceb775d74d51e915af17aaebc38d28a592843a2dd0a3a"
 +
 +[[package]]
 +name = "dirs"
 +version = "2.0.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3"
 +dependencies = [
 + "cfg-if 0.1.10",
 + "dirs-sys",
 +]
 +
 +[[package]]
 +name = "dirs-sys"
 +version = "0.3.6"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780"
 +dependencies = [
 + "libc",
 + "redox_users",
 + "winapi",
 +]
 +
 +[[package]]
 +name = "either"
 +version = "1.5.3"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
 +
 +[[package]]
 +name = "env_logger"
 +version = "0.8.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "54532e3223c5af90a6a757c90b5c5521564b07e5e7a958681bcd2afad421cdcd"
 +dependencies = [
 + "atty",
 + "humantime",
 + "log",
 + "regex",
 + "termcolor",
 +]
 +
 +[[package]]
 +name = "fnv"
 +version = "1.0.6"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"
 +
 +[[package]]
 +name = "getopts"
 +version = "0.2.21"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
 +dependencies = [
 + "unicode-width",
 +]
 +
 +[[package]]
 +name = "getrandom"
 +version = "0.2.3"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
 +dependencies = [
 + "cfg-if 1.0.0",
 + "libc",
 + "wasi",
 +]
 +
 +[[package]]
 +name = "globset"
 +version = "0.4.6"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "c152169ef1e421390738366d2f796655fec62621dabbd0fd476f905934061e4a"
 +dependencies = [
 + "aho-corasick",
 + "bstr",
 + "fnv",
 + "log",
 + "regex",
 +]
 +
 +[[package]]
 +name = "heck"
 +version = "0.3.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
 +dependencies = [
 + "unicode-segmentation",
 +]
 +
 +[[package]]
 +name = "humantime"
 +version = "2.1.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
 +
 +[[package]]
 +name = "ignore"
 +version = "0.4.17"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "b287fb45c60bb826a0dc68ff08742b9d88a2fea13d6e0c286b3172065aaf878c"
 +dependencies = [
 + "crossbeam-utils",
 + "globset",
 + "lazy_static",
 + "log",
 + "memchr",
 + "regex",
 + "same-file",
 + "thread_local",
 + "walkdir",
 + "winapi-util",
 +]
 +
 +[[package]]
 +name = "itertools"
- checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b"
++version = "0.10.3"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
 +dependencies = [
 + "either",
 +]
 +
 +[[package]]
 +name = "itoa"
 +version = "0.4.4"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f"
 +
 +[[package]]
 +name = "lazy_static"
 +version = "1.4.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 +
 +[[package]]
 +name = "libc"
 +version = "0.2.77"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235"
 +
 +[[package]]
 +name = "libm"
 +version = "0.1.4"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a"
 +
 +[[package]]
 +name = "log"
 +version = "0.4.14"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
 +dependencies = [
 + "cfg-if 1.0.0",
 +]
 +
 +[[package]]
 +name = "memchr"
 +version = "2.2.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e"
 +
 +[[package]]
 +name = "packed_simd_2"
 +version = "0.3.4"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "3278e0492f961fd4ae70909f56b2723a7e8d01a228427294e19cdfdebda89a17"
 +dependencies = [
 + "cfg-if 0.1.10",
 + "libm",
 +]
 +
 +[[package]]
 +name = "proc-macro-error"
 +version = "0.4.11"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "e7959c6467d962050d639361f7703b2051c43036d03493c36f01d440fdd3138a"
 +dependencies = [
 + "proc-macro-error-attr",
 + "proc-macro2",
 + "quote",
 + "syn",
 + "version_check",
 +]
 +
 +[[package]]
 +name = "proc-macro-error-attr"
 +version = "0.4.11"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "e4002d9f55991d5e019fb940a90e1a95eb80c24e77cb2462dd4dc869604d543a"
 +dependencies = [
 + "proc-macro2",
 + "quote",
 + "syn",
 + "syn-mid",
 + "version_check",
 +]
 +
 +[[package]]
 +name = "proc-macro2"
 +version = "1.0.26"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec"
 +dependencies = [
 + "unicode-xid",
 +]
 +
 +[[package]]
 +name = "quote"
 +version = "1.0.6"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea"
 +dependencies = [
 + "proc-macro2",
 +]
 +
 +[[package]]
 +name = "redox_syscall"
 +version = "0.2.4"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "05ec8ca9416c5ea37062b502703cd7fcb207736bc294f6e0cf367ac6fc234570"
 +dependencies = [
 + "bitflags",
 +]
 +
 +[[package]]
 +name = "redox_users"
 +version = "0.4.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
 +dependencies = [
 + "getrandom",
 + "redox_syscall",
 +]
 +
 +[[package]]
 +name = "regex"
 +version = "1.4.3"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a"
 +dependencies = [
 + "aho-corasick",
 + "memchr",
 + "regex-syntax",
 + "thread_local",
 +]
 +
 +[[package]]
 +name = "regex-syntax"
 +version = "0.6.22"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581"
 +
 +[[package]]
 +name = "rustc-workspace-hack"
 +version = "1.0.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "fc71d2faa173b74b232dedc235e3ee1696581bb132fc116fa3626d6151a1a8fb"
 +
 +[[package]]
 +name = "rustfmt-config_proc_macro"
 +version = "0.2.0"
 +dependencies = [
 + "proc-macro2",
 + "quote",
 + "syn",
 +]
 +
 +[[package]]
 +name = "rustfmt-nightly"
 +version = "1.4.38"
 +dependencies = [
 + "annotate-snippets",
 + "anyhow",
 + "bytecount",
 + "cargo_metadata",
 + "derive-new",
 + "diff",
 + "dirs",
 + "env_logger",
 + "getopts",
 + "ignore",
 + "itertools",
 + "lazy_static",
 + "log",
 + "regex",
 + "rustc-workspace-hack",
 + "rustfmt-config_proc_macro",
 + "serde",
 + "serde_json",
 + "structopt",
 + "term",
 + "thiserror",
 + "toml",
 + "unicode-segmentation",
 + "unicode-width",
 + "unicode_categories",
 +]
 +
 +[[package]]
 +name = "ryu"
 +version = "1.0.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8"
 +
 +[[package]]
 +name = "same-file"
 +version = "1.0.5"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "585e8ddcedc187886a30fa705c47985c3fa88d06624095856b36ca0b82ff4421"
 +dependencies = [
 + "winapi-util",
 +]
 +
 +[[package]]
 +name = "semver"
 +version = "1.0.4"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012"
 +dependencies = [
 + "serde",
 +]
 +
 +[[package]]
 +name = "serde"
 +version = "1.0.126"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03"
 +dependencies = [
 + "serde_derive",
 +]
 +
 +[[package]]
 +name = "serde_derive"
 +version = "1.0.126"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43"
 +dependencies = [
 + "proc-macro2",
 + "quote",
 + "syn",
 +]
 +
 +[[package]]
 +name = "serde_json"
 +version = "1.0.59"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "dcac07dbffa1c65e7f816ab9eba78eb142c6d44410f4eeba1e26e4f5dfa56b95"
 +dependencies = [
 + "itoa",
 + "ryu",
 + "serde",
 +]
 +
 +[[package]]
 +name = "strsim"
 +version = "0.8.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
 +
 +[[package]]
 +name = "structopt"
 +version = "0.3.11"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "3fe43617218c0805c6eb37160119dc3c548110a67786da7218d1c6555212f073"
 +dependencies = [
 + "clap",
 + "lazy_static",
 + "structopt-derive",
 +]
 +
 +[[package]]
 +name = "structopt-derive"
 +version = "0.4.4"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "c6e79c80e0f4efd86ca960218d4e056249be189ff1c42824dcd9a7f51a56f0bd"
 +dependencies = [
 + "heck",
 + "proc-macro-error",
 + "proc-macro2",
 + "quote",
 + "syn",
 +]
 +
 +[[package]]
 +name = "syn"
 +version = "1.0.65"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "f3a1d708c221c5a612956ef9f75b37e454e88d1f7b899fbd3a18d4252012d663"
 +dependencies = [
 + "proc-macro2",
 + "quote",
 + "unicode-xid",
 +]
 +
 +[[package]]
 +name = "syn-mid"
 +version = "0.5.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a"
 +dependencies = [
 + "proc-macro2",
 + "quote",
 + "syn",
 +]
 +
 +[[package]]
 +name = "term"
 +version = "0.6.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "c0863a3345e70f61d613eab32ee046ccd1bcc5f9105fe402c61fcd0c13eeb8b5"
 +dependencies = [
 + "dirs",
 + "winapi",
 +]
 +
 +[[package]]
 +name = "termcolor"
 +version = "1.0.5"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "96d6098003bde162e4277c70665bd87c326f5a0c3f3fbfb285787fa482d54e6e"
 +dependencies = [
 + "wincolor",
 +]
 +
 +[[package]]
 +name = "textwrap"
 +version = "0.11.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
 +dependencies = [
 + "unicode-width",
 +]
 +
 +[[package]]
 +name = "thiserror"
 +version = "1.0.6"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "cc6b305ec0e323c7b6cfff6098a22516e0063d0bb7c3d88660a890217dca099a"
 +dependencies = [
 + "thiserror-impl",
 +]
 +
 +[[package]]
 +name = "thiserror-impl"
 +version = "1.0.6"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "45ba8d810d9c48fc456b7ad54574e8bfb7c7918a57ad7a6e6a0985d7959e8597"
 +dependencies = [
 + "proc-macro2",
 + "quote",
 + "syn",
 +]
 +
 +[[package]]
 +name = "thread_local"
 +version = "1.0.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
 +dependencies = [
 + "lazy_static",
 +]
 +
 +[[package]]
 +name = "toml"
 +version = "0.5.3"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "c7aabe75941d914b72bf3e5d3932ed92ce0664d49d8432305a8b547c37227724"
 +dependencies = [
 + "serde",
 +]
 +
 +[[package]]
 +name = "unicode-segmentation"
 +version = "1.3.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "1967f4cdfc355b37fd76d2a954fb2ed3871034eb4f26d60537d88795cfc332a9"
 +
 +[[package]]
 +name = "unicode-width"
 +version = "0.1.6"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "7007dbd421b92cc6e28410fe7362e2e0a2503394908f417b68ec8d1c364c4e20"
 +
 +[[package]]
 +name = "unicode-xid"
 +version = "0.2.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
 +
 +[[package]]
 +name = "unicode_categories"
 +version = "0.1.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
 +
 +[[package]]
 +name = "vec_map"
 +version = "0.8.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
 +
 +[[package]]
 +name = "version_check"
 +version = "0.9.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce"
 +
 +[[package]]
 +name = "walkdir"
 +version = "2.2.9"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "9658c94fa8b940eab2250bd5a457f9c48b748420d71293b165c8cdbe2f55f71e"
 +dependencies = [
 + "same-file",
 + "winapi",
 + "winapi-util",
 +]
 +
 +[[package]]
 +name = "wasi"
 +version = "0.10.2+wasi-snapshot-preview1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
 +
 +[[package]]
 +name = "winapi"
 +version = "0.3.8"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
 +dependencies = [
 + "winapi-i686-pc-windows-gnu",
 + "winapi-x86_64-pc-windows-gnu",
 +]
 +
 +[[package]]
 +name = "winapi-i686-pc-windows-gnu"
 +version = "0.4.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
 +
 +[[package]]
 +name = "winapi-util"
 +version = "0.1.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9"
 +dependencies = [
 + "winapi",
 +]
 +
 +[[package]]
 +name = "winapi-x86_64-pc-windows-gnu"
 +version = "0.4.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 +
 +[[package]]
 +name = "wincolor"
 +version = "1.0.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "96f5016b18804d24db43cebf3c77269e7569b8954a8464501c216cc5e070eaa9"
 +dependencies = [
 + "winapi",
 + "winapi-util",
 +]
 +
 +[[package]]
 +name = "yansi-term"
 +version = "0.1.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "fe5c30ade05e61656247b2e334a031dfd0cc466fadef865bdcdea8d537951bf1"
 +dependencies = [
 + "winapi",
 +]
index 4476f2a449b1f815d56c56fb0ef1ba1c68d5d04c,0000000000000000000000000000000000000000..a47439b9ba96c6a17209c40d603d57292fae7aa9
mode 100644,000000..100644
--- /dev/null
@@@ -1,2864 -1,0 +1,2900 @@@
- - **Stable**: No (tracking issue: [#3389](https://github.com/rust-lang/rustfmt/issues/3386))
 +# Configuring Rustfmt
 +
 +Rustfmt is designed to be very configurable. You can create a TOML file called `rustfmt.toml` or `.rustfmt.toml`, place it in the project or any other parent directory and it will apply the options in that file. If none of these directories contain such a file, both your home directory and a directory called `rustfmt` in your [global config directory](https://docs.rs/dirs/1.0.4/dirs/fn.config_dir.html) (e.g. `.config/rustfmt/`) are checked as well.
 +
 +A possible content of `rustfmt.toml` or `.rustfmt.toml` might look like this:
 +
 +```toml
 +indent_style = "Block"
 +reorder_imports = false
 +```
 +
 +Each configuration option is either stable or unstable.
 +Stable options can be used directly, while unstable options are opt-in.
 +To enable unstable options, set `unstable_features = true` in `rustfmt.toml` or pass `--unstable-features` to rustfmt.
 +
 +# Configuration Options
 +
 +Below you find a detailed visual guide on all the supported configuration options of rustfmt:
 +
 +## `array_width`
 +
 +Maximum width of an array literal before falling back to vertical formatting.
 +
 +- **Default value**: `60`
 +- **Possible values**: any positive integer that is less than or equal to the value specified for [`max_width`](#max_width)
 +- **Stable**: Yes
 +
 +By default this option is set as a percentage of [`max_width`](#max_width) provided by [`use_small_heuristics`](#use_small_heuristics), but a value set directly for `array_width` will take precedence.
 +
 +See also [`max_width`](#max_width) and [`use_small_heuristics`](#use_small_heuristics)
 +
 +## `attr_fn_like_width`
 +
 +Maximum width of the args of a function-like attributes before falling back to vertical formatting.
 +
 +- **Default value**: `70`
 +- **Possible values**: any positive integer that is less than or equal to the value specified for [`max_width`](#max_width)
 +- **Stable**: Yes
 +
 +By default this option is set as a percentage of [`max_width`](#max_width) provided by [`use_small_heuristics`](#use_small_heuristics), but a value set directly for `attr_fn_like_width` will take precedence.
 +
 +See also [`max_width`](#max_width) and [`use_small_heuristics`](#use_small_heuristics)
 +
 +## `binop_separator`
 +
 +Where to put a binary operator when a binary expression goes multiline.
 +
 +- **Default value**: `"Front"`
 +- **Possible values**: `"Front"`, `"Back"`
 +- **Stable**: No (tracking issue: [#3368](https://github.com/rust-lang/rustfmt/issues/3368))
 +
 +#### `"Front"` (default):
 +
 +```rust
 +fn main() {
 +    let or = foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo
 +        || barbarbarbarbarbarbarbarbarbarbarbarbarbarbarbar;
 +
 +    let sum = 123456789012345678901234567890
 +        + 123456789012345678901234567890
 +        + 123456789012345678901234567890;
 +
 +    let range = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
 +        ..bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb;
 +}
 +```
 +
 +#### `"Back"`:
 +
 +```rust
 +fn main() {
 +    let or = foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo ||
 +        barbarbarbarbarbarbarbarbarbarbarbarbarbarbarbar;
 +
 +    let sum = 123456789012345678901234567890 +
 +        123456789012345678901234567890 +
 +        123456789012345678901234567890;
 +
 +    let range = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa..
 +        bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb;
 +}
 +```
 +
 +## `blank_lines_lower_bound`
 +
 +Minimum number of blank lines which must be put between items. If two items have fewer blank lines between
 +them, additional blank lines are inserted.
 +
 +- **Default value**: `0`
 +- **Possible values**: *unsigned integer*
 +- **Stable**: No (tracking issue: [#3382](https://github.com/rust-lang/rustfmt/issues/3382))
 +
 +### Example
 +Original Code (rustfmt will not change it with the default value of `0`):
 +
 +```rust
 +#![rustfmt::skip]
 +
 +fn foo() {
 +    println!("a");
 +}
 +fn bar() {
 +    println!("b");
 +    println!("c");
 +}
 +```
 +
 +#### `1`
 +```rust
 +fn foo() {
 +
 +    println!("a");
 +}
 +
 +fn bar() {
 +
 +    println!("b");
 +
 +    println!("c");
 +}
 +```
 +
 +
 +## `blank_lines_upper_bound`
 +
 +Maximum number of blank lines which can be put between items. If more than this number of consecutive empty
 +lines are found, they are trimmed down to match this integer.
 +
 +- **Default value**: `1`
 +- **Possible values**: any non-negative integer
 +- **Stable**: No (tracking issue: [#3381](https://github.com/rust-lang/rustfmt/issues/3381))
 +
 +### Example
 +Original Code:
 +
 +```rust
 +#![rustfmt::skip]
 +
 +fn foo() {
 +    println!("a");
 +}
 +
 +
 +
 +fn bar() {
 +    println!("b");
 +
 +
 +    println!("c");
 +}
 +```
 +
 +#### `1` (default):
 +```rust
 +fn foo() {
 +    println!("a");
 +}
 +
 +fn bar() {
 +    println!("b");
 +
 +    println!("c");
 +}
 +```
 +
 +#### `2`:
 +```rust
 +fn foo() {
 +    println!("a");
 +}
 +
 +
 +fn bar() {
 +    println!("b");
 +
 +
 +    println!("c");
 +}
 +```
 +
 +See also: [`blank_lines_lower_bound`](#blank_lines_lower_bound)
 +
 +## `brace_style`
 +
 +Brace style for items
 +
 +- **Default value**: `"SameLineWhere"`
 +- **Possible values**: `"AlwaysNextLine"`, `"PreferSameLine"`, `"SameLineWhere"`
 +- **Stable**: No (tracking issue: [#3376](https://github.com/rust-lang/rustfmt/issues/3376))
 +
 +### Functions
 +
 +#### `"SameLineWhere"` (default):
 +
 +```rust
 +fn lorem() {
 +    // body
 +}
 +
 +fn lorem(ipsum: usize) {
 +    // body
 +}
 +
 +fn lorem<T>(ipsum: T)
 +where
 +    T: Add + Sub + Mul + Div,
 +{
 +    // body
 +}
 +```
 +
 +#### `"AlwaysNextLine"`:
 +
 +```rust
 +fn lorem()
 +{
 +    // body
 +}
 +
 +fn lorem(ipsum: usize)
 +{
 +    // body
 +}
 +
 +fn lorem<T>(ipsum: T)
 +where
 +    T: Add + Sub + Mul + Div,
 +{
 +    // body
 +}
 +```
 +
 +#### `"PreferSameLine"`:
 +
 +```rust
 +fn lorem() {
 +    // body
 +}
 +
 +fn lorem(ipsum: usize) {
 +    // body
 +}
 +
 +fn lorem<T>(ipsum: T)
 +where
 +    T: Add + Sub + Mul + Div, {
 +    // body
 +}
 +```
 +
 +### Structs and enums
 +
 +#### `"SameLineWhere"` (default):
 +
 +```rust
 +struct Lorem {
 +    ipsum: bool,
 +}
 +
 +struct Dolor<T>
 +where
 +    T: Eq,
 +{
 +    sit: T,
 +}
 +```
 +
 +#### `"AlwaysNextLine"`:
 +
 +```rust
 +struct Lorem
 +{
 +    ipsum: bool,
 +}
 +
 +struct Dolor<T>
 +where
 +    T: Eq,
 +{
 +    sit: T,
 +}
 +```
 +
 +#### `"PreferSameLine"`:
 +
 +```rust
 +struct Lorem {
 +    ipsum: bool,
 +}
 +
 +struct Dolor<T>
 +where
 +    T: Eq, {
 +    sit: T,
 +}
 +```
 +
 +## `chain_width`
 +
 +Maximum width of a chain to fit on one line.
 +
 +- **Default value**: `60`
 +- **Possible values**: any positive integer that is less than or equal to the value specified for [`max_width`](#max_width)
 +- **Stable**: Yes
 +
 +By default this option is set as a percentage of [`max_width`](#max_width) provided by [`use_small_heuristics`](#use_small_heuristics), but a value set directly for `chain_width` will take precedence.
 +
 +See also [`max_width`](#max_width) and [`use_small_heuristics`](#use_small_heuristics)
 +
 +## `color`
 +
 +Whether to use colored output or not.
 +
 +- **Default value**: `"Auto"`
 +- **Possible values**: "Auto", "Always", "Never"
 +- **Stable**: No (tracking issue: [#3385](https://github.com/rust-lang/rustfmt/issues/3385))
 +
 +## `combine_control_expr`
 +
 +Combine control expressions with function calls.
 +
 +- **Default value**: `true`
 +- **Possible values**: `true`, `false`
 +- **Stable**: No (tracking issue: [#3369](https://github.com/rust-lang/rustfmt/issues/3369))
 +
 +#### `true` (default):
 +
 +```rust
 +fn example() {
 +    // If
 +    foo!(if x {
 +        foo();
 +    } else {
 +        bar();
 +    });
 +
 +    // IfLet
 +    foo!(if let Some(..) = x {
 +        foo();
 +    } else {
 +        bar();
 +    });
 +
 +    // While
 +    foo!(while x {
 +        foo();
 +        bar();
 +    });
 +
 +    // WhileLet
 +    foo!(while let Some(..) = x {
 +        foo();
 +        bar();
 +    });
 +
 +    // ForLoop
 +    foo!(for x in y {
 +        foo();
 +        bar();
 +    });
 +
 +    // Loop
 +    foo!(loop {
 +        foo();
 +        bar();
 +    });
 +}
 +```
 +
 +#### `false`:
 +
 +```rust
 +fn example() {
 +    // If
 +    foo!(
 +        if x {
 +            foo();
 +        } else {
 +            bar();
 +        }
 +    );
 +
 +    // IfLet
 +    foo!(
 +        if let Some(..) = x {
 +            foo();
 +        } else {
 +            bar();
 +        }
 +    );
 +
 +    // While
 +    foo!(
 +        while x {
 +            foo();
 +            bar();
 +        }
 +    );
 +
 +    // WhileLet
 +    foo!(
 +        while let Some(..) = x {
 +            foo();
 +            bar();
 +        }
 +    );
 +
 +    // ForLoop
 +    foo!(
 +        for x in y {
 +            foo();
 +            bar();
 +        }
 +    );
 +
 +    // Loop
 +    foo!(
 +        loop {
 +            foo();
 +            bar();
 +        }
 +    );
 +}
 +```
 +
 +## `comment_width`
 +
 +Maximum length of comments. No effect unless`wrap_comments = true`.
 +
 +- **Default value**: `80`
 +- **Possible values**: any positive integer
 +- **Stable**: No (tracking issue: [#3349](https://github.com/rust-lang/rustfmt/issues/3349))
 +
 +**Note:** A value of `0` results in [`wrap_comments`](#wrap_comments) being applied regardless of a line's width.
 +
 +#### `80` (default; comments shorter than `comment_width`):
 +```rust
 +// Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 +```
 +
 +#### `60` (comments longer than `comment_width`):
 +```rust
 +// Lorem ipsum dolor sit amet,
 +// consectetur adipiscing elit.
 +```
 +
 +See also [`wrap_comments`](#wrap_comments).
 +
 +## `condense_wildcard_suffixes`
 +
 +Replace strings of _ wildcards by a single .. in tuple patterns
 +
 +- **Default value**: `false`
 +- **Possible values**: `true`, `false`
 +- **Stable**: No (tracking issue: [#3384](https://github.com/rust-lang/rustfmt/issues/3384))
 +
 +#### `false` (default):
 +
 +```rust
 +fn main() {
 +    let (lorem, ipsum, _, _) = (1, 2, 3, 4);
 +    let (lorem, ipsum, ..) = (1, 2, 3, 4);
 +}
 +```
 +
 +#### `true`:
 +
 +```rust
 +fn main() {
 +    let (lorem, ipsum, ..) = (1, 2, 3, 4);
 +}
 +```
 +
 +## `control_brace_style`
 +
 +Brace style for control flow constructs
 +
 +- **Default value**: `"AlwaysSameLine"`
 +- **Possible values**: `"AlwaysNextLine"`, `"AlwaysSameLine"`, `"ClosingNextLine"`
 +- **Stable**: No (tracking issue: [#3377](https://github.com/rust-lang/rustfmt/issues/3377))
 +
 +#### `"AlwaysSameLine"` (default):
 +
 +```rust
 +fn main() {
 +    if lorem {
 +        println!("ipsum!");
 +    } else {
 +        println!("dolor!");
 +    }
 +}
 +```
 +
 +#### `"AlwaysNextLine"`:
 +
 +```rust
 +fn main() {
 +    if lorem
 +    {
 +        println!("ipsum!");
 +    }
 +    else
 +    {
 +        println!("dolor!");
 +    }
 +}
 +```
 +
 +#### `"ClosingNextLine"`:
 +
 +```rust
 +fn main() {
 +    if lorem {
 +        println!("ipsum!");
 +    }
 +    else {
 +        println!("dolor!");
 +    }
 +}
 +```
 +
 +## `disable_all_formatting`
 +
 +Don't reformat anything.
 +
 +Note that this option may be soft-deprecated in the future once the [ignore](#ignore) option is stabilized. Nightly toolchain users are encouraged to use [ignore](#ignore) instead when possible.
 +
 +- **Default value**: `false`
 +- **Possible values**: `true`, `false`
 +- **Stable**: Yes
 +
 +## `edition`
 +
 +Specifies which edition is used by the parser.
 +
 +- **Default value**: `"2015"`
 +- **Possible values**: `"2015"`, `"2018"`, `"2021"`
 +- **Stable**: Yes
 +
 +Rustfmt is able to pick up the edition used by reading the `Cargo.toml` file if executed
 +through the Cargo's formatting tool `cargo fmt`. Otherwise, the edition needs to be specified
 +in your config file:
 +
 +```toml
 +edition = "2018"
 +```
 +
 +## `empty_item_single_line`
 +
 +Put empty-body functions and impls on a single line
 +
 +- **Default value**: `true`
 +- **Possible values**: `true`, `false`
 +- **Stable**: No (tracking issue: [#3356](https://github.com/rust-lang/rustfmt/issues/3356))
 +
 +#### `true` (default):
 +
 +```rust
 +fn lorem() {}
 +
 +impl Lorem {}
 +```
 +
 +#### `false`:
 +
 +```rust
 +fn lorem() {
 +}
 +
 +impl Lorem {
 +}
 +```
 +
 +See also [`brace_style`](#brace_style), [`control_brace_style`](#control_brace_style).
 +
 +
 +## `enum_discrim_align_threshold`
 +
 +The maximum length of enum variant having discriminant, that gets vertically aligned with others.
 +Variants without discriminants would be ignored for the purpose of alignment.
 +
 +Note that this is not how much whitespace is inserted, but instead the longest variant name that
 +doesn't get ignored when aligning.
 +
 +- **Default value** : 0
 +- **Possible values**: any positive integer
 +- **Stable**: No (tracking issue: [#3372](https://github.com/rust-lang/rustfmt/issues/3372))
 +
 +#### `0` (default):
 +
 +```rust
 +enum Bar {
 +    A = 0,
 +    Bb = 1,
 +    RandomLongVariantGoesHere = 10,
 +    Ccc = 71,
 +}
 +
 +enum Bar {
 +    VeryLongVariantNameHereA = 0,
 +    VeryLongVariantNameHereBb = 1,
 +    VeryLongVariantNameHereCcc = 2,
 +}
 +```
 +
 +#### `20`:
 +
 +```rust
 +enum Foo {
 +    A   = 0,
 +    Bb  = 1,
 +    RandomLongVariantGoesHere = 10,
 +    Ccc = 2,
 +}
 +
 +enum Bar {
 +    VeryLongVariantNameHereA = 0,
 +    VeryLongVariantNameHereBb = 1,
 +    VeryLongVariantNameHereCcc = 2,
 +}
 +```
 +
 +
 +## `error_on_line_overflow`
 +
 +Error if Rustfmt is unable to get all lines within `max_width`, except for comments and string
 +literals. If this happens, then it is a bug in Rustfmt. You might be able to work around the bug by
 +refactoring your code to avoid long/complex expressions, usually by extracting a local variable or
 +using a shorter name.
 +
 +- **Default value**: `false`
 +- **Possible values**: `true`, `false`
 +- **Stable**: No (tracking issue: [#3391](https://github.com/rust-lang/rustfmt/issues/3391))
 +
 +See also [`max_width`](#max_width).
 +
 +## `error_on_unformatted`
 +
 +Error if unable to get comments or string literals within `max_width`, or they are left with
 +trailing whitespaces.
 +
 +- **Default value**: `false`
 +- **Possible values**: `true`, `false`
 +- **Stable**: No (tracking issue: [#3392](https://github.com/rust-lang/rustfmt/issues/3392))
 +
 +## `fn_args_layout`
 +
 +Control the layout of arguments in a function
 +
 +- **Default value**: `"Tall"`
 +- **Possible values**: `"Compressed"`, `"Tall"`, `"Vertical"`
 +- **Stable**: Yes
 +
 +#### `"Tall"` (default):
 +
 +```rust
 +trait Lorem {
 +    fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet);
 +
 +    fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet) {
 +        // body
 +    }
 +
 +    fn lorem(
 +        ipsum: Ipsum,
 +        dolor: Dolor,
 +        sit: Sit,
 +        amet: Amet,
 +        consectetur: Consectetur,
 +        adipiscing: Adipiscing,
 +        elit: Elit,
 +    );
 +
 +    fn lorem(
 +        ipsum: Ipsum,
 +        dolor: Dolor,
 +        sit: Sit,
 +        amet: Amet,
 +        consectetur: Consectetur,
 +        adipiscing: Adipiscing,
 +        elit: Elit,
 +    ) {
 +        // body
 +    }
 +}
 +```
 +
 +#### `"Compressed"`:
 +
 +```rust
 +trait Lorem {
 +    fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet);
 +
 +    fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet) {
 +        // body
 +    }
 +
 +    fn lorem(
 +        ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: Consectetur,
 +        adipiscing: Adipiscing, elit: Elit,
 +    );
 +
 +    fn lorem(
 +        ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: Consectetur,
 +        adipiscing: Adipiscing, elit: Elit,
 +    ) {
 +        // body
 +    }
 +}
 +```
 +
 +#### `"Vertical"`:
 +
 +```rust
 +trait Lorem {
 +    fn lorem(
 +        ipsum: Ipsum,
 +        dolor: Dolor,
 +        sit: Sit,
 +        amet: Amet,
 +    );
 +
 +    fn lorem(
 +        ipsum: Ipsum,
 +        dolor: Dolor,
 +        sit: Sit,
 +        amet: Amet,
 +    ) {
 +        // body
 +    }
 +
 +    fn lorem(
 +        ipsum: Ipsum,
 +        dolor: Dolor,
 +        sit: Sit,
 +        amet: Amet,
 +        consectetur: Consectetur,
 +        adipiscing: Adipiscing,
 +        elit: Elit,
 +    );
 +
 +    fn lorem(
 +        ipsum: Ipsum,
 +        dolor: Dolor,
 +        sit: Sit,
 +        amet: Amet,
 +        consectetur: Consectetur,
 +        adipiscing: Adipiscing,
 +        elit: Elit,
 +    ) {
 +        // body
 +    }
 +}
 +```
 +
 +## `fn_call_width`
 +
 +Maximum width of the args of a function call before falling back to vertical formatting.
 +
 +- **Default value**: `60`
 +- **Possible values**: any positive integer that is less than or equal to the value specified for [`max_width`](#max_width)
 +- **Stable**: Yes
 +
 +By default this option is set as a percentage of [`max_width`](#max_width) provided by [`use_small_heuristics`](#use_small_heuristics), but a value set directly for `fn_call_width` will take precedence.
 +
 +See also [`max_width`](#max_width) and [`use_small_heuristics`](#use_small_heuristics)
 +
 +## `fn_single_line`
 +
 +Put single-expression functions on a single line
 +
 +- **Default value**: `false`
 +- **Possible values**: `true`, `false`
 +- **Stable**: No (tracking issue: [#3358](https://github.com/rust-lang/rustfmt/issues/3358))
 +
 +#### `false` (default):
 +
 +```rust
 +fn lorem() -> usize {
 +    42
 +}
 +
 +fn lorem() -> usize {
 +    let ipsum = 42;
 +    ipsum
 +}
 +```
 +
 +#### `true`:
 +
 +```rust
 +fn lorem() -> usize { 42 }
 +
 +fn lorem() -> usize {
 +    let ipsum = 42;
 +    ipsum
 +}
 +```
 +
 +See also [`control_brace_style`](#control_brace_style).
 +
 +
 +## `force_explicit_abi`
 +
 +Always print the abi for extern items
 +
 +- **Default value**: `true`
 +- **Possible values**: `true`, `false`
 +- **Stable**: Yes
 +
 +**Note:** Non-"C" ABIs are always printed. If `false` then "C" is removed.
 +
 +#### `true` (default):
 +
 +```rust
 +extern "C" {
 +    pub static lorem: c_int;
 +}
 +```
 +
 +#### `false`:
 +
 +```rust
 +extern {
 +    pub static lorem: c_int;
 +}
 +```
 +
 +## `force_multiline_blocks`
 +
 +Force multiline closure and match arm bodies to be wrapped in a block
 +
 +- **Default value**: `false`
 +- **Possible values**: `false`, `true`
 +- **Stable**: No (tracking issue: [#3374](https://github.com/rust-lang/rustfmt/issues/3374))
 +
 +#### `false` (default):
 +
 +```rust
 +fn main() {
 +    result.and_then(|maybe_value| match maybe_value {
 +        None => foo(),
 +        Some(value) => bar(),
 +    });
 +
 +    match lorem {
 +        None => |ipsum| {
 +            println!("Hello World");
 +        },
 +        Some(dolor) => foo(),
 +    }
 +}
 +```
 +
 +#### `true`:
 +
 +```rust
 +fn main() {
 +    result.and_then(|maybe_value| {
 +        match maybe_value {
 +            None => foo(),
 +            Some(value) => bar(),
 +        }
 +    });
 +
 +    match lorem {
 +        None => {
 +            |ipsum| {
 +                println!("Hello World");
 +            }
 +        }
 +        Some(dolor) => foo(),
 +    }
 +}
 +```
 +
 +
 +## `format_code_in_doc_comments`
 +
 +Format code snippet included in doc comments.
 +
 +- **Default value**: `false`
 +- **Possible values**: `true`, `false`
 +- **Stable**: No (tracking issue: [#3348](https://github.com/rust-lang/rustfmt/issues/3348))
 +
 +#### `false` (default):
 +
 +```rust
 +/// Adds one to the number given.
 +///
 +/// # Examples
 +///
 +/// ```rust
 +/// let five=5;
 +///
 +/// assert_eq!(
 +///     6,
 +///     add_one(5)
 +/// );
 +/// # fn add_one(x: i32) -> i32 {
 +/// #     x + 1
 +/// # }
 +/// ```
 +fn add_one(x: i32) -> i32 {
 +    x + 1
 +}
 +```
 +
 +#### `true`
 +
 +```rust
 +/// Adds one to the number given.
 +///
 +/// # Examples
 +///
 +/// ```rust
 +/// let five = 5;
 +///
 +/// assert_eq!(6, add_one(5));
 +/// # fn add_one(x: i32) -> i32 {
 +/// #     x + 1
 +/// # }
 +/// ```
 +fn add_one(x: i32) -> i32 {
 +    x + 1
 +}
 +```
 +
 +## `format_generated_files`
 +
 +Format generated files. A file is considered generated
 +if any of the first five lines contain a `@generated` comment marker.
++By default, generated files are reformatted, i. e. `@generated` marker is ignored.
++This option is currently ignored for stdin (`@generated` in stdin is ignored.)
 +
 +- **Default value**: `true`
 +- **Possible values**: `true`, `false`
 +- **Stable**: No (tracking issue: [#5080](https://github.com/rust-lang/rustfmt/issues/5080))
 +
 +## `format_macro_matchers`
 +
 +Format the metavariable matching patterns in macros.
 +
 +- **Default value**: `false`
 +- **Possible values**: `true`, `false`
 +- **Stable**: No (tracking issue: [#3354](https://github.com/rust-lang/rustfmt/issues/3354))
 +
 +#### `false` (default):
 +
 +```rust
 +macro_rules! foo {
 +    ($a: ident : $b: ty) => {
 +        $a(42): $b;
 +    };
 +    ($a: ident $b: ident $c: ident) => {
 +        $a = $b + $c;
 +    };
 +}
 +```
 +
 +#### `true`:
 +
 +```rust
 +macro_rules! foo {
 +    ($a:ident : $b:ty) => {
 +        $a(42): $b;
 +    };
 +    ($a:ident $b:ident $c:ident) => {
 +        $a = $b + $c;
 +    };
 +}
 +```
 +
 +See also [`format_macro_bodies`](#format_macro_bodies).
 +
 +
 +## `format_macro_bodies`
 +
 +Format the bodies of macros.
 +
 +- **Default value**: `true`
 +- **Possible values**: `true`, `false`
 +- **Stable**: No (tracking issue: [#3355](https://github.com/rust-lang/rustfmt/issues/3355))
 +
 +#### `true` (default):
 +
 +```rust
 +macro_rules! foo {
 +    ($a: ident : $b: ty) => {
 +        $a(42): $b;
 +    };
 +    ($a: ident $b: ident $c: ident) => {
 +        $a = $b + $c;
 +    };
 +}
 +```
 +
 +#### `false`:
 +
 +```rust
 +macro_rules! foo {
 +    ($a: ident : $b: ty) => { $a(42): $b; };
 +    ($a: ident $b: ident $c: ident) => { $a=$b+$c; };
 +}
 +```
 +
 +See also [`format_macro_matchers`](#format_macro_matchers).
 +
 +
 +## `format_strings`
 +
 +Format string literals where necessary
 +
 +- **Default value**: `false`
 +- **Possible values**: `true`, `false`
 +- **Stable**: No (tracking issue: [#3353](https://github.com/rust-lang/rustfmt/issues/3353))
 +
 +#### `false` (default):
 +
 +```rust
 +fn main() {
 +    let lorem = "ipsum dolor sit amet consectetur adipiscing elit lorem ipsum dolor sit amet consectetur adipiscing";
 +}
 +```
 +
 +#### `true`:
 +
 +```rust
 +fn main() {
 +    let lorem = "ipsum dolor sit amet consectetur adipiscing elit lorem ipsum dolor sit amet \
 +                 consectetur adipiscing";
 +}
 +```
 +
 +See also [`max_width`](#max_width).
 +
 +## `hard_tabs`
 +
 +Use tab characters for indentation, spaces for alignment
 +
 +- **Default value**: `false`
 +- **Possible values**: `true`, `false`
 +- **Stable**: Yes
 +
 +#### `false` (default):
 +
 +```rust
 +fn lorem() -> usize {
 +    42 // spaces before 42
 +}
 +```
 +
 +#### `true`:
 +
 +```rust
 +fn lorem() -> usize {
 +      42 // tabs before 42
 +}
 +```
 +
 +See also: [`tab_spaces`](#tab_spaces).
 +
 +## `hex_literal_case`
 +
 +Control the case of the letters in hexadecimal literal values
 +
 +- **Default value**: `Preserve`
 +- **Possible values**: `Upper`, `Lower`
 +- **Stable**: No (tracking issue: [#5081](https://github.com/rust-lang/rustfmt/issues/5081))
 +
 +## `hide_parse_errors`
 +
 +Do not show parse errors if the parser failed to parse files.
 +
 +- **Default value**: `false`
 +- **Possible values**: `true`, `false`
 +- **Stable**: No (tracking issue: [#3390](https://github.com/rust-lang/rustfmt/issues/3390))
 +
 +## `ignore`
 +
 +Skip formatting files and directories that match the specified pattern.
 +The pattern format is the same as [.gitignore](https://git-scm.com/docs/gitignore#_pattern_format). Be sure to use Unix/forwardslash `/` style  paths. This path style will work on all platforms. Windows style paths with backslashes `\` are not supported.
 +
 +- **Default value**: format every file
 +- **Possible values**: See an example below
 +- **Stable**: No (tracking issue: [#3395](https://github.com/rust-lang/rustfmt/issues/3395))
 +
 +### Example
 +
 +If you want to ignore specific files, put the following to your config file:
 +
 +```toml
 +ignore = [
 +    "src/types.rs",
 +    "src/foo/bar.rs",
 +]
 +```
 +
 +If you want to ignore every file under `examples/`, put the following to your config file:
 +
 +```toml
 +ignore = [
 +    "examples",
 +]
 +```
 +
 +If you want to ignore every file under the directory where you put your rustfmt.toml:
 +
 +```toml
 +ignore = ["/"]
 +```
 +
 +## `imports_indent`
 +
 +Indent style of imports
 +
 +- **Default Value**: `"Block"`
 +- **Possible values**: `"Block"`, `"Visual"`
 +- **Stable**: No (tracking issue: [#3360](https://github.com/rust-lang/rustfmt/issues/3360))
 +
 +#### `"Block"` (default):
 +
 +```rust
 +use foo::{
 +    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy,
 +    zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz,
 +};
 +```
 +
 +#### `"Visual"`:
 +
 +```rust
 +use foo::{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy,
 +          zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz};
 +```
 +
 +See also: [`imports_layout`](#imports_layout).
 +
 +## `imports_layout`
 +
 +Item layout inside a imports block
 +
 +- **Default value**: "Mixed"
 +- **Possible values**: "Horizontal", "HorizontalVertical", "Mixed", "Vertical"
 +- **Stable**: No (tracking issue: [#3361](https://github.com/rust-lang/rustfmt/issues/3361))
 +
 +#### `"Mixed"` (default):
 +
 +```rust
 +use foo::{xxxxxxxxxxxxxxxxxx, yyyyyyyyyyyyyyyyyy, zzzzzzzzzzzzzzzzzz};
 +
 +use foo::{
 +    aaaaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbbbbb, cccccccccccccccccc, dddddddddddddddddd,
 +    eeeeeeeeeeeeeeeeee, ffffffffffffffffff,
 +};
 +```
 +
 +#### `"Horizontal"`:
 +
 +**Note**: This option forces all imports onto one line and may exceed `max_width`.
 +
 +```rust
 +use foo::{xxx, yyy, zzz};
 +
 +use foo::{aaa, bbb, ccc, ddd, eee, fff};
 +```
 +
 +#### `"HorizontalVertical"`:
 +
 +```rust
 +use foo::{xxxxxxxxxxxxxxxxxx, yyyyyyyyyyyyyyyyyy, zzzzzzzzzzzzzzzzzz};
 +
 +use foo::{
 +    aaaaaaaaaaaaaaaaaa,
 +    bbbbbbbbbbbbbbbbbb,
 +    cccccccccccccccccc,
 +    dddddddddddddddddd,
 +    eeeeeeeeeeeeeeeeee,
 +    ffffffffffffffffff,
 +};
 +```
 +
 +#### `"Vertical"`:
 +
 +```rust
 +use foo::{
 +    xxx,
 +    yyy,
 +    zzz,
 +};
 +
 +use foo::{
 +    aaa,
 +    bbb,
 +    ccc,
 +    ddd,
 +    eee,
 +    fff,
 +};
 +```
 +
 +## `indent_style`
 +
 +Indent on expressions or items.
 +
 +- **Default value**: `"Block"`
 +- **Possible values**: `"Block"`, `"Visual"`
 +- **Stable**: No (tracking issue: [#3346](https://github.com/rust-lang/rustfmt/issues/3346))
 +
 +### Array
 +
 +#### `"Block"` (default):
 +
 +```rust
 +fn main() {
 +    let lorem = vec![
 +        "ipsum",
 +        "dolor",
 +        "sit",
 +        "amet",
 +        "consectetur",
 +        "adipiscing",
 +        "elit",
 +    ];
 +}
 +```
 +
 +#### `"Visual"`:
 +
 +```rust
 +fn main() {
 +    let lorem = vec!["ipsum",
 +                     "dolor",
 +                     "sit",
 +                     "amet",
 +                     "consectetur",
 +                     "adipiscing",
 +                     "elit"];
 +}
 +```
 +
 +### Control flow
 +
 +#### `"Block"` (default):
 +
 +```rust
 +fn main() {
 +    if lorem_ipsum
 +        && dolor_sit
 +        && amet_consectetur
 +        && lorem_sit
 +        && dolor_consectetur
 +        && amet_ipsum
 +        && lorem_consectetur
 +    {
 +        // ...
 +    }
 +}
 +```
 +
 +#### `"Visual"`:
 +
 +```rust
 +fn main() {
 +    if lorem_ipsum
 +       && dolor_sit
 +       && amet_consectetur
 +       && lorem_sit
 +       && dolor_consectetur
 +       && amet_ipsum
 +       && lorem_consectetur
 +    {
 +        // ...
 +    }
 +}
 +```
 +
 +See also: [`control_brace_style`](#control_brace_style).
 +
 +### Function arguments
 +
 +#### `"Block"` (default):
 +
 +```rust
 +fn lorem() {}
 +
 +fn lorem(ipsum: usize) {}
 +
 +fn lorem(
 +    ipsum: usize,
 +    dolor: usize,
 +    sit: usize,
 +    amet: usize,
 +    consectetur: usize,
 +    adipiscing: usize,
 +    elit: usize,
 +) {
 +    // body
 +}
 +```
 +
 +#### `"Visual"`:
 +
 +```rust
 +fn lorem() {}
 +
 +fn lorem(ipsum: usize) {}
 +
 +fn lorem(ipsum: usize,
 +         dolor: usize,
 +         sit: usize,
 +         amet: usize,
 +         consectetur: usize,
 +         adipiscing: usize,
 +         elit: usize) {
 +    // body
 +}
 +```
 +
 +### Function calls
 +
 +#### `"Block"` (default):
 +
 +```rust
 +fn main() {
 +    lorem(
 +        "lorem",
 +        "ipsum",
 +        "dolor",
 +        "sit",
 +        "amet",
 +        "consectetur",
 +        "adipiscing",
 +        "elit",
 +    );
 +}
 +```
 +
 +#### `"Visual"`:
 +
 +```rust
 +fn main() {
 +    lorem("lorem",
 +          "ipsum",
 +          "dolor",
 +          "sit",
 +          "amet",
 +          "consectetur",
 +          "adipiscing",
 +          "elit");
 +}
 +```
 +
 +### Generics
 +
 +#### `"Block"` (default):
 +
 +```rust
 +fn lorem<
 +    Ipsum: Eq = usize,
 +    Dolor: Eq = usize,
 +    Sit: Eq = usize,
 +    Amet: Eq = usize,
 +    Adipiscing: Eq = usize,
 +    Consectetur: Eq = usize,
 +    Elit: Eq = usize,
 +>(
 +    ipsum: Ipsum,
 +    dolor: Dolor,
 +    sit: Sit,
 +    amet: Amet,
 +    adipiscing: Adipiscing,
 +    consectetur: Consectetur,
 +    elit: Elit,
 +) -> T {
 +    // body
 +}
 +```
 +
 +#### `"Visual"`:
 +
 +```rust
 +fn lorem<Ipsum: Eq = usize,
 +         Dolor: Eq = usize,
 +         Sit: Eq = usize,
 +         Amet: Eq = usize,
 +         Adipiscing: Eq = usize,
 +         Consectetur: Eq = usize,
 +         Elit: Eq = usize>(
 +    ipsum: Ipsum,
 +    dolor: Dolor,
 +    sit: Sit,
 +    amet: Amet,
 +    adipiscing: Adipiscing,
 +    consectetur: Consectetur,
 +    elit: Elit)
 +    -> T {
 +    // body
 +}
 +```
 +
 +#### Struct
 +
 +#### `"Block"` (default):
 +
 +```rust
 +fn main() {
 +    let lorem = Lorem {
 +        ipsum: dolor,
 +        sit: amet,
 +    };
 +}
 +```
 +
 +#### `"Visual"`:
 +
 +```rust
 +fn main() {
 +    let lorem = Lorem { ipsum: dolor,
 +                        sit: amet };
 +}
 +```
 +
 +See also: [`struct_lit_single_line`](#struct_lit_single_line), [`indent_style`](#indent_style).
 +
 +### Where predicates
 +
 +#### `"Block"` (default):
 +
 +```rust
 +fn lorem<Ipsum, Dolor, Sit, Amet>() -> T
 +where
 +    Ipsum: Eq,
 +    Dolor: Eq,
 +    Sit: Eq,
 +    Amet: Eq,
 +{
 +    // body
 +}
 +```
 +
 +#### `"Visual"`:
 +
 +```rust
 +fn lorem<Ipsum, Dolor, Sit, Amet>() -> T
 +    where Ipsum: Eq,
 +          Dolor: Eq,
 +          Sit: Eq,
 +          Amet: Eq
 +{
 +    // body
 +}
 +```
 +
 +## `inline_attribute_width`
 +
 +Write an item and its attribute on the same line if their combined width is below a threshold
 +
 +- **Default value**: 0
 +- **Possible values**: any positive integer
 +- **Stable**: No (tracking issue: [#3343](https://github.com/rust-lang/rustfmt/issues/3343))
 +
 +### Example
 +
 +#### `0` (default):
 +```rust
 +#[cfg(feature = "alloc")]
 +use core::slice;
 +```
 +
 +#### `50`:
 +```rust
 +#[cfg(feature = "alloc")] use core::slice;
 +```
 +
 +## `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.
 +
 +The Style Guide requires that bodies are block wrapped by default if a line break is required after the `=>`, but this option can be used to disable that behavior to prevent wrapping arm bodies in that event, so long as the body does not contain multiple statements nor line comments.
 +
 +- **Default value**: `true`
 +- **Possible values**: `true`, `false`
 +- **Stable**: No (tracking issue: [#3373](https://github.com/rust-lang/rustfmt/issues/3373))
 +
 +#### `true` (default):
 +
 +```rust
 +fn main() {
 +    match lorem {
 +        ipsum => {
 +            foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo(x)
 +        }
 +        dolor => println!("{}", sit),
 +        sit => foo(
 +            "foooooooooooooooooooooooo",
 +            "baaaaaaaaaaaaaaaaaaaaaaaarr",
 +            "baaaaaaaaaaaaaaaaaaaazzzzzzzzzzzzz",
 +            "qqqqqqqqquuuuuuuuuuuuuuuuuuuuuuuuuuxxx",
 +        ),
 +    }
 +}
 +```
 +
 +#### `false`:
 +
 +```rust
 +fn main() {
 +    match lorem {
 +        lorem =>
 +            foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo(x),
 +        ipsum => println!("{}", sit),
 +        sit => foo(
 +            "foooooooooooooooooooooooo",
 +            "baaaaaaaaaaaaaaaaaaaaaaaarr",
 +            "baaaaaaaaaaaaaaaaaaaazzzzzzzzzzzzz",
 +            "qqqqqqqqquuuuuuuuuuuuuuuuuuuuuuuuuuxxx",
 +        ),
 +    }
 +}
 +```
 +
 +See also: [`match_block_trailing_comma`](#match_block_trailing_comma).
 +
 +## `match_arm_leading_pipes`
 +
 +Controls whether to include a leading pipe on match arms
 +
 +- **Default value**: `Never`
 +- **Possible values**: `Always`, `Never`, `Preserve`
 +- **Stable**: Yes
 +
 +#### `Never` (default):
 +```rust
 +// Leading pipes are removed from this:
 +// fn foo() {
 +//     match foo {
 +//         | "foo" | "bar" => {}
 +//         | "baz"
 +//         | "something relatively long"
 +//         | "something really really really realllllllllllllly long" => println!("x"),
 +//         | "qux" => println!("y"),
 +//         _ => {}
 +//     }
 +// }
 +
 +// Becomes
 +fn foo() {
 +    match foo {
 +        "foo" | "bar" => {}
 +        "baz"
 +        | "something relatively long"
 +        | "something really really really realllllllllllllly long" => println!("x"),
 +        "qux" => println!("y"),
 +        _ => {}
 +    }
 +}
 +```
 +
 +#### `Always`:
 +```rust
 +// Leading pipes are emitted on all arms of this:
 +// fn foo() {
 +//     match foo {
 +//         "foo" | "bar" => {}
 +//         "baz"
 +//         | "something relatively long"
 +//         | "something really really really realllllllllllllly long" => println!("x"),
 +//         "qux" => println!("y"),
 +//         _ => {}
 +//     }
 +// }
 +
 +// Becomes:
 +fn foo() {
 +    match foo {
 +        | "foo" | "bar" => {}
 +        | "baz"
 +        | "something relatively long"
 +        | "something really really really realllllllllllllly long" => println!("x"),
 +        | "qux" => println!("y"),
 +        | _ => {}
 +    }
 +}
 +```
 +
 +#### `Preserve`:
 +```rust
 +fn foo() {
 +    match foo {
 +        | "foo" | "bar" => {}
 +        | "baz"
 +        | "something relatively long"
 +        | "something really really really realllllllllllllly long" => println!("x"),
 +        | "qux" => println!("y"),
 +        _ => {}
 +    }
 +
 +    match baz {
 +        "qux" => {}
 +        "foo" | "bar" => {}
 +        _ => {}
 +    }
 +}
 +```
 +
 +## `match_block_trailing_comma`
 +
 +Put a trailing comma after a block based match arm (non-block arms are not affected)
 +
 +- **Default value**: `false`
 +- **Possible values**: `true`, `false`
 +- **Stable**: Yes
 +
 +#### `false` (default):
 +
 +```rust
 +fn main() {
 +    match lorem {
 +        Lorem::Ipsum => {
 +            println!("ipsum");
 +        }
 +        Lorem::Dolor => println!("dolor"),
 +    }
 +}
 +```
 +
 +#### `true`:
 +
 +```rust
 +fn main() {
 +    match lorem {
 +        Lorem::Ipsum => {
 +            println!("ipsum");
 +        },
 +        Lorem::Dolor => println!("dolor"),
 +    }
 +}
 +```
 +
 +See also: [`trailing_comma`](#trailing_comma), [`match_arm_blocks`](#match_arm_blocks).
 +
 +## `max_width`
 +
 +Maximum width of each line
 +
 +- **Default value**: `100`
 +- **Possible values**: any positive integer
 +- **Stable**: Yes
 +
 +See also [`error_on_line_overflow`](#error_on_line_overflow).
 +
 +## `merge_derives`
 +
 +Merge multiple derives into a single one.
 +
 +- **Default value**: `true`
 +- **Possible values**: `true`, `false`
 +- **Stable**: Yes
 +
 +#### `true` (default):
 +
 +```rust
 +#[derive(Eq, PartialEq, Debug, Copy, Clone)]
 +pub enum Foo {}
 +```
 +
 +#### `false`:
 +
 +```rust
 +#[derive(Eq, PartialEq, Debug, Copy, Clone)]
 +pub enum Bar {}
 +
 +#[derive(Eq, PartialEq)]
 +#[derive(Debug)]
 +#[derive(Copy, Clone)]
 +pub enum Foo {}
 +```
 +
 +## `imports_granularity`
 +
 +How imports should be grouped into `use` statements. Imports will be merged or split to the configured level of granularity.
 +
 +- **Default value**: `Preserve`
 +- **Possible values**: `Preserve`, `Crate`, `Module`, `Item`, `One`
 +- **Stable**: No (tracking issue: [#4991](https://github.com/rust-lang/rustfmt/issues/4991))
 +
 +#### `Preserve` (default):
 +
 +Do not change the granularity of any imports and preserve the original structure written by the developer.
 +
 +```rust
 +use foo::b;
 +use foo::b::{f, g};
 +use foo::{a, c, d::e};
 +use qux::{h, i};
 +```
 +
 +#### `Crate`:
 +
 +Merge imports from the same crate into a single `use` statement. Conversely, imports from different crates are split into separate statements.
 +
 +```rust
 +use foo::{
 +    a, b,
 +    b::{f, g},
 +    c,
 +    d::e,
 +};
 +use qux::{h, i};
 +```
 +
 +#### `Module`:
 +
 +Merge imports from the same module into a single `use` statement. Conversely, imports from different modules are split into separate statements.
 +
 +```rust
 +use foo::b::{f, g};
 +use foo::d::e;
 +use foo::{a, b, c};
 +use qux::{h, i};
 +```
 +
 +#### `Item`:
 +
 +Flatten imports so that each has its own `use` statement.
 +
 +```rust
 +use foo::a;
 +use foo::b;
 +use foo::b::f;
 +use foo::b::g;
 +use foo::c;
 +use foo::d::e;
 +use qux::h;
 +use qux::i;
 +```
 +
 +#### `One`:
 +
 +Merge all imports into a single `use` statement as long as they have the same visibility.
 +
 +```rust
 +pub use foo::{x, y};
 +use {
 +    bar::{
 +        a,
 +        b::{self, f, g},
 +        c,
 +        d::e,
 +    },
 +    qux::{h, i},
 +};
 +```
 +
 +## `merge_imports`
 +
 +This option is deprecated. Use `imports_granularity = "Crate"` instead.
 +
 +- **Default value**: `false`
 +- **Possible values**: `true`, `false`
 +
 +#### `false` (default):
 +
 +```rust
 +use foo::{a, c, d};
 +use foo::{b, g};
 +use foo::{e, f};
 +```
 +
 +#### `true`:
 +
 +```rust
 +use foo::{a, b, c, d, e, f, g};
 +```
 +
 +
 +## `newline_style`
 +
 +Unix or Windows line endings
 +
 +- **Default value**: `"Auto"`
 +- **Possible values**: `"Auto"`, `"Native"`, `"Unix"`, `"Windows"`
 +- **Stable**: Yes
 +
 +#### `Auto` (default):
 +
 +The newline style is detected automatically on a per-file basis. Files
 +with mixed line endings will be converted to the first detected line
 +ending style.
 +
 +#### `Native`
 +
 +Line endings will be converted to `\r\n` on Windows and `\n` on all
 +other platforms.
 +
 +#### `Unix`
 +
 +Line endings will be converted to `\n`.
 +
 +#### `Windows`
 +
 +Line endings will be converted to `\r\n`.
 +
 +## `normalize_comments`
 +
 +Convert /* */ comments to // comments where possible
 +
 +- **Default value**: `false`
 +- **Possible values**: `true`, `false`
 +- **Stable**: No (tracking issue: [#3350](https://github.com/rust-lang/rustfmt/issues/3350))
 +
 +#### `false` (default):
 +
 +```rust
 +// Lorem ipsum:
 +fn dolor() -> usize {}
 +
 +/* sit amet: */
 +fn adipiscing() -> usize {}
 +```
 +
 +#### `true`:
 +
 +```rust
 +// Lorem ipsum:
 +fn dolor() -> usize {}
 +
 +// sit amet:
 +fn adipiscing() -> usize {}
 +```
 +
 +## `normalize_doc_attributes`
 +
 +Convert `#![doc]` and `#[doc]` attributes to `//!` and `///` doc comments.
 +
 +- **Default value**: `false`
 +- **Possible values**: `true`, `false`
 +- **Stable**: No (tracking issue: [#3351](https://github.com/rust-lang/rustfmt/issues/3351))
 +
 +#### `false` (default):
 +
 +```rust
 +#![doc = "Example documentation"]
 +
 +#[doc = "Example item documentation"]
 +pub enum Bar {}
 +
 +/// Example item documentation
 +pub enum Foo {}
 +```
 +
 +#### `true`:
 +
 +```rust
 +//! Example documentation
 +
 +/// Example item documentation
 +pub enum Foo {}
 +```
 +
 +## `overflow_delimited_expr`
 +
 +When structs, slices, arrays, and block/array-like macros are used as the last
 +argument in an expression list, allow them to overflow (like blocks/closures)
 +instead of being indented on a new line.
 +
 +- **Default value**: `false`
 +- **Possible values**: `true`, `false`
 +- **Stable**: No (tracking issue: [#3370](https://github.com/rust-lang/rustfmt/issues/3370))
 +
 +#### `false` (default):
 +
 +```rust
 +fn example() {
 +    foo(ctx, |param| {
 +        action();
 +        foo(param)
 +    });
 +
 +    foo(
 +        ctx,
 +        Bar {
 +            x: value,
 +            y: value2,
 +        },
 +    );
 +
 +    foo(
 +        ctx,
 +        &[
 +            MAROON_TOMATOES,
 +            PURPLE_POTATOES,
 +            ORGANE_ORANGES,
 +            GREEN_PEARS,
 +            RED_APPLES,
 +        ],
 +    );
 +
 +    foo(
 +        ctx,
 +        vec![
 +            MAROON_TOMATOES,
 +            PURPLE_POTATOES,
 +            ORGANE_ORANGES,
 +            GREEN_PEARS,
 +            RED_APPLES,
 +        ],
 +    );
 +}
 +```
 +
 +#### `true`:
 +
 +```rust
 +fn example() {
 +    foo(ctx, |param| {
 +        action();
 +        foo(param)
 +    });
 +
 +    foo(ctx, Bar {
 +        x: value,
 +        y: value2,
 +    });
 +
 +    foo(ctx, &[
 +        MAROON_TOMATOES,
 +        PURPLE_POTATOES,
 +        ORGANE_ORANGES,
 +        GREEN_PEARS,
 +        RED_APPLES,
 +    ]);
 +
 +    foo(ctx, vec![
 +        MAROON_TOMATOES,
 +        PURPLE_POTATOES,
 +        ORGANE_ORANGES,
 +        GREEN_PEARS,
 +        RED_APPLES,
 +    ]);
 +}
 +```
 +
 +## `remove_nested_parens`
 +
 +Remove nested parens.
 +
 +- **Default value**: `true`,
 +- **Possible values**: `true`, `false`
 +- **Stable**: Yes
 +
 +
 +#### `true` (default):
 +```rust
 +fn main() {
 +    (foo());
 +}
 +```
 +
 +#### `false`:
 +```rust
 +fn main() {
 +    (foo());
 +
 +    ((((foo()))));
 +}
 +```
 +
 +
 +## `reorder_impl_items`
 +
 +Reorder impl items. `type` and `const` are put first, then macros and methods.
 +
 +- **Default value**: `false`
 +- **Possible values**: `true`, `false`
 +- **Stable**: No (tracking issue: [#3363](https://github.com/rust-lang/rustfmt/issues/3363))
 +
 +#### `false` (default)
 +
 +```rust
 +struct Dummy;
 +
 +impl Iterator for Dummy {
 +    fn next(&mut self) -> Option<Self::Item> {
 +        None
 +    }
 +
 +    type Item = i32;
 +}
 +
 +impl Iterator for Dummy {
 +    type Item = i32;
 +
 +    fn next(&mut self) -> Option<Self::Item> {
 +        None
 +    }
 +}
 +```
 +
 +#### `true`
 +
 +```rust
 +struct Dummy;
 +
 +impl Iterator for Dummy {
 +    type Item = i32;
 +
 +    fn next(&mut self) -> Option<Self::Item> {
 +        None
 +    }
 +}
 +```
 +
 +## `reorder_imports`
 +
 +Reorder import and extern crate statements alphabetically in groups (a group is
 +separated by a newline).
 +
 +- **Default value**: `true`
 +- **Possible values**: `true`, `false`
 +- **Stable**: Yes
 +
 +#### `true` (default):
 +
 +```rust
 +use dolor;
 +use ipsum;
 +use lorem;
 +use sit;
 +```
 +
 +#### `false`:
 +
 +```rust
 +use lorem;
 +use ipsum;
 +use dolor;
 +use sit;
 +```
 +
 +## `group_imports`
 +
 +Controls the strategy for how imports are grouped together.
 +
 +- **Default value**: `Preserve`
 +- **Possible values**: `Preserve`, `StdExternalCrate`, `One`
 +- **Stable**: No (tracking issue: [#5083](https://github.com/rust-lang/rustfmt/issues/5083))
 +
 +#### `Preserve` (default):
 +
 +Preserve the source file's import groups.
 +
 +```rust
 +use super::update::convert_publish_payload;
 +use chrono::Utc;
 +
 +use alloc::alloc::Layout;
 +use juniper::{FieldError, FieldResult};
 +use uuid::Uuid;
 +
 +use std::sync::Arc;
 +
 +use broker::database::PooledConnection;
 +
 +use super::schema::{Context, Payload};
 +use crate::models::Event;
 +use core::f32;
 +```
 +
 +#### `StdExternalCrate`:
 +
 +Discard existing import groups, and create three groups for:
 +1. `std`, `core` and `alloc`,
 +2. external crates,
 +3. `self`, `super` and `crate` imports.
 +
 +```rust
 +use alloc::alloc::Layout;
 +use core::f32;
 +use std::sync::Arc;
 +
 +use broker::database::PooledConnection;
 +use chrono::Utc;
 +use juniper::{FieldError, FieldResult};
 +use uuid::Uuid;
 +
 +use super::schema::{Context, Payload};
 +use super::update::convert_publish_payload;
 +use crate::models::Event;
 +```
 +
 +#### `One`:
 +
 +Discard existing import groups, and create a single group for everything
 +
 +```rust
 +use super::schema::{Context, Payload};
 +use super::update::convert_publish_payload;
 +use crate::models::Event;
 +use alloc::alloc::Layout;
 +use broker::database::PooledConnection;
 +use chrono::Utc;
 +use core::f32;
 +use juniper::{FieldError, FieldResult};
 +use std::sync::Arc;
 +use uuid::Uuid;
 +```
 +
 +## `reorder_modules`
 +
 +Reorder `mod` declarations alphabetically in group.
 +
 +- **Default value**: `true`
 +- **Possible values**: `true`, `false`
 +- **Stable**: Yes
 +
 +#### `true` (default)
 +
 +```rust
 +mod a;
 +mod b;
 +
 +mod dolor;
 +mod ipsum;
 +mod lorem;
 +mod sit;
 +```
 +
 +#### `false`
 +
 +```rust
 +mod b;
 +mod a;
 +
 +mod lorem;
 +mod ipsum;
 +mod dolor;
 +mod sit;
 +```
 +
 +**Note** `mod` with `#[macro_export]` will not be reordered since that could change the semantics
 +of the original source code.
 +
 +## `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
 +specific version of rustfmt is used in your CI, use this option.
 +
 +- **Default value**: `CARGO_PKG_VERSION`
 +- **Possible values**: any published version (e.g. `"0.3.8"`)
 +- **Stable**: No (tracking issue: [#3386](https://github.com/rust-lang/rustfmt/issues/3386))
 +
++## `short_array_element_width_threshold`
++
++The width threshold for an array element to be considered "short".
++
++The layout of an array is dependent on the length of each of its elements. 
++If the length of every element in an array is below this threshold (all elements are "short") then the array can be formatted in the mixed/compressed style, but if any one element has a length that exceeds this threshold then the array elements will have to be formatted vertically.
++
++- **Default value**: `10`
++- **Possible values**: any positive integer that is less than or equal to the value specified for [`max_width`](#max_width)
++- **Stable**: Yes
++
++#### `10` (default):
++```rust
++fn main() {
++    pub const FORMAT_TEST: [u64; 5] = [
++        0x0000000000000000,
++        0xaaaaaaaaaaaaaaaa,
++        0xbbbbbbbbbbbbbbbb,
++        0xcccccccccccccccc,
++        0xdddddddddddddddd,
++    ];
++}
++```
++#### `20`:
++```rust
++fn main() {
++    pub const FORMAT_TEST: [u64; 5] = [
++        0x0000000000000000, 0xaaaaaaaaaaaaaaaa, 0xbbbbbbbbbbbbbbbb, 0xcccccccccccccccc,
++        0xdddddddddddddddd,
++    ];
++}
++```
++See also [`max_width`](#max_width).
++
 +## `skip_children`
 +
 +Don't reformat out of line modules
 +
 +- **Default value**: `false`
 +- **Possible values**: `true`, `false`
++- **Stable**: No (tracking issue: [#3389](https://github.com/rust-lang/rustfmt/issues/3389))
 +
 +## `single_line_if_else_max_width`
 +
 +Maximum line length for single line if-else expressions. A value of `0` (zero) results in if-else expressions always being broken into multiple lines. Note this occurs when `use_small_heuristics` is set to `Off`.
 +
 +- **Default value**: `50`
 +- **Possible values**: any positive integer that is less than or equal to the value specified for [`max_width`](#max_width)
 +- **Stable**: Yes
 +
 +By default this option is set as a percentage of [`max_width`](#max_width) provided by [`use_small_heuristics`](#use_small_heuristics), but a value set directly for `single_line_if_else_max_width` will take precedence.
 +
 +See also [`max_width`](#max_width) and [`use_small_heuristics`](#use_small_heuristics)
 +
 +## `space_after_colon`
 +
 +Leave a space after the colon.
 +
 +- **Default value**: `true`
 +- **Possible values**: `true`, `false`
 +- **Stable**: No (tracking issue: [#3366](https://github.com/rust-lang/rustfmt/issues/3366))
 +
 +#### `true` (default):
 +
 +```rust
 +fn lorem<T: Eq>(t: T) {
 +    let lorem: Dolor = Lorem {
 +        ipsum: dolor,
 +        sit: amet,
 +    };
 +}
 +```
 +
 +#### `false`:
 +
 +```rust
 +fn lorem<T:Eq>(t:T) {
 +    let lorem:Dolor = Lorem {
 +        ipsum:dolor,
 +        sit:amet,
 +    };
 +}
 +```
 +
 +See also: [`space_before_colon`](#space_before_colon).
 +
 +## `space_before_colon`
 +
 +Leave a space before the colon.
 +
 +- **Default value**: `false`
 +- **Possible values**: `true`, `false`
 +- **Stable**: No (tracking issue: [#3365](https://github.com/rust-lang/rustfmt/issues/3365))
 +
 +#### `false` (default):
 +
 +```rust
 +fn lorem<T: Eq>(t: T) {
 +    let lorem: Dolor = Lorem {
 +        ipsum: dolor,
 +        sit: amet,
 +    };
 +}
 +```
 +
 +#### `true`:
 +
 +```rust
 +fn lorem<T : Eq>(t : T) {
 +    let lorem : Dolor = Lorem {
 +        ipsum : dolor,
 +        sit : amet,
 +    };
 +}
 +```
 +
 +See also: [`space_after_colon`](#space_after_colon).
 +
 +## `spaces_around_ranges`
 +
 +Put spaces around the .., ..=, and ... range operators
 +
 +- **Default value**: `false`
 +- **Possible values**: `true`, `false`
 +- **Stable**: No (tracking issue: [#3367](https://github.com/rust-lang/rustfmt/issues/3367))
 +
 +#### `false` (default):
 +
 +```rust
 +fn main() {
 +    let lorem = 0..10;
 +    let ipsum = 0..=10;
 +
 +    match lorem {
 +        1..5 => foo(),
 +        _ => bar,
 +    }
 +
 +    match lorem {
 +        1..=5 => foo(),
 +        _ => bar,
 +    }
 +
 +    match lorem {
 +        1...5 => foo(),
 +        _ => bar,
 +    }
 +}
 +```
 +
 +#### `true`:
 +
 +```rust
 +fn main() {
 +    let lorem = 0 .. 10;
 +    let ipsum = 0 ..= 10;
 +
 +    match lorem {
 +        1 .. 5 => foo(),
 +        _ => bar,
 +    }
 +
 +    match lorem {
 +        1 ..= 5 => foo(),
 +        _ => bar,
 +    }
 +
 +    match lorem {
 +        1 ... 5 => foo(),
 +        _ => bar,
 +    }
 +}
 +```
 +
 +## `struct_field_align_threshold`
 +
 +The maximum diff of width between struct fields to be aligned with each other.
 +
 +- **Default value** : 0
 +- **Possible values**: any non-negative integer
 +- **Stable**: No (tracking issue: [#3371](https://github.com/rust-lang/rustfmt/issues/3371))
 +
 +#### `0` (default):
 +
 +```rust
 +struct Foo {
 +    x: u32,
 +    yy: u32,
 +    zzz: u32,
 +}
 +```
 +
 +#### `20`:
 +
 +```rust
 +struct Foo {
 +    x:   u32,
 +    yy:  u32,
 +    zzz: u32,
 +}
 +```
 +
 +## `struct_lit_single_line`
 +
 +Put small struct literals on a single line
 +
 +- **Default value**: `true`
 +- **Possible values**: `true`, `false`
 +- **Stable**: No (tracking issue: [#3357](https://github.com/rust-lang/rustfmt/issues/3357))
 +
 +#### `true` (default):
 +
 +```rust
 +fn main() {
 +    let lorem = Lorem { foo: bar, baz: ofo };
 +}
 +```
 +
 +#### `false`:
 +
 +```rust
 +fn main() {
 +    let lorem = Lorem {
 +        foo: bar,
 +        baz: ofo,
 +    };
 +}
 +```
 +
 +See also: [`indent_style`](#indent_style).
 +
 +## `struct_lit_width`
 +
 +Maximum width in the body of a struct literal before falling back to vertical formatting. A value of `0` (zero) results in struct literals always being broken into multiple lines. Note this occurs when `use_small_heuristics` is set to `Off`.
 +
 +- **Default value**: `18`
 +- **Possible values**: any positive integer that is less than or equal to the value specified for [`max_width`](#max_width)
 +- **Stable**: Yes
 +
 +By default this option is set as a percentage of [`max_width`](#max_width) provided by [`use_small_heuristics`](#use_small_heuristics), but a value set directly for `struct_lit_width` will take precedence.
 +
 +See also [`max_width`](#max_width), [`use_small_heuristics`](#use_small_heuristics), and [`struct_lit_single_line`](#struct_lit_single_line)
 +
 +## `struct_variant_width`
 +
 +Maximum width in the body of a struct variant before falling back to vertical formatting. A value of `0` (zero) results in struct literals always being broken into multiple lines. Note this occurs when `use_small_heuristics` is set to `Off`.
 +
 +- **Default value**: `35`
 +- **Possible values**: any positive integer that is less than or equal to the value specified for [`max_width`](#max_width)
 +- **Stable**: Yes
 +
 +By default this option is set as a percentage of [`max_width`](#max_width) provided by [`use_small_heuristics`](#use_small_heuristics), but a value set directly for `struct_variant_width` will take precedence.
 +
 +See also [`max_width`](#max_width) and [`use_small_heuristics`](#use_small_heuristics)
 +
 +## `tab_spaces`
 +
 +Number of spaces per tab
 +
 +- **Default value**: `4`
 +- **Possible values**: any positive integer
 +- **Stable**: Yes
 +
 +#### `4` (default):
 +
 +```rust
 +fn lorem() {
 +    let ipsum = dolor();
 +    let sit = vec![
 +        "amet consectetur adipiscing elit amet",
 +        "consectetur adipiscing elit amet consectetur.",
 +    ];
 +}
 +```
 +
 +#### `2`:
 +
 +```rust
 +fn lorem() {
 +  let ipsum = dolor();
 +  let sit = vec![
 +    "amet consectetur adipiscing elit amet",
 +    "consectetur adipiscing elit amet consectetur.",
 +  ];
 +}
 +```
 +
 +See also: [`hard_tabs`](#hard_tabs).
 +
 +
 +## `trailing_comma`
 +
 +How to handle trailing commas for lists
 +
 +- **Default value**: `"Vertical"`
 +- **Possible values**: `"Always"`, `"Never"`, `"Vertical"`
 +- **Stable**: No (tracking issue: [#3379](https://github.com/rust-lang/rustfmt/issues/3379))
 +
 +#### `"Vertical"` (default):
 +
 +```rust
 +fn main() {
 +    let Lorem { ipsum, dolor, sit } = amet;
 +    let Lorem {
 +        ipsum,
 +        dolor,
 +        sit,
 +        amet,
 +        consectetur,
 +        adipiscing,
 +    } = elit;
 +}
 +```
 +
 +#### `"Always"`:
 +
 +```rust
 +fn main() {
 +    let Lorem { ipsum, dolor, sit, } = amet;
 +    let Lorem {
 +        ipsum,
 +        dolor,
 +        sit,
 +        amet,
 +        consectetur,
 +        adipiscing,
 +    } = elit;
 +}
 +```
 +
 +#### `"Never"`:
 +
 +```rust
 +fn main() {
 +    let Lorem { ipsum, dolor, sit } = amet;
 +    let Lorem {
 +        ipsum,
 +        dolor,
 +        sit,
 +        amet,
 +        consectetur,
 +        adipiscing
 +    } = elit;
 +}
 +```
 +
 +See also: [`match_block_trailing_comma`](#match_block_trailing_comma).
 +
 +## `trailing_semicolon`
 +
 +Add trailing semicolon after break, continue and return
 +
 +- **Default value**: `true`
 +- **Possible values**: `true`, `false`
 +- **Stable**: No (tracking issue: [#3378](https://github.com/rust-lang/rustfmt/issues/3378))
 +
 +#### `true` (default):
 +```rust
 +fn foo() -> usize {
 +    return 0;
 +}
 +```
 +
 +#### `false`:
 +```rust
 +fn foo() -> usize {
 +    return 0
 +}
 +```
 +
 +## `type_punctuation_density`
 +
 +Determines if `+` or `=` are wrapped in spaces in the punctuation of types
 +
 +- **Default value**: `"Wide"`
 +- **Possible values**: `"Compressed"`, `"Wide"`
 +- **Stable**: No (tracking issue: [#3364](https://github.com/rust-lang/rustfmt/issues/3364))
 +
 +#### `"Wide"` (default):
 +
 +```rust
 +fn lorem<Ipsum: Dolor + Sit = Amet>() {
 +    // body
 +}
 +```
 +
 +#### `"Compressed"`:
 +
 +```rust
 +fn lorem<Ipsum: Dolor+Sit=Amet>() {
 +    // body
 +}
 +```
 +
 +## `unstable_features`
 +
 +Enable unstable features on the unstable channel.
 +
 +- **Default value**: `false`
 +- **Possible values**: `true`, `false`
 +- **Stable**: No (tracking issue: [#3387](https://github.com/rust-lang/rustfmt/issues/3387))
 +
 +## `use_field_init_shorthand`
 +
 +Use field initialize shorthand if possible.
 +
 +- **Default value**: `false`
 +- **Possible values**: `true`, `false`
 +- **Stable**: Yes
 +
 +#### `false` (default):
 +
 +```rust
 +struct Foo {
 +    x: u32,
 +    y: u32,
 +    z: u32,
 +}
 +
 +fn main() {
 +    let x = 1;
 +    let y = 2;
 +    let z = 3;
 +    let a = Foo { x, y, z };
 +    let b = Foo { x: x, y: y, z: z };
 +}
 +```
 +
 +#### `true`:
 +
 +```rust
 +struct Foo {
 +    x: u32,
 +    y: u32,
 +    z: u32,
 +}
 +
 +fn main() {
 +    let x = 1;
 +    let y = 2;
 +    let z = 3;
 +    let a = Foo { x, y, z };
 +}
 +```
 +
 +## `use_small_heuristics`
 +
 +This option can be used to simplify the management and bulk updates of the granular width configuration settings ([`fn_call_width`](#fn_call_width), [`attr_fn_like_width`](#attr_fn_like_width), [`struct_lit_width`](#struct_lit_width), [`struct_variant_width`](#struct_variant_width), [`array_width`](#array_width), [`chain_width`](#chain_width), [`single_line_if_else_max_width`](#single_line_if_else_max_width)), that respectively control when formatted constructs are multi-lined/vertical based on width.
 +
 +Note that explicitly provided values for the width configuration settings take precedence and override the calculated values determined by `use_small_heuristics`.
 +
 +- **Default value**: `"Default"`
 +- **Possible values**: `"Default"`, `"Off"`, `"Max"`
 +- **Stable**: Yes
 +
 +#### `Default` (default):
 +When `use_small_heuristics` is set to `Default`, the values for the granular width settings are calculated as a ratio of the value for `max_width`.
 +
 +The ratios are:
 +* [`fn_call_width`](#fn_call_width) - `60%`
 +* [`attr_fn_like_width`](#attr_fn_like_width) - `70%`
 +* [`struct_lit_width`](#struct_lit_width) - `18%`
 +* [`struct_variant_width`](#struct_variant_width) - `35%`
 +* [`array_width`](#array_width) - `60%`
 +* [`chain_width`](#chain_width) - `60%`
 +* [`single_line_if_else_max_width`](#single_line_if_else_max_width) - `50%`
 +
 +For example when `max_width` is set to `100`, the width settings are:
 +* `fn_call_width=60`
 +* `attr_fn_like_width=70`
 +* `struct_lit_width=18`
 +* `struct_variant_width=35`
 +* `array_width=60`
 +* `chain_width=60`
 +* `single_line_if_else_max_width=50`
 +
 +and when `max_width` is set to `200`:
 +* `fn_call_width=120`
 +* `attr_fn_like_width=140`
 +* `struct_lit_width=36`
 +* `struct_variant_width=70`
 +* `array_width=120`
 +* `chain_width=120`
 +* `single_line_if_else_max_width=100`
 +
 +```rust
 +enum Lorem {
 +    Ipsum,
 +    Dolor(bool),
 +    Sit { amet: Consectetur, adipiscing: Elit },
 +}
 +
 +fn main() {
 +    lorem(
 +        "lorem",
 +        "ipsum",
 +        "dolor",
 +        "sit",
 +        "amet",
 +        "consectetur",
 +        "adipiscing",
 +    );
 +
 +    let lorem = Lorem {
 +        ipsum: dolor,
 +        sit: amet,
 +    };
 +    let lorem = Lorem { ipsum: dolor };
 +
 +    let lorem = if ipsum { dolor } else { sit };
 +}
 +```
 +
 +#### `Off`:
 +When `use_small_heuristics` is set to `Off`, the granular width settings are functionally disabled and ignored. See the documentation for the respective width config options for specifics.
 +
 +```rust
 +enum Lorem {
 +    Ipsum,
 +    Dolor(bool),
 +    Sit {
 +        amet: Consectetur,
 +        adipiscing: Elit,
 +    },
 +}
 +
 +fn main() {
 +    lorem("lorem", "ipsum", "dolor", "sit", "amet", "consectetur", "adipiscing");
 +
 +    let lorem = Lorem {
 +        ipsum: dolor,
 +        sit: amet,
 +    };
 +
 +    let lorem = if ipsum {
 +        dolor
 +    } else {
 +        sit
 +    };
 +}
 +```
 +
 +#### `Max`:
 +When `use_small_heuristics` is set to `Max`, then each granular width setting is set to the same value as `max_width`.
 +
 +So if `max_width` is set to `200`, then all the width settings are also set to `200`.
 +* `fn_call_width=200`
 +* `attr_fn_like_width=200`
 +* `struct_lit_width=200`
 +* `struct_variant_width=200`
 +* `array_width=200`
 +* `chain_width=200`
 +* `single_line_if_else_max_width=200`
 +
 +```rust
 +enum Lorem {
 +    Ipsum,
 +    Dolor(bool),
 +    Sit { amet: Consectetur, adipiscing: Elit },
 +}
 +
 +fn main() {
 +    lorem("lorem", "ipsum", "dolor", "sit", "amet", "consectetur", "adipiscing");
 +
 +    let lorem = Lorem { ipsum: dolor, sit: amet };
 +
 +    let lorem = if ipsum { dolor } else { sit };
 +}
 +```
 +
 +
 +See also:
 +* [`max_width`](#max_width)
 +* [`fn_call_width`](#fn_call_width)
 +* [`attr_fn_like_width`](#attr_fn_like_width)
 +* [`struct_lit_width`](#struct_lit_width)
 +* [`struct_variant_width`](#struct_variant_width)
 +* [`array_width`](#array_width)
 +* [`chain_width`](#chain_width)
 +* [`single_line_if_else_max_width`](#single_line_if_else_max_width)
 +
 +## `use_try_shorthand`
 +
 +Replace uses of the try! macro by the ? shorthand
 +
 +- **Default value**: `false`
 +- **Possible values**: `true`, `false`
 +- **Stable**: Yes
 +
 +#### `false` (default):
 +
 +```rust
 +fn main() {
 +    let lorem = ipsum.map(|dolor| dolor.sit())?;
 +
 +    let lorem = try!(ipsum.map(|dolor| dolor.sit()));
 +}
 +```
 +
 +#### `true`:
 +
 +```rust
 +fn main() {
 +    let lorem = ipsum.map(|dolor| dolor.sit())?;
 +}
 +```
 +
 +## `version`
 +
 +Which version of the formatting rules to use. `Version::One` is backwards-compatible
 +with Rustfmt 1.0. Other versions are only backwards compatible within a major
 +version number.
 +
 +- **Default value**: `One`
 +- **Possible values**: `One`, `Two`
 +- **Stable**: No (tracking issue: [#3383](https://github.com/rust-lang/rustfmt/issues/3383))
 +
 +### Example
 +
 +```toml
 +version = "Two"
 +```
 +
 +## `where_single_line`
 +
 +Forces the `where` clause to be laid out on a single line.
 +
 +- **Default value**: `false`
 +- **Possible values**: `true`, `false`
 +- **Stable**: No (tracking issue: [#3359](https://github.com/rust-lang/rustfmt/issues/3359))
 +
 +#### `false` (default):
 +
 +```rust
 +impl<T> Lorem for T
 +where
 +    Option<T>: Ipsum,
 +{
 +    // body
 +}
 +```
 +
 +#### `true`:
 +
 +```rust
 +impl<T> Lorem for T
 +where Option<T>: Ipsum
 +{
 +    // body
 +}
 +```
 +
 +See also [`brace_style`](#brace_style), [`control_brace_style`](#control_brace_style).
 +
 +
 +## `wrap_comments`
 +
 +Break comments to fit on the line
 +
 +- **Default value**: `false`
 +- **Possible values**: `true`, `false`
 +- **Stable**: No (tracking issue: [#3347](https://github.com/rust-lang/rustfmt/issues/3347))
 +
 +#### `false` (default):
 +
 +```rust
 +// Lorem ipsum dolor sit amet, consectetur adipiscing elit,
 +// sed do eiusmod tempor incididunt ut labore et dolore
 +// magna aliqua. Ut enim ad minim veniam, quis nostrud
 +// exercitation ullamco laboris nisi ut aliquip ex ea
 +// commodo consequat.
 +
 +// Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
 +```
 +
 +#### `true`:
 +
 +```rust
 +// Lorem ipsum dolor sit amet, consectetur adipiscing elit,
 +// sed do eiusmod tempor incididunt ut labore et dolore
 +// magna aliqua. Ut enim ad minim veniam, quis nostrud
 +// exercitation ullamco laboris nisi ut aliquip ex ea
 +// commodo consequat.
 +```
 +
 +# Internal Options
 +
 +## `emit_mode`
 +
 +Internal option
 +
 +## `make_backup`
 +
 +Internal option, use `--backup`
 +
 +## `print_misformatted_file_names`
 +
 +Internal option, use `-l` or `--files-with-diff`
index 5b68d2748490f53f41bf70be1d42d828d18f9f1d,0000000000000000000000000000000000000000..f5cba87b07b678a47efd2d29bd3bff6278fa5bdd
mode 100644,000000..100644
--- /dev/null
@@@ -1,52 -1,0 +1,52 @@@
-     use std::io::Write;
-     use std::process::{Command, Stdio};
 +use proc_macro2::TokenStream;
 +use quote::{quote, ToTokens};
 +
 +pub fn fold_quote<F, I, T>(input: impl Iterator<Item = I>, f: F) -> TokenStream
 +where
 +    F: Fn(I) -> T,
 +    T: ToTokens,
 +{
 +    input.fold(quote! {}, |acc, x| {
 +        let y = f(x);
 +        quote! { #acc #y }
 +    })
 +}
 +
 +pub fn is_unit(v: &syn::Variant) -> bool {
 +    match v.fields {
 +        syn::Fields::Unit => true,
 +        _ => false,
 +    }
 +}
 +
 +#[cfg(feature = "debug-with-rustfmt")]
 +/// Pretty-print the output of proc macro using rustfmt.
 +pub fn debug_with_rustfmt(input: &TokenStream) {
 +    use std::env;
 +    use std::ffi::OsStr;
++    use std::io::Write;
++    use std::process::{Command, Stdio};
 +
 +    let rustfmt_var = env::var_os("RUSTFMT");
 +    let rustfmt = match &rustfmt_var {
 +        Some(rustfmt) => rustfmt,
 +        None => OsStr::new("rustfmt"),
 +    };
 +    let mut child = Command::new(rustfmt)
 +        .stdin(Stdio::piped())
 +        .stdout(Stdio::piped())
 +        .spawn()
 +        .expect("Failed to spawn rustfmt in stdio mode");
 +    {
 +        let stdin = child.stdin.as_mut().expect("Failed to get stdin");
 +        stdin
 +            .write_all(format!("{}", input).as_bytes())
 +            .expect("Failed to write to stdin");
 +    }
 +    let rustfmt_output = child.wait_with_output().expect("rustfmt has failed");
 +
 +    eprintln!(
 +        "{}",
 +        String::from_utf8(rustfmt_output.stdout).expect("rustfmt returned non-UTF8 string")
 +    );
 +}
index d4cdcec2018aa6cb61a2fd8a01527321b1a2cbb6,0000000000000000000000000000000000000000..94b57d506c20b0e86839ba819440b76cbb49de15
mode 100644,000000..100644
--- /dev/null
@@@ -1,3 -1,0 +1,3 @@@
- channel = "nightly-2021-12-29"
 +[toolchain]
++channel = "nightly-2022-03-27"
 +components = ["rustc-dev"]
index 3887a8051f20995efc83e20c88b789e8d162ef84,0000000000000000000000000000000000000000..befe12ae2c4cd7f6d80fb207cc3856464d6ef463
mode 100644,000000..100644
--- /dev/null
@@@ -1,537 -1,0 +1,541 @@@
-             if context.config.merge_derives() && is_derive(&attrs[0]) {
 +//! Format attributes and meta items.
 +
 +use rustc_ast::ast;
 +use rustc_ast::AstLike;
 +use rustc_span::{symbol::sym, Span, Symbol};
 +
 +use self::doc_comment::DocCommentFormatter;
 +use crate::comment::{contains_comment, rewrite_doc_comment, CommentStyle};
 +use crate::config::lists::*;
 +use crate::config::IndentStyle;
 +use crate::expr::rewrite_literal;
 +use crate::lists::{definitive_tactic, itemize_list, write_list, ListFormatting, Separator};
 +use crate::overflow;
 +use crate::rewrite::{Rewrite, RewriteContext};
 +use crate::shape::Shape;
 +use crate::source_map::SpanUtils;
 +use crate::types::{rewrite_path, PathContext};
 +use crate::utils::{count_newlines, mk_sp};
 +
 +mod doc_comment;
 +
 +pub(crate) fn contains_name(attrs: &[ast::Attribute], name: Symbol) -> bool {
 +    attrs.iter().any(|attr| attr.has_name(name))
 +}
 +
 +pub(crate) fn first_attr_value_str_by_name(
 +    attrs: &[ast::Attribute],
 +    name: Symbol,
 +) -> Option<Symbol> {
 +    attrs
 +        .iter()
 +        .find(|attr| attr.has_name(name))
 +        .and_then(|attr| attr.value_str())
 +}
 +
 +/// Returns attributes on the given statement.
 +pub(crate) fn get_attrs_from_stmt(stmt: &ast::Stmt) -> &[ast::Attribute] {
 +    stmt.attrs()
 +}
 +
 +pub(crate) fn get_span_without_attrs(stmt: &ast::Stmt) -> Span {
 +    match stmt.kind {
 +        ast::StmtKind::Local(ref local) => local.span,
 +        ast::StmtKind::Item(ref item) => item.span,
 +        ast::StmtKind::Expr(ref expr) | ast::StmtKind::Semi(ref expr) => expr.span,
 +        ast::StmtKind::MacCall(ref mac_stmt) => mac_stmt.mac.span(),
 +        ast::StmtKind::Empty => stmt.span,
 +    }
 +}
 +
 +/// Returns attributes that are within `outer_span`.
 +pub(crate) fn filter_inline_attrs(
 +    attrs: &[ast::Attribute],
 +    outer_span: Span,
 +) -> Vec<ast::Attribute> {
 +    attrs
 +        .iter()
 +        .filter(|a| outer_span.lo() <= a.span.lo() && a.span.hi() <= outer_span.hi())
 +        .cloned()
 +        .collect()
 +}
 +
 +fn is_derive(attr: &ast::Attribute) -> bool {
 +    attr.has_name(sym::derive)
 +}
 +
 +// The shape of the arguments to a function-like attribute.
 +fn argument_shape(
 +    left: usize,
 +    right: usize,
 +    combine: bool,
 +    shape: Shape,
 +    context: &RewriteContext<'_>,
 +) -> Option<Shape> {
 +    match context.config.indent_style() {
 +        IndentStyle::Block => {
 +            if combine {
 +                shape.offset_left(left)
 +            } else {
 +                Some(
 +                    shape
 +                        .block_indent(context.config.tab_spaces())
 +                        .with_max_width(context.config),
 +                )
 +            }
 +        }
 +        IndentStyle::Visual => shape
 +            .visual_indent(0)
 +            .shrink_left(left)
 +            .and_then(|s| s.sub_width(right)),
 +    }
 +}
 +
 +fn format_derive(
 +    derives: &[ast::Attribute],
 +    shape: Shape,
 +    context: &RewriteContext<'_>,
 +) -> Option<String> {
 +    // Collect all items from all attributes
 +    let all_items = derives
 +        .iter()
 +        .map(|attr| {
 +            // Parse the derive items and extract the span for each item; if any
 +            // attribute is not parseable, none of the attributes will be
 +            // reformatted.
 +            let item_spans = attr.meta_item_list().map(|meta_item_list| {
 +                meta_item_list
 +                    .into_iter()
 +                    .map(|nested_meta_item| nested_meta_item.span())
 +            })?;
 +
 +            let items = itemize_list(
 +                context.snippet_provider,
 +                item_spans,
 +                ")",
 +                ",",
 +                |span| span.lo(),
 +                |span| span.hi(),
 +                |span| Some(context.snippet(*span).to_owned()),
 +                // We update derive attribute spans to start after the opening '('
 +                // This helps us focus parsing to just what's inside #[derive(...)]
 +                context.snippet_provider.span_after(attr.span, "("),
 +                attr.span.hi(),
 +                false,
 +            );
 +
 +            Some(items)
 +        })
 +        // Fail if any attribute failed.
 +        .collect::<Option<Vec<_>>>()?
 +        // Collect the results into a single, flat, Vec.
 +        .into_iter()
 +        .flatten()
 +        .collect::<Vec<_>>();
 +
 +    // Collect formatting parameters.
 +    let prefix = attr_prefix(&derives[0]);
 +    let argument_shape = argument_shape(
 +        "[derive()]".len() + prefix.len(),
 +        ")]".len(),
 +        false,
 +        shape,
 +        context,
 +    )?;
 +    let one_line_shape = shape
 +        .offset_left("[derive()]".len() + prefix.len())?
 +        .sub_width("()]".len())?;
 +    let one_line_budget = one_line_shape.width;
 +
 +    let tactic = definitive_tactic(
 +        &all_items,
 +        ListTactic::HorizontalVertical,
 +        Separator::Comma,
 +        argument_shape.width,
 +    );
 +    let trailing_separator = match context.config.indent_style() {
 +        // We always add the trailing comma and remove it if it is not needed.
 +        IndentStyle::Block => SeparatorTactic::Always,
 +        IndentStyle::Visual => SeparatorTactic::Never,
 +    };
 +
 +    // Format the collection of items.
 +    let fmt = ListFormatting::new(argument_shape, context.config)
 +        .tactic(tactic)
 +        .trailing_separator(trailing_separator)
 +        .ends_with_newline(false);
 +    let item_str = write_list(&all_items, &fmt)?;
 +
 +    debug!("item_str: '{}'", item_str);
 +
 +    // Determine if the result will be nested, i.e. if we're using the block
 +    // indent style and either the items are on multiple lines or we've exceeded
 +    // our budget to fit on a single line.
 +    let nested = context.config.indent_style() == IndentStyle::Block
 +        && (item_str.contains('\n') || item_str.len() > one_line_budget);
 +
 +    // Format the final result.
 +    let mut result = String::with_capacity(128);
 +    result.push_str(prefix);
 +    result.push_str("[derive(");
 +    if nested {
 +        let nested_indent = argument_shape.indent.to_string_with_newline(context.config);
 +        result.push_str(&nested_indent);
 +        result.push_str(&item_str);
 +        result.push_str(&shape.indent.to_string_with_newline(context.config));
 +    } else if let SeparatorTactic::Always = context.config.trailing_comma() {
 +        // Retain the trailing comma.
 +        result.push_str(&item_str);
 +    } else if item_str.ends_with(',') {
 +        // Remove the trailing comma.
 +        result.push_str(&item_str[..item_str.len() - 1]);
 +    } else {
 +        result.push_str(&item_str);
 +    }
 +    result.push_str(")]");
 +
 +    Some(result)
 +}
 +
 +/// Returns the first group of attributes that fills the given predicate.
 +/// We consider two doc comments are in different group if they are separated by normal comments.
 +fn take_while_with_pred<'a, P>(
 +    context: &RewriteContext<'_>,
 +    attrs: &'a [ast::Attribute],
 +    pred: P,
 +) -> &'a [ast::Attribute]
 +where
 +    P: Fn(&ast::Attribute) -> bool,
 +{
 +    let mut len = 0;
 +    let mut iter = attrs.iter().peekable();
 +
 +    while let Some(attr) = iter.next() {
 +        if pred(attr) {
 +            len += 1;
 +        } else {
 +            break;
 +        }
 +        if let Some(next_attr) = iter.peek() {
 +            // Extract comments between two attributes.
 +            let span_between_attr = mk_sp(attr.span.hi(), next_attr.span.lo());
 +            let snippet = context.snippet(span_between_attr);
 +            if count_newlines(snippet) >= 2 || snippet.contains('/') {
 +                break;
 +            }
 +        }
 +    }
 +
 +    &attrs[..len]
 +}
 +
 +/// Rewrite the any doc comments which come before any other attributes.
 +fn rewrite_initial_doc_comments(
 +    context: &RewriteContext<'_>,
 +    attrs: &[ast::Attribute],
 +    shape: Shape,
 +) -> Option<(usize, Option<String>)> {
 +    if attrs.is_empty() {
 +        return Some((0, None));
 +    }
 +    // Rewrite doc comments
 +    let sugared_docs = take_while_with_pred(context, attrs, |a| a.is_doc_comment());
 +    if !sugared_docs.is_empty() {
 +        let snippet = sugared_docs
 +            .iter()
 +            .map(|a| context.snippet(a.span))
 +            .collect::<Vec<_>>()
 +            .join("\n");
 +        return Some((
 +            sugared_docs.len(),
 +            Some(rewrite_doc_comment(
 +                &snippet,
 +                shape.comment(context.config),
 +                context.config,
 +            )?),
 +        ));
 +    }
 +
 +    Some((0, None))
 +}
 +
 +impl Rewrite for ast::NestedMetaItem {
 +    fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
 +        match self {
 +            ast::NestedMetaItem::MetaItem(ref meta_item) => meta_item.rewrite(context, shape),
 +            ast::NestedMetaItem::Literal(ref l) => rewrite_literal(context, l, shape),
 +        }
 +    }
 +}
 +
 +fn has_newlines_before_after_comment(comment: &str) -> (&str, &str) {
 +    // Look at before and after comment and see if there are any empty lines.
 +    let comment_begin = comment.find('/');
 +    let len = comment_begin.unwrap_or_else(|| comment.len());
 +    let mlb = count_newlines(&comment[..len]) > 1;
 +    let mla = if comment_begin.is_none() {
 +        mlb
 +    } else {
 +        comment
 +            .chars()
 +            .rev()
 +            .take_while(|c| c.is_whitespace())
 +            .filter(|&c| c == '\n')
 +            .count()
 +            > 1
 +    };
 +    (if mlb { "\n" } else { "" }, if mla { "\n" } else { "" })
 +}
 +
 +impl Rewrite for ast::MetaItem {
 +    fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
 +        Some(match self.kind {
 +            ast::MetaItemKind::Word => {
 +                rewrite_path(context, PathContext::Type, None, &self.path, shape)?
 +            }
 +            ast::MetaItemKind::List(ref list) => {
 +                let path = rewrite_path(context, PathContext::Type, None, &self.path, shape)?;
 +                let has_trailing_comma = crate::expr::span_ends_with_comma(context, self.span);
 +                overflow::rewrite_with_parens(
 +                    context,
 +                    &path,
 +                    list.iter(),
 +                    // 1 = "]"
 +                    shape.sub_width(1)?,
 +                    self.span,
 +                    context.config.attr_fn_like_width(),
 +                    Some(if has_trailing_comma {
 +                        SeparatorTactic::Always
 +                    } else {
 +                        SeparatorTactic::Never
 +                    }),
 +                )?
 +            }
 +            ast::MetaItemKind::NameValue(ref literal) => {
 +                let path = rewrite_path(context, PathContext::Type, None, &self.path, shape)?;
 +                // 3 = ` = `
 +                let lit_shape = shape.shrink_left(path.len() + 3)?;
 +                // `rewrite_literal` returns `None` when `literal` exceeds max
 +                // width. Since a literal is basically unformattable unless it
 +                // is a string literal (and only if `format_strings` is set),
 +                // we might be better off ignoring the fact that the attribute
 +                // is longer than the max width and continue on formatting.
 +                // See #2479 for example.
 +                let value = rewrite_literal(context, literal, lit_shape)
 +                    .unwrap_or_else(|| context.snippet(literal.span).to_owned());
 +                format!("{} = {}", path, value)
 +            }
 +        })
 +    }
 +}
 +
 +impl Rewrite for ast::Attribute {
 +    fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
 +        let snippet = context.snippet(self.span);
 +        if self.is_doc_comment() {
 +            rewrite_doc_comment(snippet, shape.comment(context.config), context.config)
 +        } else {
 +            let should_skip = self
 +                .ident()
 +                .map(|s| context.skip_context.skip_attribute(s.name.as_str()))
 +                .unwrap_or(false);
 +            let prefix = attr_prefix(self);
 +
 +            if should_skip || contains_comment(snippet) {
 +                return Some(snippet.to_owned());
 +            }
 +
 +            if let Some(ref meta) = self.meta() {
 +                // This attribute is possibly a doc attribute needing normalization to a doc comment
 +                if context.config.normalize_doc_attributes() && meta.has_name(sym::doc) {
 +                    if let Some(ref literal) = meta.value_str() {
 +                        let comment_style = match self.style {
 +                            ast::AttrStyle::Inner => CommentStyle::Doc,
 +                            ast::AttrStyle::Outer => CommentStyle::TripleSlash,
 +                        };
 +
 +                        let literal_str = literal.as_str();
 +                        let doc_comment_formatter =
 +                            DocCommentFormatter::new(literal_str, comment_style);
 +                        let doc_comment = format!("{}", doc_comment_formatter);
 +                        return rewrite_doc_comment(
 +                            &doc_comment,
 +                            shape.comment(context.config),
 +                            context.config,
 +                        );
 +                    }
 +                }
 +
 +                // 1 = `[`
 +                let shape = shape.offset_left(prefix.len() + 1)?;
 +                Some(
 +                    meta.rewrite(context, shape)
 +                        .map_or_else(|| snippet.to_owned(), |rw| format!("{}[{}]", prefix, rw)),
 +                )
 +            } else {
 +                Some(snippet.to_owned())
 +            }
 +        }
 +    }
 +}
 +
 +impl Rewrite for [ast::Attribute] {
 +    fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
 +        if self.is_empty() {
 +            return Some(String::new());
 +        }
 +
 +        // The current remaining attributes.
 +        let mut attrs = self;
 +        let mut result = String::new();
 +
++        // Determine if the source text is annotated with `#[rustfmt::skip::attributes(derive)]`
++        // or `#![rustfmt::skip::attributes(derive)]`
++        let skip_derives = context.skip_context.skip_attribute("derive");
++
 +        // This is not just a simple map because we need to handle doc comments
 +        // (where we take as many doc comment attributes as possible) and possibly
 +        // merging derives into a single attribute.
 +        loop {
 +            if attrs.is_empty() {
 +                return Some(result);
 +            }
 +
 +            // Handle doc comments.
 +            let (doc_comment_len, doc_comment_str) =
 +                rewrite_initial_doc_comments(context, attrs, shape)?;
 +            if doc_comment_len > 0 {
 +                let doc_comment_str = doc_comment_str.expect("doc comments, but no result");
 +                result.push_str(&doc_comment_str);
 +
 +                let missing_span = attrs
 +                    .get(doc_comment_len)
 +                    .map(|next| mk_sp(attrs[doc_comment_len - 1].span.hi(), next.span.lo()));
 +                if let Some(missing_span) = missing_span {
 +                    let snippet = context.snippet(missing_span);
 +                    let (mla, mlb) = has_newlines_before_after_comment(snippet);
 +                    let comment = crate::comment::recover_missing_comment_in_span(
 +                        missing_span,
 +                        shape.with_max_width(context.config),
 +                        context,
 +                        0,
 +                    )?;
 +                    let comment = if comment.is_empty() {
 +                        format!("\n{}", mlb)
 +                    } else {
 +                        format!("{}{}\n{}", mla, comment, mlb)
 +                    };
 +                    result.push_str(&comment);
 +                    result.push_str(&shape.indent.to_string(context.config));
 +                }
 +
 +                attrs = &attrs[doc_comment_len..];
 +
 +                continue;
 +            }
 +
 +            // Handle derives if we will merge them.
++            if !skip_derives && context.config.merge_derives() && is_derive(&attrs[0]) {
 +                let derives = take_while_with_pred(context, attrs, is_derive);
 +                let derive_str = format_derive(derives, shape, context)?;
 +                result.push_str(&derive_str);
 +
 +                let missing_span = attrs
 +                    .get(derives.len())
 +                    .map(|next| mk_sp(attrs[derives.len() - 1].span.hi(), next.span.lo()));
 +                if let Some(missing_span) = missing_span {
 +                    let comment = crate::comment::recover_missing_comment_in_span(
 +                        missing_span,
 +                        shape.with_max_width(context.config),
 +                        context,
 +                        0,
 +                    )?;
 +                    result.push_str(&comment);
 +                    if let Some(next) = attrs.get(derives.len()) {
 +                        if next.is_doc_comment() {
 +                            let snippet = context.snippet(missing_span);
 +                            let (_, mlb) = has_newlines_before_after_comment(snippet);
 +                            result.push_str(mlb);
 +                        }
 +                    }
 +                    result.push('\n');
 +                    result.push_str(&shape.indent.to_string(context.config));
 +                }
 +
 +                attrs = &attrs[derives.len()..];
 +
 +                continue;
 +            }
 +
 +            // If we get here, then we have a regular attribute, just handle one
 +            // at a time.
 +
 +            let formatted_attr = attrs[0].rewrite(context, shape)?;
 +            result.push_str(&formatted_attr);
 +
 +            let missing_span = attrs
 +                .get(1)
 +                .map(|next| mk_sp(attrs[0].span.hi(), next.span.lo()));
 +            if let Some(missing_span) = missing_span {
 +                let comment = crate::comment::recover_missing_comment_in_span(
 +                    missing_span,
 +                    shape.with_max_width(context.config),
 +                    context,
 +                    0,
 +                )?;
 +                result.push_str(&comment);
 +                if let Some(next) = attrs.get(1) {
 +                    if next.is_doc_comment() {
 +                        let snippet = context.snippet(missing_span);
 +                        let (_, mlb) = has_newlines_before_after_comment(snippet);
 +                        result.push_str(mlb);
 +                    }
 +                }
 +                result.push('\n');
 +                result.push_str(&shape.indent.to_string(context.config));
 +            }
 +
 +            attrs = &attrs[1..];
 +        }
 +    }
 +}
 +
 +fn attr_prefix(attr: &ast::Attribute) -> &'static str {
 +    match attr.style {
 +        ast::AttrStyle::Inner => "#!",
 +        ast::AttrStyle::Outer => "#",
 +    }
 +}
 +
 +pub(crate) trait MetaVisitor<'ast> {
 +    fn visit_meta_item(&mut self, meta_item: &'ast ast::MetaItem) {
 +        match meta_item.kind {
 +            ast::MetaItemKind::Word => self.visit_meta_word(meta_item),
 +            ast::MetaItemKind::List(ref list) => self.visit_meta_list(meta_item, list),
 +            ast::MetaItemKind::NameValue(ref lit) => self.visit_meta_name_value(meta_item, lit),
 +        }
 +    }
 +
 +    fn visit_meta_list(
 +        &mut self,
 +        _meta_item: &'ast ast::MetaItem,
 +        list: &'ast [ast::NestedMetaItem],
 +    ) {
 +        for nm in list {
 +            self.visit_nested_meta_item(nm);
 +        }
 +    }
 +
 +    fn visit_meta_word(&mut self, _meta_item: &'ast ast::MetaItem) {}
 +
 +    fn visit_meta_name_value(&mut self, _meta_item: &'ast ast::MetaItem, _lit: &'ast ast::Lit) {}
 +
 +    fn visit_nested_meta_item(&mut self, nm: &'ast ast::NestedMetaItem) {
 +        match nm {
 +            ast::NestedMetaItem::MetaItem(ref meta_item) => self.visit_meta_item(meta_item),
 +            ast::NestedMetaItem::Literal(ref lit) => self.visit_literal(lit),
 +        }
 +    }
 +
 +    fn visit_literal(&mut self, _lit: &'ast ast::Lit) {}
 +}
index 4d845547cdfed179647f86dca9bd0e36c3740211,0000000000000000000000000000000000000000..ad10b9ede608fdb1da2ed45493d4d6696716de79
mode 100644,000000..100644
--- /dev/null
@@@ -1,709 -1,0 +1,709 @@@
-             eprintln!("{}", e);
 +use anyhow::{format_err, Result};
 +
 +use io::Error as IoError;
 +use thiserror::Error;
 +
 +use rustfmt_nightly as rustfmt;
 +
 +use std::collections::HashMap;
 +use std::env;
 +use std::fs::File;
 +use std::io::{self, stdout, Read, Write};
 +use std::path::{Path, PathBuf};
 +use std::str::FromStr;
 +
 +use getopts::{Matches, Options};
 +
 +use crate::rustfmt::{
 +    load_config, CliOptions, Color, Config, Edition, EmitMode, FileLines, FileName,
 +    FormatReportFormatterBuilder, Input, Session, Verbosity,
 +};
 +
 +fn main() {
 +    env_logger::Builder::from_env("RUSTFMT_LOG").init();
 +    let opts = make_opts();
 +
 +    let exit_code = match execute(&opts) {
 +        Ok(code) => code,
 +        Err(e) => {
-     /// Attempt to use --check with stdin, which isn't currently
-     /// supported.
-     #[error("The `--check` option is not supported with standard input.")]
-     CheckWithStdin,
-     /// Attempt to use --emit=json with stdin, which isn't currently
-     /// supported.
-     #[error("Using `--emit` other than stdout is not supported with standard input.")]
-     EmitWithStdin,
++            eprintln!("{:#}", e);
 +            1
 +        }
 +    };
 +    // Make sure standard output is flushed before we exit.
 +    std::io::stdout().flush().unwrap();
 +
 +    // Exit with given exit code.
 +    //
 +    // NOTE: this immediately terminates the process without doing any cleanup,
 +    // so make sure to finish all necessary cleanup before this is called.
 +    std::process::exit(exit_code);
 +}
 +
 +/// Rustfmt operations.
 +enum Operation {
 +    /// Format files and their child modules.
 +    Format {
 +        files: Vec<PathBuf>,
 +        minimal_config_path: Option<String>,
 +    },
 +    /// Print the help message.
 +    Help(HelpOp),
 +    /// Print version information
 +    Version,
 +    /// Output default config to a file, or stdout if None
 +    ConfigOutputDefault { path: Option<String> },
 +    /// Output current config (as if formatting to a file) to stdout
 +    ConfigOutputCurrent { path: Option<String> },
 +    /// No file specified, read from stdin
 +    Stdin { input: String },
 +}
 +
 +/// Rustfmt operations errors.
 +#[derive(Error, Debug)]
 +pub enum OperationError {
 +    /// An unknown help topic was requested.
 +    #[error("Unknown help topic: `{0}`.")]
 +    UnknownHelpTopic(String),
 +    /// An unknown print-config option was requested.
 +    #[error("Unknown print-config option: `{0}`.")]
 +    UnknownPrintConfigTopic(String),
 +    /// Attempt to generate a minimal config from standard input.
 +    #[error("The `--print-config=minimal` option doesn't work with standard input.")]
 +    MinimalPathWithStdin,
 +    /// An io error during reading or writing.
 +    #[error("{0}")]
 +    IoError(IoError),
-         return Err(OperationError::CheckWithStdin.into());
-     }
-     if let Some(emit_mode) = options.emit_mode {
-         if emit_mode != EmitMode::Stdout {
-             return Err(OperationError::EmitWithStdin.into());
++    /// Attempt to use --emit with a mode which is not currently
++    /// supported with stdandard input.
++    #[error("Emit mode {0} not supported with standard output.")]
++    StdinBadEmit(EmitMode),
 +}
 +
 +impl From<IoError> for OperationError {
 +    fn from(e: IoError) -> OperationError {
 +        OperationError::IoError(e)
 +    }
 +}
 +
 +/// Arguments to `--help`
 +enum HelpOp {
 +    None,
 +    Config,
 +    FileLines,
 +}
 +
 +fn make_opts() -> Options {
 +    let mut opts = Options::new();
 +
 +    opts.optflag(
 +        "",
 +        "check",
 +        "Run in 'check' mode. Exits with 0 if input is formatted correctly. Exits \
 +         with 1 and prints a diff if formatting is required.",
 +    );
 +    let is_nightly = is_nightly();
 +    let emit_opts = if is_nightly {
 +        "[files|stdout|coverage|checkstyle|json]"
 +    } else {
 +        "[files|stdout]"
 +    };
 +    opts.optopt("", "emit", "What data to emit and how", emit_opts);
 +    opts.optflag("", "backup", "Backup any modified files.");
 +    opts.optopt(
 +        "",
 +        "config-path",
 +        "Recursively searches the given path for the rustfmt.toml config file. If not \
 +         found reverts to the input file path",
 +        "[Path for the configuration file]",
 +    );
 +    opts.optopt("", "edition", "Rust edition to use", "[2015|2018|2021]");
 +    opts.optopt(
 +        "",
 +        "color",
 +        "Use colored output (if supported)",
 +        "[always|never|auto]",
 +    );
 +    opts.optopt(
 +        "",
 +        "print-config",
 +        "Dumps a default or minimal config to PATH. A minimal config is the \
 +         subset of the current config file used for formatting the current program. \
 +         `current` writes to stdout current config as if formatting the file at PATH.",
 +        "[default|minimal|current] PATH",
 +    );
 +    opts.optflag(
 +        "l",
 +        "files-with-diff",
 +        "Prints the names of mismatched files that were formatted. Prints the names of \
 +         files that would be formated when used with `--check` mode. ",
 +    );
 +    opts.optmulti(
 +        "",
 +        "config",
 +        "Set options from command line. These settings take priority over .rustfmt.toml",
 +        "[key1=val1,key2=val2...]",
 +    );
 +
 +    if is_nightly {
 +        opts.optflag(
 +            "",
 +            "unstable-features",
 +            "Enables unstable features. Only available on nightly channel.",
 +        );
 +        opts.optopt(
 +            "",
 +            "file-lines",
 +            "Format specified line ranges. Run with `--help=file-lines` for \
 +             more detail (unstable).",
 +            "JSON",
 +        );
 +        opts.optflag(
 +            "",
 +            "error-on-unformatted",
 +            "Error if unable to get comments or string literals within max_width, \
 +             or they are left with trailing whitespaces (unstable).",
 +        );
 +        opts.optflag(
 +            "",
 +            "skip-children",
 +            "Don't reformat child modules (unstable).",
 +        );
 +    }
 +
 +    opts.optflag("v", "verbose", "Print verbose output");
 +    opts.optflag("q", "quiet", "Print less output");
 +    opts.optflag("V", "version", "Show version information");
 +    let help_topics = if is_nightly {
 +        "`config` or `file-lines`"
 +    } else {
 +        "`config`"
 +    };
 +    let mut help_topic_msg = "Show this message or help about a specific topic: ".to_owned();
 +    help_topic_msg.push_str(help_topics);
 +
 +    opts.optflagopt("h", "help", &help_topic_msg, "=TOPIC");
 +
 +    opts
 +}
 +
 +fn is_nightly() -> bool {
 +    option_env!("CFG_RELEASE_CHANNEL").map_or(true, |c| c == "nightly" || c == "dev")
 +}
 +
 +// Returned i32 is an exit code
 +fn execute(opts: &Options) -> Result<i32> {
 +    let matches = opts.parse(env::args().skip(1))?;
 +    let options = GetOptsOptions::from_matches(&matches)?;
 +
 +    match determine_operation(&matches)? {
 +        Operation::Help(HelpOp::None) => {
 +            print_usage_to_stdout(opts, "");
 +            Ok(0)
 +        }
 +        Operation::Help(HelpOp::Config) => {
 +            Config::print_docs(&mut stdout(), options.unstable_features);
 +            Ok(0)
 +        }
 +        Operation::Help(HelpOp::FileLines) => {
 +            print_help_file_lines();
 +            Ok(0)
 +        }
 +        Operation::Version => {
 +            print_version();
 +            Ok(0)
 +        }
 +        Operation::ConfigOutputDefault { path } => {
 +            let toml = Config::default().all_options().to_toml()?;
 +            if let Some(path) = path {
 +                let mut file = File::create(path)?;
 +                file.write_all(toml.as_bytes())?;
 +            } else {
 +                io::stdout().write_all(toml.as_bytes())?;
 +            }
 +            Ok(0)
 +        }
 +        Operation::ConfigOutputCurrent { path } => {
 +            let path = match path {
 +                Some(path) => path,
 +                None => return Err(format_err!("PATH required for `--print-config current`")),
 +            };
 +
 +            let file = PathBuf::from(path);
 +            let file = file.canonicalize().unwrap_or(file);
 +
 +            let (config, _) = load_config(Some(file.parent().unwrap()), Some(options))?;
 +            let toml = config.all_options().to_toml()?;
 +            io::stdout().write_all(toml.as_bytes())?;
 +
 +            Ok(0)
 +        }
 +        Operation::Stdin { input } => format_string(input, options),
 +        Operation::Format {
 +            files,
 +            minimal_config_path,
 +        } => format(files, minimal_config_path, &options),
 +    }
 +}
 +
 +fn format_string(input: String, options: GetOptsOptions) -> Result<i32> {
 +    // try to read config from local directory
 +    let (mut config, _) = load_config(Some(Path::new(".")), Some(options.clone()))?;
 +
 +    if options.check {
-     // emit mode is always Stdout for Stdin.
-     config.set().emit_mode(EmitMode::Stdout);
++        config.set().emit_mode(EmitMode::Diff);
++    } else {
++        match options.emit_mode {
++            // Emit modes which work with standard input
++            // None means default, which is Stdout.
++            None | Some(EmitMode::Stdout) | Some(EmitMode::Checkstyle) | Some(EmitMode::Json) => {}
++            Some(emit_mode) => {
++                return Err(OperationError::StdinBadEmit(emit_mode).into());
++            }
 +        }
++        config
++            .set()
++            .emit_mode(options.emit_mode.unwrap_or(EmitMode::Stdout));
 +    }
-         "{}Format Rust code\n\nusage: {} [options] <file>...",
-         sep,
-         env::args_os().next().unwrap().to_string_lossy()
 +    config.set().verbose(Verbosity::Quiet);
 +
 +    // parse file_lines
 +    config.set().file_lines(options.file_lines);
 +    for f in config.file_lines().files() {
 +        match *f {
 +            FileName::Stdin => {}
 +            _ => eprintln!("Warning: Extra file listed in file_lines option '{}'", f),
 +        }
 +    }
 +
 +    let out = &mut stdout();
 +    let mut session = Session::new(config, Some(out));
 +    format_and_emit_report(&mut session, Input::Text(input));
 +
 +    let exit_code = if session.has_operational_errors() || session.has_parsing_errors() {
 +        1
 +    } else {
 +        0
 +    };
 +    Ok(exit_code)
 +}
 +
 +fn format(
 +    files: Vec<PathBuf>,
 +    minimal_config_path: Option<String>,
 +    options: &GetOptsOptions,
 +) -> Result<i32> {
 +    options.verify_file_lines(&files);
 +    let (config, config_path) = load_config(None, Some(options.clone()))?;
 +
 +    if config.verbose() == Verbosity::Verbose {
 +        if let Some(path) = config_path.as_ref() {
 +            println!("Using rustfmt config file {}", path.display());
 +        }
 +    }
 +
 +    let out = &mut stdout();
 +    let mut session = Session::new(config, Some(out));
 +
 +    for file in files {
 +        if !file.exists() {
 +            eprintln!("Error: file `{}` does not exist", file.to_str().unwrap());
 +            session.add_operational_error();
 +        } else if file.is_dir() {
 +            eprintln!("Error: `{}` is a directory", file.to_str().unwrap());
 +            session.add_operational_error();
 +        } else {
 +            // Check the file directory if the config-path could not be read or not provided
 +            if config_path.is_none() {
 +                let (local_config, config_path) =
 +                    load_config(Some(file.parent().unwrap()), Some(options.clone()))?;
 +                if local_config.verbose() == Verbosity::Verbose {
 +                    if let Some(path) = config_path {
 +                        println!(
 +                            "Using rustfmt config file {} for {}",
 +                            path.display(),
 +                            file.display()
 +                        );
 +                    }
 +                }
 +
 +                session.override_config(local_config, |sess| {
 +                    format_and_emit_report(sess, Input::File(file))
 +                });
 +            } else {
 +                format_and_emit_report(&mut session, Input::File(file));
 +            }
 +        }
 +    }
 +
 +    // If we were given a path via dump-minimal-config, output any options
 +    // that were used during formatting as TOML.
 +    if let Some(path) = minimal_config_path {
 +        let mut file = File::create(path)?;
 +        let toml = session.config.used_options().to_toml()?;
 +        file.write_all(toml.as_bytes())?;
 +    }
 +
 +    let exit_code = if session.has_operational_errors()
 +        || session.has_parsing_errors()
 +        || ((session.has_diff() || session.has_check_errors()) && options.check)
 +    {
 +        1
 +    } else {
 +        0
 +    };
 +    Ok(exit_code)
 +}
 +
 +fn format_and_emit_report<T: Write>(session: &mut Session<'_, T>, input: Input) {
 +    match session.format(input) {
 +        Ok(report) => {
 +            if report.has_warnings() {
 +                eprintln!(
 +                    "{}",
 +                    FormatReportFormatterBuilder::new(&report)
 +                        .enable_colors(should_print_with_colors(session))
 +                        .build()
 +                );
 +            }
 +        }
 +        Err(msg) => {
 +            eprintln!("Error writing files: {}", msg);
 +            session.add_operational_error();
 +        }
 +    }
 +}
 +
 +fn should_print_with_colors<T: Write>(session: &mut Session<'_, T>) -> bool {
 +    match term::stderr() {
 +        Some(ref t)
 +            if session.config.color().use_colored_tty()
 +                && t.supports_color()
 +                && t.supports_attr(term::Attr::Bold) =>
 +        {
 +            true
 +        }
 +        _ => false,
 +    }
 +}
 +
 +fn print_usage_to_stdout(opts: &Options, reason: &str) {
 +    let sep = if reason.is_empty() {
 +        String::new()
 +    } else {
 +        format!("{}\n\n", reason)
 +    };
 +    let msg = format!(
++        "{}Format Rust code\n\nusage: rustfmt [options] <file>...",
++        sep
 +    );
 +    println!("{}", opts.usage(&msg));
 +}
 +
 +fn print_help_file_lines() {
 +    println!(
 +        "If you want to restrict reformatting to specific sets of lines, you can
 +use the `--file-lines` option. Its argument is a JSON array of objects
 +with `file` and `range` properties, where `file` is a file name, and
 +`range` is an array representing a range of lines like `[7,13]`. Ranges
 +are 1-based and inclusive of both end points. Specifying an empty array
 +will result in no files being formatted. For example,
 +
 +```
 +rustfmt --file-lines '[
 +    {{\"file\":\"src/lib.rs\",\"range\":[7,13]}},
 +    {{\"file\":\"src/lib.rs\",\"range\":[21,29]}},
 +    {{\"file\":\"src/foo.rs\",\"range\":[10,11]}},
 +    {{\"file\":\"src/foo.rs\",\"range\":[15,15]}}]'
 +```
 +
 +would format lines `7-13` and `21-29` of `src/lib.rs`, and lines `10-11`,
 +and `15` of `src/foo.rs`. No other files would be formatted, even if they
 +are included as out of line modules from `src/lib.rs`."
 +    );
 +}
 +
 +fn print_version() {
 +    let version_info = format!(
 +        "{}-{}",
 +        option_env!("CARGO_PKG_VERSION").unwrap_or("unknown"),
 +        include_str!(concat!(env!("OUT_DIR"), "/commit-info.txt"))
 +    );
 +
 +    println!("rustfmt {}", version_info);
 +}
 +
 +fn determine_operation(matches: &Matches) -> Result<Operation, OperationError> {
 +    if matches.opt_present("h") {
 +        let topic = matches.opt_str("h");
 +        if topic == None {
 +            return Ok(Operation::Help(HelpOp::None));
 +        } else if topic == Some("config".to_owned()) {
 +            return Ok(Operation::Help(HelpOp::Config));
 +        } else if topic == Some("file-lines".to_owned()) && is_nightly() {
 +            return Ok(Operation::Help(HelpOp::FileLines));
 +        } else {
 +            return Err(OperationError::UnknownHelpTopic(topic.unwrap()));
 +        }
 +    }
 +    let mut free_matches = matches.free.iter();
 +
 +    let mut minimal_config_path = None;
 +    if let Some(kind) = matches.opt_str("print-config") {
 +        let path = free_matches.next().cloned();
 +        match kind.as_str() {
 +            "default" => return Ok(Operation::ConfigOutputDefault { path }),
 +            "current" => return Ok(Operation::ConfigOutputCurrent { path }),
 +            "minimal" => {
 +                minimal_config_path = path;
 +                if minimal_config_path.is_none() {
 +                    eprintln!("WARNING: PATH required for `--print-config minimal`.");
 +                }
 +            }
 +            _ => {
 +                return Err(OperationError::UnknownPrintConfigTopic(kind));
 +            }
 +        }
 +    }
 +
 +    if matches.opt_present("version") {
 +        return Ok(Operation::Version);
 +    }
 +
 +    let files: Vec<_> = free_matches
 +        .map(|s| {
 +            let p = PathBuf::from(s);
 +            // we will do comparison later, so here tries to canonicalize first
 +            // to get the expected behavior.
 +            p.canonicalize().unwrap_or(p)
 +        })
 +        .collect();
 +
 +    // if no file argument is supplied, read from stdin
 +    if files.is_empty() {
 +        if minimal_config_path.is_some() {
 +            return Err(OperationError::MinimalPathWithStdin);
 +        }
 +        let mut buffer = String::new();
 +        io::stdin().read_to_string(&mut buffer)?;
 +
 +        return Ok(Operation::Stdin { input: buffer });
 +    }
 +
 +    Ok(Operation::Format {
 +        files,
 +        minimal_config_path,
 +    })
 +}
 +
 +const STABLE_EMIT_MODES: [EmitMode; 3] = [EmitMode::Files, EmitMode::Stdout, EmitMode::Diff];
 +
 +/// Parsed command line options.
 +#[derive(Clone, Debug, Default)]
 +struct GetOptsOptions {
 +    skip_children: Option<bool>,
 +    quiet: bool,
 +    verbose: bool,
 +    config_path: Option<PathBuf>,
 +    inline_config: HashMap<String, String>,
 +    emit_mode: Option<EmitMode>,
 +    backup: bool,
 +    check: bool,
 +    edition: Option<Edition>,
 +    color: Option<Color>,
 +    file_lines: FileLines, // Default is all lines in all files.
 +    unstable_features: bool,
 +    error_on_unformatted: Option<bool>,
 +    print_misformatted_file_names: bool,
 +}
 +
 +impl GetOptsOptions {
 +    pub fn from_matches(matches: &Matches) -> Result<GetOptsOptions> {
 +        let mut options = GetOptsOptions::default();
 +        options.verbose = matches.opt_present("verbose");
 +        options.quiet = matches.opt_present("quiet");
 +        if options.verbose && options.quiet {
 +            return Err(format_err!("Can't use both `--verbose` and `--quiet`"));
 +        }
 +
 +        let rust_nightly = is_nightly();
 +
 +        if rust_nightly {
 +            options.unstable_features = matches.opt_present("unstable-features");
 +
 +            if options.unstable_features {
 +                if matches.opt_present("skip-children") {
 +                    options.skip_children = Some(true);
 +                }
 +                if matches.opt_present("error-on-unformatted") {
 +                    options.error_on_unformatted = Some(true);
 +                }
 +                if let Some(ref file_lines) = matches.opt_str("file-lines") {
 +                    options.file_lines = file_lines.parse()?;
 +                }
 +            } else {
 +                let mut unstable_options = vec![];
 +                if matches.opt_present("skip-children") {
 +                    unstable_options.push("`--skip-children`");
 +                }
 +                if matches.opt_present("error-on-unformatted") {
 +                    unstable_options.push("`--error-on-unformatted`");
 +                }
 +                if matches.opt_present("file-lines") {
 +                    unstable_options.push("`--file-lines`");
 +                }
 +                if !unstable_options.is_empty() {
 +                    let s = if unstable_options.len() == 1 { "" } else { "s" };
 +                    return Err(format_err!(
 +                        "Unstable option{} ({}) used without `--unstable-features`",
 +                        s,
 +                        unstable_options.join(", "),
 +                    ));
 +                }
 +            }
 +        }
 +
 +        options.config_path = matches.opt_str("config-path").map(PathBuf::from);
 +
 +        options.inline_config = matches
 +            .opt_strs("config")
 +            .iter()
 +            .flat_map(|config| config.split(','))
 +            .map(
 +                |key_val| match key_val.char_indices().find(|(_, ch)| *ch == '=') {
 +                    Some((middle, _)) => {
 +                        let (key, val) = (&key_val[..middle], &key_val[middle + 1..]);
 +                        if !Config::is_valid_key_val(key, val) {
 +                            Err(format_err!("invalid key=val pair: `{}`", key_val))
 +                        } else {
 +                            Ok((key.to_string(), val.to_string()))
 +                        }
 +                    }
 +
 +                    None => Err(format_err!(
 +                        "--config expects comma-separated list of key=val pairs, found `{}`",
 +                        key_val
 +                    )),
 +                },
 +            )
 +            .collect::<Result<HashMap<_, _>, _>>()?;
 +
 +        options.check = matches.opt_present("check");
 +        if let Some(ref emit_str) = matches.opt_str("emit") {
 +            if options.check {
 +                return Err(format_err!("Invalid to use `--emit` and `--check`"));
 +            }
 +
 +            options.emit_mode = Some(emit_mode_from_emit_str(emit_str)?);
 +        }
 +
 +        if let Some(ref edition_str) = matches.opt_str("edition") {
 +            options.edition = Some(edition_from_edition_str(edition_str)?);
 +        }
 +
 +        if matches.opt_present("backup") {
 +            options.backup = true;
 +        }
 +
 +        if matches.opt_present("files-with-diff") {
 +            options.print_misformatted_file_names = true;
 +        }
 +
 +        if !rust_nightly {
 +            if let Some(ref emit_mode) = options.emit_mode {
 +                if !STABLE_EMIT_MODES.contains(emit_mode) {
 +                    return Err(format_err!(
 +                        "Invalid value for `--emit` - using an unstable \
 +                         value without `--unstable-features`",
 +                    ));
 +                }
 +            }
 +        }
 +
 +        if let Some(ref color) = matches.opt_str("color") {
 +            match Color::from_str(color) {
 +                Ok(color) => options.color = Some(color),
 +                _ => return Err(format_err!("Invalid color: {}", color)),
 +            }
 +        }
 +
 +        Ok(options)
 +    }
 +
 +    fn verify_file_lines(&self, files: &[PathBuf]) {
 +        for f in self.file_lines.files() {
 +            match *f {
 +                FileName::Real(ref f) if files.contains(f) => {}
 +                FileName::Real(_) => {
 +                    eprintln!("Warning: Extra file listed in file_lines option '{}'", f)
 +                }
 +                FileName::Stdin => eprintln!("Warning: Not a file '{}'", f),
 +            }
 +        }
 +    }
 +}
 +
 +impl CliOptions for GetOptsOptions {
 +    fn apply_to(self, config: &mut Config) {
 +        if self.verbose {
 +            config.set().verbose(Verbosity::Verbose);
 +        } else if self.quiet {
 +            config.set().verbose(Verbosity::Quiet);
 +        } else {
 +            config.set().verbose(Verbosity::Normal);
 +        }
 +        config.set().file_lines(self.file_lines);
 +        config.set().unstable_features(self.unstable_features);
 +        if let Some(skip_children) = self.skip_children {
 +            config.set().skip_children(skip_children);
 +        }
 +        if let Some(error_on_unformatted) = self.error_on_unformatted {
 +            config.set().error_on_unformatted(error_on_unformatted);
 +        }
 +        if let Some(edition) = self.edition {
 +            config.set().edition(edition);
 +        }
 +        if self.check {
 +            config.set().emit_mode(EmitMode::Diff);
 +        } else if let Some(emit_mode) = self.emit_mode {
 +            config.set().emit_mode(emit_mode);
 +        }
 +        if self.backup {
 +            config.set().make_backup(true);
 +        }
 +        if let Some(color) = self.color {
 +            config.set().color(color);
 +        }
 +        if self.print_misformatted_file_names {
 +            config.set().print_misformatted_file_names(true);
 +        }
 +
 +        for (key, val) in self.inline_config {
 +            config.override_value(&key, &val);
 +        }
 +    }
 +
 +    fn config_path(&self) -> Option<&Path> {
 +        self.config_path.as_deref()
 +    }
 +}
 +
 +fn edition_from_edition_str(edition_str: &str) -> Result<Edition> {
 +    match edition_str {
 +        "2015" => Ok(Edition::Edition2015),
 +        "2018" => Ok(Edition::Edition2018),
 +        "2021" => Ok(Edition::Edition2021),
 +        _ => Err(format_err!("Invalid value for `--edition`")),
 +    }
 +}
 +
 +fn emit_mode_from_emit_str(emit_str: &str) -> Result<EmitMode> {
 +    match emit_str {
 +        "files" => Ok(EmitMode::Files),
 +        "stdout" => Ok(EmitMode::Stdout),
 +        "coverage" => Ok(EmitMode::Coverage),
 +        "checkstyle" => Ok(EmitMode::Checkstyle),
 +        "json" => Ok(EmitMode::Json),
 +        _ => Err(format_err!("Invalid value for `--emit`")),
 +    }
 +}
index 759b21218c353e70787f853a6c09725bf7132b10,0000000000000000000000000000000000000000..8cb7b4585ecb27c2d54f7149034ba5c36037a358
mode 100644,000000..100644
--- /dev/null
@@@ -1,546 -1,0 +1,545 @@@
-             .map(|p| p.targets)
-             .flatten()
 +// Inspired by Paul Woolcock's cargo-fmt (https://github.com/pwoolcoc/cargo-fmt/).
 +
 +#![deny(warnings)]
 +#![allow(clippy::match_like_matches_macro)]
 +
 +use std::cmp::Ordering;
 +use std::collections::{BTreeMap, BTreeSet};
 +use std::env;
 +use std::ffi::OsStr;
 +use std::fs;
 +use std::hash::{Hash, Hasher};
 +use std::io::{self, Write};
 +use std::iter::FromIterator;
 +use std::path::{Path, PathBuf};
 +use std::process::Command;
 +use std::str;
 +
 +use structopt::StructOpt;
 +
 +#[path = "test/mod.rs"]
 +#[cfg(test)]
 +mod cargo_fmt_tests;
 +
 +#[derive(StructOpt, Debug)]
 +#[structopt(
 +    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")]
 +    quiet: bool,
 +
 +    /// Use verbose output
 +    #[structopt(short = "v", long = "verbose")]
 +    verbose: bool,
 +
 +    /// Print rustfmt version and exit
 +    #[structopt(long = "version")]
 +    version: bool,
 +
 +    /// Specify package to format
 +    #[structopt(short = "p", long = "package", value_name = "package")]
 +    packages: Vec<String>,
 +
 +    /// Specify path to Cargo.toml
 +    #[structopt(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")]
 +    message_format: Option<String>,
 +
 +    /// Options passed to rustfmt
 +    // 'raw = true' to make `--` explicit.
 +    #[structopt(name = "rustfmt_options", raw(true))]
 +    rustfmt_options: Vec<String>,
 +
 +    /// Format all packages, and also their local path-based dependencies
 +    #[structopt(long = "all")]
 +    format_all: bool,
 +
 +    /// Run rustfmt in check mode
 +    #[structopt(long = "check")]
 +    check: bool,
 +}
 +
 +fn main() {
 +    let exit_status = execute();
 +    std::io::stdout().flush().unwrap();
 +    std::process::exit(exit_status);
 +}
 +
 +const SUCCESS: i32 = 0;
 +const FAILURE: i32 = 1;
 +
 +fn execute() -> i32 {
 +    // Drop extra `fmt` argument provided by `cargo`.
 +    let mut found_fmt = false;
 +    let args = env::args().filter(|x| {
 +        if found_fmt {
 +            true
 +        } else {
 +            found_fmt = x == "fmt";
 +            x != "fmt"
 +        }
 +    });
 +
 +    let opts = Opts::from_iter(args);
 +
 +    let verbosity = match (opts.verbose, opts.quiet) {
 +        (false, false) => Verbosity::Normal,
 +        (false, true) => Verbosity::Quiet,
 +        (true, false) => Verbosity::Verbose,
 +        (true, true) => {
 +            print_usage_to_stderr("quiet mode and verbose mode are not compatible");
 +            return FAILURE;
 +        }
 +    };
 +
 +    if opts.version {
 +        return handle_command_status(get_rustfmt_info(&[String::from("--version")]));
 +    }
 +    if opts.rustfmt_options.iter().any(|s| {
 +        ["--print-config", "-h", "--help", "-V", "--version"].contains(&s.as_str())
 +            || s.starts_with("--help=")
 +            || s.starts_with("--print-config=")
 +    }) {
 +        return handle_command_status(get_rustfmt_info(&opts.rustfmt_options));
 +    }
 +
 +    let strategy = CargoFmtStrategy::from_opts(&opts);
 +    let mut rustfmt_args = opts.rustfmt_options;
 +    if opts.check {
 +        let check_flag = "--check";
 +        if !rustfmt_args.iter().any(|o| o == check_flag) {
 +            rustfmt_args.push(check_flag.to_owned());
 +        }
 +    }
 +    if let Some(message_format) = opts.message_format {
 +        if let Err(msg) = convert_message_format_to_rustfmt_args(&message_format, &mut rustfmt_args)
 +        {
 +            print_usage_to_stderr(&msg);
 +            return FAILURE;
 +        }
 +    }
 +
 +    if let Some(specified_manifest_path) = opts.manifest_path {
 +        if !specified_manifest_path.ends_with("Cargo.toml") {
 +            print_usage_to_stderr("the manifest-path must be a path to a Cargo.toml file");
 +            return FAILURE;
 +        }
 +        let manifest_path = PathBuf::from(specified_manifest_path);
 +        handle_command_status(format_crate(
 +            verbosity,
 +            &strategy,
 +            rustfmt_args,
 +            Some(&manifest_path),
 +        ))
 +    } else {
 +        handle_command_status(format_crate(verbosity, &strategy, rustfmt_args, None))
 +    }
 +}
 +
 +fn rustfmt_command() -> Command {
 +    let rustfmt_var = env::var_os("RUSTFMT");
 +    let rustfmt = match &rustfmt_var {
 +        Some(rustfmt) => rustfmt,
 +        None => OsStr::new("rustfmt"),
 +    };
 +    Command::new(rustfmt)
 +}
 +
 +fn convert_message_format_to_rustfmt_args(
 +    message_format: &str,
 +    rustfmt_args: &mut Vec<String>,
 +) -> Result<(), String> {
 +    let mut contains_emit_mode = false;
 +    let mut contains_check = false;
 +    let mut contains_list_files = false;
 +    for arg in rustfmt_args.iter() {
 +        if arg.starts_with("--emit") {
 +            contains_emit_mode = true;
 +        }
 +        if arg == "--check" {
 +            contains_check = true;
 +        }
 +        if arg == "-l" || arg == "--files-with-diff" {
 +            contains_list_files = true;
 +        }
 +    }
 +    match message_format {
 +        "short" => {
 +            if !contains_list_files {
 +                rustfmt_args.push(String::from("-l"));
 +            }
 +            Ok(())
 +        }
 +        "json" => {
 +            if contains_emit_mode {
 +                return Err(String::from(
 +                    "cannot include --emit arg when --message-format is set to json",
 +                ));
 +            }
 +            if contains_check {
 +                return Err(String::from(
 +                    "cannot include --check arg when --message-format is set to json",
 +                ));
 +            }
 +            rustfmt_args.push(String::from("--emit"));
 +            rustfmt_args.push(String::from("json"));
 +            Ok(())
 +        }
 +        "human" => Ok(()),
 +        _ => {
 +            return Err(format!(
 +                "invalid --message-format value: {}. Allowed values are: short|json|human",
 +                message_format
 +            ));
 +        }
 +    }
 +}
 +
 +fn print_usage_to_stderr(reason: &str) {
 +    eprintln!("{}", reason);
 +    let app = Opts::clap();
 +    app.after_help("")
 +        .write_help(&mut io::stderr())
 +        .expect("failed to write to stderr");
 +}
 +
 +#[derive(Debug, Clone, Copy, PartialEq)]
 +pub enum Verbosity {
 +    Verbose,
 +    Normal,
 +    Quiet,
 +}
 +
 +fn handle_command_status(status: Result<i32, io::Error>) -> i32 {
 +    match status {
 +        Err(e) => {
 +            print_usage_to_stderr(&e.to_string());
 +            FAILURE
 +        }
 +        Ok(status) => status,
 +    }
 +}
 +
 +fn get_rustfmt_info(args: &[String]) -> Result<i32, io::Error> {
 +    let mut command = rustfmt_command()
 +        .stdout(std::process::Stdio::inherit())
 +        .args(args)
 +        .spawn()
 +        .map_err(|e| match e.kind() {
 +            io::ErrorKind::NotFound => io::Error::new(
 +                io::ErrorKind::Other,
 +                "Could not run rustfmt, please make sure it is in your PATH.",
 +            ),
 +            _ => e,
 +        })?;
 +    let result = command.wait()?;
 +    if result.success() {
 +        Ok(SUCCESS)
 +    } else {
 +        Ok(result.code().unwrap_or(SUCCESS))
 +    }
 +}
 +
 +fn format_crate(
 +    verbosity: Verbosity,
 +    strategy: &CargoFmtStrategy,
 +    rustfmt_args: Vec<String>,
 +    manifest_path: Option<&Path>,
 +) -> Result<i32, io::Error> {
 +    let targets = get_targets(strategy, manifest_path)?;
 +
 +    // Currently only bin and lib files get formatted.
 +    run_rustfmt(&targets, &rustfmt_args, verbosity)
 +}
 +
 +/// Target uses a `path` field for equality and hashing.
 +#[derive(Debug)]
 +pub struct Target {
 +    /// A path to the main source file of the target.
 +    path: PathBuf,
 +    /// A kind of target (e.g., lib, bin, example, ...).
 +    kind: String,
 +    /// Rust edition for this target.
 +    edition: String,
 +}
 +
 +impl Target {
 +    pub fn from_target(target: &cargo_metadata::Target) -> Self {
 +        let path = PathBuf::from(&target.src_path);
 +        let canonicalized = fs::canonicalize(&path).unwrap_or(path);
 +
 +        Target {
 +            path: canonicalized,
 +            kind: target.kind[0].clone(),
 +            edition: target.edition.clone(),
 +        }
 +    }
 +}
 +
 +impl PartialEq for Target {
 +    fn eq(&self, other: &Target) -> bool {
 +        self.path == other.path
 +    }
 +}
 +
 +impl PartialOrd for Target {
 +    fn partial_cmp(&self, other: &Target) -> Option<Ordering> {
 +        Some(self.path.cmp(&other.path))
 +    }
 +}
 +
 +impl Ord for Target {
 +    fn cmp(&self, other: &Target) -> Ordering {
 +        self.path.cmp(&other.path)
 +    }
 +}
 +
 +impl Eq for Target {}
 +
 +impl Hash for Target {
 +    fn hash<H: Hasher>(&self, state: &mut H) {
 +        self.path.hash(state);
 +    }
 +}
 +
 +#[derive(Debug, PartialEq, Eq)]
 +pub enum CargoFmtStrategy {
 +    /// Format every packages and dependencies.
 +    All,
 +    /// Format packages that are specified by the command line argument.
 +    Some(Vec<String>),
 +    /// Format the root packages only.
 +    Root,
 +}
 +
 +impl CargoFmtStrategy {
 +    pub fn from_opts(opts: &Opts) -> CargoFmtStrategy {
 +        match (opts.format_all, opts.packages.is_empty()) {
 +            (false, true) => CargoFmtStrategy::Root,
 +            (true, _) => CargoFmtStrategy::All,
 +            (false, false) => CargoFmtStrategy::Some(opts.packages.clone()),
 +        }
 +    }
 +}
 +
 +/// Based on the specified `CargoFmtStrategy`, returns a set of main source files.
 +fn get_targets(
 +    strategy: &CargoFmtStrategy,
 +    manifest_path: Option<&Path>,
 +) -> Result<BTreeSet<Target>, io::Error> {
 +    let mut targets = BTreeSet::new();
 +
 +    match *strategy {
 +        CargoFmtStrategy::Root => get_targets_root_only(manifest_path, &mut targets)?,
 +        CargoFmtStrategy::All => {
 +            get_targets_recursive(manifest_path, &mut targets, &mut BTreeSet::new())?
 +        }
 +        CargoFmtStrategy::Some(ref hitlist) => {
 +            get_targets_with_hitlist(manifest_path, hitlist, &mut targets)?
 +        }
 +    }
 +
 +    if targets.is_empty() {
 +        Err(io::Error::new(
 +            io::ErrorKind::Other,
 +            "Failed to find targets".to_owned(),
 +        ))
 +    } else {
 +        Ok(targets)
 +    }
 +}
 +
 +fn get_targets_root_only(
 +    manifest_path: Option<&Path>,
 +    targets: &mut BTreeSet<Target>,
 +) -> Result<(), io::Error> {
 +    let metadata = get_cargo_metadata(manifest_path)?;
 +    let workspace_root_path = PathBuf::from(&metadata.workspace_root).canonicalize()?;
 +    let (in_workspace_root, current_dir_manifest) = if let Some(target_manifest) = manifest_path {
 +        (
 +            workspace_root_path == target_manifest,
 +            target_manifest.canonicalize()?,
 +        )
 +    } else {
 +        let current_dir = env::current_dir()?.canonicalize()?;
 +        (
 +            workspace_root_path == current_dir,
 +            current_dir.join("Cargo.toml"),
 +        )
 +    };
 +
 +    let package_targets = match metadata.packages.len() {
 +        1 => metadata.packages.into_iter().next().unwrap().targets,
 +        _ => metadata
 +            .packages
 +            .into_iter()
 +            .filter(|p| {
 +                in_workspace_root
 +                    || PathBuf::from(&p.manifest_path)
 +                        .canonicalize()
 +                        .unwrap_or_default()
 +                        == current_dir_manifest
 +            })
++            .flat_map(|p| p.targets)
 +            .collect(),
 +    };
 +
 +    for target in package_targets {
 +        targets.insert(Target::from_target(&target));
 +    }
 +
 +    Ok(())
 +}
 +
 +fn get_targets_recursive(
 +    manifest_path: Option<&Path>,
 +    targets: &mut BTreeSet<Target>,
 +    visited: &mut BTreeSet<String>,
 +) -> Result<(), io::Error> {
 +    let metadata = get_cargo_metadata(manifest_path)?;
 +    for package in &metadata.packages {
 +        add_targets(&package.targets, targets);
 +
 +        // Look for local dependencies using information available since cargo v1.51
 +        // It's theoretically possible someone could use a newer version of rustfmt with
 +        // a much older version of `cargo`, but we don't try to explicitly support that scenario.
 +        // If someone reports an issue with path-based deps not being formatted, be sure to
 +        // confirm their version of `cargo` (not `cargo-fmt`) is >= v1.51
 +        // https://github.com/rust-lang/cargo/pull/8994
 +        for dependency in &package.dependencies {
 +            if dependency.path.is_none() || visited.contains(&dependency.name) {
 +                continue;
 +            }
 +
 +            let manifest_path = PathBuf::from(dependency.path.as_ref().unwrap()).join("Cargo.toml");
 +            if manifest_path.exists()
 +                && !metadata
 +                    .packages
 +                    .iter()
 +                    .any(|p| p.manifest_path.eq(&manifest_path))
 +            {
 +                visited.insert(dependency.name.to_owned());
 +                get_targets_recursive(Some(&manifest_path), targets, visited)?;
 +            }
 +        }
 +    }
 +
 +    Ok(())
 +}
 +
 +fn get_targets_with_hitlist(
 +    manifest_path: Option<&Path>,
 +    hitlist: &[String],
 +    targets: &mut BTreeSet<Target>,
 +) -> Result<(), io::Error> {
 +    let metadata = get_cargo_metadata(manifest_path)?;
 +    let mut workspace_hitlist: BTreeSet<&String> = BTreeSet::from_iter(hitlist);
 +
 +    for package in metadata.packages {
 +        if workspace_hitlist.remove(&package.name) {
 +            for target in package.targets {
 +                targets.insert(Target::from_target(&target));
 +            }
 +        }
 +    }
 +
 +    if workspace_hitlist.is_empty() {
 +        Ok(())
 +    } else {
 +        let package = workspace_hitlist.iter().next().unwrap();
 +        Err(io::Error::new(
 +            io::ErrorKind::InvalidInput,
 +            format!("package `{}` is not a member of the workspace", package),
 +        ))
 +    }
 +}
 +
 +fn add_targets(target_paths: &[cargo_metadata::Target], targets: &mut BTreeSet<Target>) {
 +    for target in target_paths {
 +        targets.insert(Target::from_target(target));
 +    }
 +}
 +
 +fn run_rustfmt(
 +    targets: &BTreeSet<Target>,
 +    fmt_args: &[String],
 +    verbosity: Verbosity,
 +) -> Result<i32, io::Error> {
 +    let by_edition = targets
 +        .iter()
 +        .inspect(|t| {
 +            if verbosity == Verbosity::Verbose {
 +                println!("[{} ({})] {:?}", t.kind, t.edition, t.path)
 +            }
 +        })
 +        .fold(BTreeMap::new(), |mut h, t| {
 +            h.entry(&t.edition).or_insert_with(Vec::new).push(&t.path);
 +            h
 +        });
 +
 +    let mut status = vec![];
 +    for (edition, files) in by_edition {
 +        let stdout = if verbosity == Verbosity::Quiet {
 +            std::process::Stdio::null()
 +        } else {
 +            std::process::Stdio::inherit()
 +        };
 +
 +        if verbosity == Verbosity::Verbose {
 +            print!("rustfmt");
 +            print!(" --edition {}", edition);
 +            fmt_args.iter().for_each(|f| print!(" {}", f));
 +            files.iter().for_each(|f| print!(" {}", f.display()));
 +            println!();
 +        }
 +
 +        let mut command = rustfmt_command()
 +            .stdout(stdout)
 +            .args(files)
 +            .args(&["--edition", edition])
 +            .args(fmt_args)
 +            .spawn()
 +            .map_err(|e| match e.kind() {
 +                io::ErrorKind::NotFound => io::Error::new(
 +                    io::ErrorKind::Other,
 +                    "Could not run rustfmt, please make sure it is in your PATH.",
 +                ),
 +                _ => e,
 +            })?;
 +
 +        status.push(command.wait()?);
 +    }
 +
 +    Ok(status
 +        .iter()
 +        .filter_map(|s| if s.success() { None } else { s.code() })
 +        .next()
 +        .unwrap_or(SUCCESS))
 +}
 +
 +fn get_cargo_metadata(manifest_path: Option<&Path>) -> Result<cargo_metadata::Metadata, io::Error> {
 +    let mut cmd = cargo_metadata::MetadataCommand::new();
 +    cmd.no_deps();
 +    if let Some(manifest_path) = manifest_path {
 +        cmd.manifest_path(manifest_path);
 +    }
 +    cmd.other_options(vec![String::from("--offline")]);
 +
 +    match cmd.exec() {
 +        Ok(metadata) => Ok(metadata),
 +        Err(_) => {
 +            cmd.other_options(vec![]);
 +            match cmd.exec() {
 +                Ok(metadata) => Ok(metadata),
 +                Err(error) => Err(io::Error::new(io::ErrorKind::Other, error.to_string())),
 +            }
 +        }
 +    }
 +}
index 0f850b9b2f2fba2b84be6e51101161fbf5773a2e,0000000000000000000000000000000000000000..f9d8a0fa70c00fb6538e76c42af0698d7e2a043b
mode 100644,000000..100644
--- /dev/null
@@@ -1,1961 -1,0 +1,2003 @@@
- /// An item starts with either a star `*` or a dash `-`. Different level of indentation are
- /// handled by shrinking the shape accordingly.
 +// Formatting and tools for comments.
 +
 +use std::{self, borrow::Cow, iter};
 +
 +use itertools::{multipeek, MultiPeek};
 +use lazy_static::lazy_static;
 +use regex::Regex;
 +use rustc_span::Span;
 +
 +use crate::config::Config;
 +use crate::rewrite::RewriteContext;
 +use crate::shape::{Indent, Shape};
 +use crate::string::{rewrite_string, StringFormat};
 +use crate::utils::{
 +    count_newlines, first_line_width, last_line_width, trim_left_preserve_layout,
 +    trimmed_last_line_width, unicode_str_width,
 +};
 +use crate::{ErrorKind, FormattingError};
 +
 +lazy_static! {
 +    /// A regex matching reference doc links.
 +    ///
 +    /// ```markdown
 +    /// /// An [example].
 +    /// ///
 +    /// /// [example]: this::is::a::link
 +    /// ```
 +    static ref REFERENCE_LINK_URL: Regex = Regex::new(r"^\[.+\]\s?:").unwrap();
 +}
 +
 +fn is_custom_comment(comment: &str) -> bool {
 +    if !comment.starts_with("//") {
 +        false
 +    } else if let Some(c) = comment.chars().nth(2) {
 +        !c.is_alphanumeric() && !c.is_whitespace()
 +    } else {
 +        false
 +    }
 +}
 +
 +#[derive(Copy, Clone, PartialEq, Eq)]
 +pub(crate) enum CommentStyle<'a> {
 +    DoubleSlash,
 +    TripleSlash,
 +    Doc,
 +    SingleBullet,
 +    DoubleBullet,
 +    Exclamation,
 +    Custom(&'a str),
 +}
 +
 +fn custom_opener(s: &str) -> &str {
 +    s.lines().next().map_or("", |first_line| {
 +        first_line
 +            .find(' ')
 +            .map_or(first_line, |space_index| &first_line[0..=space_index])
 +    })
 +}
 +
 +impl<'a> CommentStyle<'a> {
 +    /// Returns `true` if the commenting style covers a line only.
 +    pub(crate) fn is_line_comment(&self) -> bool {
 +        match *self {
 +            CommentStyle::DoubleSlash
 +            | CommentStyle::TripleSlash
 +            | CommentStyle::Doc
 +            | CommentStyle::Custom(_) => true,
 +            _ => false,
 +        }
 +    }
 +
 +    /// Returns `true` if the commenting style can span over multiple lines.
 +    pub(crate) fn is_block_comment(&self) -> bool {
 +        match *self {
 +            CommentStyle::SingleBullet | CommentStyle::DoubleBullet | CommentStyle::Exclamation => {
 +                true
 +            }
 +            _ => false,
 +        }
 +    }
 +
 +    /// Returns `true` if the commenting style is for documentation.
 +    pub(crate) fn is_doc_comment(&self) -> bool {
 +        matches!(*self, CommentStyle::TripleSlash | CommentStyle::Doc)
 +    }
 +
 +    pub(crate) fn opener(&self) -> &'a str {
 +        match *self {
 +            CommentStyle::DoubleSlash => "// ",
 +            CommentStyle::TripleSlash => "/// ",
 +            CommentStyle::Doc => "//! ",
 +            CommentStyle::SingleBullet => "/* ",
 +            CommentStyle::DoubleBullet => "/** ",
 +            CommentStyle::Exclamation => "/*! ",
 +            CommentStyle::Custom(opener) => opener,
 +        }
 +    }
 +
 +    pub(crate) fn closer(&self) -> &'a str {
 +        match *self {
 +            CommentStyle::DoubleSlash
 +            | CommentStyle::TripleSlash
 +            | CommentStyle::Custom(..)
 +            | CommentStyle::Doc => "",
 +            CommentStyle::SingleBullet | CommentStyle::DoubleBullet | CommentStyle::Exclamation => {
 +                " */"
 +            }
 +        }
 +    }
 +
 +    pub(crate) fn line_start(&self) -> &'a str {
 +        match *self {
 +            CommentStyle::DoubleSlash => "// ",
 +            CommentStyle::TripleSlash => "/// ",
 +            CommentStyle::Doc => "//! ",
 +            CommentStyle::SingleBullet | CommentStyle::DoubleBullet | CommentStyle::Exclamation => {
 +                " * "
 +            }
 +            CommentStyle::Custom(opener) => opener,
 +        }
 +    }
 +
 +    pub(crate) fn to_str_tuplet(&self) -> (&'a str, &'a str, &'a str) {
 +        (self.opener(), self.closer(), self.line_start())
 +    }
 +}
 +
 +pub(crate) fn comment_style(orig: &str, normalize_comments: bool) -> CommentStyle<'_> {
 +    if !normalize_comments {
 +        if orig.starts_with("/**") && !orig.starts_with("/**/") {
 +            CommentStyle::DoubleBullet
 +        } else if orig.starts_with("/*!") {
 +            CommentStyle::Exclamation
 +        } else if orig.starts_with("/*") {
 +            CommentStyle::SingleBullet
 +        } else if orig.starts_with("///") && orig.chars().nth(3).map_or(true, |c| c != '/') {
 +            CommentStyle::TripleSlash
 +        } else if orig.starts_with("//!") {
 +            CommentStyle::Doc
 +        } else if is_custom_comment(orig) {
 +            CommentStyle::Custom(custom_opener(orig))
 +        } else {
 +            CommentStyle::DoubleSlash
 +        }
 +    } else if (orig.starts_with("///") && orig.chars().nth(3).map_or(true, |c| c != '/'))
 +        || (orig.starts_with("/**") && !orig.starts_with("/**/"))
 +    {
 +        CommentStyle::TripleSlash
 +    } else if orig.starts_with("//!") || orig.starts_with("/*!") {
 +        CommentStyle::Doc
 +    } else if is_custom_comment(orig) {
 +        CommentStyle::Custom(custom_opener(orig))
 +    } else {
 +        CommentStyle::DoubleSlash
 +    }
 +}
 +
 +/// Returns true if the last line of the passed string finishes with a block-comment.
 +pub(crate) fn is_last_comment_block(s: &str) -> bool {
 +    s.trim_end().ends_with("*/")
 +}
 +
 +/// Combine `prev_str` and `next_str` into a single `String`. `span` may contain
 +/// comments between two strings. If there are such comments, then that will be
 +/// recovered. If `allow_extend` is true and there is no comment between the two
 +/// strings, then they will be put on a single line as long as doing so does not
 +/// exceed max width.
 +pub(crate) fn combine_strs_with_missing_comments(
 +    context: &RewriteContext<'_>,
 +    prev_str: &str,
 +    next_str: &str,
 +    span: Span,
 +    shape: Shape,
 +    allow_extend: bool,
 +) -> Option<String> {
 +    trace!(
 +        "combine_strs_with_missing_comments `{}` `{}` {:?} {:?}",
 +        prev_str,
 +        next_str,
 +        span,
 +        shape
 +    );
 +
 +    let mut result =
 +        String::with_capacity(prev_str.len() + next_str.len() + shape.indent.width() + 128);
 +    result.push_str(prev_str);
 +    let mut allow_one_line = !prev_str.contains('\n') && !next_str.contains('\n');
 +    let first_sep =
 +        if prev_str.is_empty() || next_str.is_empty() || trimmed_last_line_width(prev_str) == 0 {
 +            ""
 +        } else {
 +            " "
 +        };
 +    let mut one_line_width =
 +        last_line_width(prev_str) + first_line_width(next_str) + first_sep.len();
 +
 +    let config = context.config;
 +    let indent = shape.indent;
 +    let missing_comment = rewrite_missing_comment(span, shape, context)?;
 +
 +    if missing_comment.is_empty() {
 +        if allow_extend && one_line_width <= shape.width {
 +            result.push_str(first_sep);
 +        } else if !prev_str.is_empty() {
 +            result.push_str(&indent.to_string_with_newline(config))
 +        }
 +        result.push_str(next_str);
 +        return Some(result);
 +    }
 +
 +    // We have a missing comment between the first expression and the second expression.
 +
 +    // Peek the the original source code and find out whether there is a newline between the first
 +    // expression and the second expression or the missing comment. We will preserve the original
 +    // layout whenever possible.
 +    let original_snippet = context.snippet(span);
 +    let prefer_same_line = if let Some(pos) = original_snippet.find('/') {
 +        !original_snippet[..pos].contains('\n')
 +    } else {
 +        !original_snippet.contains('\n')
 +    };
 +
 +    one_line_width -= first_sep.len();
 +    let first_sep = if prev_str.is_empty() || missing_comment.is_empty() {
 +        Cow::from("")
 +    } else {
 +        let one_line_width = last_line_width(prev_str) + first_line_width(&missing_comment) + 1;
 +        if prefer_same_line && one_line_width <= shape.width {
 +            Cow::from(" ")
 +        } else {
 +            indent.to_string_with_newline(config)
 +        }
 +    };
 +    result.push_str(&first_sep);
 +    result.push_str(&missing_comment);
 +
 +    let second_sep = if missing_comment.is_empty() || next_str.is_empty() {
 +        Cow::from("")
 +    } else if missing_comment.starts_with("//") {
 +        indent.to_string_with_newline(config)
 +    } else {
 +        one_line_width += missing_comment.len() + first_sep.len() + 1;
 +        allow_one_line &= !missing_comment.starts_with("//") && !missing_comment.contains('\n');
 +        if prefer_same_line && allow_one_line && one_line_width <= shape.width {
 +            Cow::from(" ")
 +        } else {
 +            indent.to_string_with_newline(config)
 +        }
 +    };
 +    result.push_str(&second_sep);
 +    result.push_str(next_str);
 +
 +    Some(result)
 +}
 +
 +pub(crate) fn rewrite_doc_comment(orig: &str, shape: Shape, config: &Config) -> Option<String> {
 +    identify_comment(orig, false, shape, config, true)
 +}
 +
 +pub(crate) fn rewrite_comment(
 +    orig: &str,
 +    block_style: bool,
 +    shape: Shape,
 +    config: &Config,
 +) -> Option<String> {
 +    identify_comment(orig, block_style, shape, config, false)
 +}
 +
 +fn identify_comment(
 +    orig: &str,
 +    block_style: bool,
 +    shape: Shape,
 +    config: &Config,
 +    is_doc_comment: bool,
 +) -> Option<String> {
 +    let style = comment_style(orig, false);
 +
 +    // Computes the byte length of line taking into account a newline if the line is part of a
 +    // paragraph.
 +    fn compute_len(orig: &str, line: &str) -> usize {
 +        if orig.len() > line.len() {
 +            if orig.as_bytes()[line.len()] == b'\r' {
 +                line.len() + 2
 +            } else {
 +                line.len() + 1
 +            }
 +        } else {
 +            line.len()
 +        }
 +    }
 +
 +    // Get the first group of line comments having the same commenting style.
 +    //
 +    // Returns a tuple with:
 +    // - a boolean indicating if there is a blank line
 +    // - a number indicating the size of the first group of comments
 +    fn consume_same_line_comments(
 +        style: CommentStyle<'_>,
 +        orig: &str,
 +        line_start: &str,
 +    ) -> (bool, usize) {
 +        let mut first_group_ending = 0;
 +        let mut hbl = false;
 +
 +        for line in orig.lines() {
 +            let trimmed_line = line.trim_start();
 +            if trimmed_line.is_empty() {
 +                hbl = true;
 +                break;
 +            } else if trimmed_line.starts_with(line_start)
 +                || comment_style(trimmed_line, false) == style
 +            {
 +                first_group_ending += compute_len(&orig[first_group_ending..], line);
 +            } else {
 +                break;
 +            }
 +        }
 +        (hbl, first_group_ending)
 +    }
 +
 +    let (has_bare_lines, first_group_ending) = match style {
 +        CommentStyle::DoubleSlash | CommentStyle::TripleSlash | CommentStyle::Doc => {
 +            let line_start = style.line_start().trim_start();
 +            consume_same_line_comments(style, orig, line_start)
 +        }
 +        CommentStyle::Custom(opener) => {
 +            let trimmed_opener = opener.trim_end();
 +            consume_same_line_comments(style, orig, trimmed_opener)
 +        }
 +        // for a block comment, search for the closing symbol
 +        CommentStyle::DoubleBullet | CommentStyle::SingleBullet | CommentStyle::Exclamation => {
 +            let closer = style.closer().trim_start();
 +            let mut count = orig.matches(closer).count();
 +            let mut closing_symbol_offset = 0;
 +            let mut hbl = false;
 +            let mut first = true;
 +            for line in orig.lines() {
 +                closing_symbol_offset += compute_len(&orig[closing_symbol_offset..], line);
 +                let mut trimmed_line = line.trim_start();
 +                if !trimmed_line.starts_with('*')
 +                    && !trimmed_line.starts_with("//")
 +                    && !trimmed_line.starts_with("/*")
 +                {
 +                    hbl = true;
 +                }
 +
 +                // Remove opener from consideration when searching for closer
 +                if first {
 +                    let opener = style.opener().trim_end();
 +                    trimmed_line = &trimmed_line[opener.len()..];
 +                    first = false;
 +                }
 +                if trimmed_line.ends_with(closer) {
 +                    count -= 1;
 +                    if count == 0 {
 +                        break;
 +                    }
 +                }
 +            }
 +            (hbl, closing_symbol_offset)
 +        }
 +    };
 +
 +    let (first_group, rest) = orig.split_at(first_group_ending);
 +    let rewritten_first_group =
 +        if !config.normalize_comments() && has_bare_lines && style.is_block_comment() {
 +            trim_left_preserve_layout(first_group, shape.indent, config)?
 +        } else if !config.normalize_comments()
 +            && !config.wrap_comments()
 +            && !config.format_code_in_doc_comments()
 +        {
 +            light_rewrite_comment(first_group, shape.indent, config, is_doc_comment)
 +        } else {
 +            rewrite_comment_inner(
 +                first_group,
 +                block_style,
 +                style,
 +                shape,
 +                config,
 +                is_doc_comment || style.is_doc_comment(),
 +            )?
 +        };
 +    if rest.is_empty() {
 +        Some(rewritten_first_group)
 +    } else {
 +        identify_comment(
 +            rest.trim_start(),
 +            block_style,
 +            shape,
 +            config,
 +            is_doc_comment,
 +        )
 +        .map(|rest_str| {
 +            format!(
 +                "{}\n{}{}{}",
 +                rewritten_first_group,
 +                // insert back the blank line
 +                if has_bare_lines && style.is_line_comment() {
 +                    "\n"
 +                } else {
 +                    ""
 +                },
 +                shape.indent.to_string(config),
 +                rest_str
 +            )
 +        })
 +    }
 +}
 +
 +/// Enum indicating if the code block contains rust based on attributes
 +enum CodeBlockAttribute {
 +    Rust,
 +    NotRust,
 +}
 +
 +impl CodeBlockAttribute {
 +    /// Parse comma separated attributes list. Return rust only if all
 +    /// attributes are valid rust attributes
 +    /// See <https://doc.rust-lang.org/rustdoc/print.html#attributes>
 +    fn new(attributes: &str) -> CodeBlockAttribute {
 +        for attribute in attributes.split(',') {
 +            match attribute.trim() {
 +                "" | "rust" | "should_panic" | "no_run" | "edition2015" | "edition2018"
 +                | "edition2021" => (),
 +                "ignore" | "compile_fail" | "text" => return CodeBlockAttribute::NotRust,
 +                _ => return CodeBlockAttribute::NotRust,
 +            }
 +        }
 +        CodeBlockAttribute::Rust
 +    }
 +}
 +
 +/// Block that is formatted as an item.
 +///
-     /// the number of whitespaces up to the item sigil
++/// An item starts with either a star `*` a dash `-` or a greater-than `>`.
++/// Different level of indentation are handled by shrinking the shape accordingly.
 +struct ItemizedBlock {
 +    /// the lines that are identified as part of an itemized block
 +    lines: Vec<String>,
-     /// sequence of whitespaces to prefix new lines that are part of the item
++    /// the number of characters (typically whitespaces) up to the item sigil
 +    indent: usize,
 +    /// the string that marks the start of an item
 +    opener: String,
-         trimmed.starts_with("* ") || trimmed.starts_with("- ")
++    /// sequence of characters (typically whitespaces) to prefix new lines that are part of the item
 +    line_start: String,
 +}
 +
 +impl ItemizedBlock {
 +    /// Returns `true` if the line is formatted as an item
 +    fn is_itemized_line(line: &str) -> bool {
 +        let trimmed = line.trim_start();
-         let indent = space_to_sigil + 2;
++        trimmed.starts_with("* ") || trimmed.starts_with("- ") || trimmed.starts_with("> ")
 +    }
 +
 +    /// Creates a new ItemizedBlock described with the given line.
 +    /// The `is_itemized_line` needs to be called first.
 +    fn new(line: &str) -> ItemizedBlock {
 +        let space_to_sigil = line.chars().take_while(|c| c.is_whitespace()).count();
-             line_start: " ".repeat(indent),
++        // +2 = '* ', which will add the appropriate amount of whitespace to keep itemized
++        // content formatted correctly.
++        let mut indent = space_to_sigil + 2;
++        let mut line_start = " ".repeat(indent);
++
++        // Markdown blockquote start with a "> "
++        if line.trim_start().starts_with(">") {
++            // remove the original +2 indent because there might be multiple nested block quotes
++            // and it's easier to reason about the final indent by just taking the length
++            // of th new line_start. We update the indent because it effects the max width
++            // of each formatted line.
++            line_start = itemized_block_quote_start(line, line_start, 2);
++            indent = line_start.len();
++        }
 +        ItemizedBlock {
 +            lines: vec![line[indent..].to_string()],
 +            indent,
 +            opener: line[..indent].to_string(),
-         if self.fmt.config.wrap_comments()
++            line_start,
 +        }
 +    }
 +
 +    /// Returns a `StringFormat` used for formatting the content of an item.
 +    fn create_string_format<'a>(&'a self, fmt: &'a StringFormat<'_>) -> StringFormat<'a> {
 +        StringFormat {
 +            opener: "",
 +            closer: "",
 +            line_start: "",
 +            line_end: "",
 +            shape: Shape::legacy(fmt.shape.width.saturating_sub(self.indent), Indent::empty()),
 +            trim_end: true,
 +            config: fmt.config,
 +        }
 +    }
 +
 +    /// Returns `true` if the line is part of the current itemized block.
 +    /// If it is, then it is added to the internal lines list.
 +    fn add_line(&mut self, line: &str) -> bool {
 +        if !ItemizedBlock::is_itemized_line(line)
 +            && self.indent <= line.chars().take_while(|c| c.is_whitespace()).count()
 +        {
 +            self.lines.push(line.to_string());
 +            return true;
 +        }
 +        false
 +    }
 +
 +    /// Returns the block as a string, with each line trimmed at the start.
 +    fn trimmed_block_as_string(&self) -> String {
 +        self.lines
 +            .iter()
 +            .map(|line| format!("{} ", line.trim_start()))
 +            .collect::<String>()
 +    }
 +
 +    /// Returns the block as a string under its original form.
 +    fn original_block_as_string(&self) -> String {
 +        self.lines.join("\n")
 +    }
 +}
 +
++/// Determine the line_start when formatting markdown block quotes.
++/// The original line_start likely contains indentation (whitespaces), which we'd like to
++/// replace with '> ' characters.
++fn itemized_block_quote_start(line: &str, mut line_start: String, remove_indent: usize) -> String {
++    let quote_level = line
++        .chars()
++        .take_while(|c| !c.is_alphanumeric())
++        .fold(0, |acc, c| if c == '>' { acc + 1 } else { acc });
++
++    for _ in 0..remove_indent {
++        line_start.pop();
++    }
++
++    for _ in 0..quote_level {
++        line_start.push_str("> ")
++    }
++    line_start
++}
++
 +struct CommentRewrite<'a> {
 +    result: String,
 +    code_block_buffer: String,
 +    is_prev_line_multi_line: bool,
 +    code_block_attr: Option<CodeBlockAttribute>,
 +    item_block: Option<ItemizedBlock>,
 +    comment_line_separator: String,
 +    indent_str: String,
 +    max_width: usize,
 +    fmt_indent: Indent,
 +    fmt: StringFormat<'a>,
 +
 +    opener: String,
 +    closer: String,
 +    line_start: String,
 +    style: CommentStyle<'a>,
 +}
 +
 +impl<'a> CommentRewrite<'a> {
 +    fn new(
 +        orig: &'a str,
 +        block_style: bool,
 +        shape: Shape,
 +        config: &'a Config,
 +    ) -> CommentRewrite<'a> {
 +        let ((opener, closer, line_start), style) = if block_style {
 +            (
 +                CommentStyle::SingleBullet.to_str_tuplet(),
 +                CommentStyle::SingleBullet,
 +            )
 +        } else {
 +            let style = comment_style(orig, config.normalize_comments());
 +            (style.to_str_tuplet(), style)
 +        };
 +
 +        let max_width = shape
 +            .width
 +            .checked_sub(closer.len() + opener.len())
 +            .unwrap_or(1);
 +        let indent_str = shape.indent.to_string_with_newline(config).to_string();
 +
 +        let mut cr = CommentRewrite {
 +            result: String::with_capacity(orig.len() * 2),
 +            code_block_buffer: String::with_capacity(128),
 +            is_prev_line_multi_line: false,
 +            code_block_attr: None,
 +            item_block: None,
 +            comment_line_separator: format!("{}{}", indent_str, line_start),
 +            max_width,
 +            indent_str,
 +            fmt_indent: shape.indent,
 +
 +            fmt: StringFormat {
 +                opener: "",
 +                closer: "",
 +                line_start,
 +                line_end: "",
 +                shape: Shape::legacy(max_width, shape.indent),
 +                trim_end: true,
 +                config,
 +            },
 +
 +            opener: opener.to_owned(),
 +            closer: closer.to_owned(),
 +            line_start: line_start.to_owned(),
 +            style,
 +        };
 +        cr.result.push_str(opener);
 +        cr
 +    }
 +
 +    fn join_block(s: &str, sep: &str) -> String {
 +        let mut result = String::with_capacity(s.len() + 128);
 +        let mut iter = s.lines().peekable();
 +        while let Some(line) = iter.next() {
 +            result.push_str(line);
 +            result.push_str(match iter.peek() {
 +                Some(next_line) if next_line.is_empty() => sep.trim_end(),
 +                Some(..) => sep,
 +                None => "",
 +            });
 +        }
 +        result
 +    }
 +
 +    /// Check if any characters were written to the result buffer after the start of the comment.
 +    /// when calling [`CommentRewrite::new()`] the result buffer is initiazlied with the opening
 +    /// characters for the comment.
 +    fn buffer_contains_comment(&self) -> bool {
 +        // if self.result.len() < self.opener.len() then an empty comment is in the buffer
 +        // if self.result.len() > self.opener.len() then a non empty comment is in the buffer
 +        self.result.len() != self.opener.len()
 +    }
 +
 +    fn finish(mut self) -> String {
 +        if !self.code_block_buffer.is_empty() {
 +            // There is a code block that is not properly enclosed by backticks.
 +            // We will leave them untouched.
 +            self.result.push_str(&self.comment_line_separator);
 +            self.result.push_str(&Self::join_block(
 +                &trim_custom_comment_prefix(&self.code_block_buffer),
 +                &self.comment_line_separator,
 +            ));
 +        }
 +
 +        if let Some(ref ib) = self.item_block {
 +            // the last few lines are part of an itemized block
 +            self.fmt.shape = Shape::legacy(self.max_width, self.fmt_indent);
 +            let item_fmt = ib.create_string_format(&self.fmt);
 +
 +            // only push a comment_line_separator for ItemizedBlocks if the comment is not empty
 +            if self.buffer_contains_comment() {
 +                self.result.push_str(&self.comment_line_separator);
 +            }
 +
 +            self.result.push_str(&ib.opener);
 +            match rewrite_string(
 +                &ib.trimmed_block_as_string(),
 +                &item_fmt,
 +                self.max_width.saturating_sub(ib.indent),
 +            ) {
 +                Some(s) => self.result.push_str(&Self::join_block(
 +                    &s,
 +                    &format!("{}{}", self.comment_line_separator, ib.line_start),
 +                )),
 +                None => self.result.push_str(&Self::join_block(
 +                    &ib.original_block_as_string(),
 +                    &self.comment_line_separator,
 +                )),
 +            };
 +        }
 +
 +        self.result.push_str(&self.closer);
 +        if self.result.ends_with(&self.opener) && self.opener.ends_with(' ') {
 +            // Trailing space.
 +            self.result.pop();
 +        }
 +
 +        self.result
 +    }
 +
 +    fn handle_line(
 +        &mut self,
 +        orig: &'a str,
 +        i: usize,
 +        line: &'a str,
 +        has_leading_whitespace: bool,
++        is_doc_comment: bool,
 +    ) -> bool {
 +        let num_newlines = count_newlines(orig);
 +        let is_last = i == num_newlines;
 +        let needs_new_comment_line = if self.style.is_block_comment() {
 +            num_newlines > 0 || self.buffer_contains_comment()
 +        } else {
 +            self.buffer_contains_comment()
 +        };
 +
 +        if let Some(ref mut ib) = self.item_block {
 +            if ib.add_line(line) {
 +                return false;
 +            }
 +            self.is_prev_line_multi_line = false;
 +            self.fmt.shape = Shape::legacy(self.max_width, self.fmt_indent);
 +            let item_fmt = ib.create_string_format(&self.fmt);
 +
 +            // only push a comment_line_separator if we need to start a new comment line
 +            if needs_new_comment_line {
 +                self.result.push_str(&self.comment_line_separator);
 +            }
 +
 +            self.result.push_str(&ib.opener);
 +            match rewrite_string(
 +                &ib.trimmed_block_as_string(),
 +                &item_fmt,
 +                self.max_width.saturating_sub(ib.indent),
 +            ) {
 +                Some(s) => self.result.push_str(&Self::join_block(
 +                    &s,
 +                    &format!("{}{}", self.comment_line_separator, ib.line_start),
 +                )),
 +                None => self.result.push_str(&Self::join_block(
 +                    &ib.original_block_as_string(),
 +                    &self.comment_line_separator,
 +                )),
 +            };
 +        } else if self.code_block_attr.is_some() {
 +            if line.starts_with("```") {
 +                let code_block = match self.code_block_attr.as_ref().unwrap() {
 +                    CodeBlockAttribute::Rust
 +                        if self.fmt.config.format_code_in_doc_comments()
 +                            && !self.code_block_buffer.is_empty() =>
 +                    {
 +                        let mut config = self.fmt.config.clone();
 +                        config.set().wrap_comments(false);
 +                        if let Some(s) =
 +                            crate::format_code_block(&self.code_block_buffer, &config, false)
 +                        {
 +                            trim_custom_comment_prefix(&s.snippet)
 +                        } else {
 +                            trim_custom_comment_prefix(&self.code_block_buffer)
 +                        }
 +                    }
 +                    _ => trim_custom_comment_prefix(&self.code_block_buffer),
 +                };
 +                if !code_block.is_empty() {
 +                    self.result.push_str(&self.comment_line_separator);
 +                    self.result
 +                        .push_str(&Self::join_block(&code_block, &self.comment_line_separator));
 +                }
 +                self.code_block_buffer.clear();
 +                self.result.push_str(&self.comment_line_separator);
 +                self.result.push_str(line);
 +                self.code_block_attr = None;
 +            } else {
 +                self.code_block_buffer
 +                    .push_str(&hide_sharp_behind_comment(line));
 +                self.code_block_buffer.push('\n');
 +            }
 +            return false;
 +        }
 +
 +        self.code_block_attr = None;
 +        self.item_block = None;
 +        if let Some(stripped) = line.strip_prefix("```") {
 +            self.code_block_attr = Some(CodeBlockAttribute::new(stripped))
 +        } else if self.fmt.config.wrap_comments() && ItemizedBlock::is_itemized_line(line) {
 +            let ib = ItemizedBlock::new(line);
 +            self.item_block = Some(ib);
 +            return false;
 +        }
 +
 +        if self.result == self.opener {
 +            let force_leading_whitespace = &self.opener == "/* " && count_newlines(orig) == 0;
 +            if !has_leading_whitespace && !force_leading_whitespace && self.result.ends_with(' ') {
 +                self.result.pop();
 +            }
 +            if line.is_empty() {
 +                return false;
 +            }
 +        } else if self.is_prev_line_multi_line && !line.is_empty() {
 +            self.result.push(' ')
 +        } else if is_last && line.is_empty() {
 +            // trailing blank lines are unwanted
 +            if !self.closer.is_empty() {
 +                self.result.push_str(&self.indent_str);
 +            }
 +            return true;
 +        } else {
 +            self.result.push_str(&self.comment_line_separator);
 +            if !has_leading_whitespace && self.result.ends_with(' ') {
 +                self.result.pop();
 +            }
 +        }
 +
-             && !has_url(line)
-         {
++        let is_markdown_header_doc_comment = is_doc_comment && line.starts_with("#");
++
++        // We only want to wrap the comment if:
++        // 1) wrap_comments = true is configured
++        // 2) The comment is not the start of a markdown header doc comment
++        // 3) The comment width exceeds the shape's width
++        // 4) No URLS were found in the commnet
++        let should_wrap_comment = self.fmt.config.wrap_comments()
++            && !is_markdown_header_doc_comment
 +            && unicode_str_width(line) > self.fmt.shape.width
-         if rewriter.handle_line(orig, i, line, has_leading_whitespace) {
++            && !has_url(line);
++
++        if should_wrap_comment {
 +            match rewrite_string(line, &self.fmt, self.max_width) {
 +                Some(ref s) => {
 +                    self.is_prev_line_multi_line = s.contains('\n');
 +                    self.result.push_str(s);
 +                }
 +                None if self.is_prev_line_multi_line => {
 +                    // We failed to put the current `line` next to the previous `line`.
 +                    // Remove the trailing space, then start rewrite on the next line.
 +                    self.result.pop();
 +                    self.result.push_str(&self.comment_line_separator);
 +                    self.fmt.shape = Shape::legacy(self.max_width, self.fmt_indent);
 +                    match rewrite_string(line, &self.fmt, self.max_width) {
 +                        Some(ref s) => {
 +                            self.is_prev_line_multi_line = s.contains('\n');
 +                            self.result.push_str(s);
 +                        }
 +                        None => {
 +                            self.is_prev_line_multi_line = false;
 +                            self.result.push_str(line);
 +                        }
 +                    }
 +                }
 +                None => {
 +                    self.is_prev_line_multi_line = false;
 +                    self.result.push_str(line);
 +                }
 +            }
 +
 +            self.fmt.shape = if self.is_prev_line_multi_line {
 +                // 1 = " "
 +                let offset = 1 + last_line_width(&self.result) - self.line_start.len();
 +                Shape {
 +                    width: self.max_width.saturating_sub(offset),
 +                    indent: self.fmt_indent,
 +                    offset: self.fmt.shape.offset + offset,
 +                }
 +            } else {
 +                Shape::legacy(self.max_width, self.fmt_indent)
 +            };
 +        } else {
 +            if line.is_empty() && self.result.ends_with(' ') && !is_last {
 +                // Remove space if this is an empty comment or a doc comment.
 +                self.result.pop();
 +            }
 +            self.result.push_str(line);
 +            self.fmt.shape = Shape::legacy(self.max_width, self.fmt_indent);
 +            self.is_prev_line_multi_line = false;
 +        }
 +
 +        false
 +    }
 +}
 +
 +fn rewrite_comment_inner(
 +    orig: &str,
 +    block_style: bool,
 +    style: CommentStyle<'_>,
 +    shape: Shape,
 +    config: &Config,
 +    is_doc_comment: bool,
 +) -> Option<String> {
 +    let mut rewriter = CommentRewrite::new(orig, block_style, shape, config);
 +
 +    let line_breaks = count_newlines(orig.trim_end());
 +    let lines = orig
 +        .lines()
 +        .enumerate()
 +        .map(|(i, mut line)| {
 +            line = trim_end_unless_two_whitespaces(line.trim_start(), is_doc_comment);
 +            // Drop old closer.
 +            if i == line_breaks && line.ends_with("*/") && !line.starts_with("//") {
 +                line = line[..(line.len() - 2)].trim_end();
 +            }
 +
 +            line
 +        })
 +        .map(|s| left_trim_comment_line(s, &style))
 +        .map(|(line, has_leading_whitespace)| {
 +            if orig.starts_with("/*") && line_breaks == 0 {
 +                (
 +                    line.trim_start(),
 +                    has_leading_whitespace || config.normalize_comments(),
 +                )
 +            } else {
 +                (line, has_leading_whitespace || config.normalize_comments())
 +            }
 +        });
 +
 +    for (i, (line, has_leading_whitespace)) in lines.enumerate() {
++        if rewriter.handle_line(orig, i, line, has_leading_whitespace, is_doc_comment) {
 +            break;
 +        }
 +    }
 +
 +    Some(rewriter.finish())
 +}
 +
 +const RUSTFMT_CUSTOM_COMMENT_PREFIX: &str = "//#### ";
 +
 +fn hide_sharp_behind_comment(s: &str) -> Cow<'_, str> {
 +    let s_trimmed = s.trim();
 +    if s_trimmed.starts_with("# ") || s_trimmed == "#" {
 +        Cow::from(format!("{}{}", RUSTFMT_CUSTOM_COMMENT_PREFIX, s))
 +    } else {
 +        Cow::from(s)
 +    }
 +}
 +
 +fn trim_custom_comment_prefix(s: &str) -> String {
 +    s.lines()
 +        .map(|line| {
 +            let left_trimmed = line.trim_start();
 +            if left_trimmed.starts_with(RUSTFMT_CUSTOM_COMMENT_PREFIX) {
 +                left_trimmed.trim_start_matches(RUSTFMT_CUSTOM_COMMENT_PREFIX)
 +            } else {
 +                line
 +            }
 +        })
 +        .collect::<Vec<_>>()
 +        .join("\n")
 +}
 +
 +/// Returns `true` if the given string MAY include URLs or alike.
 +fn has_url(s: &str) -> bool {
 +    // This function may return false positive, but should get its job done in most cases.
 +    s.contains("https://")
 +        || s.contains("http://")
 +        || s.contains("ftp://")
 +        || s.contains("file://")
 +        || REFERENCE_LINK_URL.is_match(s)
 +}
 +
 +/// Given the span, rewrite the missing comment inside it if available.
 +/// Note that the given span must only include comments (or leading/trailing whitespaces).
 +pub(crate) fn rewrite_missing_comment(
 +    span: Span,
 +    shape: Shape,
 +    context: &RewriteContext<'_>,
 +) -> Option<String> {
 +    let missing_snippet = context.snippet(span);
 +    let trimmed_snippet = missing_snippet.trim();
 +    // check the span starts with a comment
 +    let pos = trimmed_snippet.find('/');
 +    if !trimmed_snippet.is_empty() && pos.is_some() {
 +        rewrite_comment(trimmed_snippet, false, shape, context.config)
 +    } else {
 +        Some(String::new())
 +    }
 +}
 +
 +/// Recover the missing comments in the specified span, if available.
 +/// The layout of the comments will be preserved as long as it does not break the code
 +/// and its total width does not exceed the max width.
 +pub(crate) fn recover_missing_comment_in_span(
 +    span: Span,
 +    shape: Shape,
 +    context: &RewriteContext<'_>,
 +    used_width: usize,
 +) -> Option<String> {
 +    let missing_comment = rewrite_missing_comment(span, shape, context)?;
 +    if missing_comment.is_empty() {
 +        Some(String::new())
 +    } else {
 +        let missing_snippet = context.snippet(span);
 +        let pos = missing_snippet.find('/')?;
 +        // 1 = ` `
 +        let total_width = missing_comment.len() + used_width + 1;
 +        let force_new_line_before_comment =
 +            missing_snippet[..pos].contains('\n') || total_width > context.config.max_width();
 +        let sep = if force_new_line_before_comment {
 +            shape.indent.to_string_with_newline(context.config)
 +        } else {
 +            Cow::from(" ")
 +        };
 +        Some(format!("{}{}", sep, missing_comment))
 +    }
 +}
 +
 +/// Trim trailing whitespaces unless they consist of two or more whitespaces.
 +fn trim_end_unless_two_whitespaces(s: &str, is_doc_comment: bool) -> &str {
 +    if is_doc_comment && s.ends_with("  ") {
 +        s
 +    } else {
 +        s.trim_end()
 +    }
 +}
 +
 +/// Trims whitespace and aligns to indent, but otherwise does not change comments.
 +fn light_rewrite_comment(
 +    orig: &str,
 +    offset: Indent,
 +    config: &Config,
 +    is_doc_comment: bool,
 +) -> String {
 +    let lines: Vec<&str> = orig
 +        .lines()
 +        .map(|l| {
 +            // This is basically just l.trim(), but in the case that a line starts
 +            // with `*` we want to leave one space before it, so it aligns with the
 +            // `*` in `/*`.
 +            let first_non_whitespace = l.find(|c| !char::is_whitespace(c));
 +            let left_trimmed = if let Some(fnw) = first_non_whitespace {
 +                if l.as_bytes()[fnw] == b'*' && fnw > 0 {
 +                    &l[fnw - 1..]
 +                } else {
 +                    &l[fnw..]
 +                }
 +            } else {
 +                ""
 +            };
 +            // Preserve markdown's double-space line break syntax in doc comment.
 +            trim_end_unless_two_whitespaces(left_trimmed, is_doc_comment)
 +        })
 +        .collect();
 +    lines.join(&format!("\n{}", offset.to_string(config)))
 +}
 +
 +/// Trims comment characters and possibly a single space from the left of a string.
 +/// Does not trim all whitespace. If a single space is trimmed from the left of the string,
 +/// this function returns true.
 +fn left_trim_comment_line<'a>(line: &'a str, style: &CommentStyle<'_>) -> (&'a str, bool) {
 +    if line.starts_with("//! ")
 +        || line.starts_with("/// ")
 +        || line.starts_with("/*! ")
 +        || line.starts_with("/** ")
 +    {
 +        (&line[4..], true)
 +    } else if let CommentStyle::Custom(opener) = *style {
 +        if let Some(stripped) = line.strip_prefix(opener) {
 +            (stripped, true)
 +        } else {
 +            (&line[opener.trim_end().len()..], false)
 +        }
 +    } else if line.starts_with("/* ")
 +        || line.starts_with("// ")
 +        || line.starts_with("//!")
 +        || line.starts_with("///")
 +        || line.starts_with("** ")
 +        || line.starts_with("/*!")
 +        || (line.starts_with("/**") && !line.starts_with("/**/"))
 +    {
 +        (&line[3..], line.chars().nth(2).unwrap() == ' ')
 +    } else if line.starts_with("/*")
 +        || line.starts_with("* ")
 +        || line.starts_with("//")
 +        || line.starts_with("**")
 +    {
 +        (&line[2..], line.chars().nth(1).unwrap() == ' ')
 +    } else if let Some(stripped) = line.strip_prefix('*') {
 +        (stripped, false)
 +    } else {
 +        (line, line.starts_with(' '))
 +    }
 +}
 +
 +pub(crate) trait FindUncommented {
 +    fn find_uncommented(&self, pat: &str) -> Option<usize>;
 +    fn find_last_uncommented(&self, pat: &str) -> Option<usize>;
 +}
 +
 +impl FindUncommented for str {
 +    fn find_uncommented(&self, pat: &str) -> Option<usize> {
 +        let mut needle_iter = pat.chars();
 +        for (kind, (i, b)) in CharClasses::new(self.char_indices()) {
 +            match needle_iter.next() {
 +                None => {
 +                    return Some(i - pat.len());
 +                }
 +                Some(c) => match kind {
 +                    FullCodeCharKind::Normal | FullCodeCharKind::InString if b == c => {}
 +                    _ => {
 +                        needle_iter = pat.chars();
 +                    }
 +                },
 +            }
 +        }
 +
 +        // Handle case where the pattern is a suffix of the search string
 +        match needle_iter.next() {
 +            Some(_) => None,
 +            None => Some(self.len() - pat.len()),
 +        }
 +    }
 +
 +    fn find_last_uncommented(&self, pat: &str) -> Option<usize> {
 +        if let Some(left) = self.find_uncommented(pat) {
 +            let mut result = left;
 +            // add 1 to use find_last_uncommented for &str after pat
 +            while let Some(next) = self[(result + 1)..].find_last_uncommented(pat) {
 +                result += next + 1;
 +            }
 +            Some(result)
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +// Returns the first byte position after the first comment. The given string
 +// is expected to be prefixed by a comment, including delimiters.
 +// Good: `/* /* inner */ outer */ code();`
 +// Bad:  `code(); // hello\n world!`
 +pub(crate) fn find_comment_end(s: &str) -> Option<usize> {
 +    let mut iter = CharClasses::new(s.char_indices());
 +    for (kind, (i, _c)) in &mut iter {
 +        if kind == FullCodeCharKind::Normal || kind == FullCodeCharKind::InString {
 +            return Some(i);
 +        }
 +    }
 +
 +    // Handle case where the comment ends at the end of `s`.
 +    if iter.status == CharClassesStatus::Normal {
 +        Some(s.len())
 +    } else {
 +        None
 +    }
 +}
 +
 +/// Returns `true` if text contains any comment.
 +pub(crate) fn contains_comment(text: &str) -> bool {
 +    CharClasses::new(text.chars()).any(|(kind, _)| kind.is_comment())
 +}
 +
 +pub(crate) struct CharClasses<T>
 +where
 +    T: Iterator,
 +    T::Item: RichChar,
 +{
 +    base: MultiPeek<T>,
 +    status: CharClassesStatus,
 +}
 +
 +pub(crate) trait RichChar {
 +    fn get_char(&self) -> char;
 +}
 +
 +impl RichChar for char {
 +    fn get_char(&self) -> char {
 +        *self
 +    }
 +}
 +
 +impl RichChar for (usize, char) {
 +    fn get_char(&self) -> char {
 +        self.1
 +    }
 +}
 +
 +#[derive(PartialEq, Eq, Debug, Clone, Copy)]
 +enum CharClassesStatus {
 +    Normal,
 +    /// Character is within a string
 +    LitString,
 +    LitStringEscape,
 +    /// Character is within a raw string
 +    LitRawString(u32),
 +    RawStringPrefix(u32),
 +    RawStringSuffix(u32),
 +    LitChar,
 +    LitCharEscape,
 +    /// Character inside a block comment, with the integer indicating the nesting deepness of the
 +    /// comment
 +    BlockComment(u32),
 +    /// Character inside a block-commented string, with the integer indicating the nesting deepness
 +    /// of the comment
 +    StringInBlockComment(u32),
 +    /// Status when the '/' has been consumed, but not yet the '*', deepness is
 +    /// the new deepness (after the comment opening).
 +    BlockCommentOpening(u32),
 +    /// Status when the '*' has been consumed, but not yet the '/', deepness is
 +    /// the new deepness (after the comment closing).
 +    BlockCommentClosing(u32),
 +    /// Character is within a line comment
 +    LineComment,
 +}
 +
 +/// Distinguish between functional part of code and comments
 +#[derive(PartialEq, Eq, Debug, Clone, Copy)]
 +pub(crate) enum CodeCharKind {
 +    Normal,
 +    Comment,
 +}
 +
 +/// Distinguish between functional part of code and comments,
 +/// describing opening and closing of comments for ease when chunking
 +/// code from tagged characters
 +#[derive(PartialEq, Eq, Debug, Clone, Copy)]
 +pub(crate) enum FullCodeCharKind {
 +    Normal,
 +    /// The first character of a comment, there is only one for a comment (always '/')
 +    StartComment,
 +    /// Any character inside a comment including the second character of comment
 +    /// marks ("//", "/*")
 +    InComment,
 +    /// Last character of a comment, '\n' for a line comment, '/' for a block comment.
 +    EndComment,
 +    /// Start of a mutlitine string inside a comment
 +    StartStringCommented,
 +    /// End of a mutlitine string inside a comment
 +    EndStringCommented,
 +    /// Inside a commented string
 +    InStringCommented,
 +    /// Start of a mutlitine string
 +    StartString,
 +    /// End of a mutlitine string
 +    EndString,
 +    /// Inside a string.
 +    InString,
 +}
 +
 +impl FullCodeCharKind {
 +    pub(crate) fn is_comment(self) -> bool {
 +        match self {
 +            FullCodeCharKind::StartComment
 +            | FullCodeCharKind::InComment
 +            | FullCodeCharKind::EndComment
 +            | FullCodeCharKind::StartStringCommented
 +            | FullCodeCharKind::InStringCommented
 +            | FullCodeCharKind::EndStringCommented => true,
 +            _ => false,
 +        }
 +    }
 +
 +    /// Returns true if the character is inside a comment
 +    pub(crate) fn inside_comment(self) -> bool {
 +        match self {
 +            FullCodeCharKind::InComment
 +            | FullCodeCharKind::StartStringCommented
 +            | FullCodeCharKind::InStringCommented
 +            | FullCodeCharKind::EndStringCommented => true,
 +            _ => false,
 +        }
 +    }
 +
 +    pub(crate) fn is_string(self) -> bool {
 +        self == FullCodeCharKind::InString || self == FullCodeCharKind::StartString
 +    }
 +
 +    /// Returns true if the character is within a commented string
 +    pub(crate) fn is_commented_string(self) -> bool {
 +        self == FullCodeCharKind::InStringCommented
 +            || self == FullCodeCharKind::StartStringCommented
 +    }
 +
 +    fn to_codecharkind(self) -> CodeCharKind {
 +        if self.is_comment() {
 +            CodeCharKind::Comment
 +        } else {
 +            CodeCharKind::Normal
 +        }
 +    }
 +}
 +
 +impl<T> CharClasses<T>
 +where
 +    T: Iterator,
 +    T::Item: RichChar,
 +{
 +    pub(crate) fn new(base: T) -> CharClasses<T> {
 +        CharClasses {
 +            base: multipeek(base),
 +            status: CharClassesStatus::Normal,
 +        }
 +    }
 +}
 +
 +fn is_raw_string_suffix<T>(iter: &mut MultiPeek<T>, count: u32) -> bool
 +where
 +    T: Iterator,
 +    T::Item: RichChar,
 +{
 +    for _ in 0..count {
 +        match iter.peek() {
 +            Some(c) if c.get_char() == '#' => continue,
 +            _ => return false,
 +        }
 +    }
 +    true
 +}
 +
 +impl<T> Iterator for CharClasses<T>
 +where
 +    T: Iterator,
 +    T::Item: RichChar,
 +{
 +    type Item = (FullCodeCharKind, T::Item);
 +
 +    fn next(&mut self) -> Option<(FullCodeCharKind, T::Item)> {
 +        let item = self.base.next()?;
 +        let chr = item.get_char();
 +        let mut char_kind = FullCodeCharKind::Normal;
 +        self.status = match self.status {
 +            CharClassesStatus::LitRawString(sharps) => {
 +                char_kind = FullCodeCharKind::InString;
 +                match chr {
 +                    '"' => {
 +                        if sharps == 0 {
 +                            char_kind = FullCodeCharKind::Normal;
 +                            CharClassesStatus::Normal
 +                        } else if is_raw_string_suffix(&mut self.base, sharps) {
 +                            CharClassesStatus::RawStringSuffix(sharps)
 +                        } else {
 +                            CharClassesStatus::LitRawString(sharps)
 +                        }
 +                    }
 +                    _ => CharClassesStatus::LitRawString(sharps),
 +                }
 +            }
 +            CharClassesStatus::RawStringPrefix(sharps) => {
 +                char_kind = FullCodeCharKind::InString;
 +                match chr {
 +                    '#' => CharClassesStatus::RawStringPrefix(sharps + 1),
 +                    '"' => CharClassesStatus::LitRawString(sharps),
 +                    _ => CharClassesStatus::Normal, // Unreachable.
 +                }
 +            }
 +            CharClassesStatus::RawStringSuffix(sharps) => {
 +                match chr {
 +                    '#' => {
 +                        if sharps == 1 {
 +                            CharClassesStatus::Normal
 +                        } else {
 +                            char_kind = FullCodeCharKind::InString;
 +                            CharClassesStatus::RawStringSuffix(sharps - 1)
 +                        }
 +                    }
 +                    _ => CharClassesStatus::Normal, // Unreachable
 +                }
 +            }
 +            CharClassesStatus::LitString => {
 +                char_kind = FullCodeCharKind::InString;
 +                match chr {
 +                    '"' => CharClassesStatus::Normal,
 +                    '\\' => CharClassesStatus::LitStringEscape,
 +                    _ => CharClassesStatus::LitString,
 +                }
 +            }
 +            CharClassesStatus::LitStringEscape => {
 +                char_kind = FullCodeCharKind::InString;
 +                CharClassesStatus::LitString
 +            }
 +            CharClassesStatus::LitChar => match chr {
 +                '\\' => CharClassesStatus::LitCharEscape,
 +                '\'' => CharClassesStatus::Normal,
 +                _ => CharClassesStatus::LitChar,
 +            },
 +            CharClassesStatus::LitCharEscape => CharClassesStatus::LitChar,
 +            CharClassesStatus::Normal => match chr {
 +                'r' => match self.base.peek().map(RichChar::get_char) {
 +                    Some('#') | Some('"') => {
 +                        char_kind = FullCodeCharKind::InString;
 +                        CharClassesStatus::RawStringPrefix(0)
 +                    }
 +                    _ => CharClassesStatus::Normal,
 +                },
 +                '"' => {
 +                    char_kind = FullCodeCharKind::InString;
 +                    CharClassesStatus::LitString
 +                }
 +                '\'' => {
 +                    // HACK: Work around mut borrow.
 +                    match self.base.peek() {
 +                        Some(next) if next.get_char() == '\\' => {
 +                            self.status = CharClassesStatus::LitChar;
 +                            return Some((char_kind, item));
 +                        }
 +                        _ => (),
 +                    }
 +
 +                    match self.base.peek() {
 +                        Some(next) if next.get_char() == '\'' => CharClassesStatus::LitChar,
 +                        _ => CharClassesStatus::Normal,
 +                    }
 +                }
 +                '/' => match self.base.peek() {
 +                    Some(next) if next.get_char() == '*' => {
 +                        self.status = CharClassesStatus::BlockCommentOpening(1);
 +                        return Some((FullCodeCharKind::StartComment, item));
 +                    }
 +                    Some(next) if next.get_char() == '/' => {
 +                        self.status = CharClassesStatus::LineComment;
 +                        return Some((FullCodeCharKind::StartComment, item));
 +                    }
 +                    _ => CharClassesStatus::Normal,
 +                },
 +                _ => CharClassesStatus::Normal,
 +            },
 +            CharClassesStatus::StringInBlockComment(deepness) => {
 +                char_kind = FullCodeCharKind::InStringCommented;
 +                if chr == '"' {
 +                    CharClassesStatus::BlockComment(deepness)
 +                } else if chr == '*' && self.base.peek().map(RichChar::get_char) == Some('/') {
 +                    char_kind = FullCodeCharKind::InComment;
 +                    CharClassesStatus::BlockCommentClosing(deepness - 1)
 +                } else {
 +                    CharClassesStatus::StringInBlockComment(deepness)
 +                }
 +            }
 +            CharClassesStatus::BlockComment(deepness) => {
 +                assert_ne!(deepness, 0);
 +                char_kind = FullCodeCharKind::InComment;
 +                match self.base.peek() {
 +                    Some(next) if next.get_char() == '/' && chr == '*' => {
 +                        CharClassesStatus::BlockCommentClosing(deepness - 1)
 +                    }
 +                    Some(next) if next.get_char() == '*' && chr == '/' => {
 +                        CharClassesStatus::BlockCommentOpening(deepness + 1)
 +                    }
 +                    _ if chr == '"' => CharClassesStatus::StringInBlockComment(deepness),
 +                    _ => self.status,
 +                }
 +            }
 +            CharClassesStatus::BlockCommentOpening(deepness) => {
 +                assert_eq!(chr, '*');
 +                self.status = CharClassesStatus::BlockComment(deepness);
 +                return Some((FullCodeCharKind::InComment, item));
 +            }
 +            CharClassesStatus::BlockCommentClosing(deepness) => {
 +                assert_eq!(chr, '/');
 +                if deepness == 0 {
 +                    self.status = CharClassesStatus::Normal;
 +                    return Some((FullCodeCharKind::EndComment, item));
 +                } else {
 +                    self.status = CharClassesStatus::BlockComment(deepness);
 +                    return Some((FullCodeCharKind::InComment, item));
 +                }
 +            }
 +            CharClassesStatus::LineComment => match chr {
 +                '\n' => {
 +                    self.status = CharClassesStatus::Normal;
 +                    return Some((FullCodeCharKind::EndComment, item));
 +                }
 +                _ => {
 +                    self.status = CharClassesStatus::LineComment;
 +                    return Some((FullCodeCharKind::InComment, item));
 +                }
 +            },
 +        };
 +        Some((char_kind, item))
 +    }
 +}
 +
 +/// An iterator over the lines of a string, paired with the char kind at the
 +/// end of the line.
 +pub(crate) struct LineClasses<'a> {
 +    base: iter::Peekable<CharClasses<std::str::Chars<'a>>>,
 +    kind: FullCodeCharKind,
 +}
 +
 +impl<'a> LineClasses<'a> {
 +    pub(crate) fn new(s: &'a str) -> Self {
 +        LineClasses {
 +            base: CharClasses::new(s.chars()).peekable(),
 +            kind: FullCodeCharKind::Normal,
 +        }
 +    }
 +}
 +
 +impl<'a> Iterator for LineClasses<'a> {
 +    type Item = (FullCodeCharKind, String);
 +
 +    fn next(&mut self) -> Option<Self::Item> {
 +        self.base.peek()?;
 +
 +        let mut line = String::new();
 +
 +        let start_kind = match self.base.peek() {
 +            Some((kind, _)) => *kind,
 +            None => unreachable!(),
 +        };
 +
 +        for (kind, c) in self.base.by_ref() {
 +            // needed to set the kind of the ending character on the last line
 +            self.kind = kind;
 +            if c == '\n' {
 +                self.kind = match (start_kind, kind) {
 +                    (FullCodeCharKind::Normal, FullCodeCharKind::InString) => {
 +                        FullCodeCharKind::StartString
 +                    }
 +                    (FullCodeCharKind::InString, FullCodeCharKind::Normal) => {
 +                        FullCodeCharKind::EndString
 +                    }
 +                    (FullCodeCharKind::InComment, FullCodeCharKind::InStringCommented) => {
 +                        FullCodeCharKind::StartStringCommented
 +                    }
 +                    (FullCodeCharKind::InStringCommented, FullCodeCharKind::InComment) => {
 +                        FullCodeCharKind::EndStringCommented
 +                    }
 +                    _ => kind,
 +                };
 +                break;
 +            }
 +            line.push(c);
 +        }
 +
 +        // Workaround for CRLF newline.
 +        if line.ends_with('\r') {
 +            line.pop();
 +        }
 +
 +        Some((self.kind, line))
 +    }
 +}
 +
 +/// Iterator over functional and commented parts of a string. Any part of a string is either
 +/// functional code, either *one* block comment, either *one* line comment. Whitespace between
 +/// comments is functional code. Line comments contain their ending newlines.
 +struct UngroupedCommentCodeSlices<'a> {
 +    slice: &'a str,
 +    iter: iter::Peekable<CharClasses<std::str::CharIndices<'a>>>,
 +}
 +
 +impl<'a> UngroupedCommentCodeSlices<'a> {
 +    fn new(code: &'a str) -> UngroupedCommentCodeSlices<'a> {
 +        UngroupedCommentCodeSlices {
 +            slice: code,
 +            iter: CharClasses::new(code.char_indices()).peekable(),
 +        }
 +    }
 +}
 +
 +impl<'a> Iterator for UngroupedCommentCodeSlices<'a> {
 +    type Item = (CodeCharKind, usize, &'a str);
 +
 +    fn next(&mut self) -> Option<Self::Item> {
 +        let (kind, (start_idx, _)) = self.iter.next()?;
 +        match kind {
 +            FullCodeCharKind::Normal | FullCodeCharKind::InString => {
 +                // Consume all the Normal code
 +                while let Some(&(char_kind, _)) = self.iter.peek() {
 +                    if char_kind.is_comment() {
 +                        break;
 +                    }
 +                    let _ = self.iter.next();
 +                }
 +            }
 +            FullCodeCharKind::StartComment => {
 +                // Consume the whole comment
 +                loop {
 +                    match self.iter.next() {
 +                        Some((kind, ..)) if kind.inside_comment() => continue,
 +                        _ => break,
 +                    }
 +                }
 +            }
 +            _ => panic!(),
 +        }
 +        let slice = match self.iter.peek() {
 +            Some(&(_, (end_idx, _))) => &self.slice[start_idx..end_idx],
 +            None => &self.slice[start_idx..],
 +        };
 +        Some((
 +            if kind.is_comment() {
 +                CodeCharKind::Comment
 +            } else {
 +                CodeCharKind::Normal
 +            },
 +            start_idx,
 +            slice,
 +        ))
 +    }
 +}
 +
 +/// Iterator over an alternating sequence of functional and commented parts of
 +/// a string. The first item is always a, possibly zero length, subslice of
 +/// functional text. Line style comments contain their ending newlines.
 +pub(crate) struct CommentCodeSlices<'a> {
 +    slice: &'a str,
 +    last_slice_kind: CodeCharKind,
 +    last_slice_end: usize,
 +}
 +
 +impl<'a> CommentCodeSlices<'a> {
 +    pub(crate) fn new(slice: &'a str) -> CommentCodeSlices<'a> {
 +        CommentCodeSlices {
 +            slice,
 +            last_slice_kind: CodeCharKind::Comment,
 +            last_slice_end: 0,
 +        }
 +    }
 +}
 +
 +impl<'a> Iterator for CommentCodeSlices<'a> {
 +    type Item = (CodeCharKind, usize, &'a str);
 +
 +    fn next(&mut self) -> Option<Self::Item> {
 +        if self.last_slice_end == self.slice.len() {
 +            return None;
 +        }
 +
 +        let mut sub_slice_end = self.last_slice_end;
 +        let mut first_whitespace = None;
 +        let subslice = &self.slice[self.last_slice_end..];
 +        let mut iter = CharClasses::new(subslice.char_indices());
 +
 +        for (kind, (i, c)) in &mut iter {
 +            let is_comment_connector = self.last_slice_kind == CodeCharKind::Normal
 +                && &subslice[..2] == "//"
 +                && [' ', '\t'].contains(&c);
 +
 +            if is_comment_connector && first_whitespace.is_none() {
 +                first_whitespace = Some(i);
 +            }
 +
 +            if kind.to_codecharkind() == self.last_slice_kind && !is_comment_connector {
 +                let last_index = match first_whitespace {
 +                    Some(j) => j,
 +                    None => i,
 +                };
 +                sub_slice_end = self.last_slice_end + last_index;
 +                break;
 +            }
 +
 +            if !is_comment_connector {
 +                first_whitespace = None;
 +            }
 +        }
 +
 +        if let (None, true) = (iter.next(), sub_slice_end == self.last_slice_end) {
 +            // This was the last subslice.
 +            sub_slice_end = match first_whitespace {
 +                Some(i) => self.last_slice_end + i,
 +                None => self.slice.len(),
 +            };
 +        }
 +
 +        let kind = match self.last_slice_kind {
 +            CodeCharKind::Comment => CodeCharKind::Normal,
 +            CodeCharKind::Normal => CodeCharKind::Comment,
 +        };
 +        let res = (
 +            kind,
 +            self.last_slice_end,
 +            &self.slice[self.last_slice_end..sub_slice_end],
 +        );
 +        self.last_slice_end = sub_slice_end;
 +        self.last_slice_kind = kind;
 +
 +        Some(res)
 +    }
 +}
 +
 +/// Checks is `new` didn't miss any comment from `span`, if it removed any, return previous text
 +/// (if it fits in the width/offset, else return `None`), else return `new`
 +pub(crate) fn recover_comment_removed(
 +    new: String,
 +    span: Span,
 +    context: &RewriteContext<'_>,
 +) -> Option<String> {
 +    let snippet = context.snippet(span);
 +    if snippet != new && changed_comment_content(snippet, &new) {
 +        // We missed some comments. Warn and keep the original text.
 +        if context.config.error_on_unformatted() {
 +            context.report.append(
 +                context.parse_sess.span_to_filename(span),
 +                vec![FormattingError::from_span(
 +                    span,
 +                    context.parse_sess,
 +                    ErrorKind::LostComment,
 +                )],
 +            );
 +        }
 +        Some(snippet.to_owned())
 +    } else {
 +        Some(new)
 +    }
 +}
 +
 +pub(crate) fn filter_normal_code(code: &str) -> String {
 +    let mut buffer = String::with_capacity(code.len());
 +    LineClasses::new(code).for_each(|(kind, line)| match kind {
 +        FullCodeCharKind::Normal
 +        | FullCodeCharKind::StartString
 +        | FullCodeCharKind::InString
 +        | FullCodeCharKind::EndString => {
 +            buffer.push_str(&line);
 +            buffer.push('\n');
 +        }
 +        _ => (),
 +    });
 +    if !code.ends_with('\n') && buffer.ends_with('\n') {
 +        buffer.pop();
 +    }
 +    buffer
 +}
 +
 +/// Returns `true` if the two strings of code have the same payload of comments.
 +/// The payload of comments is everything in the string except:
 +/// - actual code (not comments),
 +/// - comment start/end marks,
 +/// - whitespace,
 +/// - '*' at the beginning of lines in block comments.
 +fn changed_comment_content(orig: &str, new: &str) -> bool {
 +    // Cannot write this as a fn since we cannot return types containing closures.
 +    let code_comment_content = |code| {
 +        let slices = UngroupedCommentCodeSlices::new(code);
 +        slices
 +            .filter(|&(ref kind, _, _)| *kind == CodeCharKind::Comment)
 +            .flat_map(|(_, _, s)| CommentReducer::new(s))
 +    };
 +    let res = code_comment_content(orig).ne(code_comment_content(new));
 +    debug!(
 +        "comment::changed_comment_content: {}\norig: '{}'\nnew: '{}'\nraw_old: {}\nraw_new: {}",
 +        res,
 +        orig,
 +        new,
 +        code_comment_content(orig).collect::<String>(),
 +        code_comment_content(new).collect::<String>()
 +    );
 +    res
 +}
 +
 +/// Iterator over the 'payload' characters of a comment.
 +/// It skips whitespace, comment start/end marks, and '*' at the beginning of lines.
 +/// The comment must be one comment, ie not more than one start mark (no multiple line comments,
 +/// for example).
 +struct CommentReducer<'a> {
 +    is_block: bool,
 +    at_start_line: bool,
 +    iter: std::str::Chars<'a>,
 +}
 +
 +impl<'a> CommentReducer<'a> {
 +    fn new(comment: &'a str) -> CommentReducer<'a> {
 +        let is_block = comment.starts_with("/*");
 +        let comment = remove_comment_header(comment);
 +        CommentReducer {
 +            is_block,
 +            // There are no supplementary '*' on the first line.
 +            at_start_line: false,
 +            iter: comment.chars(),
 +        }
 +    }
 +}
 +
 +impl<'a> Iterator for CommentReducer<'a> {
 +    type Item = char;
 +
 +    fn next(&mut self) -> Option<Self::Item> {
 +        loop {
 +            let mut c = self.iter.next()?;
 +            if self.is_block && self.at_start_line {
 +                while c.is_whitespace() {
 +                    c = self.iter.next()?;
 +                }
 +                // Ignore leading '*'.
 +                if c == '*' {
 +                    c = self.iter.next()?;
 +                }
 +            } else if c == '\n' {
 +                self.at_start_line = true;
 +            }
 +            if !c.is_whitespace() {
 +                return Some(c);
 +            }
 +        }
 +    }
 +}
 +
 +fn remove_comment_header(comment: &str) -> &str {
 +    if comment.starts_with("///") || comment.starts_with("//!") {
 +        &comment[3..]
 +    } else if let Some(stripped) = comment.strip_prefix("//") {
 +        stripped
 +    } else if (comment.starts_with("/**") && !comment.starts_with("/**/"))
 +        || comment.starts_with("/*!")
 +    {
 +        &comment[3..comment.len() - 2]
 +    } else {
 +        assert!(
 +            comment.starts_with("/*"),
 +            "string '{}' is not a comment",
 +            comment
 +        );
 +        &comment[2..comment.len() - 2]
 +    }
 +}
 +
 +#[cfg(test)]
 +mod test {
 +    use super::*;
 +    use crate::shape::{Indent, Shape};
 +
 +    #[test]
 +    fn char_classes() {
 +        let mut iter = CharClasses::new("//\n\n".chars());
 +
 +        assert_eq!((FullCodeCharKind::StartComment, '/'), iter.next().unwrap());
 +        assert_eq!((FullCodeCharKind::InComment, '/'), iter.next().unwrap());
 +        assert_eq!((FullCodeCharKind::EndComment, '\n'), iter.next().unwrap());
 +        assert_eq!((FullCodeCharKind::Normal, '\n'), iter.next().unwrap());
 +        assert_eq!(None, iter.next());
 +    }
 +
 +    #[test]
 +    fn comment_code_slices() {
 +        let input = "code(); /* test */ 1 + 1";
 +        let mut iter = CommentCodeSlices::new(input);
 +
 +        assert_eq!((CodeCharKind::Normal, 0, "code(); "), iter.next().unwrap());
 +        assert_eq!(
 +            (CodeCharKind::Comment, 8, "/* test */"),
 +            iter.next().unwrap()
 +        );
 +        assert_eq!((CodeCharKind::Normal, 18, " 1 + 1"), iter.next().unwrap());
 +        assert_eq!(None, iter.next());
 +    }
 +
 +    #[test]
 +    fn comment_code_slices_two() {
 +        let input = "// comment\n    test();";
 +        let mut iter = CommentCodeSlices::new(input);
 +
 +        assert_eq!((CodeCharKind::Normal, 0, ""), iter.next().unwrap());
 +        assert_eq!(
 +            (CodeCharKind::Comment, 0, "// comment\n"),
 +            iter.next().unwrap()
 +        );
 +        assert_eq!(
 +            (CodeCharKind::Normal, 11, "    test();"),
 +            iter.next().unwrap()
 +        );
 +        assert_eq!(None, iter.next());
 +    }
 +
 +    #[test]
 +    fn comment_code_slices_three() {
 +        let input = "1 // comment\n    // comment2\n\n";
 +        let mut iter = CommentCodeSlices::new(input);
 +
 +        assert_eq!((CodeCharKind::Normal, 0, "1 "), iter.next().unwrap());
 +        assert_eq!(
 +            (CodeCharKind::Comment, 2, "// comment\n    // comment2\n"),
 +            iter.next().unwrap()
 +        );
 +        assert_eq!((CodeCharKind::Normal, 29, "\n"), iter.next().unwrap());
 +        assert_eq!(None, iter.next());
 +    }
 +
 +    #[test]
 +    #[rustfmt::skip]
 +    fn format_doc_comments() {
 +        let mut wrap_normalize_config: crate::config::Config = Default::default();
 +        wrap_normalize_config.set().wrap_comments(true);
 +        wrap_normalize_config.set().normalize_comments(true);
 +
 +        let mut wrap_config: crate::config::Config = Default::default();
 +        wrap_config.set().wrap_comments(true);
 +
 +        let comment = rewrite_comment(" //test",
 +                                      true,
 +                                      Shape::legacy(100, Indent::new(0, 100)),
 +                                      &wrap_normalize_config).unwrap();
 +        assert_eq!("/* test */", comment);
 +
 +        let comment = rewrite_comment("// comment on a",
 +                                      false,
 +                                      Shape::legacy(10, Indent::empty()),
 +                                      &wrap_normalize_config).unwrap();
 +        assert_eq!("// comment\n// on a", comment);
 +
 +        let comment = rewrite_comment("//  A multi line comment\n             // between args.",
 +                                      false,
 +                                      Shape::legacy(60, Indent::new(0, 12)),
 +                                      &wrap_normalize_config).unwrap();
 +        assert_eq!("//  A multi line comment\n            // between args.", comment);
 +
 +        let input = "// comment";
 +        let expected =
 +            "/* comment */";
 +        let comment = rewrite_comment(input,
 +                                      true,
 +                                      Shape::legacy(9, Indent::new(0, 69)),
 +                                      &wrap_normalize_config).unwrap();
 +        assert_eq!(expected, comment);
 +
 +        let comment = rewrite_comment("/*   trimmed    */",
 +                                      true,
 +                                      Shape::legacy(100, Indent::new(0, 100)),
 +                                      &wrap_normalize_config).unwrap();
 +        assert_eq!("/* trimmed */", comment);
 +
 +        // Check that different comment style are properly recognised.
 +        let comment = rewrite_comment(r#"/// test1
 +                                         /// test2
 +                                         /*
 +                                          * test3
 +                                          */"#,
 +                                      false,
 +                                      Shape::legacy(100, Indent::new(0, 0)),
 +                                      &wrap_normalize_config).unwrap();
 +        assert_eq!("/// test1\n/// test2\n// test3", comment);
 +
 +        // Check that the blank line marks the end of a commented paragraph.
 +        let comment = rewrite_comment(r#"// test1
 +
 +                                         // test2"#,
 +                                      false,
 +                                      Shape::legacy(100, Indent::new(0, 0)),
 +                                      &wrap_normalize_config).unwrap();
 +        assert_eq!("// test1\n\n// test2", comment);
 +
 +        // Check that the blank line marks the end of a custom-commented paragraph.
 +        let comment = rewrite_comment(r#"//@ test1
 +
 +                                         //@ test2"#,
 +                                      false,
 +                                      Shape::legacy(100, Indent::new(0, 0)),
 +                                      &wrap_normalize_config).unwrap();
 +        assert_eq!("//@ test1\n\n//@ test2", comment);
 +
 +        // Check that bare lines are just indented but otherwise left unchanged.
 +        let comment = rewrite_comment(r#"// test1
 +                                         /*
 +                                           a bare line!
 +
 +                                                another bare line!
 +                                          */"#,
 +                                      false,
 +                                      Shape::legacy(100, Indent::new(0, 0)),
 +                                      &wrap_config).unwrap();
 +        assert_eq!("// test1\n/*\n a bare line!\n\n      another bare line!\n*/", comment);
 +    }
 +
 +    // This is probably intended to be a non-test fn, but it is not used.
 +    // We should keep this around unless it helps us test stuff to remove it.
 +    fn uncommented(text: &str) -> String {
 +        CharClasses::new(text.chars())
 +            .filter_map(|(s, c)| match s {
 +                FullCodeCharKind::Normal | FullCodeCharKind::InString => Some(c),
 +                _ => None,
 +            })
 +            .collect()
 +    }
 +
 +    #[test]
 +    fn test_uncommented() {
 +        assert_eq!(&uncommented("abc/*...*/"), "abc");
 +        assert_eq!(
 +            &uncommented("// .... /* \n../* /* *** / */ */a/* // */c\n"),
 +            "..ac\n"
 +        );
 +        assert_eq!(&uncommented("abc \" /* */\" qsdf"), "abc \" /* */\" qsdf");
 +    }
 +
 +    #[test]
 +    fn test_contains_comment() {
 +        assert_eq!(contains_comment("abc"), false);
 +        assert_eq!(contains_comment("abc // qsdf"), true);
 +        assert_eq!(contains_comment("abc /* kqsdf"), true);
 +        assert_eq!(contains_comment("abc \" /* */\" qsdf"), false);
 +    }
 +
 +    #[test]
 +    fn test_find_uncommented() {
 +        fn check(haystack: &str, needle: &str, expected: Option<usize>) {
 +            assert_eq!(expected, haystack.find_uncommented(needle));
 +        }
 +
 +        check("/*/ */test", "test", Some(6));
 +        check("//test\ntest", "test", Some(7));
 +        check("/* comment only */", "whatever", None);
 +        check(
 +            "/* comment */ some text /* more commentary */ result",
 +            "result",
 +            Some(46),
 +        );
 +        check("sup // sup", "p", Some(2));
 +        check("sup", "x", None);
 +        check(r#"Ï€? /**/ Ï€ is nice!"#, r#"Ï€ is nice"#, Some(9));
 +        check("/*sup yo? \n sup*/ sup", "p", Some(20));
 +        check("hel/*lohello*/lo", "hello", None);
 +        check("acb", "ab", None);
 +        check(",/*A*/ ", ",", Some(0));
 +        check("abc", "abc", Some(0));
 +        check("/* abc */", "abc", None);
 +        check("/**/abc/* */", "abc", Some(4));
 +        check("\"/* abc */\"", "abc", Some(4));
 +        check("\"/* abc", "abc", Some(4));
 +    }
 +
 +    #[test]
 +    fn test_filter_normal_code() {
 +        let s = r#"
 +fn main() {
 +    println!("hello, world");
 +}
 +"#;
 +        assert_eq!(s, filter_normal_code(s));
 +        let s_with_comment = r#"
 +fn main() {
 +    // hello, world
 +    println!("hello, world");
 +}
 +"#;
 +        assert_eq!(s, filter_normal_code(s_with_comment));
 +    }
 +}
index 7b498dc46b320cd1444274cf17921fd5394326a9,0000000000000000000000000000000000000000..e4e51a3f3b40927e05b2021c4e259a9f66a30d6a
mode 100644,000000..100644
--- /dev/null
@@@ -1,440 -1,0 +1,440 @@@
-             FileName::Stdin => write!(f, "stdin"),
 +//! This module contains types and functions to support formatting specific line ranges.
 +
 +use itertools::Itertools;
 +use std::collections::HashMap;
 +use std::path::PathBuf;
 +use std::{cmp, fmt, iter, str};
 +
 +use rustc_data_structures::sync::Lrc;
 +use rustc_span::{self, SourceFile};
 +use serde::{ser, Deserialize, Deserializer, Serialize, Serializer};
 +use serde_json as json;
 +use thiserror::Error;
 +
 +/// A range of lines in a file, inclusive of both ends.
 +pub struct LineRange {
 +    pub(crate) file: Lrc<SourceFile>,
 +    pub(crate) lo: usize,
 +    pub(crate) hi: usize,
 +}
 +
 +/// Defines the name of an input - either a file or stdin.
 +#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
 +pub enum FileName {
 +    Real(PathBuf),
 +    Stdin,
 +}
 +
 +impl From<rustc_span::FileName> for FileName {
 +    fn from(name: rustc_span::FileName) -> FileName {
 +        match name {
 +            rustc_span::FileName::Real(rustc_span::RealFileName::LocalPath(p)) => FileName::Real(p),
 +            rustc_span::FileName::Custom(ref f) if f == "stdin" => FileName::Stdin,
 +            _ => unreachable!(),
 +        }
 +    }
 +}
 +
 +impl fmt::Display for FileName {
 +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 +        match self {
 +            FileName::Real(p) => write!(f, "{}", p.to_str().unwrap()),
++            FileName::Stdin => write!(f, "<stdin>"),
 +        }
 +    }
 +}
 +
 +impl<'de> Deserialize<'de> for FileName {
 +    fn deserialize<D>(deserializer: D) -> Result<FileName, D::Error>
 +    where
 +        D: Deserializer<'de>,
 +    {
 +        let s = String::deserialize(deserializer)?;
 +        if s == "stdin" {
 +            Ok(FileName::Stdin)
 +        } else {
 +            Ok(FileName::Real(s.into()))
 +        }
 +    }
 +}
 +
 +impl Serialize for FileName {
 +    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
 +    where
 +        S: Serializer,
 +    {
 +        let s = match self {
 +            FileName::Stdin => Ok("stdin"),
 +            FileName::Real(path) => path
 +                .to_str()
 +                .ok_or_else(|| ser::Error::custom("path can't be serialized as UTF-8 string")),
 +        };
 +
 +        s.and_then(|s| serializer.serialize_str(s))
 +    }
 +}
 +
 +impl LineRange {
 +    pub(crate) fn file_name(&self) -> FileName {
 +        self.file.name.clone().into()
 +    }
 +}
 +
 +/// A range that is inclusive of both ends.
 +#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Deserialize)]
 +pub struct Range {
 +    lo: usize,
 +    hi: usize,
 +}
 +
 +impl<'a> From<&'a LineRange> for Range {
 +    fn from(range: &'a LineRange) -> Range {
 +        Range::new(range.lo, range.hi)
 +    }
 +}
 +
 +impl fmt::Display for Range {
 +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 +        write!(f, "{}..{}", self.lo, self.hi)
 +    }
 +}
 +
 +impl Range {
 +    pub fn new(lo: usize, hi: usize) -> Range {
 +        Range { lo, hi }
 +    }
 +
 +    fn is_empty(self) -> bool {
 +        self.lo > self.hi
 +    }
 +
 +    #[allow(dead_code)]
 +    fn contains(self, other: Range) -> bool {
 +        if other.is_empty() {
 +            true
 +        } else {
 +            !self.is_empty() && self.lo <= other.lo && self.hi >= other.hi
 +        }
 +    }
 +
 +    fn intersects(self, other: Range) -> bool {
 +        if self.is_empty() || other.is_empty() {
 +            false
 +        } else {
 +            (self.lo <= other.hi && other.hi <= self.hi)
 +                || (other.lo <= self.hi && self.hi <= other.hi)
 +        }
 +    }
 +
 +    fn adjacent_to(self, other: Range) -> bool {
 +        if self.is_empty() || other.is_empty() {
 +            false
 +        } else {
 +            self.hi + 1 == other.lo || other.hi + 1 == self.lo
 +        }
 +    }
 +
 +    /// Returns a new `Range` with lines from `self` and `other` if they were adjacent or
 +    /// intersect; returns `None` otherwise.
 +    fn merge(self, other: Range) -> Option<Range> {
 +        if self.adjacent_to(other) || self.intersects(other) {
 +            Some(Range::new(
 +                cmp::min(self.lo, other.lo),
 +                cmp::max(self.hi, other.hi),
 +            ))
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +/// A set of lines in files.
 +///
 +/// It is represented as a multimap keyed on file names, with values a collection of
 +/// non-overlapping ranges sorted by their start point. An inner `None` is interpreted to mean all
 +/// lines in all files.
 +#[derive(Clone, Debug, Default, PartialEq)]
 +pub struct FileLines(Option<HashMap<FileName, Vec<Range>>>);
 +
 +impl fmt::Display for FileLines {
 +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 +        match &self.0 {
 +            None => write!(f, "None")?,
 +            Some(map) => {
 +                for (file_name, ranges) in map.iter() {
 +                    write!(f, "{}: ", file_name)?;
 +                    write!(f, "{}\n", ranges.iter().format(", "))?;
 +                }
 +            }
 +        };
 +        Ok(())
 +    }
 +}
 +
 +/// Normalizes the ranges so that the invariants for `FileLines` hold: ranges are non-overlapping,
 +/// and ordered by their start point.
 +fn normalize_ranges(ranges: &mut HashMap<FileName, Vec<Range>>) {
 +    for ranges in ranges.values_mut() {
 +        ranges.sort();
 +        let mut result = vec![];
 +        let mut iter = ranges.iter_mut().peekable();
 +        while let Some(next) = iter.next() {
 +            let mut next = *next;
 +            while let Some(&&mut peek) = iter.peek() {
 +                if let Some(merged) = next.merge(peek) {
 +                    iter.next().unwrap();
 +                    next = merged;
 +                } else {
 +                    break;
 +                }
 +            }
 +            result.push(next)
 +        }
 +        *ranges = result;
 +    }
 +}
 +
 +impl FileLines {
 +    /// Creates a `FileLines` that contains all lines in all files.
 +    pub(crate) fn all() -> FileLines {
 +        FileLines(None)
 +    }
 +
 +    /// Returns `true` if this `FileLines` contains all lines in all files.
 +    pub(crate) fn is_all(&self) -> bool {
 +        self.0.is_none()
 +    }
 +
 +    pub fn from_ranges(mut ranges: HashMap<FileName, Vec<Range>>) -> FileLines {
 +        normalize_ranges(&mut ranges);
 +        FileLines(Some(ranges))
 +    }
 +
 +    /// Returns an iterator over the files contained in `self`.
 +    pub fn files(&self) -> Files<'_> {
 +        Files(self.0.as_ref().map(HashMap::keys))
 +    }
 +
 +    /// Returns JSON representation as accepted by the `--file-lines JSON` arg.
 +    pub fn to_json_spans(&self) -> Vec<JsonSpan> {
 +        match &self.0 {
 +            None => vec![],
 +            Some(file_ranges) => file_ranges
 +                .iter()
 +                .flat_map(|(file, ranges)| ranges.iter().map(move |r| (file, r)))
 +                .map(|(file, range)| JsonSpan {
 +                    file: file.to_owned(),
 +                    range: (range.lo, range.hi),
 +                })
 +                .collect(),
 +        }
 +    }
 +
 +    /// Returns `true` if `self` includes all lines in all files. Otherwise runs `f` on all ranges
 +    /// in the designated file (if any) and returns true if `f` ever does.
 +    fn file_range_matches<F>(&self, file_name: &FileName, f: F) -> bool
 +    where
 +        F: FnMut(&Range) -> bool,
 +    {
 +        let map = match self.0 {
 +            // `None` means "all lines in all files".
 +            None => return true,
 +            Some(ref map) => map,
 +        };
 +
 +        match canonicalize_path_string(file_name).and_then(|file| map.get(&file)) {
 +            Some(ranges) => ranges.iter().any(f),
 +            None => false,
 +        }
 +    }
 +
 +    /// Returns `true` if `range` is fully contained in `self`.
 +    #[allow(dead_code)]
 +    pub(crate) fn contains(&self, range: &LineRange) -> bool {
 +        self.file_range_matches(&range.file_name(), |r| r.contains(Range::from(range)))
 +    }
 +
 +    /// Returns `true` if any lines in `range` are in `self`.
 +    pub(crate) fn intersects(&self, range: &LineRange) -> bool {
 +        self.file_range_matches(&range.file_name(), |r| r.intersects(Range::from(range)))
 +    }
 +
 +    /// Returns `true` if `line` from `file_name` is in `self`.
 +    pub(crate) fn contains_line(&self, file_name: &FileName, line: usize) -> bool {
 +        self.file_range_matches(file_name, |r| r.lo <= line && r.hi >= line)
 +    }
 +
 +    /// Returns `true` if all the lines between `lo` and `hi` from `file_name` are in `self`.
 +    pub(crate) fn contains_range(&self, file_name: &FileName, lo: usize, hi: usize) -> bool {
 +        self.file_range_matches(file_name, |r| r.contains(Range::new(lo, hi)))
 +    }
 +}
 +
 +/// `FileLines` files iterator.
 +pub struct Files<'a>(Option<::std::collections::hash_map::Keys<'a, FileName, Vec<Range>>>);
 +
 +impl<'a> iter::Iterator for Files<'a> {
 +    type Item = &'a FileName;
 +
 +    fn next(&mut self) -> Option<&'a FileName> {
 +        self.0.as_mut().and_then(Iterator::next)
 +    }
 +}
 +
 +fn canonicalize_path_string(file: &FileName) -> Option<FileName> {
 +    match *file {
 +        FileName::Real(ref path) => path.canonicalize().ok().map(FileName::Real),
 +        _ => Some(file.clone()),
 +    }
 +}
 +
 +#[derive(Error, Debug)]
 +pub enum FileLinesError {
 +    #[error("{0}")]
 +    Json(json::Error),
 +    #[error("Can't canonicalize {0}")]
 +    CannotCanonicalize(FileName),
 +}
 +
 +// This impl is needed for `Config::override_value` to work for use in tests.
 +impl str::FromStr for FileLines {
 +    type Err = FileLinesError;
 +
 +    fn from_str(s: &str) -> Result<FileLines, Self::Err> {
 +        let v: Vec<JsonSpan> = json::from_str(s).map_err(FileLinesError::Json)?;
 +        let mut m = HashMap::new();
 +        for js in v {
 +            let (s, r) = JsonSpan::into_tuple(js)?;
 +            m.entry(s).or_insert_with(Vec::new).push(r);
 +        }
 +        Ok(FileLines::from_ranges(m))
 +    }
 +}
 +
 +// For JSON decoding.
 +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Deserialize, Serialize)]
 +pub struct JsonSpan {
 +    file: FileName,
 +    range: (usize, usize),
 +}
 +
 +impl JsonSpan {
 +    fn into_tuple(self) -> Result<(FileName, Range), FileLinesError> {
 +        let (lo, hi) = self.range;
 +        let canonical = canonicalize_path_string(&self.file)
 +            .ok_or(FileLinesError::CannotCanonicalize(self.file))?;
 +        Ok((canonical, Range::new(lo, hi)))
 +    }
 +}
 +
 +// This impl is needed for inclusion in the `Config` struct. We don't have a toml representation
 +// for `FileLines`, so it will just panic instead.
 +impl<'de> ::serde::de::Deserialize<'de> for FileLines {
 +    fn deserialize<D>(_: D) -> Result<Self, D::Error>
 +    where
 +        D: ::serde::de::Deserializer<'de>,
 +    {
 +        panic!(
 +            "FileLines cannot be deserialized from a project rustfmt.toml file: please \
 +             specify it via the `--file-lines` option instead"
 +        );
 +    }
 +}
 +
 +// We also want to avoid attempting to serialize a FileLines to toml. The
 +// `Config` struct should ensure this impl is never reached.
 +impl ::serde::ser::Serialize for FileLines {
 +    fn serialize<S>(&self, _: S) -> Result<S::Ok, S::Error>
 +    where
 +        S: ::serde::ser::Serializer,
 +    {
 +        unreachable!("FileLines cannot be serialized. This is a rustfmt bug.");
 +    }
 +}
 +
 +#[cfg(test)]
 +mod test {
 +    use super::Range;
 +
 +    #[test]
 +    fn test_range_intersects() {
 +        assert!(Range::new(1, 2).intersects(Range::new(1, 1)));
 +        assert!(Range::new(1, 2).intersects(Range::new(2, 2)));
 +        assert!(!Range::new(1, 2).intersects(Range::new(0, 0)));
 +        assert!(!Range::new(1, 2).intersects(Range::new(3, 10)));
 +        assert!(!Range::new(1, 3).intersects(Range::new(5, 5)));
 +    }
 +
 +    #[test]
 +    fn test_range_adjacent_to() {
 +        assert!(!Range::new(1, 2).adjacent_to(Range::new(1, 1)));
 +        assert!(!Range::new(1, 2).adjacent_to(Range::new(2, 2)));
 +        assert!(Range::new(1, 2).adjacent_to(Range::new(0, 0)));
 +        assert!(Range::new(1, 2).adjacent_to(Range::new(3, 10)));
 +        assert!(!Range::new(1, 3).adjacent_to(Range::new(5, 5)));
 +    }
 +
 +    #[test]
 +    fn test_range_contains() {
 +        assert!(Range::new(1, 2).contains(Range::new(1, 1)));
 +        assert!(Range::new(1, 2).contains(Range::new(2, 2)));
 +        assert!(!Range::new(1, 2).contains(Range::new(0, 0)));
 +        assert!(!Range::new(1, 2).contains(Range::new(3, 10)));
 +    }
 +
 +    #[test]
 +    fn test_range_merge() {
 +        assert_eq!(None, Range::new(1, 3).merge(Range::new(5, 5)));
 +        assert_eq!(None, Range::new(4, 7).merge(Range::new(0, 1)));
 +        assert_eq!(
 +            Some(Range::new(3, 7)),
 +            Range::new(3, 5).merge(Range::new(4, 7))
 +        );
 +        assert_eq!(
 +            Some(Range::new(3, 7)),
 +            Range::new(3, 5).merge(Range::new(5, 7))
 +        );
 +        assert_eq!(
 +            Some(Range::new(3, 7)),
 +            Range::new(3, 5).merge(Range::new(6, 7))
 +        );
 +        assert_eq!(
 +            Some(Range::new(3, 7)),
 +            Range::new(3, 7).merge(Range::new(4, 5))
 +        );
 +    }
 +
 +    use super::json::{self, json};
 +    use super::{FileLines, FileName};
 +    use std::{collections::HashMap, path::PathBuf};
 +
 +    #[test]
 +    fn file_lines_to_json() {
 +        let ranges: HashMap<FileName, Vec<Range>> = [
 +            (
 +                FileName::Real(PathBuf::from("src/main.rs")),
 +                vec![Range::new(1, 3), Range::new(5, 7)],
 +            ),
 +            (
 +                FileName::Real(PathBuf::from("src/lib.rs")),
 +                vec![Range::new(1, 7)],
 +            ),
 +        ]
 +        .iter()
 +        .cloned()
 +        .collect();
 +
 +        let file_lines = FileLines::from_ranges(ranges);
 +        let mut spans = file_lines.to_json_spans();
 +        spans.sort();
 +        let json = json::to_value(&spans).unwrap();
 +        assert_eq!(
 +            json,
 +            json! {[
 +                {"file": "src/lib.rs",  "range": [1, 7]},
 +                {"file": "src/main.rs", "range": [1, 3]},
 +                {"file": "src/main.rs", "range": [5, 7]},
 +            ]}
 +        );
 +    }
 +}
index cd90e0904b6cd540d82c95548e614a6dc02847b5,0000000000000000000000000000000000000000..18e1854612bf79de163227e7c79d572505bfc206
mode 100644,000000..100644
--- /dev/null
@@@ -1,954 -1,0 +1,959 @@@
-                     return Err(e);
 +use std::cell::Cell;
 +use std::default::Default;
 +use std::fs::File;
 +use std::io::{Error, ErrorKind, Read};
 +use std::path::{Path, PathBuf};
 +use std::{env, fs};
 +
 +use regex::Regex;
 +use thiserror::Error;
 +
 +use crate::config::config_type::ConfigType;
 +#[allow(unreachable_pub)]
 +pub use crate::config::file_lines::{FileLines, FileName, Range};
 +#[allow(unreachable_pub)]
 +pub use crate::config::lists::*;
 +#[allow(unreachable_pub)]
 +pub use crate::config::options::*;
 +
 +#[macro_use]
 +pub(crate) mod config_type;
 +#[macro_use]
 +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
 +// is defined as follows:
 +//
 +// `name: value type, default value, is stable, description;`
 +create_config! {
 +    // Fundamental stuff
 +    max_width: usize, 100, true, "Maximum width of each line";
 +    hard_tabs: bool, false, true, "Use tab characters for indentation, spaces for alignment";
 +    tab_spaces: usize, 4, true, "Number of spaces per tab";
 +    newline_style: NewlineStyle, NewlineStyle::Auto, true, "Unix or Windows line endings";
 +    indent_style: IndentStyle, IndentStyle::Block, false, "How do we indent expressions or items";
 +
 +    // Width Heuristics
 +    use_small_heuristics: Heuristics, Heuristics::Default, true, "Whether to use different \
 +        formatting for items and expressions if they satisfy a heuristic notion of 'small'";
 +    width_heuristics: WidthHeuristics, WidthHeuristics::scaled(100), false,
 +        "'small' heuristic values";
 +    fn_call_width: usize, 60, true, "Maximum width of the args of a function call before \
 +        falling back to vertical formatting.";
 +    attr_fn_like_width: usize, 70, true, "Maximum width of the args of a function-like \
 +        attributes before falling back to vertical formatting.";
 +    struct_lit_width: usize, 18, true, "Maximum width in the body of a struct lit before \
 +        falling back to vertical formatting.";
 +    struct_variant_width: usize, 35, true, "Maximum width in the body of a struct variant before \
 +        falling back to vertical formatting.";
 +    array_width: usize, 60, true,  "Maximum width of an array literal before falling \
 +        back to vertical formatting.";
 +    chain_width: usize, 60, true, "Maximum length of a chain to fit on a single line.";
 +    single_line_if_else_max_width: usize, 50, true, "Maximum line length for single line if-else \
 +        expressions. A value of zero means always break if-else expressions.";
 +
 +    // Comments. macros, and strings
 +    wrap_comments: bool, false, false, "Break comments to fit on the line";
 +    format_code_in_doc_comments: bool, false, false, "Format the code snippet in doc comments.";
 +    comment_width: usize, 80, false,
 +        "Maximum length of comments. No effect unless wrap_comments = true";
 +    normalize_comments: bool, false, false, "Convert /* */ comments to // comments where possible";
 +    normalize_doc_attributes: bool, false, false, "Normalize doc attributes as doc comments";
 +    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";
 +    format_macro_bodies: bool, true, false, "Format the bodies of macros";
 +    hex_literal_case: HexLiteralCase, HexLiteralCase::Preserve, false,
 +        "Format hexadecimal integer literals";
 +
 +    // Single line expressions and items
 +    empty_item_single_line: bool, true, false,
 +        "Put empty-body functions and impls on a single line";
 +    struct_lit_single_line: bool, true, false,
 +        "Put small struct literals on a single line";
 +    fn_single_line: bool, false, false, "Put single-expression functions on a single line";
 +    where_single_line: bool, false, false, "Force where-clauses to be on a single line";
 +
 +    // Imports
 +    imports_indent: IndentStyle, IndentStyle::Block, false, "Indent of imports";
 +    imports_layout: ListTactic, ListTactic::Mixed, false, "Item layout inside a import block";
 +    imports_granularity: ImportGranularity, ImportGranularity::Preserve, false,
 +        "Merge or split imports to the provided granularity";
 +    group_imports: GroupImportsTactic, GroupImportsTactic::Preserve, false,
 +        "Controls the strategy for how imports are grouped together";
 +    merge_imports: bool, false, false, "(deprecated: use imports_granularity instead)";
 +
 +    // Ordering
 +    reorder_imports: bool, true, true, "Reorder import and extern crate statements alphabetically";
 +    reorder_modules: bool, true, true, "Reorder module statements alphabetically in group";
 +    reorder_impl_items: bool, false, false, "Reorder impl items";
 +
 +    // Spaces around punctuation
 +    type_punctuation_density: TypeDensity, TypeDensity::Wide, false,
 +        "Determines if '+' or '=' are wrapped in spaces in the punctuation of types";
 +    space_before_colon: bool, false, false, "Leave a space before the colon";
 +    space_after_colon: bool, true, false, "Leave a space after the colon";
 +    spaces_around_ranges: bool, false, false, "Put spaces around the  .. and ..= range operators";
 +    binop_separator: SeparatorPlace, SeparatorPlace::Front, false,
 +        "Where to put a binary operator when a binary expression goes multiline";
 +
 +    // Misc.
 +    remove_nested_parens: bool, true, true, "Remove nested parens";
 +    combine_control_expr: bool, true, false, "Combine control expressions with function calls";
++    short_array_element_width_threshold: usize, 10, true,
++        "Width threshold for an array element to be considered short";
 +    overflow_delimited_expr: bool, false, false,
 +        "Allow trailing bracket/brace delimited expressions to overflow";
 +    struct_field_align_threshold: usize, 0, false,
 +        "Align struct fields if their diffs fits within threshold";
 +    enum_discrim_align_threshold: usize, 0, false,
 +        "Align enum variants discrims, if their diffs fit within threshold";
 +    match_arm_blocks: bool, true, false, "Wrap the body of arms in blocks when it does not fit on \
 +        the same line with the pattern of arms";
 +    match_arm_leading_pipes: MatchArmLeadingPipe, MatchArmLeadingPipe::Never, true,
 +        "Determines whether leading pipes are emitted on match arms";
 +    force_multiline_blocks: bool, false, false,
 +        "Force multiline closure bodies and match arms to be wrapped in a block";
 +    fn_args_layout: Density, Density::Tall, true,
 +        "Control the layout of arguments in a function";
 +    brace_style: BraceStyle, BraceStyle::SameLineWhere, false, "Brace style for items";
 +    control_brace_style: ControlBraceStyle, ControlBraceStyle::AlwaysSameLine, false,
 +        "Brace style for control flow constructs";
 +    trailing_semicolon: bool, true, false,
 +        "Add trailing semicolon after break, continue and return";
 +    trailing_comma: SeparatorTactic, SeparatorTactic::Vertical, false,
 +        "How to handle trailing commas for lists";
 +    match_block_trailing_comma: bool, false, true,
 +        "Put a trailing comma after a block based match arm (non-block arms are not affected)";
 +    blank_lines_upper_bound: usize, 1, false,
 +        "Maximum number of blank lines which can be put between items";
 +    blank_lines_lower_bound: usize, 0, false,
 +        "Minimum number of blank lines which must be put between items";
 +    edition: Edition, Edition::Edition2015, true, "The edition of the parser (RFC 2052)";
 +    version: Version, Version::One, false, "Version of formatting rules";
 +    inline_attribute_width: usize, 0, false,
 +        "Write an item and its attribute on the same line \
 +        if their combined width is below a threshold";
 +    format_generated_files: bool, true, false, "Format generated files";
 +
 +    // Options that can change the source code beyond whitespace/blocks (somewhat linty things)
 +    merge_derives: bool, true, true, "Merge multiple `#[derive(...)]` into a single one";
 +    use_try_shorthand: bool, false, true, "Replace uses of the try! macro by the ? shorthand";
 +    use_field_init_shorthand: bool, false, true, "Use field initialization shorthand if possible";
 +    force_explicit_abi: bool, true, true, "Always print the abi for extern items";
 +    condense_wildcard_suffixes: bool, false, false, "Replace strings of _ wildcards by a single .. \
 +                                                     in tuple patterns";
 +
 +    // Control options (changes the operation of rustfmt, rather than the formatting)
 +    color: Color, Color::Auto, false,
 +        "What Color option to use when none is supplied: Always, Never, Auto";
 +    required_version: String, env!("CARGO_PKG_VERSION").to_owned(), false,
 +        "Require a specific version of rustfmt";
 +    unstable_features: bool, false, false,
 +            "Enables unstable features. Only available on nightly channel";
 +    disable_all_formatting: bool, false, true, "Don't reformat anything";
 +    skip_children: bool, false, false, "Don't reformat out of line modules";
 +    hide_parse_errors: bool, false, false, "Hide errors from the parser";
 +    error_on_line_overflow: bool, false, false, "Error if unable to get all lines within max_width";
 +    error_on_unformatted: bool, false, false,
 +        "Error if unable to get comments or string literals within max_width, \
 +         or they are left with trailing whitespaces";
 +    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";
 +
 +    // Not user-facing
 +    verbose: Verbosity, Verbosity::Normal, false, "How much to information to emit to the user";
 +    file_lines: FileLines, FileLines::all(), false,
 +        "Lines to format; this is not supported in rustfmt.toml, and can only be specified \
 +         via the --file-lines option";
 +    emit_mode: EmitMode, EmitMode::Files, false,
 +        "What emit Mode to use when none is supplied";
 +    make_backup: bool, false, false, "Backup changed files";
 +    print_misformatted_file_names: bool, false, true,
 +        "Prints the names of mismatched files that were formatted. Prints the names of \
 +         files that would be formated when used with `--check` mode. ";
 +}
 +
 +#[derive(Error, Debug)]
 +#[error("Could not output config: {0}")]
 +pub struct ToTomlError(toml::ser::Error);
 +
 +impl PartialConfig {
 +    pub fn to_toml(&self) -> Result<String, ToTomlError> {
 +        // Non-user-facing options can't be specified in TOML
 +        let mut cloned = self.clone();
 +        cloned.file_lines = None;
 +        cloned.verbose = None;
 +        cloned.width_heuristics = None;
 +        cloned.print_misformatted_file_names = None;
 +        cloned.merge_imports = None;
 +
 +        ::toml::to_string(&cloned).map_err(ToTomlError)
 +    }
 +}
 +
 +impl Config {
 +    pub(crate) fn version_meets_requirement(&self) -> bool {
 +        if self.was_set().required_version() {
 +            let version = env!("CARGO_PKG_VERSION");
 +            let required_version = self.required_version();
 +            if version != required_version {
 +                println!(
 +                    "Error: rustfmt version ({}) doesn't match the required version ({})",
 +                    version, required_version,
 +                );
 +                return false;
 +            }
 +        }
 +
 +        true
 +    }
 +
 +    /// Constructs a `Config` from the toml file specified at `file_path`.
 +    ///
 +    /// This method only looks at the provided path, for a method that
 +    /// searches parents for a `rustfmt.toml` see `from_resolved_toml_path`.
 +    ///
 +    /// Returns a `Config` if the config could be read and parsed from
 +    /// the file, otherwise errors.
 +    pub(super) fn from_toml_path(file_path: &Path) -> Result<Config, Error> {
 +        let mut file = File::open(&file_path)?;
 +        let mut toml = String::new();
 +        file.read_to_string(&mut toml)?;
 +        Config::from_toml(&toml, file_path.parent().unwrap())
 +            .map_err(|err| Error::new(ErrorKind::InvalidData, err))
 +    }
 +
 +    /// Resolves the config for input in `dir`.
 +    ///
 +    /// Searches for `rustfmt.toml` beginning with `dir`, and
 +    /// recursively checking parents of `dir` if no config file is found.
 +    /// If no config file exists in `dir` or in any parent, a
 +    /// default `Config` will be returned (and the returned path will be empty).
 +    ///
 +    /// Returns the `Config` to use, and the path of the project file if there was
 +    /// one.
 +    pub(super) fn from_resolved_toml_path(dir: &Path) -> Result<(Config, Option<PathBuf>), Error> {
 +        /// Try to find a project file in the given directory and its parents.
 +        /// Returns the path of a the nearest project file if one exists,
 +        /// or `None` if no project file was found.
 +        fn resolve_project_file(dir: &Path) -> Result<Option<PathBuf>, Error> {
 +            let mut current = if dir.is_relative() {
 +                env::current_dir()?.join(dir)
 +            } else {
 +                dir.to_path_buf()
 +            };
 +
 +            current = fs::canonicalize(current)?;
 +
 +            loop {
 +                match get_toml_path(&current) {
 +                    Ok(Some(path)) => return Ok(Some(path)),
 +                    Err(e) => return Err(e),
 +                    _ => (),
 +                }
 +
 +                // If the current directory has no parent, we're done searching.
 +                if !current.pop() {
 +                    break;
 +                }
 +            }
 +
 +            // If nothing was found, check in the home directory.
 +            if let Some(home_dir) = dirs::home_dir() {
 +                if let Some(path) = get_toml_path(&home_dir)? {
 +                    return Ok(Some(path));
 +                }
 +            }
 +
 +            // If none was found ther either, check in the user's configuration directory.
 +            if let Some(mut config_dir) = dirs::config_dir() {
 +                config_dir.push("rustfmt");
 +                if let Some(path) = get_toml_path(&config_dir)? {
 +                    return Ok(Some(path));
 +                }
 +            }
 +
 +            Ok(None)
 +        }
 +
 +        match resolve_project_file(dir)? {
 +            None => Ok((Config::default(), None)),
 +            Some(path) => Config::from_toml_path(&path).map(|config| (config, Some(path))),
 +        }
 +    }
 +
 +    pub(crate) fn from_toml(toml: &str, dir: &Path) -> Result<Config, String> {
 +        let parsed: ::toml::Value = toml
 +            .parse()
 +            .map_err(|e| format!("Could not parse TOML: {}", e))?;
 +        let mut err = String::new();
 +        let table = parsed
 +            .as_table()
 +            .ok_or_else(|| String::from("Parsed config was not table"))?;
 +        for key in table.keys() {
 +            if !Config::is_valid_name(key) {
 +                let msg = &format!("Warning: Unknown configuration option `{}`\n", key);
 +                err.push_str(msg)
 +            }
 +        }
 +        match parsed.try_into() {
 +            Ok(parsed_config) => {
 +                if !err.is_empty() {
 +                    eprint!("{}", err);
 +                }
 +                Ok(Config::default().fill_from_parsed_config(parsed_config, dir))
 +            }
 +            Err(e) => {
 +                err.push_str("Error: Decoding config file failed:\n");
 +                err.push_str(format!("{}\n", e).as_str());
 +                err.push_str("Please check your config file.");
 +                Err(err)
 +            }
 +        }
 +    }
 +}
 +
 +/// Loads a config by checking the client-supplied options and if appropriate, the
 +/// file system (including searching the file system for overrides).
 +pub fn load_config<O: CliOptions>(
 +    file_path: Option<&Path>,
 +    options: Option<O>,
 +) -> Result<(Config, Option<PathBuf>), Error> {
 +    let over_ride = match options {
 +        Some(ref opts) => config_path(opts)?,
 +        None => None,
 +    };
 +
 +    let result = if let Some(over_ride) = over_ride {
 +        Config::from_toml_path(over_ride.as_ref()).map(|p| (p, Some(over_ride.to_owned())))
 +    } else if let Some(file_path) = file_path {
 +        Config::from_resolved_toml_path(file_path)
 +    } else {
 +        Ok((Config::default(), None))
 +    };
 +
 +    result.map(|(mut c, p)| {
 +        if let Some(options) = options {
 +            options.apply_to(&mut c);
 +        }
 +        (c, p)
 +    })
 +}
 +
 +// Check for the presence of known config file names (`rustfmt.toml, `.rustfmt.toml`) in `dir`
 +//
 +// Return the path if a config file exists, empty if no file exists, and Error for IO errors
 +fn get_toml_path(dir: &Path) -> Result<Option<PathBuf>, Error> {
 +    const CONFIG_FILE_NAMES: [&str; 2] = [".rustfmt.toml", "rustfmt.toml"];
 +    for config_file_name in &CONFIG_FILE_NAMES {
 +        let config_file = dir.join(config_file_name);
 +        match fs::metadata(&config_file) {
 +            // Only return if it's a file to handle the unlikely situation of a directory named
 +            // `rustfmt.toml`.
 +            Ok(ref md) if md.is_file() => return Ok(Some(config_file)),
 +            // Return the error if it's something other than `NotFound`; otherwise we didn't
 +            // find the project file yet, and continue searching.
 +            Err(e) => {
 +                if e.kind() != ErrorKind::NotFound {
++                    let ctx = format!("Failed to get metadata for config file {:?}", &config_file);
++                    let err = anyhow::Error::new(e).context(ctx);
++                    return Err(Error::new(ErrorKind::Other, err));
 +                }
 +            }
 +            _ => {}
 +        }
 +    }
 +    Ok(None)
 +}
 +
 +fn config_path(options: &dyn CliOptions) -> Result<Option<PathBuf>, Error> {
 +    let config_path_not_found = |path: &str| -> Result<Option<PathBuf>, Error> {
 +        Err(Error::new(
 +            ErrorKind::NotFound,
 +            format!(
 +                "Error: unable to find a config file for the given path: `{}`",
 +                path
 +            ),
 +        ))
 +    };
 +
 +    // Read the config_path and convert to parent dir if a file is provided.
 +    // If a config file cannot be found from the given path, return error.
 +    match options.config_path() {
 +        Some(path) if !path.exists() => config_path_not_found(path.to_str().unwrap()),
 +        Some(path) if path.is_dir() => {
 +            let config_file_path = get_toml_path(path)?;
 +            if config_file_path.is_some() {
 +                Ok(config_file_path)
 +            } else {
 +                config_path_not_found(path.to_str().unwrap())
 +            }
 +        }
 +        path => Ok(path.map(ToOwned::to_owned)),
 +    }
 +}
 +
 +#[cfg(test)]
 +mod test {
 +    use super::*;
 +    use std::str;
 +
 +    use rustfmt_config_proc_macro::{nightly_only_test, stable_only_test};
 +
 +    #[allow(dead_code)]
 +    mod mock {
 +        use super::super::*;
 +
 +        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,
 +                "Skip formatting the specified files and directories.";
 +            verbose: Verbosity, Verbosity::Normal, false,
 +                "How much to information to emit to the user";
 +            file_lines: FileLines, FileLines::all(), false,
 +                "Lines to format; this is not supported in rustfmt.toml, and can only be specified \
 +                    via the --file-lines option";
 +
 +            // merge_imports deprecation
 +            imports_granularity: ImportGranularity, ImportGranularity::Preserve, false,
 +                "Merge imports";
 +            merge_imports: bool, false, false, "(deprecated: use imports_granularity instead)";
 +
 +            // Width Heuristics
 +            use_small_heuristics: Heuristics, Heuristics::Default, true,
 +                "Whether to use different formatting for items and \
 +                 expressions if they satisfy a heuristic notion of 'small'.";
 +            width_heuristics: WidthHeuristics, WidthHeuristics::scaled(100), false,
 +                "'small' heuristic values";
 +
 +            fn_call_width: usize, 60, true, "Maximum width of the args of a function call before \
 +                falling back to vertical formatting.";
 +            attr_fn_like_width: usize, 70, true, "Maximum width of the args of a function-like \
 +                attributes before falling back to vertical formatting.";
 +            struct_lit_width: usize, 18, true, "Maximum width in the body of a struct lit before \
 +                falling back to vertical formatting.";
 +            struct_variant_width: usize, 35, true, "Maximum width in the body of a struct \
 +                variant before falling back to vertical formatting.";
 +            array_width: usize, 60, true,  "Maximum width of an array literal before falling \
 +                back to vertical formatting.";
 +            chain_width: usize, 60, true, "Maximum length of a chain to fit on a single line.";
 +            single_line_if_else_max_width: usize, 50, true, "Maximum line length for single \
 +                line if-else expressions. A value of zero means always break if-else expressions.";
 +
 +            // Options that are used by the tests
 +            stable_option: bool, false, true, "A stable option";
 +            unstable_option: bool, false, false, "An unstable option";
 +        }
 +    }
 +
 +    #[test]
 +    fn test_config_set() {
 +        let mut config = Config::default();
 +        config.set().verbose(Verbosity::Quiet);
 +        assert_eq!(config.verbose(), Verbosity::Quiet);
 +        config.set().verbose(Verbosity::Normal);
 +        assert_eq!(config.verbose(), Verbosity::Normal);
 +    }
 +
 +    #[test]
 +    fn test_config_used_to_toml() {
 +        let config = Config::default();
 +
 +        let merge_derives = config.merge_derives();
 +        let skip_children = config.skip_children();
 +
 +        let used_options = config.used_options();
 +        let toml = used_options.to_toml().unwrap();
 +        assert_eq!(
 +            toml,
 +            format!(
 +                "merge_derives = {}\nskip_children = {}\n",
 +                merge_derives, skip_children,
 +            )
 +        );
 +    }
 +
 +    #[test]
 +    fn test_was_set() {
 +        let config = Config::from_toml("hard_tabs = true", Path::new("")).unwrap();
 +
 +        assert_eq!(config.was_set().hard_tabs(), true);
 +        assert_eq!(config.was_set().verbose(), false);
 +    }
 +
 +    #[test]
 +    fn test_print_docs_exclude_unstable() {
 +        use self::mock::Config;
 +
 +        let mut output = Vec::new();
 +        Config::print_docs(&mut output, false);
 +
 +        let s = str::from_utf8(&output).unwrap();
 +
 +        assert_eq!(s.contains("stable_option"), true);
 +        assert_eq!(s.contains("unstable_option"), false);
 +        assert_eq!(s.contains("(unstable)"), false);
 +    }
 +
 +    #[test]
 +    fn test_print_docs_include_unstable() {
 +        use self::mock::Config;
 +
 +        let mut output = Vec::new();
 +        Config::print_docs(&mut output, true);
 +
 +        let s = str::from_utf8(&output).unwrap();
 +        assert_eq!(s.contains("stable_option"), true);
 +        assert_eq!(s.contains("unstable_option"), true);
 +        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!(
 +            r#"max_width = 100
 +hard_tabs = false
 +tab_spaces = 4
 +newline_style = "Auto"
 +indent_style = "Block"
 +use_small_heuristics = "Default"
 +fn_call_width = 60
 +attr_fn_like_width = 70
 +struct_lit_width = 18
 +struct_variant_width = 35
 +array_width = 60
 +chain_width = 60
 +single_line_if_else_max_width = 50
 +wrap_comments = false
 +format_code_in_doc_comments = false
 +comment_width = 80
 +normalize_comments = false
 +normalize_doc_attributes = false
 +license_template_path = ""
 +format_strings = false
 +format_macro_matchers = false
 +format_macro_bodies = true
 +hex_literal_case = "Preserve"
 +empty_item_single_line = true
 +struct_lit_single_line = true
 +fn_single_line = false
 +where_single_line = false
 +imports_indent = "Block"
 +imports_layout = "Mixed"
 +imports_granularity = "Preserve"
 +group_imports = "Preserve"
 +reorder_imports = true
 +reorder_modules = true
 +reorder_impl_items = false
 +type_punctuation_density = "Wide"
 +space_before_colon = false
 +space_after_colon = true
 +spaces_around_ranges = false
 +binop_separator = "Front"
 +remove_nested_parens = true
 +combine_control_expr = true
++short_array_element_width_threshold = 10
 +overflow_delimited_expr = false
 +struct_field_align_threshold = 0
 +enum_discrim_align_threshold = 0
 +match_arm_blocks = true
 +match_arm_leading_pipes = "Never"
 +force_multiline_blocks = false
 +fn_args_layout = "Tall"
 +brace_style = "SameLineWhere"
 +control_brace_style = "AlwaysSameLine"
 +trailing_semicolon = true
 +trailing_comma = "Vertical"
 +match_block_trailing_comma = false
 +blank_lines_upper_bound = 1
 +blank_lines_lower_bound = 0
 +edition = "2015"
 +version = "One"
 +inline_attribute_width = 0
 +format_generated_files = true
 +merge_derives = true
 +use_try_shorthand = false
 +use_field_init_shorthand = false
 +force_explicit_abi = true
 +condense_wildcard_suffixes = false
 +color = "Auto"
 +required_version = "{}"
 +unstable_features = false
 +disable_all_formatting = false
 +skip_children = false
 +hide_parse_errors = false
 +error_on_line_overflow = false
 +error_on_unformatted = false
 +report_todo = "Never"
 +report_fixme = "Never"
 +ignore = []
 +emit_mode = "Files"
 +make_backup = false
 +"#,
 +            env!("CARGO_PKG_VERSION")
 +        );
 +        let toml = Config::default().all_options().to_toml().unwrap();
 +        assert_eq!(&toml, &default_config);
 +    }
 +
 +    #[stable_only_test]
 +    #[test]
 +    fn test_as_not_nightly_channel() {
 +        let mut config = Config::default();
 +        assert_eq!(config.was_set().unstable_features(), false);
 +        config.set().unstable_features(true);
 +        assert_eq!(config.was_set().unstable_features(), false);
 +    }
 +
 +    #[nightly_only_test]
 +    #[test]
 +    fn test_as_nightly_channel() {
 +        let mut config = Config::default();
 +        config.set().unstable_features(true);
 +        // When we don't set the config from toml or command line options it
 +        // doesn't get marked as set by the user.
 +        assert_eq!(config.was_set().unstable_features(), false);
 +        config.set().unstable_features(true);
 +        assert_eq!(config.unstable_features(), true);
 +    }
 +
 +    #[nightly_only_test]
 +    #[test]
 +    fn test_unstable_from_toml() {
 +        let config = Config::from_toml("unstable_features = true", Path::new("")).unwrap();
 +        assert_eq!(config.was_set().unstable_features(), true);
 +        assert_eq!(config.unstable_features(), true);
 +    }
 +
 +    #[cfg(test)]
 +    mod deprecated_option_merge_imports {
 +        use super::*;
 +
 +        #[nightly_only_test]
 +        #[test]
 +        fn test_old_option_set() {
 +            let toml = r#"
 +                unstable_features = true
 +                merge_imports = true
 +            "#;
 +            let config = Config::from_toml(toml, Path::new("")).unwrap();
 +            assert_eq!(config.imports_granularity(), ImportGranularity::Crate);
 +        }
 +
 +        #[nightly_only_test]
 +        #[test]
 +        fn test_both_set() {
 +            let toml = r#"
 +                unstable_features = true
 +                merge_imports = true
 +                imports_granularity = "Preserve"
 +            "#;
 +            let config = Config::from_toml(toml, Path::new("")).unwrap();
 +            assert_eq!(config.imports_granularity(), ImportGranularity::Preserve);
 +        }
 +
 +        #[nightly_only_test]
 +        #[test]
 +        fn test_new_overridden() {
 +            let toml = r#"
 +                unstable_features = true
 +                merge_imports = true
 +            "#;
 +            let mut config = Config::from_toml(toml, Path::new("")).unwrap();
 +            config.override_value("imports_granularity", "Preserve");
 +            assert_eq!(config.imports_granularity(), ImportGranularity::Preserve);
 +        }
 +
 +        #[nightly_only_test]
 +        #[test]
 +        fn test_old_overridden() {
 +            let toml = r#"
 +                unstable_features = true
 +                imports_granularity = "Module"
 +            "#;
 +            let mut config = Config::from_toml(toml, Path::new("")).unwrap();
 +            config.override_value("merge_imports", "true");
 +            // no effect: the new option always takes precedence
 +            assert_eq!(config.imports_granularity(), ImportGranularity::Module);
 +        }
 +    }
 +
 +    #[cfg(test)]
 +    mod use_small_heuristics {
 +        use super::*;
 +
 +        #[test]
 +        fn test_default_sets_correct_widths() {
 +            let toml = r#"
 +                use_small_heuristics = "Default"
 +                max_width = 200
 +            "#;
 +            let config = Config::from_toml(toml, Path::new("")).unwrap();
 +            assert_eq!(config.array_width(), 120);
 +            assert_eq!(config.attr_fn_like_width(), 140);
 +            assert_eq!(config.chain_width(), 120);
 +            assert_eq!(config.fn_call_width(), 120);
 +            assert_eq!(config.single_line_if_else_max_width(), 100);
 +            assert_eq!(config.struct_lit_width(), 36);
 +            assert_eq!(config.struct_variant_width(), 70);
 +        }
 +
 +        #[test]
 +        fn test_max_sets_correct_widths() {
 +            let toml = r#"
 +                use_small_heuristics = "Max"
 +                max_width = 120
 +            "#;
 +            let config = Config::from_toml(toml, Path::new("")).unwrap();
 +            assert_eq!(config.array_width(), 120);
 +            assert_eq!(config.attr_fn_like_width(), 120);
 +            assert_eq!(config.chain_width(), 120);
 +            assert_eq!(config.fn_call_width(), 120);
 +            assert_eq!(config.single_line_if_else_max_width(), 120);
 +            assert_eq!(config.struct_lit_width(), 120);
 +            assert_eq!(config.struct_variant_width(), 120);
 +        }
 +
 +        #[test]
 +        fn test_off_sets_correct_widths() {
 +            let toml = r#"
 +                use_small_heuristics = "Off"
 +                max_width = 100
 +            "#;
 +            let config = Config::from_toml(toml, Path::new("")).unwrap();
 +            assert_eq!(config.array_width(), usize::max_value());
 +            assert_eq!(config.attr_fn_like_width(), usize::max_value());
 +            assert_eq!(config.chain_width(), usize::max_value());
 +            assert_eq!(config.fn_call_width(), usize::max_value());
 +            assert_eq!(config.single_line_if_else_max_width(), 0);
 +            assert_eq!(config.struct_lit_width(), 0);
 +            assert_eq!(config.struct_variant_width(), 0);
 +        }
 +
 +        #[test]
 +        fn test_override_works_with_default() {
 +            let toml = r#"
 +                use_small_heuristics = "Default"
 +                array_width = 20
 +                attr_fn_like_width = 40
 +                chain_width = 20
 +                fn_call_width = 90
 +                single_line_if_else_max_width = 40
 +                struct_lit_width = 30
 +                struct_variant_width = 34
 +            "#;
 +            let config = Config::from_toml(toml, Path::new("")).unwrap();
 +            assert_eq!(config.array_width(), 20);
 +            assert_eq!(config.attr_fn_like_width(), 40);
 +            assert_eq!(config.chain_width(), 20);
 +            assert_eq!(config.fn_call_width(), 90);
 +            assert_eq!(config.single_line_if_else_max_width(), 40);
 +            assert_eq!(config.struct_lit_width(), 30);
 +            assert_eq!(config.struct_variant_width(), 34);
 +        }
 +
 +        #[test]
 +        fn test_override_with_max() {
 +            let toml = r#"
 +                use_small_heuristics = "Max"
 +                array_width = 20
 +                attr_fn_like_width = 40
 +                chain_width = 20
 +                fn_call_width = 90
 +                single_line_if_else_max_width = 40
 +                struct_lit_width = 30
 +                struct_variant_width = 34
 +            "#;
 +            let config = Config::from_toml(toml, Path::new("")).unwrap();
 +            assert_eq!(config.array_width(), 20);
 +            assert_eq!(config.attr_fn_like_width(), 40);
 +            assert_eq!(config.chain_width(), 20);
 +            assert_eq!(config.fn_call_width(), 90);
 +            assert_eq!(config.single_line_if_else_max_width(), 40);
 +            assert_eq!(config.struct_lit_width(), 30);
 +            assert_eq!(config.struct_variant_width(), 34);
 +        }
 +
 +        #[test]
 +        fn test_override_with_off() {
 +            let toml = r#"
 +                use_small_heuristics = "Off"
 +                array_width = 20
 +                attr_fn_like_width = 40
 +                chain_width = 20
 +                fn_call_width = 90
 +                single_line_if_else_max_width = 40
 +                struct_lit_width = 30
 +                struct_variant_width = 34
 +            "#;
 +            let config = Config::from_toml(toml, Path::new("")).unwrap();
 +            assert_eq!(config.array_width(), 20);
 +            assert_eq!(config.attr_fn_like_width(), 40);
 +            assert_eq!(config.chain_width(), 20);
 +            assert_eq!(config.fn_call_width(), 90);
 +            assert_eq!(config.single_line_if_else_max_width(), 40);
 +            assert_eq!(config.struct_lit_width(), 30);
 +            assert_eq!(config.struct_variant_width(), 34);
 +        }
 +
 +        #[test]
 +        fn test_fn_call_width_config_exceeds_max_width() {
 +            let toml = r#"
 +                max_width = 90
 +                fn_call_width = 95
 +            "#;
 +            let config = Config::from_toml(toml, Path::new("")).unwrap();
 +            assert_eq!(config.fn_call_width(), 90);
 +        }
 +
 +        #[test]
 +        fn test_attr_fn_like_width_config_exceeds_max_width() {
 +            let toml = r#"
 +                max_width = 80
 +                attr_fn_like_width = 90
 +            "#;
 +            let config = Config::from_toml(toml, Path::new("")).unwrap();
 +            assert_eq!(config.attr_fn_like_width(), 80);
 +        }
 +
 +        #[test]
 +        fn test_struct_lit_config_exceeds_max_width() {
 +            let toml = r#"
 +                max_width = 78
 +                struct_lit_width = 90
 +            "#;
 +            let config = Config::from_toml(toml, Path::new("")).unwrap();
 +            assert_eq!(config.struct_lit_width(), 78);
 +        }
 +
 +        #[test]
 +        fn test_struct_variant_width_config_exceeds_max_width() {
 +            let toml = r#"
 +                max_width = 80
 +                struct_variant_width = 90
 +            "#;
 +            let config = Config::from_toml(toml, Path::new("")).unwrap();
 +            assert_eq!(config.struct_variant_width(), 80);
 +        }
 +
 +        #[test]
 +        fn test_array_width_config_exceeds_max_width() {
 +            let toml = r#"
 +                max_width = 60
 +                array_width = 80
 +            "#;
 +            let config = Config::from_toml(toml, Path::new("")).unwrap();
 +            assert_eq!(config.array_width(), 60);
 +        }
 +
 +        #[test]
 +        fn test_chain_width_config_exceeds_max_width() {
 +            let toml = r#"
 +                max_width = 80
 +                chain_width = 90
 +            "#;
 +            let config = Config::from_toml(toml, Path::new("")).unwrap();
 +            assert_eq!(config.chain_width(), 80);
 +        }
 +
 +        #[test]
 +        fn test_single_line_if_else_max_width_config_exceeds_max_width() {
 +            let toml = r#"
 +                max_width = 70
 +                single_line_if_else_max_width = 90
 +            "#;
 +            let config = Config::from_toml(toml, Path::new("")).unwrap();
 +            assert_eq!(config.single_line_if_else_max_width(), 70);
 +        }
 +
 +        #[test]
 +        fn test_override_fn_call_width_exceeds_max_width() {
 +            let mut config = Config::default();
 +            config.override_value("fn_call_width", "101");
 +            assert_eq!(config.fn_call_width(), 100);
 +        }
 +
 +        #[test]
 +        fn test_override_attr_fn_like_width_exceeds_max_width() {
 +            let mut config = Config::default();
 +            config.override_value("attr_fn_like_width", "101");
 +            assert_eq!(config.attr_fn_like_width(), 100);
 +        }
 +
 +        #[test]
 +        fn test_override_struct_lit_exceeds_max_width() {
 +            let mut config = Config::default();
 +            config.override_value("struct_lit_width", "101");
 +            assert_eq!(config.struct_lit_width(), 100);
 +        }
 +
 +        #[test]
 +        fn test_override_struct_variant_width_exceeds_max_width() {
 +            let mut config = Config::default();
 +            config.override_value("struct_variant_width", "101");
 +            assert_eq!(config.struct_variant_width(), 100);
 +        }
 +
 +        #[test]
 +        fn test_override_array_width_exceeds_max_width() {
 +            let mut config = Config::default();
 +            config.override_value("array_width", "101");
 +            assert_eq!(config.array_width(), 100);
 +        }
 +
 +        #[test]
 +        fn test_override_chain_width_exceeds_max_width() {
 +            let mut config = Config::default();
 +            config.override_value("chain_width", "101");
 +            assert_eq!(config.chain_width(), 100);
 +        }
 +
 +        #[test]
 +        fn test_override_single_line_if_else_max_width_exceeds_max_width() {
 +            let mut config = Config::default();
 +            config.override_value("single_line_if_else_max_width", "101");
 +            assert_eq!(config.single_line_if_else_max_width(), 100);
 +        }
 +    }
 +}
index 76f2527db3dad1d69c78d8b54c7eeea4244b4469,0000000000000000000000000000000000000000..545b259979d911d04b8200a3459fbe2b5cfce07c
mode 100644,000000..100644
--- /dev/null
@@@ -1,148 -1,0 +1,150 @@@
- use std::path::Path;
 +use self::xml::XmlEscaped;
 +use super::*;
 +use crate::rustfmt_diff::{make_diff, DiffLine, Mismatch};
 +use std::io::{self, Write};
-         let filename = ensure_real_path(filename);
 +
 +mod xml;
 +
 +#[derive(Debug, Default)]
 +pub(crate) struct CheckstyleEmitter;
 +
 +impl Emitter for CheckstyleEmitter {
 +    fn emit_header(&self, output: &mut dyn Write) -> Result<(), io::Error> {
 +        writeln!(output, r#"<?xml version="1.0" encoding="utf-8"?>"#)?;
 +        write!(output, r#"<checkstyle version="4.3">"#)?;
 +        Ok(())
 +    }
 +
 +    fn emit_footer(&self, output: &mut dyn Write) -> Result<(), io::Error> {
 +        writeln!(output, "</checkstyle>")
 +    }
 +
 +    fn emit_formatted_file(
 +        &mut self,
 +        output: &mut dyn Write,
 +        FormattedFile {
 +            filename,
 +            original_text,
 +            formatted_text,
 +        }: FormattedFile<'_>,
 +    ) -> Result<EmitterResult, io::Error> {
 +        const CONTEXT_SIZE: usize = 0;
-     filename: &Path,
 +        let diff = make_diff(original_text, formatted_text, CONTEXT_SIZE);
 +        output_checkstyle_file(output, filename, diff)?;
 +        Ok(EmitterResult::default())
 +    }
 +}
 +
 +pub(crate) fn output_checkstyle_file<T>(
 +    mut writer: T,
-     write!(writer, r#"<file name="{}">"#, filename.display())?;
++    filename: &FileName,
 +    diff: Vec<Mismatch>,
 +) -> Result<(), io::Error>
 +where
 +    T: Write,
 +{
-         let _ = output_checkstyle_file(&mut writer, &PathBuf::from(file_name), vec![]);
++    write!(writer, r#"<file name="{}">"#, filename)?;
 +    for mismatch in diff {
 +        let begin_line = mismatch.line_number;
 +        let mut current_line;
 +        let mut line_counter = 0;
 +        for line in mismatch.lines {
 +            // Do nothing with `DiffLine::Context` and `DiffLine::Resulting`.
 +            if let DiffLine::Expected(message) = line {
 +                current_line = begin_line + line_counter;
 +                line_counter += 1;
 +                write!(
 +                    writer,
 +                    r#"<error line="{}" severity="warning" message="Should be `{}`" />"#,
 +                    current_line,
 +                    XmlEscaped(&message)
 +                )?;
 +            }
 +        }
 +    }
 +    write!(writer, "</file>")?;
 +    Ok(())
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use super::*;
 +    use std::path::PathBuf;
 +
 +    #[test]
 +    fn emits_empty_record_on_file_with_no_mismatches() {
 +        let file_name = "src/well_formatted.rs";
 +        let mut writer = Vec::new();
++        let _ = output_checkstyle_file(
++            &mut writer,
++            &FileName::Real(PathBuf::from(file_name)),
++            vec![],
++        );
 +        assert_eq!(
 +            &writer[..],
 +            format!(r#"<file name="{}"></file>"#, file_name).as_bytes()
 +        );
 +    }
 +
 +    // https://github.com/rust-lang/rustfmt/issues/1636
 +    #[test]
 +    fn emits_single_xml_tree_containing_all_files() {
 +        let bin_file = "src/bin.rs";
 +        let bin_original = vec!["fn main() {", "println!(\"Hello, world!\");", "}"];
 +        let bin_formatted = vec!["fn main() {", "    println!(\"Hello, world!\");", "}"];
 +        let lib_file = "src/lib.rs";
 +        let lib_original = vec!["fn greet() {", "println!(\"Greetings!\");", "}"];
 +        let lib_formatted = vec!["fn greet() {", "    println!(\"Greetings!\");", "}"];
 +        let mut writer = Vec::new();
 +        let mut emitter = CheckstyleEmitter::default();
 +        let _ = emitter.emit_header(&mut writer);
 +        let _ = emitter
 +            .emit_formatted_file(
 +                &mut writer,
 +                FormattedFile {
 +                    filename: &FileName::Real(PathBuf::from(bin_file)),
 +                    original_text: &bin_original.join("\n"),
 +                    formatted_text: &bin_formatted.join("\n"),
 +                },
 +            )
 +            .unwrap();
 +        let _ = emitter
 +            .emit_formatted_file(
 +                &mut writer,
 +                FormattedFile {
 +                    filename: &FileName::Real(PathBuf::from(lib_file)),
 +                    original_text: &lib_original.join("\n"),
 +                    formatted_text: &lib_formatted.join("\n"),
 +                },
 +            )
 +            .unwrap();
 +        let _ = emitter.emit_footer(&mut writer);
 +        let exp_bin_xml = vec![
 +            format!(r#"<file name="{}">"#, bin_file),
 +            format!(
 +                r#"<error line="2" severity="warning" message="Should be `{}`" />"#,
 +                XmlEscaped(r#"    println!("Hello, world!");"#),
 +            ),
 +            String::from("</file>"),
 +        ];
 +        let exp_lib_xml = vec![
 +            format!(r#"<file name="{}">"#, lib_file),
 +            format!(
 +                r#"<error line="2" severity="warning" message="Should be `{}`" />"#,
 +                XmlEscaped(r#"    println!("Greetings!");"#),
 +            ),
 +            String::from("</file>"),
 +        ];
 +        assert_eq!(
 +            String::from_utf8(writer).unwrap(),
 +            vec![
 +                r#"<?xml version="1.0" encoding="utf-8"?>"#,
 +                "\n",
 +                r#"<checkstyle version="4.3">"#,
 +                &format!("{}{}", exp_bin_xml.join(""), exp_lib_xml.join("")),
 +                "</checkstyle>\n",
 +            ]
 +            .join(""),
 +        );
 +    }
 +}
index 7264ad8bbf365baaf0f2d7e74ff3fb8e61bd5e7d,0000000000000000000000000000000000000000..5e1f1344656052dc2b5c26306bed3d8e414c77f1
mode 100644,000000..100644
--- /dev/null
@@@ -1,138 -1,0 +1,137 @@@
-                 writeln!(output, "{}", ensure_real_path(filename).display())?;
 +use super::*;
 +use crate::config::Config;
 +use crate::rustfmt_diff::{make_diff, print_diff};
 +
 +pub(crate) struct DiffEmitter {
 +    config: Config,
 +}
 +
 +impl DiffEmitter {
 +    pub(crate) fn new(config: Config) -> Self {
 +        Self { config }
 +    }
 +}
 +
 +impl Emitter for DiffEmitter {
 +    fn emit_formatted_file(
 +        &mut self,
 +        output: &mut dyn Write,
 +        FormattedFile {
 +            filename,
 +            original_text,
 +            formatted_text,
 +        }: FormattedFile<'_>,
 +    ) -> Result<EmitterResult, io::Error> {
 +        const CONTEXT_SIZE: usize = 3;
 +        let mismatch = make_diff(original_text, formatted_text, CONTEXT_SIZE);
 +        let has_diff = !mismatch.is_empty();
 +
 +        if has_diff {
 +            if self.config.print_misformatted_file_names() {
-             let file_path = ensure_real_path(filename);
-             writeln!(output, "Incorrect newline style in {}", file_path.display())?;
++                writeln!(output, "{}", filename)?;
 +            } else {
 +                print_diff(
 +                    mismatch,
 +                    |line_num| format!("Diff in {} at line {}:", filename, line_num),
 +                    &self.config,
 +                );
 +            }
 +        } else if original_text != formatted_text {
 +            // This occurs when the only difference between the original and formatted values
 +            // is the newline style. This happens because The make_diff function compares the
 +            // original and formatted values line by line, independent of line endings.
++            writeln!(output, "Incorrect newline style in {}", filename)?;
 +            return Ok(EmitterResult { has_diff: true });
 +        }
 +
 +        Ok(EmitterResult { has_diff })
 +    }
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use super::*;
 +    use crate::config::Config;
 +    use crate::FileName;
 +    use std::path::PathBuf;
 +
 +    #[test]
 +    fn does_not_print_when_no_files_reformatted() {
 +        let mut writer = Vec::new();
 +        let config = Config::default();
 +        let mut emitter = DiffEmitter::new(config);
 +        let result = emitter
 +            .emit_formatted_file(
 +                &mut writer,
 +                FormattedFile {
 +                    filename: &FileName::Real(PathBuf::from("src/lib.rs")),
 +                    original_text: "fn empty() {}\n",
 +                    formatted_text: "fn empty() {}\n",
 +                },
 +            )
 +            .unwrap();
 +        assert_eq!(result.has_diff, false);
 +        assert_eq!(writer.len(), 0);
 +    }
 +
 +    #[test]
 +    fn prints_file_names_when_config_is_enabled() {
 +        let bin_file = "src/bin.rs";
 +        let bin_original = "fn main() {\nprintln!(\"Hello, world!\");\n}";
 +        let bin_formatted = "fn main() {\n    println!(\"Hello, world!\");\n}";
 +        let lib_file = "src/lib.rs";
 +        let lib_original = "fn greet() {\nprintln!(\"Greetings!\");\n}";
 +        let lib_formatted = "fn greet() {\n    println!(\"Greetings!\");\n}";
 +
 +        let mut writer = Vec::new();
 +        let mut config = Config::default();
 +        config.set().print_misformatted_file_names(true);
 +        let mut emitter = DiffEmitter::new(config);
 +        let _ = emitter
 +            .emit_formatted_file(
 +                &mut writer,
 +                FormattedFile {
 +                    filename: &FileName::Real(PathBuf::from(bin_file)),
 +                    original_text: bin_original,
 +                    formatted_text: bin_formatted,
 +                },
 +            )
 +            .unwrap();
 +        let _ = emitter
 +            .emit_formatted_file(
 +                &mut writer,
 +                FormattedFile {
 +                    filename: &FileName::Real(PathBuf::from(lib_file)),
 +                    original_text: lib_original,
 +                    formatted_text: lib_formatted,
 +                },
 +            )
 +            .unwrap();
 +
 +        assert_eq!(
 +            String::from_utf8(writer).unwrap(),
 +            format!("{}\n{}\n", bin_file, lib_file),
 +        )
 +    }
 +
 +    #[test]
 +    fn prints_newline_message_with_only_newline_style_diff() {
 +        let mut writer = Vec::new();
 +        let config = Config::default();
 +        let mut emitter = DiffEmitter::new(config);
 +        let _ = emitter
 +            .emit_formatted_file(
 +                &mut writer,
 +                FormattedFile {
 +                    filename: &FileName::Real(PathBuf::from("src/lib.rs")),
 +                    original_text: "fn empty() {}\n",
 +                    formatted_text: "fn empty() {}\r\n",
 +                },
 +            )
 +            .unwrap();
 +        assert_eq!(
 +            String::from_utf8(writer).unwrap(),
 +            String::from("Incorrect newline style in src/lib.rs\n")
 +        );
 +    }
 +}
index 269dd2d4daf57d81aa9fa23e73a11702bf35e62d,0000000000000000000000000000000000000000..c7f68d4675a67beb10d5af09bdd779dc7dbecb66
mode 100644,000000..100644
--- /dev/null
@@@ -1,349 -1,0 +1,346 @@@
- use std::path::Path;
 +use super::*;
 +use crate::rustfmt_diff::{make_diff, DiffLine, Mismatch};
 +use serde::Serialize;
 +use serde_json::to_string as to_json_string;
 +use std::io::{self, Write};
-     num_files: u32,
 +
 +#[derive(Debug, Default)]
 +pub(crate) struct JsonEmitter {
- #[derive(Debug, Default, Serialize)]
++    mismatched_files: Vec<MismatchedFile>,
 +}
 +
- #[derive(Debug, Default, Serialize)]
++#[derive(Debug, Default, PartialEq, Serialize)]
 +struct MismatchedBlock {
 +    original_begin_line: u32,
 +    original_end_line: u32,
 +    expected_begin_line: u32,
 +    expected_end_line: u32,
 +    original: String,
 +    expected: String,
 +}
 +
-     fn emit_header(&self, output: &mut dyn Write) -> Result<(), io::Error> {
-         write!(output, "[")?;
-         Ok(())
-     }
++#[derive(Debug, Default, PartialEq, Serialize)]
 +struct MismatchedFile {
 +    name: String,
 +    mismatches: Vec<MismatchedBlock>,
 +}
 +
 +impl Emitter for JsonEmitter {
-         write!(output, "]")?;
-         Ok(())
 +    fn emit_footer(&self, output: &mut dyn Write) -> Result<(), io::Error> {
-         output: &mut dyn Write,
++        writeln!(output, "{}", &to_json_string(&self.mismatched_files)?)
 +    }
 +
 +    fn emit_formatted_file(
 +        &mut self,
-         let filename = ensure_real_path(filename);
++        _output: &mut dyn Write,
 +        FormattedFile {
 +            filename,
 +            original_text,
 +            formatted_text,
 +        }: FormattedFile<'_>,
 +    ) -> Result<EmitterResult, io::Error> {
 +        const CONTEXT_SIZE: usize = 0;
-             output_json_file(output, filename, diff, self.num_files)?;
-             self.num_files += 1;
 +        let diff = make_diff(original_text, formatted_text, CONTEXT_SIZE);
 +        let has_diff = !diff.is_empty();
 +
 +        if has_diff {
- fn output_json_file<T>(
-     mut writer: T,
-     filename: &Path,
-     diff: Vec<Mismatch>,
-     num_emitted_files: u32,
- ) -> Result<(), io::Error>
- where
-     T: Write,
- {
-     let mut mismatches = vec![];
-     for mismatch in diff {
-         let original_begin_line = mismatch.line_number_orig;
-         let expected_begin_line = mismatch.line_number;
-         let mut original_end_line = original_begin_line;
-         let mut expected_end_line = expected_begin_line;
-         let mut original_line_counter = 0;
-         let mut expected_line_counter = 0;
-         let mut original_lines = vec![];
-         let mut expected_lines = vec![];
++            self.add_misformatted_file(filename, diff)?;
 +        }
 +
 +        Ok(EmitterResult { has_diff })
 +    }
 +}
 +
-         for line in mismatch.lines {
-             match line {
-                 DiffLine::Expected(msg) => {
-                     expected_end_line = expected_begin_line + expected_line_counter;
-                     expected_line_counter += 1;
-                     expected_lines.push(msg)
-                 }
-                 DiffLine::Resulting(msg) => {
-                     original_end_line = original_begin_line + original_line_counter;
-                     original_line_counter += 1;
-                     original_lines.push(msg)
++impl JsonEmitter {
++    fn add_misformatted_file(
++        &mut self,
++        filename: &FileName,
++        diff: Vec<Mismatch>,
++    ) -> Result<(), io::Error> {
++        let mut mismatches = vec![];
++        for mismatch in diff {
++            let original_begin_line = mismatch.line_number_orig;
++            let expected_begin_line = mismatch.line_number;
++            let mut original_end_line = original_begin_line;
++            let mut expected_end_line = expected_begin_line;
++            let mut original_line_counter = 0;
++            let mut expected_line_counter = 0;
++            let mut original = String::new();
++            let mut expected = String::new();
 +
-                 DiffLine::Context(_) => continue,
++            for line in mismatch.lines {
++                match line {
++                    DiffLine::Expected(msg) => {
++                        expected_end_line = expected_begin_line + expected_line_counter;
++                        expected_line_counter += 1;
++                        expected.push_str(&msg);
++                        expected.push('\n');
++                    }
++                    DiffLine::Resulting(msg) => {
++                        original_end_line = original_begin_line + original_line_counter;
++                        original_line_counter += 1;
++                        original.push_str(&msg);
++                        original.push('\n');
++                    }
++                    DiffLine::Context(_) => continue,
 +                }
-         }
 +            }
-         mismatches.push(MismatchedBlock {
-             original_begin_line,
-             original_end_line,
-             expected_begin_line,
-             expected_end_line,
-             original: original_lines.join("\n"),
-             expected: expected_lines.join("\n"),
 +
-     let json = to_json_string(&MismatchedFile {
-         name: String::from(filename.to_str().unwrap()),
-         mismatches,
-     })?;
-     let prefix = if num_emitted_files > 0 { "," } else { "" };
-     write!(writer, "{}{}", prefix, &json)?;
-     Ok(())
++            mismatches.push(MismatchedBlock {
++                original_begin_line,
++                original_end_line,
++                expected_begin_line,
++                expected_end_line,
++                original,
++                expected,
++            });
++        }
++        self.mismatched_files.push(MismatchedFile {
++            name: format!("{}", filename),
++            mismatches,
 +        });
++        Ok(())
 +    }
-                 original: String::from("fn Foo<T>() where T: Bar {"),
-                 expected: String::from("fn Foo<T>()\nwhere\n    T: Bar,\n{"),
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use super::*;
 +    use crate::FileName;
 +    use std::path::PathBuf;
 +
 +    #[test]
 +    fn expected_line_range_correct_when_single_line_split() {
++        let mut emitter = JsonEmitter {
++            mismatched_files: vec![],
++        };
 +        let file = "foo/bar.rs";
 +        let mismatched_file = MismatchedFile {
 +            name: String::from(file),
 +            mismatches: vec![MismatchedBlock {
 +                original_begin_line: 79,
 +                original_end_line: 79,
 +                expected_begin_line: 79,
 +                expected_end_line: 82,
-         let mut writer = Vec::new();
-         let exp_json = to_json_string(&mismatched_file).unwrap();
-         let _ = output_json_file(&mut writer, &PathBuf::from(file), vec![mismatch], 0);
-         assert_eq!(&writer[..], format!("{}", exp_json).as_bytes());
++                original: String::from("fn Foo<T>() where T: Bar {\n"),
++                expected: String::from("fn Foo<T>()\nwhere\n    T: Bar,\n{\n"),
 +            }],
 +        };
 +        let mismatch = Mismatch {
 +            line_number: 79,
 +            line_number_orig: 79,
 +            lines: vec![
 +                DiffLine::Resulting(String::from("fn Foo<T>() where T: Bar {")),
 +                DiffLine::Expected(String::from("fn Foo<T>()")),
 +                DiffLine::Expected(String::from("where")),
 +                DiffLine::Expected(String::from("    T: Bar,")),
 +                DiffLine::Expected(String::from("{")),
 +            ],
 +        };
 +
-                     "fn foo(_x: &u64) -> Option<&(dyn::std::error::Error + 'static)> {",
++        let _ = emitter
++            .add_misformatted_file(&FileName::Real(PathBuf::from(file)), vec![mismatch])
++            .unwrap();
++
++        assert_eq!(emitter.mismatched_files.len(), 1);
++        assert_eq!(emitter.mismatched_files[0], mismatched_file);
 +    }
 +
 +    #[test]
 +    fn context_lines_ignored() {
++        let mut emitter = JsonEmitter {
++            mismatched_files: vec![],
++        };
 +        let file = "src/lib.rs";
 +        let mismatched_file = MismatchedFile {
 +            name: String::from(file),
 +            mismatches: vec![MismatchedBlock {
 +                original_begin_line: 5,
 +                original_end_line: 5,
 +                expected_begin_line: 5,
 +                expected_end_line: 5,
 +                original: String::from(
-                     "fn foo(_x: &u64) -> Option<&(dyn ::std::error::Error + 'static)> {",
++                    "fn foo(_x: &u64) -> Option<&(dyn::std::error::Error + 'static)> {\n",
 +                ),
 +                expected: String::from(
-         let mut writer = Vec::new();
-         let exp_json = to_json_string(&mismatched_file).unwrap();
-         let _ = output_json_file(&mut writer, &PathBuf::from(file), vec![mismatch], 0);
-         assert_eq!(&writer[..], format!("{}", exp_json).as_bytes());
++                    "fn foo(_x: &u64) -> Option<&(dyn ::std::error::Error + 'static)> {\n",
 +                ),
 +            }],
 +        };
 +        let mismatch = Mismatch {
 +            line_number: 5,
 +            line_number_orig: 5,
 +            lines: vec![
 +                DiffLine::Context(String::new()),
 +                DiffLine::Resulting(String::from(
 +                    "fn foo(_x: &u64) -> Option<&(dyn::std::error::Error + 'static)> {",
 +                )),
 +                DiffLine::Context(String::new()),
 +                DiffLine::Expected(String::from(
 +                    "fn foo(_x: &u64) -> Option<&(dyn ::std::error::Error + 'static)> {",
 +                )),
 +                DiffLine::Context(String::new()),
 +            ],
 +        };
 +
-         assert_eq!(&writer[..], "[]".as_bytes());
++        let _ = emitter
++            .add_misformatted_file(&FileName::Real(PathBuf::from(file)), vec![mismatch])
++            .unwrap();
++
++        assert_eq!(emitter.mismatched_files.len(), 1);
++        assert_eq!(emitter.mismatched_files[0], mismatched_file);
 +    }
 +
 +    #[test]
 +    fn emits_empty_array_on_no_diffs() {
 +        let mut writer = Vec::new();
 +        let mut emitter = JsonEmitter::default();
 +        let _ = emitter.emit_header(&mut writer);
 +        let result = emitter
 +            .emit_formatted_file(
 +                &mut writer,
 +                FormattedFile {
 +                    filename: &FileName::Real(PathBuf::from("src/lib.rs")),
 +                    original_text: "fn empty() {}\n",
 +                    formatted_text: "fn empty() {}\n",
 +                },
 +            )
 +            .unwrap();
 +        let _ = emitter.emit_footer(&mut writer);
 +        assert_eq!(result.has_diff, false);
-         let exp_json = to_json_string(&MismatchedFile {
++        assert_eq!(&writer[..], "[]\n".as_bytes());
 +    }
 +
 +    #[test]
 +    fn emits_array_with_files_with_diffs() {
 +        let file_name = "src/bin.rs";
 +        let original = vec![
 +            "fn main() {",
 +            "println!(\"Hello, world!\");",
 +            "}",
 +            "",
 +            "#[cfg(test)]",
 +            "mod tests {",
 +            "#[test]",
 +            "fn it_works() {",
 +            "    assert_eq!(2 + 2, 4);",
 +            "}",
 +            "}",
 +        ];
 +        let formatted = vec![
 +            "fn main() {",
 +            "    println!(\"Hello, world!\");",
 +            "}",
 +            "",
 +            "#[cfg(test)]",
 +            "mod tests {",
 +            "    #[test]",
 +            "    fn it_works() {",
 +            "        assert_eq!(2 + 2, 4);",
 +            "    }",
 +            "}",
 +        ];
 +        let mut writer = Vec::new();
 +        let mut emitter = JsonEmitter::default();
 +        let _ = emitter.emit_header(&mut writer);
 +        let result = emitter
 +            .emit_formatted_file(
 +                &mut writer,
 +                FormattedFile {
 +                    filename: &FileName::Real(PathBuf::from(file_name)),
 +                    original_text: &original.join("\n"),
 +                    formatted_text: &formatted.join("\n"),
 +                },
 +            )
 +            .unwrap();
 +        let _ = emitter.emit_footer(&mut writer);
-                     original: String::from("println!(\"Hello, world!\");"),
-                     expected: String::from("    println!(\"Hello, world!\");"),
++        let exp_json = to_json_string(&vec![MismatchedFile {
 +            name: String::from(file_name),
 +            mismatches: vec![
 +                MismatchedBlock {
 +                    original_begin_line: 2,
 +                    original_end_line: 2,
 +                    expected_begin_line: 2,
 +                    expected_end_line: 2,
-                         "#[test]\nfn it_works() {\n    assert_eq!(2 + 2, 4);\n}",
++                    original: String::from("println!(\"Hello, world!\");\n"),
++                    expected: String::from("    println!(\"Hello, world!\");\n"),
 +                },
 +                MismatchedBlock {
 +                    original_begin_line: 7,
 +                    original_end_line: 10,
 +                    expected_begin_line: 7,
 +                    expected_end_line: 10,
 +                    original: String::from(
-                         "    #[test]\n    fn it_works() {\n        assert_eq!(2 + 2, 4);\n    }",
++                        "#[test]\nfn it_works() {\n    assert_eq!(2 + 2, 4);\n}\n",
 +                    ),
 +                    expected: String::from(
-         })
++                        "    #[test]\n    fn it_works() {\n        assert_eq!(2 + 2, 4);\n    }\n",
 +                    ),
 +                },
 +            ],
-         assert_eq!(&writer[..], format!("[{}]", exp_json).as_bytes());
++        }])
 +        .unwrap();
 +        assert_eq!(result.has_diff, true);
-         let exp_bin_json = to_json_string(&MismatchedFile {
++        assert_eq!(&writer[..], format!("{}\n", exp_json).as_bytes());
 +    }
 +
 +    #[test]
 +    fn emits_valid_json_with_multiple_files() {
 +        let bin_file = "src/bin.rs";
 +        let bin_original = vec!["fn main() {", "println!(\"Hello, world!\");", "}"];
 +        let bin_formatted = vec!["fn main() {", "    println!(\"Hello, world!\");", "}"];
 +        let lib_file = "src/lib.rs";
 +        let lib_original = vec!["fn greet() {", "println!(\"Greetings!\");", "}"];
 +        let lib_formatted = vec!["fn greet() {", "    println!(\"Greetings!\");", "}"];
 +        let mut writer = Vec::new();
 +        let mut emitter = JsonEmitter::default();
 +        let _ = emitter.emit_header(&mut writer);
 +        let _ = emitter
 +            .emit_formatted_file(
 +                &mut writer,
 +                FormattedFile {
 +                    filename: &FileName::Real(PathBuf::from(bin_file)),
 +                    original_text: &bin_original.join("\n"),
 +                    formatted_text: &bin_formatted.join("\n"),
 +                },
 +            )
 +            .unwrap();
 +        let _ = emitter
 +            .emit_formatted_file(
 +                &mut writer,
 +                FormattedFile {
 +                    filename: &FileName::Real(PathBuf::from(lib_file)),
 +                    original_text: &lib_original.join("\n"),
 +                    formatted_text: &lib_formatted.join("\n"),
 +                },
 +            )
 +            .unwrap();
 +        let _ = emitter.emit_footer(&mut writer);
-                 original: String::from("println!(\"Hello, world!\");"),
-                 expected: String::from("    println!(\"Hello, world!\");"),
++        let exp_bin = MismatchedFile {
 +            name: String::from(bin_file),
 +            mismatches: vec![MismatchedBlock {
 +                original_begin_line: 2,
 +                original_end_line: 2,
 +                expected_begin_line: 2,
 +                expected_end_line: 2,
-         })
-         .unwrap();
-         let exp_lib_json = to_json_string(&MismatchedFile {
++                original: String::from("println!(\"Hello, world!\");\n"),
++                expected: String::from("    println!(\"Hello, world!\");\n"),
 +            }],
-                 original: String::from("println!(\"Greetings!\");"),
-                 expected: String::from("    println!(\"Greetings!\");"),
++        };
++
++        let exp_lib = MismatchedFile {
 +            name: String::from(lib_file),
 +            mismatches: vec![MismatchedBlock {
 +                original_begin_line: 2,
 +                original_end_line: 2,
 +                expected_begin_line: 2,
 +                expected_end_line: 2,
-         })
-         .unwrap();
-         assert_eq!(
-             &writer[..],
-             format!("[{},{}]", exp_bin_json, exp_lib_json).as_bytes()
-         );
++                original: String::from("println!(\"Greetings!\");\n"),
++                expected: String::from("    println!(\"Greetings!\");\n"),
 +            }],
++        };
++
++        let exp_json = to_json_string(&vec![exp_bin, exp_lib]).unwrap();
++        assert_eq!(&writer[..], format!("{}\n", exp_json).as_bytes());
 +    }
 +}
index c820259256c4724d0cd335573ab5b8de49e6ad05,0000000000000000000000000000000000000000..90406cdb95e2b1474440593fce77698df58b08c3
mode 100644,000000..100644
--- /dev/null
@@@ -1,150 -1,0 +1,151 @@@
 +use crate::formatting::FormattingError;
 +use crate::{ErrorKind, FormatReport};
 +use annotate_snippets::display_list::{DisplayList, FormatOptions};
 +use annotate_snippets::snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation};
 +use std::fmt::{self, Display};
 +
 +/// A builder for [`FormatReportFormatter`].
 +pub struct FormatReportFormatterBuilder<'a> {
 +    report: &'a FormatReport,
 +    enable_colors: bool,
 +}
 +
 +impl<'a> FormatReportFormatterBuilder<'a> {
 +    /// Creates a new [`FormatReportFormatterBuilder`].
 +    pub fn new(report: &'a FormatReport) -> Self {
 +        Self {
 +            report,
 +            enable_colors: false,
 +        }
 +    }
 +
 +    /// Enables colors and formatting in the output.
++    #[must_use]
 +    pub fn enable_colors(self, enable_colors: bool) -> Self {
 +        Self {
 +            enable_colors,
 +            ..self
 +        }
 +    }
 +
 +    /// Creates a new [`FormatReportFormatter`] from the settings in this builder.
 +    pub fn build(self) -> FormatReportFormatter<'a> {
 +        FormatReportFormatter {
 +            report: self.report,
 +            enable_colors: self.enable_colors,
 +        }
 +    }
 +}
 +
 +/// Formats the warnings/errors in a [`FormatReport`].
 +///
 +/// Can be created using a [`FormatReportFormatterBuilder`].
 +pub struct FormatReportFormatter<'a> {
 +    report: &'a FormatReport,
 +    enable_colors: bool,
 +}
 +
 +impl<'a> Display for FormatReportFormatter<'a> {
 +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 +        let errors_by_file = &self.report.internal.borrow().0;
 +
 +        let opt = FormatOptions {
 +            color: self.enable_colors,
 +            ..Default::default()
 +        };
 +
 +        for (file, errors) in errors_by_file {
 +            for error in errors {
 +                let error_kind = error.kind.to_string();
 +                let title = Some(Annotation {
 +                    id: if error.is_internal() {
 +                        Some("internal")
 +                    } else {
 +                        None
 +                    },
 +                    label: Some(&error_kind),
 +                    annotation_type: error_kind_to_snippet_annotation_type(&error.kind),
 +                });
 +
 +                let message_suffix = error.msg_suffix();
 +                let footer = if !message_suffix.is_empty() {
 +                    Some(Annotation {
 +                        id: None,
 +                        label: Some(message_suffix),
 +                        annotation_type: AnnotationType::Note,
 +                    })
 +                } else {
 +                    None
 +                };
 +
 +                let origin = format!("{}:{}", file, error.line);
 +                let slice = Slice {
 +                    source: &error.line_buffer.clone(),
 +                    line_start: error.line,
 +                    origin: Some(origin.as_str()),
 +                    fold: false,
 +                    annotations: slice_annotation(error).into_iter().collect(),
 +                };
 +
 +                let snippet = Snippet {
 +                    title,
 +                    footer: footer.into_iter().collect(),
 +                    slices: vec![slice],
 +                    opt,
 +                };
 +                writeln!(f, "{}\n", DisplayList::from(snippet))?;
 +            }
 +        }
 +
 +        if !errors_by_file.is_empty() {
 +            let label = format!(
 +                "rustfmt has failed to format. See previous {} errors.",
 +                self.report.warning_count()
 +            );
 +            let snippet = Snippet {
 +                title: Some(Annotation {
 +                    id: None,
 +                    label: Some(&label),
 +                    annotation_type: AnnotationType::Warning,
 +                }),
 +                footer: Vec::new(),
 +                slices: Vec::new(),
 +                opt,
 +            };
 +            writeln!(f, "{}", DisplayList::from(snippet))?;
 +        }
 +
 +        Ok(())
 +    }
 +}
 +
 +fn slice_annotation(error: &FormattingError) -> Option<SourceAnnotation<'_>> {
 +    let (range_start, range_length) = error.format_len();
 +    let range_end = range_start + range_length;
 +
 +    if range_length > 0 {
 +        Some(SourceAnnotation {
 +            annotation_type: AnnotationType::Error,
 +            range: (range_start, range_end),
 +            label: "",
 +        })
 +    } else {
 +        None
 +    }
 +}
 +
 +fn error_kind_to_snippet_annotation_type(error_kind: &ErrorKind) -> AnnotationType {
 +    match error_kind {
 +        ErrorKind::LineOverflow(..)
 +        | ErrorKind::TrailingWhitespace
 +        | ErrorKind::IoError(_)
 +        | ErrorKind::ModuleResolutionError(_)
 +        | ErrorKind::ParseError
 +        | ErrorKind::LostComment
 +        | ErrorKind::LicenseCheck
 +        | ErrorKind::BadAttr
 +        | ErrorKind::InvalidGlobPattern(_)
 +        | ErrorKind::VersionMismatch => AnnotationType::Error,
 +        ErrorKind::BadIssue(_) | ErrorKind::DeprecatedAttr => AnnotationType::Warning,
 +    }
 +}
index 7738eee0a76049b769a2aa547ccdc908483c16fd,0000000000000000000000000000000000000000..d955949496a67c7214902c39cc174952e6fb883c
mode 100644,000000..100644
--- /dev/null
@@@ -1,53 -1,0 +1,52 @@@
-     use std::path::{Path, PathBuf};
-     use crate::config::{Config, FileName};
-     use crate::ignore_path::IgnorePathSet;
 +use ignore::{self, gitignore};
 +
 +use crate::config::{FileName, IgnoreList};
 +
 +pub(crate) struct IgnorePathSet {
 +    ignore_set: gitignore::Gitignore,
 +}
 +
 +impl IgnorePathSet {
 +    pub(crate) fn from_ignore_list(ignore_list: &IgnoreList) -> Result<Self, ignore::Error> {
 +        let mut ignore_builder = gitignore::GitignoreBuilder::new(ignore_list.rustfmt_toml_path());
 +
 +        for ignore_path in ignore_list {
 +            ignore_builder.add_line(None, ignore_path.to_str().unwrap())?;
 +        }
 +
 +        Ok(IgnorePathSet {
 +            ignore_set: ignore_builder.build()?,
 +        })
 +    }
 +
 +    pub(crate) fn is_match(&self, file_name: &FileName) -> bool {
 +        match file_name {
 +            FileName::Stdin => false,
 +            FileName::Real(p) => self
 +                .ignore_set
 +                .matched_path_or_any_parents(p, false)
 +                .is_ignore(),
 +        }
 +    }
 +}
 +
 +#[cfg(test)]
 +mod test {
 +    use rustfmt_config_proc_macro::nightly_only_test;
 +
 +    #[nightly_only_test]
 +    #[test]
 +    fn test_ignore_path_set() {
++        use crate::config::{Config, FileName};
++        use crate::ignore_path::IgnorePathSet;
++        use std::path::{Path, PathBuf};
++
 +        let config =
 +            Config::from_toml(r#"ignore = ["foo.rs", "bar_dir/*"]"#, Path::new("")).unwrap();
 +        let ignore_path_set = IgnorePathSet::from_ignore_list(&config.ignore()).unwrap();
 +
 +        assert!(ignore_path_set.is_match(&FileName::Real(PathBuf::from("src/foo.rs"))));
 +        assert!(ignore_path_set.is_match(&FileName::Real(PathBuf::from("bar_dir/baz.rs"))));
 +        assert!(!ignore_path_set.is_match(&FileName::Real(PathBuf::from("src/bar.rs"))));
 +    }
 +}
index 40e0d06f99df8d14b7279dabb7bf503c747a6fcc,0000000000000000000000000000000000000000..0231980948686e2b691be3de84ae8a92ea4308ba
mode 100644,000000..100644
--- /dev/null
@@@ -1,1312 -1,0 +1,1340 @@@
-         for flattened in use_tree.flatten() {
 +use std::borrow::Cow;
 +use std::cmp::Ordering;
 +use std::fmt;
 +
 +use rustc_ast::ast::{self, UseTreeKind};
 +use rustc_span::{
 +    symbol::{self, sym},
 +    BytePos, Span, DUMMY_SP,
 +};
 +
 +use crate::comment::combine_strs_with_missing_comments;
 +use crate::config::lists::*;
 +use crate::config::{Edition, IndentStyle};
 +use crate::lists::{
 +    definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator,
 +};
 +use crate::rewrite::{Rewrite, RewriteContext};
 +use crate::shape::Shape;
 +use crate::source_map::SpanUtils;
 +use crate::spanned::Spanned;
 +use crate::utils::{is_same_visibility, mk_sp, rewrite_ident};
 +use crate::visitor::FmtVisitor;
 +
 +/// Returns a name imported by a `use` declaration.
 +/// E.g., returns `Ordering` for `std::cmp::Ordering` and `self` for `std::cmp::self`.
 +pub(crate) fn path_to_imported_ident(path: &ast::Path) -> symbol::Ident {
 +    path.segments.last().unwrap().ident
 +}
 +
 +impl<'a> FmtVisitor<'a> {
 +    pub(crate) fn format_import(&mut self, item: &ast::Item, tree: &ast::UseTree) {
 +        let span = item.span();
 +        let shape = self.shape();
 +        let rw = UseTree::from_ast(
 +            &self.get_context(),
 +            tree,
 +            None,
 +            Some(item.vis.clone()),
 +            Some(item.span.lo()),
 +            Some(item.attrs.clone()),
 +        )
 +        .rewrite_top_level(&self.get_context(), shape);
 +        match rw {
 +            Some(ref s) if s.is_empty() => {
 +                // Format up to last newline
 +                let prev_span = mk_sp(self.last_pos, source!(self, span).lo());
 +                let trimmed_snippet = self.snippet(prev_span).trim_end();
 +                let span_end = self.last_pos + BytePos(trimmed_snippet.len() as u32);
 +                self.format_missing(span_end);
 +                // We have an excessive newline from the removed import.
 +                if self.buffer.ends_with('\n') {
 +                    self.buffer.pop();
 +                    self.line_number -= 1;
 +                }
 +                self.last_pos = source!(self, span).hi();
 +            }
 +            Some(ref s) => {
 +                self.format_missing_with_indent(source!(self, span).lo());
 +                self.push_str(s);
 +                self.last_pos = source!(self, span).hi();
 +            }
 +            None => {
 +                self.format_missing_with_indent(source!(self, span).lo());
 +                self.format_missing(source!(self, span).hi());
 +            }
 +        }
 +    }
 +}
 +
 +// Ordering of imports
 +
 +// We order imports by translating to our own representation and then sorting.
 +// The Rust AST data structures are really bad for this. Rustfmt applies a bunch
 +// of normalisations to imports and since we want to sort based on the result
 +// of these (and to maintain idempotence) we must apply the same normalisations
 +// to the data structures for sorting.
 +//
 +// We sort `self` and `super` before other imports, then identifier imports,
 +// then glob imports, then lists of imports. We do not take aliases into account
 +// when ordering unless the imports are identical except for the alias (rare in
 +// practice).
 +
 +// FIXME(#2531): we should unify the comparison code here with the formatting
 +// code elsewhere since we are essentially string-ifying twice. Furthermore, by
 +// parsing to our own format on comparison, we repeat a lot of work when
 +// sorting.
 +
 +// FIXME we do a lot of allocation to make our own representation.
 +#[derive(Clone, Eq, PartialEq)]
 +pub(crate) enum UseSegment {
 +    Ident(String, Option<String>),
 +    Slf(Option<String>),
 +    Super(Option<String>),
 +    Crate(Option<String>),
 +    Glob,
 +    List(Vec<UseTree>),
 +}
 +
 +#[derive(Clone)]
 +pub(crate) struct UseTree {
 +    pub(crate) path: Vec<UseSegment>,
 +    pub(crate) span: Span,
 +    // Comment information within nested use tree.
 +    pub(crate) list_item: Option<ListItem>,
 +    // Additional fields for top level use items.
 +    // Should we have another struct for top-level use items rather than reusing this?
 +    visibility: Option<ast::Visibility>,
 +    attrs: Option<Vec<ast::Attribute>>,
 +}
 +
 +impl PartialEq for UseTree {
 +    fn eq(&self, other: &UseTree) -> bool {
 +        self.path == other.path
 +    }
 +}
 +impl Eq for UseTree {}
 +
 +impl Spanned for UseTree {
 +    fn span(&self) -> Span {
 +        let lo = if let Some(ref attrs) = self.attrs {
 +            attrs.iter().next().map_or(self.span.lo(), |a| a.span.lo())
 +        } else {
 +            self.span.lo()
 +        };
 +        mk_sp(lo, self.span.hi())
 +    }
 +}
 +
 +impl UseSegment {
 +    // Clone a version of self with any top-level alias removed.
 +    fn remove_alias(&self) -> UseSegment {
 +        match *self {
 +            UseSegment::Ident(ref s, _) => UseSegment::Ident(s.clone(), None),
 +            UseSegment::Slf(_) => UseSegment::Slf(None),
 +            UseSegment::Super(_) => UseSegment::Super(None),
 +            UseSegment::Crate(_) => UseSegment::Crate(None),
 +            _ => self.clone(),
 +        }
 +    }
 +
 +    // Check if self == other with their aliases removed.
 +    fn equal_except_alias(&self, other: &Self) -> bool {
 +        match (self, other) {
 +            (UseSegment::Ident(ref s1, _), UseSegment::Ident(ref s2, _)) => s1 == s2,
 +            (UseSegment::Slf(_), UseSegment::Slf(_))
 +            | (UseSegment::Super(_), UseSegment::Super(_))
 +            | (UseSegment::Crate(_), UseSegment::Crate(_))
 +            | (UseSegment::Glob, UseSegment::Glob) => true,
 +            (UseSegment::List(ref list1), UseSegment::List(ref list2)) => list1 == list2,
 +            _ => false,
 +        }
 +    }
 +
 +    fn get_alias(&self) -> Option<&str> {
 +        match self {
 +            UseSegment::Ident(_, a)
 +            | UseSegment::Slf(a)
 +            | UseSegment::Super(a)
 +            | UseSegment::Crate(a) => a.as_deref(),
 +            _ => None,
 +        }
 +    }
 +
 +    fn from_path_segment(
 +        context: &RewriteContext<'_>,
 +        path_seg: &ast::PathSegment,
 +        modsep: bool,
 +    ) -> Option<UseSegment> {
 +        let name = rewrite_ident(context, path_seg.ident);
 +        if name.is_empty() || name == "{{root}}" {
 +            return None;
 +        }
 +        Some(match name {
 +            "self" => UseSegment::Slf(None),
 +            "super" => UseSegment::Super(None),
 +            "crate" => UseSegment::Crate(None),
 +            _ => {
 +                let mod_sep = if modsep { "::" } else { "" };
 +                UseSegment::Ident(format!("{}{}", mod_sep, name), None)
 +            }
 +        })
 +    }
 +}
 +
 +pub(crate) fn merge_use_trees(use_trees: Vec<UseTree>, merge_by: SharedPrefix) -> Vec<UseTree> {
 +    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() {
 +            result.push(use_tree);
 +            continue;
 +        }
 +
-         .map(|mut tree| {
-             // If a path ends in `::self`, rewrite it to `::{self}`.
-             if let Some(UseSegment::Slf(..)) = tree.path.last() {
-                 let self_segment = tree.path.pop().unwrap();
-                 tree.path.push(UseSegment::List(vec![UseTree::from_path(
-                     vec![self_segment],
-                     DUMMY_SP,
-                 )]));
-             }
-             tree
-         })
++        for mut flattened in use_tree.flatten() {
 +            if let Some(tree) = result
 +                .iter_mut()
 +                .find(|tree| tree.share_prefix(&flattened, merge_by))
 +            {
 +                tree.merge(&flattened, merge_by);
 +            } else {
++                // If this is the first tree with this prefix, handle potential trailing ::self
++                if merge_by == SharedPrefix::Module {
++                    flattened = flattened.nest_trailing_self();
++                }
 +                result.push(flattened);
 +            }
 +        }
 +    }
 +    result
 +}
 +
 +pub(crate) fn flatten_use_trees(use_trees: Vec<UseTree>) -> Vec<UseTree> {
 +    use_trees
 +        .into_iter()
 +        .flat_map(UseTree::flatten)
-             UseSegment::Ident(ref s, _) => write!(f, "{}", s),
++        .map(UseTree::nest_trailing_self)
 +        .collect()
 +}
 +
 +impl fmt::Debug for UseTree {
 +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 +        fmt::Display::fmt(self, f)
 +    }
 +}
 +
 +impl fmt::Debug for UseSegment {
 +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 +        fmt::Display::fmt(self, f)
 +    }
 +}
 +
 +impl fmt::Display for UseSegment {
 +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 +        match *self {
 +            UseSegment::Glob => write!(f, "*"),
-             if a.equal_except_alias(b) {
++            UseSegment::Ident(ref s, Some(ref alias)) => write!(f, "{} as {}", s, alias),
++            UseSegment::Ident(ref s, None) => write!(f, "{}", s),
 +            UseSegment::Slf(..) => write!(f, "self"),
 +            UseSegment::Super(..) => write!(f, "super"),
 +            UseSegment::Crate(..) => write!(f, "crate"),
 +            UseSegment::List(ref list) => {
 +                write!(f, "{{")?;
 +                for (i, item) in list.iter().enumerate() {
 +                    if i != 0 {
 +                        write!(f, ", ")?;
 +                    }
 +                    write!(f, "{}", item)?;
 +                }
 +                write!(f, "}}")
 +            }
 +        }
 +    }
 +}
 +impl fmt::Display for UseTree {
 +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 +        for (i, segment) in self.path.iter().enumerate() {
 +            if i != 0 {
 +                write!(f, "::")?;
 +            }
 +            write!(f, "{}", segment)?;
 +        }
 +        Ok(())
 +    }
 +}
 +
 +impl UseTree {
 +    // Rewrite use tree with `use ` and a trailing `;`.
 +    pub(crate) fn rewrite_top_level(
 +        &self,
 +        context: &RewriteContext<'_>,
 +        shape: Shape,
 +    ) -> Option<String> {
 +        let vis = self.visibility.as_ref().map_or(Cow::from(""), |vis| {
 +            crate::utils::format_visibility(context, vis)
 +        });
 +        let use_str = self
 +            .rewrite(context, shape.offset_left(vis.len())?)
 +            .map(|s| {
 +                if s.is_empty() {
 +                    s
 +                } else {
 +                    format!("{}use {};", vis, s)
 +                }
 +            })?;
 +        match self.attrs {
 +            Some(ref attrs) if !attrs.is_empty() => {
 +                let attr_str = attrs.rewrite(context, shape)?;
 +                let lo = attrs.last().as_ref()?.span.hi();
 +                let hi = self.span.lo();
 +                let span = mk_sp(lo, hi);
 +
 +                let allow_extend = if attrs.len() == 1 {
 +                    let line_len = attr_str.len() + 1 + use_str.len();
 +                    !attrs.first().unwrap().is_doc_comment()
 +                        && context.config.inline_attribute_width() >= line_len
 +                } else {
 +                    false
 +                };
 +
 +                combine_strs_with_missing_comments(
 +                    context,
 +                    &attr_str,
 +                    &use_str,
 +                    span,
 +                    shape,
 +                    allow_extend,
 +                )
 +            }
 +            _ => Some(use_str),
 +        }
 +    }
 +
 +    // FIXME: Use correct span?
 +    // The given span is essentially incorrect, since we are reconstructing
 +    // use-statements. This should not be a problem, though, since we have
 +    // already tried to extract comment and observed that there are no comment
 +    // around the given use item, and the span will not be used afterward.
 +    fn from_path(path: Vec<UseSegment>, span: Span) -> UseTree {
 +        UseTree {
 +            path,
 +            span,
 +            list_item: None,
 +            visibility: None,
 +            attrs: None,
 +        }
 +    }
 +
 +    pub(crate) fn from_ast_with_normalization(
 +        context: &RewriteContext<'_>,
 +        item: &ast::Item,
 +    ) -> Option<UseTree> {
 +        match item.kind {
 +            ast::ItemKind::Use(ref use_tree) => Some(
 +                UseTree::from_ast(
 +                    context,
 +                    use_tree,
 +                    None,
 +                    Some(item.vis.clone()),
 +                    Some(item.span.lo()),
 +                    if item.attrs.is_empty() {
 +                        None
 +                    } else {
 +                        Some(item.attrs.clone())
 +                    },
 +                )
 +                .normalize(),
 +            ),
 +            _ => None,
 +        }
 +    }
 +
 +    fn from_ast(
 +        context: &RewriteContext<'_>,
 +        a: &ast::UseTree,
 +        list_item: Option<ListItem>,
 +        visibility: Option<ast::Visibility>,
 +        opt_lo: Option<BytePos>,
 +        attrs: Option<Vec<ast::Attribute>>,
 +    ) -> UseTree {
 +        let span = if let Some(lo) = opt_lo {
 +            mk_sp(lo, a.span.hi())
 +        } else {
 +            a.span
 +        };
 +        let mut result = UseTree {
 +            path: vec![],
 +            span,
 +            list_item,
 +            visibility,
 +            attrs,
 +        };
 +
 +        let leading_modsep =
 +            context.config.edition() >= Edition::Edition2018 && a.prefix.is_global();
 +
 +        let mut modsep = leading_modsep;
 +
 +        for p in &a.prefix.segments {
 +            if let Some(use_segment) = UseSegment::from_path_segment(context, p, modsep) {
 +                result.path.push(use_segment);
 +                modsep = false;
 +            }
 +        }
 +
 +        match a.kind {
 +            UseTreeKind::Glob => {
 +                // in case of a global path and the glob starts at the root, e.g., "::*"
 +                if a.prefix.segments.len() == 1 && leading_modsep {
 +                    result.path.push(UseSegment::Ident("".to_owned(), None));
 +                }
 +                result.path.push(UseSegment::Glob);
 +            }
 +            UseTreeKind::Nested(ref list) => {
 +                // Extract comments between nested use items.
 +                // This needs to be done before sorting use items.
 +                let items = itemize_list(
 +                    context.snippet_provider,
 +                    list.iter().map(|(tree, _)| tree),
 +                    "}",
 +                    ",",
 +                    |tree| tree.span.lo(),
 +                    |tree| tree.span.hi(),
 +                    |_| Some("".to_owned()), // We only need comments for now.
 +                    context.snippet_provider.span_after(a.span, "{"),
 +                    a.span.hi(),
 +                    false,
 +                );
 +
 +                // in case of a global path and the nested list starts at the root,
 +                // e.g., "::{foo, bar}"
 +                if a.prefix.segments.len() == 1 && leading_modsep {
 +                    result.path.push(UseSegment::Ident("".to_owned(), None));
 +                }
 +                result.path.push(UseSegment::List(
 +                    list.iter()
 +                        .zip(items)
 +                        .map(|(t, list_item)| {
 +                            Self::from_ast(context, &t.0, Some(list_item), None, None, None)
 +                        })
 +                        .collect(),
 +                ));
 +            }
 +            UseTreeKind::Simple(ref rename, ..) => {
 +                // If the path has leading double colons and is composed of only 2 segments, then we
 +                // bypass the call to path_to_imported_ident which would get only the ident and
 +                // lose the path root, e.g., `that` in `::that`.
 +                // The span of `a.prefix` contains the leading colons.
 +                let name = if a.prefix.segments.len() == 2 && leading_modsep {
 +                    context.snippet(a.prefix.span).to_owned()
 +                } else {
 +                    rewrite_ident(context, path_to_imported_ident(&a.prefix)).to_owned()
 +                };
 +                let alias = rename.and_then(|ident| {
 +                    if ident.name == sym::underscore_imports {
 +                        // for impl-only-use
 +                        Some("_".to_owned())
 +                    } else if ident == path_to_imported_ident(&a.prefix) {
 +                        None
 +                    } else {
 +                        Some(rewrite_ident(context, ident).to_owned())
 +                    }
 +                });
 +                let segment = match name.as_ref() {
 +                    "self" => UseSegment::Slf(alias),
 +                    "super" => UseSegment::Super(alias),
 +                    "crate" => UseSegment::Crate(alias),
 +                    _ => UseSegment::Ident(name, alias),
 +                };
 +
 +                // `name` is already in result.
 +                result.path.pop();
 +                result.path.push(segment);
 +            }
 +        }
 +        result
 +    }
 +
 +    // Do the adjustments that rustfmt does elsewhere to use paths.
 +    pub(crate) fn normalize(mut self) -> UseTree {
 +        let mut last = self.path.pop().expect("Empty use tree?");
 +        // Hack around borrow checker.
 +        let mut normalize_sole_list = false;
 +        let mut aliased_self = false;
 +
 +        // Remove foo::{} or self without attributes.
 +        match last {
 +            _ if self.attrs.is_some() => (),
 +            UseSegment::List(ref list) if list.is_empty() => {
 +                self.path = vec![];
 +                return self;
 +            }
 +            UseSegment::Slf(None) if self.path.is_empty() && self.visibility.is_some() => {
 +                self.path = vec![];
 +                return self;
 +            }
 +            _ => (),
 +        }
 +
 +        // Normalise foo::self -> foo.
 +        if let UseSegment::Slf(None) = last {
 +            if !self.path.is_empty() {
 +                return self;
 +            }
 +        }
 +
 +        // Normalise foo::self as bar -> foo as bar.
 +        if let UseSegment::Slf(_) = last {
 +            if let Some(UseSegment::Ident(_, None)) = self.path.last() {
 +                aliased_self = true;
 +            }
 +        }
 +
 +        let mut done = false;
 +        if aliased_self {
 +            match self.path.last_mut() {
 +                Some(UseSegment::Ident(_, ref mut old_rename)) => {
 +                    assert!(old_rename.is_none());
 +                    if let UseSegment::Slf(Some(rename)) = last.clone() {
 +                        *old_rename = Some(rename);
 +                        done = true;
 +                    }
 +                }
 +                _ => unreachable!(),
 +            }
 +        }
 +
 +        if done {
 +            return self;
 +        }
 +
 +        // Normalise foo::{bar} -> foo::bar
 +        if let UseSegment::List(ref list) = last {
 +            if list.len() == 1 && list[0].to_string() != "self" {
 +                normalize_sole_list = true;
 +            }
 +        }
 +
 +        if normalize_sole_list {
 +            match last {
 +                UseSegment::List(list) => {
 +                    for seg in &list[0].path {
 +                        self.path.push(seg.clone());
 +                    }
 +                    return self.normalize();
 +                }
 +                _ => unreachable!(),
 +            }
 +        }
 +
 +        // Recursively normalize elements of a list use (including sorting the list).
 +        if let UseSegment::List(list) = last {
 +            let mut list = list.into_iter().map(UseTree::normalize).collect::<Vec<_>>();
 +            list.sort();
 +            last = UseSegment::List(list);
 +        }
 +
 +        self.path.push(last);
 +        self
 +    }
 +
 +    fn has_comment(&self) -> bool {
 +        self.list_item.as_ref().map_or(false, ListItem::has_comment)
 +    }
 +
 +    fn same_visibility(&self, other: &UseTree) -> bool {
 +        match (&self.visibility, &other.visibility) {
 +            (
 +                Some(ast::Visibility {
 +                    kind: ast::VisibilityKind::Inherited,
 +                    ..
 +                }),
 +                None,
 +            )
 +            | (
 +                None,
 +                Some(ast::Visibility {
 +                    kind: ast::VisibilityKind::Inherited,
 +                    ..
 +                }),
 +            )
 +            | (None, None) => true,
 +            (Some(ref a), Some(ref b)) => is_same_visibility(a, b),
 +            _ => false,
 +        }
 +    }
 +
 +    fn share_prefix(&self, other: &UseTree, shared_prefix: SharedPrefix) -> bool {
 +        if self.path.is_empty()
 +            || other.path.is_empty()
 +            || self.attrs.is_some()
 +            || !self.same_visibility(other)
 +        {
 +            false
 +        } else {
 +            match shared_prefix {
 +                SharedPrefix::Crate => self.path[0] == other.path[0],
 +                SharedPrefix::Module => {
 +                    self.path[..self.path.len() - 1] == other.path[..other.path.len() - 1]
 +                }
 +                SharedPrefix::One => true,
 +            }
 +        }
 +    }
 +
 +    fn flatten(self) -> Vec<UseTree> {
 +        if self.path.is_empty() {
 +            return vec![self];
 +        }
 +        match self.path.clone().last().unwrap() {
 +            UseSegment::List(list) => {
 +                if list.len() == 1 && list[0].path.len() == 1 {
 +                    if let UseSegment::Slf(..) = list[0].path[0] {
 +                        return vec![self];
 +                    };
 +                }
 +                let prefix = &self.path[..self.path.len() - 1];
 +                let mut result = vec![];
 +                for nested_use_tree in list {
 +                    for flattend in &mut nested_use_tree.clone().flatten() {
 +                        let mut new_path = prefix.to_vec();
 +                        new_path.append(&mut flattend.path);
 +                        result.push(UseTree {
 +                            path: new_path,
 +                            span: self.span,
 +                            list_item: None,
 +                            visibility: self.visibility.clone(),
 +                            attrs: None,
 +                        });
 +                    }
 +                }
 +
 +                result
 +            }
 +            _ => vec![self],
 +        }
 +    }
 +
 +    fn merge(&mut self, other: &UseTree, merge_by: SharedPrefix) {
 +        let mut prefix = 0;
 +        for (a, b) in self.path.iter().zip(other.path.iter()) {
++            // only discard the alias at the root of the tree
++            if (prefix == 0 && a.equal_except_alias(b)) || a == b {
 +                prefix += 1;
 +            } else {
 +                break;
 +            }
 +        }
 +        if let Some(new_path) = merge_rest(&self.path, &other.path, prefix, merge_by) {
 +            self.path = new_path;
 +            self.span = self.span.to(other.span);
 +        }
 +    }
++
++    /// If this tree ends in `::self`, rewrite it to `::{self}`.
++    fn nest_trailing_self(mut self) -> UseTree {
++        if let Some(UseSegment::Slf(..)) = self.path.last() {
++            let self_segment = self.path.pop().unwrap();
++            self.path.push(UseSegment::List(vec![UseTree::from_path(
++                vec![self_segment],
++                DUMMY_SP,
++            )]));
++        }
++        self
++    }
 +}
 +
 +fn merge_rest(
 +    a: &[UseSegment],
 +    b: &[UseSegment],
 +    mut len: usize,
 +    merge_by: SharedPrefix,
 +) -> Option<Vec<UseSegment>> {
 +    if a.len() == len && b.len() == len {
 +        return None;
 +    }
 +    if a.len() != len && b.len() != len {
 +        if let UseSegment::List(ref list) = a[len] {
 +            let mut list = list.clone();
 +            merge_use_trees_inner(
 +                &mut list,
 +                UseTree::from_path(b[len..].to_vec(), DUMMY_SP),
 +                merge_by,
 +            );
 +            let mut new_path = b[..len].to_vec();
 +            new_path.push(UseSegment::List(list));
 +            return Some(new_path);
 +        }
 +    } else if len == 1 {
 +        let (common, rest) = if a.len() == len {
 +            (&a[0], &b[1..])
 +        } else {
 +            (&b[0], &a[1..])
 +        };
 +        let mut list = vec![UseTree::from_path(
 +            vec![UseSegment::Slf(common.get_alias().map(ToString::to_string))],
 +            DUMMY_SP,
 +        )];
 +        match rest {
 +            [UseSegment::List(rest_list)] => list.extend(rest_list.clone()),
 +            _ => list.push(UseTree::from_path(rest.to_vec(), DUMMY_SP)),
 +        }
 +        return Some(vec![b[0].clone(), UseSegment::List(list)]);
 +    } else {
 +        len -= 1;
 +    }
 +    let mut list = vec![
 +        UseTree::from_path(a[len..].to_vec(), DUMMY_SP),
 +        UseTree::from_path(b[len..].to_vec(), DUMMY_SP),
 +    ];
 +    list.sort();
 +    let mut new_path = b[..len].to_vec();
 +    new_path.push(UseSegment::List(list));
 +    Some(new_path)
 +}
 +
 +fn merge_use_trees_inner(trees: &mut Vec<UseTree>, use_tree: UseTree, merge_by: SharedPrefix) {
 +    struct SimilarTree<'a> {
 +        similarity: usize,
 +        path_len: usize,
 +        tree: &'a mut UseTree,
 +    }
 +
 +    let similar_trees = trees.iter_mut().filter_map(|tree| {
 +        if tree.share_prefix(&use_tree, merge_by) {
 +            // In the case of `SharedPrefix::One`, `similarity` is used for deciding with which
 +            // tree `use_tree` should be merge.
 +            // In other cases `similarity` won't be used, so set it to `0` as a dummy value.
 +            let similarity = if merge_by == SharedPrefix::One {
 +                tree.path
 +                    .iter()
 +                    .zip(&use_tree.path)
 +                    .take_while(|(a, b)| a.equal_except_alias(b))
 +                    .count()
 +            } else {
 +                0
 +            };
 +
 +            let path_len = tree.path.len();
 +            Some(SimilarTree {
 +                similarity,
 +                tree,
 +                path_len,
 +            })
 +        } else {
 +            None
 +        }
 +    });
 +
 +    if use_tree.path.len() == 1 && merge_by == SharedPrefix::Crate {
 +        if let Some(tree) = similar_trees.min_by_key(|tree| tree.path_len) {
 +            if tree.path_len == 1 {
 +                return;
 +            }
 +        }
 +    } else if merge_by == SharedPrefix::One {
 +        if let Some(sim_tree) = similar_trees.max_by_key(|tree| tree.similarity) {
 +            if sim_tree.similarity > 0 {
 +                sim_tree.tree.merge(&use_tree, merge_by);
 +                return;
 +            }
 +        }
 +    } else if let Some(sim_tree) = similar_trees.max_by_key(|tree| tree.path_len) {
 +        if sim_tree.path_len > 1 {
 +            sim_tree.tree.merge(&use_tree, merge_by);
 +            return;
 +        }
 +    }
 +    trees.push(use_tree);
 +    trees.sort();
 +}
 +
 +impl PartialOrd for UseSegment {
 +    fn partial_cmp(&self, other: &UseSegment) -> Option<Ordering> {
 +        Some(self.cmp(other))
 +    }
 +}
 +impl PartialOrd for UseTree {
 +    fn partial_cmp(&self, other: &UseTree) -> Option<Ordering> {
 +        Some(self.cmp(other))
 +    }
 +}
 +impl Ord for UseSegment {
 +    fn cmp(&self, other: &UseSegment) -> Ordering {
 +        use self::UseSegment::*;
 +
 +        fn is_upper_snake_case(s: &str) -> bool {
 +            s.chars()
 +                .all(|c| c.is_uppercase() || c == '_' || c.is_numeric())
 +        }
 +
 +        match (self, other) {
 +            (&Slf(ref a), &Slf(ref b))
 +            | (&Super(ref a), &Super(ref b))
 +            | (&Crate(ref a), &Crate(ref b)) => a.cmp(b),
 +            (&Glob, &Glob) => Ordering::Equal,
 +            (&Ident(ref ia, ref aa), &Ident(ref ib, ref ab)) => {
 +                // snake_case < CamelCase < UPPER_SNAKE_CASE
 +                if ia.starts_with(char::is_uppercase) && ib.starts_with(char::is_lowercase) {
 +                    return Ordering::Greater;
 +                }
 +                if ia.starts_with(char::is_lowercase) && ib.starts_with(char::is_uppercase) {
 +                    return Ordering::Less;
 +                }
 +                if is_upper_snake_case(ia) && !is_upper_snake_case(ib) {
 +                    return Ordering::Greater;
 +                }
 +                if !is_upper_snake_case(ia) && is_upper_snake_case(ib) {
 +                    return Ordering::Less;
 +                }
 +                let ident_ord = ia.cmp(ib);
 +                if ident_ord != Ordering::Equal {
 +                    return ident_ord;
 +                }
 +                if aa.is_none() && ab.is_some() {
 +                    return Ordering::Less;
 +                }
 +                if aa.is_some() && ab.is_none() {
 +                    return Ordering::Greater;
 +                }
 +                aa.cmp(ab)
 +            }
 +            (&List(ref a), &List(ref b)) => {
 +                for (a, b) in a.iter().zip(b.iter()) {
 +                    let ord = a.cmp(b);
 +                    if ord != Ordering::Equal {
 +                        return ord;
 +                    }
 +                }
 +
 +                a.len().cmp(&b.len())
 +            }
 +            (&Slf(_), _) => Ordering::Less,
 +            (_, &Slf(_)) => Ordering::Greater,
 +            (&Super(_), _) => Ordering::Less,
 +            (_, &Super(_)) => Ordering::Greater,
 +            (&Crate(_), _) => Ordering::Less,
 +            (_, &Crate(_)) => Ordering::Greater,
 +            (&Ident(..), _) => Ordering::Less,
 +            (_, &Ident(..)) => Ordering::Greater,
 +            (&Glob, _) => Ordering::Less,
 +            (_, &Glob) => Ordering::Greater,
 +        }
 +    }
 +}
 +impl Ord for UseTree {
 +    fn cmp(&self, other: &UseTree) -> Ordering {
 +        for (a, b) in self.path.iter().zip(other.path.iter()) {
 +            let ord = a.cmp(b);
 +            // The comparison without aliases is a hack to avoid situations like
 +            // comparing `a::b` to `a as c` - where the latter should be ordered
 +            // first since it is shorter.
 +            if ord != Ordering::Equal && a.remove_alias().cmp(&b.remove_alias()) != Ordering::Equal
 +            {
 +                return ord;
 +            }
 +        }
 +
 +        self.path.len().cmp(&other.path.len())
 +    }
 +}
 +
 +fn rewrite_nested_use_tree(
 +    context: &RewriteContext<'_>,
 +    use_tree_list: &[UseTree],
 +    shape: Shape,
 +) -> Option<String> {
 +    let mut list_items = Vec::with_capacity(use_tree_list.len());
 +    let nested_shape = match context.config.imports_indent() {
 +        IndentStyle::Block => shape
 +            .block_indent(context.config.tab_spaces())
 +            .with_max_width(context.config)
 +            .sub_width(1)?,
 +        IndentStyle::Visual => shape.visual_indent(0),
 +    };
 +    for use_tree in use_tree_list {
 +        if let Some(mut list_item) = use_tree.list_item.clone() {
 +            list_item.item = use_tree.rewrite(context, nested_shape);
 +            list_items.push(list_item);
 +        } else {
 +            list_items.push(ListItem::from_str(use_tree.rewrite(context, nested_shape)?));
 +        }
 +    }
 +    let has_nested_list = use_tree_list.iter().any(|use_segment| {
 +        use_segment.path.last().map_or(false, |last_segment| {
 +            matches!(last_segment, UseSegment::List(..))
 +        })
 +    });
 +
 +    let remaining_width = if has_nested_list {
 +        0
 +    } else {
 +        shape.width.saturating_sub(2)
 +    };
 +
 +    let tactic = definitive_tactic(
 +        &list_items,
 +        context.config.imports_layout(),
 +        Separator::Comma,
 +        remaining_width,
 +    );
 +
 +    let ends_with_newline = context.config.imports_indent() == IndentStyle::Block
 +        && tactic != DefinitiveListTactic::Horizontal;
 +    let trailing_separator = if ends_with_newline {
 +        context.config.trailing_comma()
 +    } else {
 +        SeparatorTactic::Never
 +    };
 +    let fmt = ListFormatting::new(nested_shape, context.config)
 +        .tactic(tactic)
 +        .trailing_separator(trailing_separator)
 +        .ends_with_newline(ends_with_newline)
 +        .preserve_newline(true)
 +        .nested(has_nested_list);
 +
 +    let list_str = write_list(&list_items, &fmt)?;
 +
 +    let result = if (list_str.contains('\n') || list_str.len() > remaining_width)
 +        && context.config.imports_indent() == IndentStyle::Block
 +    {
 +        format!(
 +            "{{\n{}{}\n{}}}",
 +            nested_shape.indent.to_string(context.config),
 +            list_str,
 +            shape.indent.to_string(context.config)
 +        )
 +    } else {
 +        format!("{{{}}}", list_str)
 +    };
 +
 +    Some(result)
 +}
 +
 +impl Rewrite for UseSegment {
 +    fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
 +        Some(match self {
 +            UseSegment::Ident(ref ident, Some(ref rename)) => format!("{} as {}", ident, rename),
 +            UseSegment::Ident(ref ident, None) => ident.clone(),
 +            UseSegment::Slf(Some(ref rename)) => format!("self as {}", rename),
 +            UseSegment::Slf(None) => "self".to_owned(),
 +            UseSegment::Super(Some(ref rename)) => format!("super as {}", rename),
 +            UseSegment::Super(None) => "super".to_owned(),
 +            UseSegment::Crate(Some(ref rename)) => format!("crate as {}", rename),
 +            UseSegment::Crate(None) => "crate".to_owned(),
 +            UseSegment::Glob => "*".to_owned(),
 +            UseSegment::List(ref use_tree_list) => rewrite_nested_use_tree(
 +                context,
 +                use_tree_list,
 +                // 1 = "{" and "}"
 +                shape.offset_left(1)?.sub_width(1)?,
 +            )?,
 +        })
 +    }
 +}
 +
 +impl Rewrite for UseTree {
 +    // This does NOT format attributes and visibility or add a trailing `;`.
 +    fn rewrite(&self, context: &RewriteContext<'_>, mut shape: Shape) -> Option<String> {
 +        let mut result = String::with_capacity(256);
 +        let mut iter = self.path.iter().peekable();
 +        while let Some(segment) = iter.next() {
 +            let segment_str = segment.rewrite(context, shape)?;
 +            result.push_str(&segment_str);
 +            if iter.peek().is_some() {
 +                result.push_str("::");
 +                // 2 = "::"
 +                shape = shape.offset_left(2 + segment_str.len())?;
 +            }
 +        }
 +        Some(result)
 +    }
 +}
 +
 +#[derive(Copy, Clone, Debug, PartialEq, Eq)]
 +pub(crate) enum SharedPrefix {
 +    Crate,
 +    Module,
 +    One,
 +}
 +
 +#[cfg(test)]
 +mod test {
 +    use super::*;
 +    use rustc_span::DUMMY_SP;
 +
 +    // Parse the path part of an import. This parser is not robust and is only
 +    // suitable for use in a test harness.
 +    fn parse_use_tree(s: &str) -> UseTree {
 +        use std::iter::Peekable;
 +        use std::mem::swap;
 +        use std::str::Chars;
 +
 +        struct Parser<'a> {
 +            input: Peekable<Chars<'a>>,
 +        }
 +
 +        impl<'a> Parser<'a> {
 +            fn bump(&mut self) {
 +                self.input.next().unwrap();
 +            }
 +
 +            fn eat(&mut self, c: char) {
 +                assert_eq!(self.input.next().unwrap(), c);
 +            }
 +
 +            fn push_segment(
 +                result: &mut Vec<UseSegment>,
 +                buf: &mut String,
 +                alias_buf: &mut Option<String>,
 +            ) {
 +                if !buf.is_empty() {
 +                    let mut alias = None;
 +                    swap(alias_buf, &mut alias);
 +
 +                    match buf.as_ref() {
 +                        "self" => {
 +                            result.push(UseSegment::Slf(alias));
 +                            *buf = String::new();
 +                            *alias_buf = None;
 +                        }
 +                        "super" => {
 +                            result.push(UseSegment::Super(alias));
 +                            *buf = String::new();
 +                            *alias_buf = None;
 +                        }
 +                        "crate" => {
 +                            result.push(UseSegment::Crate(alias));
 +                            *buf = String::new();
 +                            *alias_buf = None;
 +                        }
 +                        _ => {
 +                            let mut name = String::new();
 +                            swap(buf, &mut name);
 +                            result.push(UseSegment::Ident(name, alias));
 +                        }
 +                    }
 +                }
 +            }
 +
 +            fn parse_in_list(&mut self) -> UseTree {
 +                let mut result = vec![];
 +                let mut buf = String::new();
 +                let mut alias_buf = None;
 +                while let Some(&c) = self.input.peek() {
 +                    match c {
 +                        '{' => {
 +                            assert!(buf.is_empty());
 +                            self.bump();
 +                            result.push(UseSegment::List(self.parse_list()));
 +                            self.eat('}');
 +                        }
 +                        '*' => {
 +                            assert!(buf.is_empty());
 +                            self.bump();
 +                            result.push(UseSegment::Glob);
 +                        }
 +                        ':' => {
 +                            self.bump();
 +                            self.eat(':');
 +                            Self::push_segment(&mut result, &mut buf, &mut alias_buf);
 +                        }
 +                        '}' | ',' => {
 +                            Self::push_segment(&mut result, &mut buf, &mut alias_buf);
 +                            return UseTree {
 +                                path: result,
 +                                span: DUMMY_SP,
 +                                list_item: None,
 +                                visibility: None,
 +                                attrs: None,
 +                            };
 +                        }
 +                        ' ' => {
 +                            self.bump();
 +                            self.eat('a');
 +                            self.eat('s');
 +                            self.eat(' ');
 +                            alias_buf = Some(String::new());
 +                        }
 +                        c => {
 +                            self.bump();
 +                            if let Some(ref mut buf) = alias_buf {
 +                                buf.push(c);
 +                            } else {
 +                                buf.push(c);
 +                            }
 +                        }
 +                    }
 +                }
 +                Self::push_segment(&mut result, &mut buf, &mut alias_buf);
 +                UseTree {
 +                    path: result,
 +                    span: DUMMY_SP,
 +                    list_item: None,
 +                    visibility: None,
 +                    attrs: None,
 +                }
 +            }
 +
 +            fn parse_list(&mut self) -> Vec<UseTree> {
 +                let mut result = vec![];
 +                loop {
 +                    match self.input.peek().unwrap() {
 +                        ',' | ' ' => self.bump(),
 +                        '}' => {
 +                            return result;
 +                        }
 +                        _ => result.push(self.parse_in_list()),
 +                    }
 +                }
 +            }
 +        }
 +
 +        let mut parser = Parser {
 +            input: s.chars().peekable(),
 +        };
 +        parser.parse_in_list()
 +    }
 +
 +    macro_rules! parse_use_trees {
 +        ($($s:expr),* $(,)*) => {
 +            vec![
 +                $(parse_use_tree($s),)*
 +            ]
 +        }
 +    }
 +
 +    macro_rules! test_merge {
 +        ($by:ident, [$($input:expr),* $(,)*], [$($output:expr),* $(,)*]) => {
 +            assert_eq!(
 +                merge_use_trees(parse_use_trees!($($input,)*), SharedPrefix::$by),
 +                parse_use_trees!($($output,)*),
 +            );
 +        }
 +    }
 +
 +    #[test]
 +    fn test_use_tree_merge_crate() {
 +        test_merge!(
 +            Crate,
 +            ["a::b::{c, d}", "a::b::{e, f}"],
 +            ["a::b::{c, d, e, f}"]
 +        );
 +        test_merge!(Crate, ["a::b::c", "a::b"], ["a::{b, b::c}"]);
 +        test_merge!(Crate, ["a::b", "a::b"], ["a::b"]);
 +        test_merge!(Crate, ["a", "a::b", "a::b::c"], ["a::{self, b, b::c}"]);
 +        test_merge!(
 +            Crate,
 +            ["a", "a::b", "a::b::c", "a::b::c::d"],
 +            ["a::{self, b, b::{c, c::d}}"]
 +        );
 +        test_merge!(
 +            Crate,
 +            ["a", "a::b", "a::b::c", "a::b"],
 +            ["a::{self, b, b::c}"]
 +        );
 +        test_merge!(
 +            Crate,
 +            ["a::{b::{self, c}, d::e}", "a::d::f"],
 +            ["a::{b::{self, c}, d::{e, f}}"]
 +        );
 +        test_merge!(
 +            Crate,
 +            ["a::d::f", "a::{b::{self, c}, d::e}"],
 +            ["a::{b::{self, c}, d::{e, f}}"]
 +        );
 +        test_merge!(
 +            Crate,
 +            ["a::{c, d, b}", "a::{d, e, b, a, f}", "a::{f, g, c}"],
 +            ["a::{a, b, c, d, e, f, g}"]
 +        );
 +        test_merge!(
 +            Crate,
 +            ["a::{self}", "b::{self as foo}"],
 +            ["a::{self}", "b::{self as foo}"]
 +        );
 +    }
 +
 +    #[test]
 +    fn test_use_tree_merge_module() {
 +        test_merge!(
 +            Module,
 +            ["foo::b", "foo::{a, c, d::e}"],
 +            ["foo::{a, b, c}", "foo::d::e"]
 +        );
 +
 +        test_merge!(
 +            Module,
 +            ["foo::{a::b, a::c, d::e, d::f}"],
 +            ["foo::a::{b, c}", "foo::d::{e, f}"]
 +        );
 +    }
 +
 +    #[test]
 +    fn test_use_tree_merge_one() {
 +        test_merge!(One, ["a", "b"], ["{a, b}"]);
 +
 +        test_merge!(One, ["a::{aa, ab}", "b", "a"], ["{a::{self, aa, ab}, b}"]);
 +
 +        test_merge!(One, ["a as x", "b as y"], ["{a as x, b as y}"]);
 +
 +        test_merge!(
 +            One,
 +            ["a::{aa as xa, ab}", "b", "a"],
 +            ["{a::{self, aa as xa, ab}, b}"]
 +        );
 +
 +        test_merge!(
 +            One,
 +            ["a", "a::{aa, ab::{aba, abb}}"],
 +            ["a::{self, aa, ab::{aba, abb}}"]
 +        );
 +
 +        test_merge!(One, ["a", "b::{ba, *}"], ["{a, b::{ba, *}}"]);
 +
 +        test_merge!(One, ["a", "b", "a::aa"], ["{a::{self, aa}, b}"]);
 +
 +        test_merge!(
 +            One,
 +            ["a::aa::aaa", "a::ac::aca", "a::aa::*"],
 +            ["a::{aa::{aaa, *}, ac::aca}"]
 +        );
 +
 +        test_merge!(
 +            One,
 +            ["a", "b::{ba, bb}", "a::{aa::*, ab::aba}"],
 +            ["{a::{self, aa::*, ab::aba}, b::{ba, bb}}"]
 +        );
 +
 +        test_merge!(
 +            One,
 +            ["b", "a::ac::{aca, acb}", "a::{aa::*, ab}"],
 +            ["{a::{aa::*, ab, ac::{aca, acb}}, b}"]
 +        );
 +    }
 +
 +    #[test]
 +    fn test_flatten_use_trees() {
 +        assert_eq!(
 +            flatten_use_trees(parse_use_trees!["foo::{a::{b, c}, d::e}"]),
 +            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::*}"]),
 +            parse_use_trees![
 +                "foo::{self}",
 +                "foo::a",
 +                "foo::b::c",
 +                "foo::b::d",
 +                "foo::e::*"
 +            ]
 +        );
 +    }
 +
 +    #[test]
 +    fn test_use_tree_flatten() {
 +        assert_eq!(
 +            parse_use_tree("a::b::{c, d, e, f}").flatten(),
 +            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_trees![
 +                "a::b::c::d",
 +                "a::b::c::e",
 +                "a::b::c::f",
 +                "a::b::g",
 +                "a::b::h::i",
 +                "a::b::h::j",
 +                "a::b::h::k",
 +            ]
 +        );
 +    }
 +
 +    #[test]
 +    fn test_use_tree_normalize() {
 +        assert_eq!(parse_use_tree("a::self").normalize(), parse_use_tree("a"));
 +        assert_eq!(
 +            parse_use_tree("a::self as foo").normalize(),
 +            parse_use_tree("a as foo")
 +        );
 +        assert_eq!(
 +            parse_use_tree("a::{self}").normalize(),
 +            parse_use_tree("a::{self}")
 +        );
 +        assert_eq!(parse_use_tree("a::{b}").normalize(), parse_use_tree("a::b"));
 +        assert_eq!(
 +            parse_use_tree("a::{b, c::self}").normalize(),
 +            parse_use_tree("a::{b, c}")
 +        );
 +        assert_eq!(
 +            parse_use_tree("a::{b as bar, c::self}").normalize(),
 +            parse_use_tree("a::{b as bar, c}")
 +        );
 +    }
 +
 +    #[test]
 +    fn test_use_tree_ord() {
 +        assert!(parse_use_tree("a").normalize() < parse_use_tree("aa").normalize());
 +        assert!(parse_use_tree("a").normalize() < parse_use_tree("a::a").normalize());
 +        assert!(parse_use_tree("a").normalize() < parse_use_tree("*").normalize());
 +        assert!(parse_use_tree("a").normalize() < parse_use_tree("{a, b}").normalize());
 +        assert!(parse_use_tree("*").normalize() < parse_use_tree("{a, b}").normalize());
 +
 +        assert!(
 +            parse_use_tree("aaaaaaaaaaaaaaa::{bb, cc, dddddddd}").normalize()
 +                < parse_use_tree("aaaaaaaaaaaaaaa::{bb, cc, ddddddddd}").normalize()
 +        );
 +        assert!(
 +            parse_use_tree("serde::de::{Deserialize}").normalize()
 +                < parse_use_tree("serde_json").normalize()
 +        );
 +        assert!(parse_use_tree("a::b::c").normalize() < parse_use_tree("a::b::*").normalize());
 +        assert!(
 +            parse_use_tree("foo::{Bar, Baz}").normalize()
 +                < parse_use_tree("{Bar, Baz}").normalize()
 +        );
 +
 +        assert!(
 +            parse_use_tree("foo::{qux as bar}").normalize()
 +                < parse_use_tree("foo::{self as bar}").normalize()
 +        );
 +        assert!(
 +            parse_use_tree("foo::{qux as bar}").normalize()
 +                < parse_use_tree("foo::{baz, qux as bar}").normalize()
 +        );
 +        assert!(
 +            parse_use_tree("foo::{self as bar, baz}").normalize()
 +                < parse_use_tree("foo::{baz, qux as bar}").normalize()
 +        );
 +
 +        assert!(parse_use_tree("foo").normalize() < parse_use_tree("Foo").normalize());
 +        assert!(parse_use_tree("foo").normalize() < parse_use_tree("foo::Bar").normalize());
 +
 +        assert!(
 +            parse_use_tree("std::cmp::{d, c, b, a}").normalize()
 +                < parse_use_tree("std::cmp::{b, e, g, f}").normalize()
 +        );
 +    }
++
++    #[test]
++    fn test_use_tree_nest_trailing_self() {
++        assert_eq!(
++            parse_use_tree("a::b::self").nest_trailing_self(),
++            parse_use_tree("a::b::{self}")
++        );
++        assert_eq!(
++            parse_use_tree("a::b::c").nest_trailing_self(),
++            parse_use_tree("a::b::c")
++        );
++        assert_eq!(
++            parse_use_tree("a::b::{c, d}").nest_trailing_self(),
++            parse_use_tree("a::b::{c, d}")
++        );
++        assert_eq!(
++            parse_use_tree("a::b::{self, c}").nest_trailing_self(),
++            parse_use_tree("a::b::{self, c}")
++        );
++    }
 +}
index 8498cb6addaa17375217846b654e2c842db16cd6,0000000000000000000000000000000000000000..92f423bbb6275d647e6116a5ca6f6d1c48e8693d
mode 100644,000000..100644
--- /dev/null
@@@ -1,3314 -1,0 +1,3330 @@@
-     let body_lo = context.snippet_provider.span_after(span, "{");
 +// Formatting top-level items - functions, structs, enums, traits, impls.
 +
 +use std::borrow::Cow;
 +use std::cmp::{max, min, Ordering};
 +
 +use regex::Regex;
 +use rustc_ast::visit;
 +use rustc_ast::{ast, ptr};
 +use rustc_span::{symbol, BytePos, Span, DUMMY_SP};
 +
 +use crate::attr::filter_inline_attrs;
 +use crate::comment::{
 +    combine_strs_with_missing_comments, contains_comment, is_last_comment_block,
 +    recover_comment_removed, recover_missing_comment_in_span, rewrite_missing_comment,
 +    FindUncommented,
 +};
 +use crate::config::lists::*;
 +use crate::config::{BraceStyle, Config, IndentStyle, Version};
 +use crate::expr::{
 +    is_empty_block, is_simple_block_stmt, rewrite_assign_rhs, rewrite_assign_rhs_with,
 +    rewrite_assign_rhs_with_comments, RhsAssignKind, RhsTactics,
 +};
 +use crate::lists::{definitive_tactic, itemize_list, write_list, ListFormatting, Separator};
 +use crate::macros::{rewrite_macro, MacroPosition};
 +use crate::overflow;
 +use crate::rewrite::{Rewrite, RewriteContext};
 +use crate::shape::{Indent, Shape};
 +use crate::source_map::{LineRangeUtils, SpanUtils};
 +use crate::spanned::Spanned;
 +use crate::stmt::Stmt;
 +use crate::types::opaque_ty;
 +use crate::utils::*;
 +use crate::vertical::rewrite_with_alignment;
 +use crate::visitor::FmtVisitor;
 +
 +const DEFAULT_VISIBILITY: ast::Visibility = ast::Visibility {
 +    kind: ast::VisibilityKind::Inherited,
 +    span: DUMMY_SP,
 +    tokens: None,
 +};
 +
 +fn type_annotation_separator(config: &Config) -> &str {
 +    colon_spaces(config)
 +}
 +
 +// Statements of the form
 +// let pat: ty = init;
 +impl Rewrite for ast::Local {
 +    fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
 +        debug!(
 +            "Local::rewrite {:?} {} {:?}",
 +            self, shape.width, shape.indent
 +        );
 +
 +        skip_out_of_file_lines_range!(context, self.span);
 +
 +        if contains_skip(&self.attrs) || matches!(self.kind, ast::LocalKind::InitElse(..)) {
 +            return None;
 +        }
 +
 +        let attrs_str = self.attrs.rewrite(context, shape)?;
 +        let mut result = if attrs_str.is_empty() {
 +            "let ".to_owned()
 +        } else {
 +            combine_strs_with_missing_comments(
 +                context,
 +                &attrs_str,
 +                "let ",
 +                mk_sp(
 +                    self.attrs.last().map(|a| a.span.hi()).unwrap(),
 +                    self.span.lo(),
 +                ),
 +                shape,
 +                false,
 +            )?
 +        };
 +
 +        // 4 = "let ".len()
 +        let pat_shape = shape.offset_left(4)?;
 +        // 1 = ;
 +        let pat_shape = pat_shape.sub_width(1)?;
 +        let pat_str = self.pat.rewrite(context, pat_shape)?;
 +        result.push_str(&pat_str);
 +
 +        // String that is placed within the assignment pattern and expression.
 +        let infix = {
 +            let mut infix = String::with_capacity(32);
 +
 +            if let Some(ref ty) = self.ty {
 +                let separator = type_annotation_separator(context.config);
 +                let ty_shape = if pat_str.contains('\n') {
 +                    shape.with_max_width(context.config)
 +                } else {
 +                    shape
 +                }
 +                .offset_left(last_line_width(&result) + separator.len())?
 +                // 2 = ` =`
 +                .sub_width(2)?;
 +
 +                let rewrite = ty.rewrite(context, ty_shape)?;
 +
 +                infix.push_str(separator);
 +                infix.push_str(&rewrite);
 +            }
 +
 +            if self.kind.init().is_some() {
 +                infix.push_str(" =");
 +            }
 +
 +            infix
 +        };
 +
 +        result.push_str(&infix);
 +
 +        if let Some((init, _els)) = self.kind.init_else_opt() {
 +            // 1 = trailing semicolon;
 +            let nested_shape = shape.sub_width(1)?;
 +
 +            result = rewrite_assign_rhs(
 +                context,
 +                result,
 +                init,
 +                &RhsAssignKind::Expr(&init.kind, init.span),
 +                nested_shape,
 +            )?;
 +            // todo else
 +        }
 +
 +        result.push(';');
 +        Some(result)
 +    }
 +}
 +
 +// FIXME convert to using rewrite style rather than visitor
 +// FIXME format modules in this style
 +#[allow(dead_code)]
 +#[derive(Debug)]
 +struct Item<'a> {
 +    unsafety: ast::Unsafe,
 +    abi: Cow<'static, str>,
 +    vis: Option<&'a ast::Visibility>,
 +    body: Vec<BodyElement<'a>>,
 +    span: Span,
 +}
 +
 +impl<'a> Item<'a> {
 +    fn from_foreign_mod(fm: &'a ast::ForeignMod, span: Span, config: &Config) -> Item<'a> {
 +        Item {
 +            unsafety: fm.unsafety,
 +            abi: format_extern(
 +                ast::Extern::from_abi(fm.abi),
 +                config.force_explicit_abi(),
 +                true,
 +            ),
 +            vis: None,
 +            body: fm
 +                .items
 +                .iter()
 +                .map(|i| BodyElement::ForeignItem(i))
 +                .collect(),
 +            span,
 +        }
 +    }
 +}
 +
 +#[derive(Debug)]
 +enum BodyElement<'a> {
 +    // Stmt(&'a ast::Stmt),
 +    // Field(&'a ast::ExprField),
 +    // Variant(&'a ast::Variant),
 +    // Item(&'a ast::Item),
 +    ForeignItem(&'a ast::ForeignItem),
 +}
 +
 +/// Represents a fn's signature.
 +pub(crate) struct FnSig<'a> {
 +    decl: &'a ast::FnDecl,
 +    generics: &'a ast::Generics,
 +    ext: ast::Extern,
 +    is_async: Cow<'a, ast::Async>,
 +    constness: ast::Const,
 +    defaultness: ast::Defaultness,
 +    unsafety: ast::Unsafe,
 +    visibility: &'a ast::Visibility,
 +}
 +
 +impl<'a> FnSig<'a> {
 +    pub(crate) fn from_method_sig(
 +        method_sig: &'a ast::FnSig,
 +        generics: &'a ast::Generics,
 +        visibility: &'a ast::Visibility,
 +    ) -> FnSig<'a> {
 +        FnSig {
 +            unsafety: method_sig.header.unsafety,
 +            is_async: Cow::Borrowed(&method_sig.header.asyncness),
 +            constness: method_sig.header.constness,
 +            defaultness: ast::Defaultness::Final,
 +            ext: method_sig.header.ext,
 +            decl: &*method_sig.decl,
 +            generics,
 +            visibility,
 +        }
 +    }
 +
 +    pub(crate) fn from_fn_kind(
 +        fn_kind: &'a visit::FnKind<'_>,
 +        generics: &'a ast::Generics,
 +        decl: &'a ast::FnDecl,
 +        defaultness: ast::Defaultness,
 +    ) -> FnSig<'a> {
 +        match *fn_kind {
 +            visit::FnKind::Fn(fn_ctxt, _, fn_sig, vis, _) => match fn_ctxt {
 +                visit::FnCtxt::Assoc(..) => {
 +                    let mut fn_sig = FnSig::from_method_sig(fn_sig, generics, vis);
 +                    fn_sig.defaultness = defaultness;
 +                    fn_sig
 +                }
 +                _ => FnSig {
 +                    decl,
 +                    generics,
 +                    ext: fn_sig.header.ext,
 +                    constness: fn_sig.header.constness,
 +                    is_async: Cow::Borrowed(&fn_sig.header.asyncness),
 +                    defaultness,
 +                    unsafety: fn_sig.header.unsafety,
 +                    visibility: vis,
 +                },
 +            },
 +            _ => unreachable!(),
 +        }
 +    }
 +
 +    fn to_str(&self, context: &RewriteContext<'_>) -> String {
 +        let mut result = String::with_capacity(128);
 +        // Vis defaultness constness unsafety abi.
 +        result.push_str(&*format_visibility(context, self.visibility));
 +        result.push_str(format_defaultness(self.defaultness));
 +        result.push_str(format_constness(self.constness));
 +        result.push_str(format_async(&self.is_async));
 +        result.push_str(format_unsafety(self.unsafety));
 +        result.push_str(&format_extern(
 +            self.ext,
 +            context.config.force_explicit_abi(),
 +            false,
 +        ));
 +        result
 +    }
 +}
 +
 +impl<'a> FmtVisitor<'a> {
 +    fn format_item(&mut self, item: &Item<'_>) {
 +        self.buffer.push_str(format_unsafety(item.unsafety));
 +        self.buffer.push_str(&item.abi);
 +
 +        let snippet = self.snippet(item.span);
 +        let brace_pos = snippet.find_uncommented("{").unwrap();
 +
 +        self.push_str("{");
 +        if !item.body.is_empty() || contains_comment(&snippet[brace_pos..]) {
 +            // FIXME: this skips comments between the extern keyword and the opening
 +            // brace.
 +            self.last_pos = item.span.lo() + BytePos(brace_pos as u32 + 1);
 +            self.block_indent = self.block_indent.block_indent(self.config);
 +
 +            if !item.body.is_empty() {
 +                for item in &item.body {
 +                    self.format_body_element(item);
 +                }
 +            }
 +
 +            self.format_missing_no_indent(item.span.hi() - BytePos(1));
 +            self.block_indent = self.block_indent.block_unindent(self.config);
 +            let indent_str = self.block_indent.to_string(self.config);
 +            self.push_str(&indent_str);
 +        }
 +
 +        self.push_str("}");
 +        self.last_pos = item.span.hi();
 +    }
 +
 +    fn format_body_element(&mut self, element: &BodyElement<'_>) {
 +        match *element {
 +            BodyElement::ForeignItem(item) => self.format_foreign_item(item),
 +        }
 +    }
 +
 +    pub(crate) fn format_foreign_mod(&mut self, fm: &ast::ForeignMod, span: Span) {
 +        let item = Item::from_foreign_mod(fm, span, self.config);
 +        self.format_item(&item);
 +    }
 +
 +    fn format_foreign_item(&mut self, item: &ast::ForeignItem) {
 +        let rewrite = item.rewrite(&self.get_context(), self.shape());
 +        let hi = item.span.hi();
 +        let span = if item.attrs.is_empty() {
 +            item.span
 +        } else {
 +            mk_sp(item.attrs[0].span.lo(), hi)
 +        };
 +        self.push_rewrite(span, rewrite);
 +        self.last_pos = hi;
 +    }
 +
 +    pub(crate) fn rewrite_fn_before_block(
 +        &mut self,
 +        indent: Indent,
 +        ident: symbol::Ident,
 +        fn_sig: &FnSig<'_>,
 +        span: Span,
 +    ) -> Option<(String, FnBraceStyle)> {
 +        let context = self.get_context();
 +
 +        let mut fn_brace_style = newline_for_brace(self.config, &fn_sig.generics.where_clause);
 +        let (result, _, force_newline_brace) =
 +            rewrite_fn_base(&context, indent, ident, fn_sig, span, fn_brace_style)?;
 +
 +        // 2 = ` {`
 +        if self.config.brace_style() == BraceStyle::AlwaysNextLine
 +            || force_newline_brace
 +            || last_line_width(&result) + 2 > self.shape().width
 +        {
 +            fn_brace_style = FnBraceStyle::NextLine
 +        }
 +
 +        Some((result, fn_brace_style))
 +    }
 +
 +    pub(crate) fn rewrite_required_fn(
 +        &mut self,
 +        indent: Indent,
 +        ident: symbol::Ident,
 +        sig: &ast::FnSig,
 +        vis: &ast::Visibility,
 +        generics: &ast::Generics,
 +        span: Span,
 +    ) -> Option<String> {
 +        // Drop semicolon or it will be interpreted as comment.
 +        let span = mk_sp(span.lo(), span.hi() - BytePos(1));
 +        let context = self.get_context();
 +
 +        let (mut result, ends_with_comment, _) = rewrite_fn_base(
 +            &context,
 +            indent,
 +            ident,
 +            &FnSig::from_method_sig(sig, generics, vis),
 +            span,
 +            FnBraceStyle::None,
 +        )?;
 +
 +        // If `result` ends with a comment, then remember to add a newline
 +        if ends_with_comment {
 +            result.push_str(&indent.to_string_with_newline(context.config));
 +        }
 +
 +        // Re-attach semicolon
 +        result.push(';');
 +
 +        Some(result)
 +    }
 +
 +    pub(crate) fn single_line_fn(
 +        &self,
 +        fn_str: &str,
 +        block: &ast::Block,
 +        inner_attrs: Option<&[ast::Attribute]>,
 +    ) -> Option<String> {
 +        if fn_str.contains('\n') || inner_attrs.map_or(false, |a| !a.is_empty()) {
 +            return None;
 +        }
 +
 +        let context = self.get_context();
 +
 +        if self.config.empty_item_single_line()
 +            && is_empty_block(&context, block, None)
 +            && self.block_indent.width() + fn_str.len() + 3 <= self.config.max_width()
 +            && !last_line_contains_single_line_comment(fn_str)
 +        {
 +            return Some(format!("{} {{}}", fn_str));
 +        }
 +
 +        if !self.config.fn_single_line() || !is_simple_block_stmt(&context, block, None) {
 +            return None;
 +        }
 +
 +        let res = Stmt::from_ast_node(block.stmts.first()?, true)
 +            .rewrite(&self.get_context(), self.shape())?;
 +
 +        let width = self.block_indent.width() + fn_str.len() + res.len() + 5;
 +        if !res.contains('\n') && width <= self.config.max_width() {
 +            Some(format!("{} {{ {} }}", fn_str, res))
 +        } else {
 +            None
 +        }
 +    }
 +
 +    pub(crate) fn visit_static(&mut self, static_parts: &StaticParts<'_>) {
 +        let rewrite = rewrite_static(&self.get_context(), static_parts, self.block_indent);
 +        self.push_rewrite(static_parts.span, rewrite);
 +    }
 +
 +    pub(crate) fn visit_struct(&mut self, struct_parts: &StructParts<'_>) {
 +        let is_tuple = match struct_parts.def {
 +            ast::VariantData::Tuple(..) => true,
 +            _ => false,
 +        };
 +        let rewrite = format_struct(&self.get_context(), struct_parts, self.block_indent, None)
 +            .map(|s| if is_tuple { s + ";" } else { s });
 +        self.push_rewrite(struct_parts.span, rewrite);
 +    }
 +
 +    pub(crate) fn visit_enum(
 +        &mut self,
 +        ident: symbol::Ident,
 +        vis: &ast::Visibility,
 +        enum_def: &ast::EnumDef,
 +        generics: &ast::Generics,
 +        span: Span,
 +    ) {
 +        let enum_header =
 +            format_header(&self.get_context(), "enum ", ident, vis, self.block_indent);
 +        self.push_str(&enum_header);
 +
 +        let enum_snippet = self.snippet(span);
 +        let brace_pos = enum_snippet.find_uncommented("{").unwrap();
 +        let body_start = span.lo() + BytePos(brace_pos as u32 + 1);
 +        let generics_str = format_generics(
 +            &self.get_context(),
 +            generics,
 +            self.config.brace_style(),
 +            if enum_def.variants.is_empty() {
 +                BracePos::ForceSameLine
 +            } else {
 +                BracePos::Auto
 +            },
 +            self.block_indent,
 +            // make a span that starts right after `enum Foo`
 +            mk_sp(ident.span.hi(), body_start),
 +            last_line_width(&enum_header),
 +        )
 +        .unwrap();
 +        self.push_str(&generics_str);
 +
 +        self.last_pos = body_start;
 +
 +        match self.format_variant_list(enum_def, body_start, span.hi()) {
 +            Some(ref s) if enum_def.variants.is_empty() => self.push_str(s),
 +            rw => {
 +                self.push_rewrite(mk_sp(body_start, span.hi()), rw);
 +                self.block_indent = self.block_indent.block_unindent(self.config);
 +            }
 +        }
 +    }
 +
 +    // Format the body of an enum definition
 +    fn format_variant_list(
 +        &mut self,
 +        enum_def: &ast::EnumDef,
 +        body_lo: BytePos,
 +        body_hi: BytePos,
 +    ) -> Option<String> {
 +        if enum_def.variants.is_empty() {
 +            let mut buffer = String::with_capacity(128);
 +            // 1 = "}"
 +            let span = mk_sp(body_lo, body_hi - BytePos(1));
 +            format_empty_struct_or_tuple(
 +                &self.get_context(),
 +                span,
 +                self.block_indent,
 +                &mut buffer,
 +                "",
 +                "}",
 +            );
 +            return Some(buffer);
 +        }
 +        let mut result = String::with_capacity(1024);
 +        let original_offset = self.block_indent;
 +        self.block_indent = self.block_indent.block_indent(self.config);
 +
 +        // If enum variants have discriminants, try to vertically align those,
 +        // provided the discrims are not shifted too much  to the right
 +        let align_threshold: usize = self.config.enum_discrim_align_threshold();
 +        let discr_ident_lens: Vec<usize> = enum_def
 +            .variants
 +            .iter()
 +            .filter(|var| var.disr_expr.is_some())
 +            .map(|var| rewrite_ident(&self.get_context(), var.ident).len())
 +            .collect();
 +        // cut the list at the point of longest discrim shorter than the threshold
 +        // All of the discrims under the threshold will get padded, and all above - left as is.
 +        let pad_discrim_ident_to = *discr_ident_lens
 +            .iter()
 +            .filter(|&l| *l <= align_threshold)
 +            .max()
 +            .unwrap_or(&0);
 +
 +        let itemize_list_with = |one_line_width: usize| {
 +            itemize_list(
 +                self.snippet_provider,
 +                enum_def.variants.iter(),
 +                "}",
 +                ",",
 +                |f| {
 +                    if !f.attrs.is_empty() {
 +                        f.attrs[0].span.lo()
 +                    } else {
 +                        f.span.lo()
 +                    }
 +                },
 +                |f| f.span.hi(),
 +                |f| self.format_variant(f, one_line_width, pad_discrim_ident_to),
 +                body_lo,
 +                body_hi,
 +                false,
 +            )
 +            .collect()
 +        };
 +        let mut items: Vec<_> = itemize_list_with(self.config.struct_variant_width());
 +
 +        // If one of the variants use multiple lines, use multi-lined formatting for all variants.
 +        let has_multiline_variant = items.iter().any(|item| item.inner_as_ref().contains('\n'));
 +        let has_single_line_variant = items.iter().any(|item| !item.inner_as_ref().contains('\n'));
 +        if has_multiline_variant && has_single_line_variant {
 +            items = itemize_list_with(0);
 +        }
 +
 +        let shape = self.shape().sub_width(2)?;
 +        let fmt = ListFormatting::new(shape, self.config)
 +            .trailing_separator(self.config.trailing_comma())
 +            .preserve_newline(true);
 +
 +        let list = write_list(&items, &fmt)?;
 +        result.push_str(&list);
 +        result.push_str(&original_offset.to_string_with_newline(self.config));
 +        result.push('}');
 +        Some(result)
 +    }
 +
 +    // Variant of an enum.
 +    fn format_variant(
 +        &self,
 +        field: &ast::Variant,
 +        one_line_width: usize,
 +        pad_discrim_ident_to: usize,
 +    ) -> Option<String> {
 +        if contains_skip(&field.attrs) {
 +            let lo = field.attrs[0].span.lo();
 +            let span = mk_sp(lo, field.span.hi());
 +            return Some(self.snippet(span).to_owned());
 +        }
 +
 +        let context = self.get_context();
 +        // 1 = ','
 +        let shape = self.shape().sub_width(1)?;
 +        let attrs_str = field.attrs.rewrite(&context, shape)?;
 +        let lo = field
 +            .attrs
 +            .last()
 +            .map_or(field.span.lo(), |attr| attr.span.hi());
 +        let span = mk_sp(lo, field.span.lo());
 +
 +        let variant_body = match field.data {
 +            ast::VariantData::Tuple(..) | ast::VariantData::Struct(..) => format_struct(
 +                &context,
 +                &StructParts::from_variant(field),
 +                self.block_indent,
 +                Some(one_line_width),
 +            )?,
 +            ast::VariantData::Unit(..) => rewrite_ident(&context, field.ident).to_owned(),
 +        };
 +
 +        let variant_body = if let Some(ref expr) = field.disr_expr {
 +            let lhs = format!("{:1$} =", variant_body, pad_discrim_ident_to);
 +            let ex = &*expr.value;
 +            rewrite_assign_rhs_with(
 +                &context,
 +                lhs,
 +                ex,
 +                shape,
 +                &RhsAssignKind::Expr(&ex.kind, ex.span),
 +                RhsTactics::AllowOverflow,
 +            )?
 +        } else {
 +            variant_body
 +        };
 +
 +        combine_strs_with_missing_comments(&context, &attrs_str, &variant_body, span, shape, false)
 +    }
 +
 +    fn visit_impl_items(&mut self, items: &[ptr::P<ast::AssocItem>]) {
 +        if self.get_context().config.reorder_impl_items() {
 +            type TyOpt = Option<ptr::P<ast::Ty>>;
 +            use crate::ast::AssocItemKind::*;
 +            let is_type = |ty: &TyOpt| opaque_ty(ty).is_none();
 +            let is_opaque = |ty: &TyOpt| opaque_ty(ty).is_some();
 +            let both_type = |l: &TyOpt, r: &TyOpt| is_type(l) && is_type(r);
 +            let both_opaque = |l: &TyOpt, r: &TyOpt| is_opaque(l) && is_opaque(r);
 +            let need_empty_line = |a: &ast::AssocItemKind, b: &ast::AssocItemKind| match (a, b) {
 +                (TyAlias(lty), TyAlias(rty))
 +                    if both_type(&lty.ty, &rty.ty) || both_opaque(&lty.ty, &rty.ty) =>
 +                {
 +                    false
 +                }
 +                (Const(..), Const(..)) => false,
 +                _ => true,
 +            };
 +
 +            // Create visitor for each items, then reorder them.
 +            let mut buffer = vec![];
 +            for item in items {
 +                self.visit_impl_item(item);
 +                buffer.push((self.buffer.clone(), item.clone()));
 +                self.buffer.clear();
 +            }
 +
 +            buffer.sort_by(|(_, a), (_, b)| match (&a.kind, &b.kind) {
 +                (TyAlias(lty), TyAlias(rty))
 +                    if both_type(&lty.ty, &rty.ty) || both_opaque(&lty.ty, &rty.ty) =>
 +                {
 +                    a.ident.as_str().cmp(b.ident.as_str())
 +                }
 +                (Const(..), Const(..)) | (MacCall(..), MacCall(..)) => {
 +                    a.ident.as_str().cmp(b.ident.as_str())
 +                }
 +                (Fn(..), Fn(..)) => a.span.lo().cmp(&b.span.lo()),
 +                (TyAlias(ty), _) if is_type(&ty.ty) => Ordering::Less,
 +                (_, TyAlias(ty)) if is_type(&ty.ty) => Ordering::Greater,
 +                (TyAlias(..), _) => Ordering::Less,
 +                (_, TyAlias(..)) => Ordering::Greater,
 +                (Const(..), _) => Ordering::Less,
 +                (_, Const(..)) => Ordering::Greater,
 +                (MacCall(..), _) => Ordering::Less,
 +                (_, MacCall(..)) => Ordering::Greater,
 +            });
 +            let mut prev_kind = None;
 +            for (buf, item) in buffer {
 +                // Make sure that there are at least a single empty line between
 +                // different impl items.
 +                if prev_kind
 +                    .as_ref()
 +                    .map_or(false, |prev_kind| need_empty_line(prev_kind, &item.kind))
 +                {
 +                    self.push_str("\n");
 +                }
 +                let indent_str = self.block_indent.to_string_with_newline(self.config);
 +                self.push_str(&indent_str);
 +                self.push_str(buf.trim());
 +                prev_kind = Some(item.kind.clone());
 +            }
 +        } else {
 +            for item in items {
 +                self.visit_impl_item(item);
 +            }
 +        }
 +    }
 +}
 +
 +pub(crate) fn format_impl(
 +    context: &RewriteContext<'_>,
 +    item: &ast::Item,
 +    iimpl: &ast::Impl,
 +    offset: Indent,
 +) -> Option<String> {
 +    let ast::Impl {
 +        generics,
 +        self_ty,
 +        items,
 +        ..
 +    } = iimpl;
 +    let mut result = String::with_capacity(128);
 +    let ref_and_type = format_impl_ref_and_type(context, item, iimpl, offset)?;
 +    let sep = offset.to_string_with_newline(context.config);
 +    result.push_str(&ref_and_type);
 +
 +    let where_budget = if result.contains('\n') {
 +        context.config.max_width()
 +    } else {
 +        context.budget(last_line_width(&result))
 +    };
 +
 +    let mut option = WhereClauseOption::snuggled(&ref_and_type);
 +    let snippet = context.snippet(item.span);
 +    let open_pos = snippet.find_uncommented("{")? + 1;
 +    if !contains_comment(&snippet[open_pos..])
 +        && items.is_empty()
 +        && generics.where_clause.predicates.len() == 1
 +        && !result.contains('\n')
 +    {
 +        option.suppress_comma();
 +        option.snuggle();
 +        option.allow_single_line();
 +    }
 +
 +    let missing_span = mk_sp(self_ty.span.hi(), item.span.hi());
 +    let where_span_end = context.snippet_provider.opt_span_before(missing_span, "{");
 +    let where_clause_str = rewrite_where_clause(
 +        context,
 +        &generics.where_clause.predicates,
 +        generics.where_clause.span,
 +        context.config.brace_style(),
 +        Shape::legacy(where_budget, offset.block_only()),
 +        false,
 +        "{",
 +        where_span_end,
 +        self_ty.span.hi(),
 +        option,
 +    )?;
 +
 +    // If there is no where-clause, we may have missing comments between the trait name and
 +    // the opening brace.
 +    if generics.where_clause.predicates.is_empty() {
 +        if let Some(hi) = where_span_end {
 +            match recover_missing_comment_in_span(
 +                mk_sp(self_ty.span.hi(), hi),
 +                Shape::indented(offset, context.config),
 +                context,
 +                last_line_width(&result),
 +            ) {
 +                Some(ref missing_comment) if !missing_comment.is_empty() => {
 +                    result.push_str(missing_comment);
 +                }
 +                _ => (),
 +            }
 +        }
 +    }
 +
 +    if is_impl_single_line(context, items.as_slice(), &result, &where_clause_str, item)? {
 +        result.push_str(&where_clause_str);
 +        if where_clause_str.contains('\n') || last_line_contains_single_line_comment(&result) {
 +            // if the where_clause contains extra comments AND
 +            // there is only one where-clause predicate
 +            // recover the suppressed comma in single line where_clause formatting
 +            if generics.where_clause.predicates.len() == 1 {
 +                result.push(',');
 +            }
 +            result.push_str(&format!("{}{{{}}}", sep, sep));
 +        } else {
 +            result.push_str(" {}");
 +        }
 +        return Some(result);
 +    }
 +
 +    result.push_str(&where_clause_str);
 +
 +    let need_newline = last_line_contains_single_line_comment(&result) || result.contains('\n');
 +    match context.config.brace_style() {
 +        _ if need_newline => result.push_str(&sep),
 +        BraceStyle::AlwaysNextLine => result.push_str(&sep),
 +        BraceStyle::PreferSameLine => result.push(' '),
 +        BraceStyle::SameLineWhere => {
 +            if !where_clause_str.is_empty() {
 +                result.push_str(&sep);
 +            } else {
 +                result.push(' ');
 +            }
 +        }
 +    }
 +
 +    result.push('{');
 +    // this is an impl body snippet(impl SampleImpl { /* here */ })
 +    let lo = max(self_ty.span.hi(), generics.where_clause.span.hi());
 +    let snippet = context.snippet(mk_sp(lo, item.span.hi()));
 +    let open_pos = snippet.find_uncommented("{")? + 1;
 +
 +    if !items.is_empty() || contains_comment(&snippet[open_pos..]) {
 +        let mut visitor = FmtVisitor::from_context(context);
 +        let item_indent = offset.block_only().block_indent(context.config);
 +        visitor.block_indent = item_indent;
 +        visitor.last_pos = lo + BytePos(open_pos as u32);
 +
 +        visitor.visit_attrs(&item.attrs, ast::AttrStyle::Inner);
 +        visitor.visit_impl_items(items);
 +
 +        visitor.format_missing(item.span.hi() - BytePos(1));
 +
 +        let inner_indent_str = visitor.block_indent.to_string_with_newline(context.config);
 +        let outer_indent_str = offset.block_only().to_string_with_newline(context.config);
 +
 +        result.push_str(&inner_indent_str);
 +        result.push_str(visitor.buffer.trim());
 +        result.push_str(&outer_indent_str);
 +    } else if need_newline || !context.config.empty_item_single_line() {
 +        result.push_str(&sep);
 +    }
 +
 +    result.push('}');
 +
 +    Some(result)
 +}
 +
 +fn is_impl_single_line(
 +    context: &RewriteContext<'_>,
 +    items: &[ptr::P<ast::AssocItem>],
 +    result: &str,
 +    where_clause_str: &str,
 +    item: &ast::Item,
 +) -> Option<bool> {
 +    let snippet = context.snippet(item.span);
 +    let open_pos = snippet.find_uncommented("{")? + 1;
 +
 +    Some(
 +        context.config.empty_item_single_line()
 +            && items.is_empty()
 +            && !result.contains('\n')
 +            && result.len() + where_clause_str.len() <= context.config.max_width()
 +            && !contains_comment(&snippet[open_pos..]),
 +    )
 +}
 +
 +fn format_impl_ref_and_type(
 +    context: &RewriteContext<'_>,
 +    item: &ast::Item,
 +    iimpl: &ast::Impl,
 +    offset: Indent,
 +) -> Option<String> {
 +    let ast::Impl {
 +        unsafety,
 +        polarity,
 +        defaultness,
 +        constness,
 +        ref generics,
 +        of_trait: ref trait_ref,
 +        ref self_ty,
 +        ..
 +    } = *iimpl;
 +    let mut result = String::with_capacity(128);
 +
 +    result.push_str(&format_visibility(context, &item.vis));
 +    result.push_str(format_defaultness(defaultness));
 +    result.push_str(format_unsafety(unsafety));
 +
 +    let shape = if context.config.version() == Version::Two {
 +        Shape::indented(offset + last_line_width(&result), context.config)
 +    } else {
 +        generics_shape_from_config(
 +            context.config,
 +            Shape::indented(offset + last_line_width(&result), context.config),
 +            0,
 +        )?
 +    };
 +    let generics_str = rewrite_generics(context, "impl", generics, shape)?;
 +    result.push_str(&generics_str);
 +    result.push_str(format_constness_right(constness));
 +
 +    let polarity_str = match polarity {
 +        ast::ImplPolarity::Negative(_) => "!",
 +        ast::ImplPolarity::Positive => "",
 +    };
 +
 +    let polarity_overhead;
 +    let trait_ref_overhead;
 +    if let Some(ref trait_ref) = *trait_ref {
 +        let result_len = last_line_width(&result);
 +        result.push_str(&rewrite_trait_ref(
 +            context,
 +            trait_ref,
 +            offset,
 +            polarity_str,
 +            result_len,
 +        )?);
 +        polarity_overhead = 0; // already written
 +        trait_ref_overhead = " for".len();
 +    } else {
 +        polarity_overhead = polarity_str.len();
 +        trait_ref_overhead = 0;
 +    }
 +
 +    // Try to put the self type in a single line.
 +    let curly_brace_overhead = if generics.where_clause.predicates.is_empty() {
 +        // If there is no where-clause adapt budget for type formatting to take space and curly
 +        // brace into account.
 +        match context.config.brace_style() {
 +            BraceStyle::AlwaysNextLine => 0,
 +            _ => 2,
 +        }
 +    } else {
 +        0
 +    };
 +    let used_space =
 +        last_line_width(&result) + polarity_overhead + trait_ref_overhead + curly_brace_overhead;
 +    // 1 = space before the type.
 +    let budget = context.budget(used_space + 1);
 +    if let Some(self_ty_str) = self_ty.rewrite(context, Shape::legacy(budget, offset)) {
 +        if !self_ty_str.contains('\n') {
 +            if trait_ref.is_some() {
 +                result.push_str(" for ");
 +            } else {
 +                result.push(' ');
 +                result.push_str(polarity_str);
 +            }
 +            result.push_str(&self_ty_str);
 +            return Some(result);
 +        }
 +    }
 +
 +    // Couldn't fit the self type on a single line, put it on a new line.
 +    result.push('\n');
 +    // Add indentation of one additional tab.
 +    let new_line_offset = offset.block_indent(context.config);
 +    result.push_str(&new_line_offset.to_string(context.config));
 +    if trait_ref.is_some() {
 +        result.push_str("for ");
 +    } else {
 +        result.push_str(polarity_str);
 +    }
 +    let budget = context.budget(last_line_width(&result) + polarity_overhead);
 +    let type_offset = match context.config.indent_style() {
 +        IndentStyle::Visual => new_line_offset + trait_ref_overhead,
 +        IndentStyle::Block => new_line_offset,
 +    };
 +    result.push_str(&*self_ty.rewrite(context, Shape::legacy(budget, type_offset))?);
 +    Some(result)
 +}
 +
 +fn rewrite_trait_ref(
 +    context: &RewriteContext<'_>,
 +    trait_ref: &ast::TraitRef,
 +    offset: Indent,
 +    polarity_str: &str,
 +    result_len: usize,
 +) -> Option<String> {
 +    // 1 = space between generics and trait_ref
 +    let used_space = 1 + polarity_str.len() + result_len;
 +    let shape = Shape::indented(offset + used_space, context.config);
 +    if let Some(trait_ref_str) = trait_ref.rewrite(context, shape) {
 +        if !trait_ref_str.contains('\n') {
 +            return Some(format!(" {}{}", polarity_str, trait_ref_str));
 +        }
 +    }
 +    // We could not make enough space for trait_ref, so put it on new line.
 +    let offset = offset.block_indent(context.config);
 +    let shape = Shape::indented(offset, context.config);
 +    let trait_ref_str = trait_ref.rewrite(context, shape)?;
 +    Some(format!(
 +        "{}{}{}",
 +        offset.to_string_with_newline(context.config),
 +        polarity_str,
 +        trait_ref_str
 +    ))
 +}
 +
 +pub(crate) struct StructParts<'a> {
 +    prefix: &'a str,
 +    ident: symbol::Ident,
 +    vis: &'a ast::Visibility,
 +    def: &'a ast::VariantData,
 +    generics: Option<&'a ast::Generics>,
 +    span: Span,
 +}
 +
 +impl<'a> StructParts<'a> {
 +    fn format_header(&self, context: &RewriteContext<'_>, offset: Indent) -> String {
 +        format_header(context, self.prefix, self.ident, self.vis, offset)
 +    }
 +
 +    fn from_variant(variant: &'a ast::Variant) -> Self {
 +        StructParts {
 +            prefix: "",
 +            ident: variant.ident,
 +            vis: &DEFAULT_VISIBILITY,
 +            def: &variant.data,
 +            generics: None,
 +            span: variant.span,
 +        }
 +    }
 +
 +    pub(crate) fn from_item(item: &'a ast::Item) -> Self {
 +        let (prefix, def, generics) = match item.kind {
 +            ast::ItemKind::Struct(ref def, ref generics) => ("struct ", def, generics),
 +            ast::ItemKind::Union(ref def, ref generics) => ("union ", def, generics),
 +            _ => unreachable!(),
 +        };
 +        StructParts {
 +            prefix,
 +            ident: item.ident,
 +            vis: &item.vis,
 +            def,
 +            generics: Some(generics),
 +            span: item.span,
 +        }
 +    }
 +}
 +
 +fn format_struct(
 +    context: &RewriteContext<'_>,
 +    struct_parts: &StructParts<'_>,
 +    offset: Indent,
 +    one_line_width: Option<usize>,
 +) -> Option<String> {
 +    match *struct_parts.def {
 +        ast::VariantData::Unit(..) => format_unit_struct(context, struct_parts, offset),
 +        ast::VariantData::Tuple(ref fields, _) => {
 +            format_tuple_struct(context, struct_parts, fields, offset)
 +        }
 +        ast::VariantData::Struct(ref fields, _) => {
 +            format_struct_struct(context, struct_parts, fields, offset, one_line_width)
 +        }
 +    }
 +}
 +
 +pub(crate) fn format_trait(
 +    context: &RewriteContext<'_>,
 +    item: &ast::Item,
 +    offset: Indent,
 +) -> Option<String> {
 +    if let ast::ItemKind::Trait(trait_kind) = &item.kind {
 +        let ast::Trait {
 +            is_auto,
 +            unsafety,
 +            ref generics,
 +            ref bounds,
 +            ref items,
 +        } = **trait_kind;
 +        let mut result = String::with_capacity(128);
 +        let header = format!(
 +            "{}{}{}trait ",
 +            format_visibility(context, &item.vis),
 +            format_unsafety(unsafety),
 +            format_auto(is_auto),
 +        );
 +        result.push_str(&header);
 +
 +        let body_lo = context.snippet_provider.span_after(item.span, "{");
 +
 +        let shape = Shape::indented(offset, context.config).offset_left(result.len())?;
 +        let generics_str =
 +            rewrite_generics(context, rewrite_ident(context, item.ident), generics, shape)?;
 +        result.push_str(&generics_str);
 +
 +        // FIXME(#2055): rustfmt fails to format when there are comments between trait bounds.
 +        if !bounds.is_empty() {
 +            let ident_hi = context
 +                .snippet_provider
 +                .span_after(item.span, item.ident.as_str());
 +            let bound_hi = bounds.last().unwrap().span().hi();
 +            let snippet = context.snippet(mk_sp(ident_hi, bound_hi));
 +            if contains_comment(snippet) {
 +                return None;
 +            }
 +
 +            result = rewrite_assign_rhs_with(
 +                context,
 +                result + ":",
 +                bounds,
 +                shape,
 +                &RhsAssignKind::Bounds,
 +                RhsTactics::ForceNextLineWithoutIndent,
 +            )?;
 +        }
 +
 +        // Rewrite where-clause.
 +        if !generics.where_clause.predicates.is_empty() {
 +            let where_on_new_line = context.config.indent_style() != IndentStyle::Block;
 +
 +            let where_budget = context.budget(last_line_width(&result));
 +            let pos_before_where = if bounds.is_empty() {
 +                generics.where_clause.span.lo()
 +            } else {
 +                bounds[bounds.len() - 1].span().hi()
 +            };
 +            let option = WhereClauseOption::snuggled(&generics_str);
 +            let where_clause_str = rewrite_where_clause(
 +                context,
 +                &generics.where_clause.predicates,
 +                generics.where_clause.span,
 +                context.config.brace_style(),
 +                Shape::legacy(where_budget, offset.block_only()),
 +                where_on_new_line,
 +                "{",
 +                None,
 +                pos_before_where,
 +                option,
 +            )?;
 +            // If the where-clause cannot fit on the same line,
 +            // put the where-clause on a new line
 +            if !where_clause_str.contains('\n')
 +                && last_line_width(&result) + where_clause_str.len() + offset.width()
 +                    > context.config.comment_width()
 +            {
 +                let width = offset.block_indent + context.config.tab_spaces() - 1;
 +                let where_indent = Indent::new(0, width);
 +                result.push_str(&where_indent.to_string_with_newline(context.config));
 +            }
 +            result.push_str(&where_clause_str);
 +        } else {
 +            let item_snippet = context.snippet(item.span);
 +            if let Some(lo) = item_snippet.find('/') {
 +                // 1 = `{`
 +                let comment_hi = body_lo - BytePos(1);
 +                let comment_lo = item.span.lo() + BytePos(lo as u32);
 +                if comment_lo < comment_hi {
 +                    match recover_missing_comment_in_span(
 +                        mk_sp(comment_lo, comment_hi),
 +                        Shape::indented(offset, context.config),
 +                        context,
 +                        last_line_width(&result),
 +                    ) {
 +                        Some(ref missing_comment) if !missing_comment.is_empty() => {
 +                            result.push_str(missing_comment);
 +                        }
 +                        _ => (),
 +                    }
 +                }
 +            }
 +        }
 +
 +        let block_span = mk_sp(generics.where_clause.span.hi(), item.span.hi());
 +        let snippet = context.snippet(block_span);
 +        let open_pos = snippet.find_uncommented("{")? + 1;
 +
 +        match context.config.brace_style() {
 +            _ if last_line_contains_single_line_comment(&result)
 +                || last_line_width(&result) + 2 > context.budget(offset.width()) =>
 +            {
 +                result.push_str(&offset.to_string_with_newline(context.config));
 +            }
 +            _ if context.config.empty_item_single_line()
 +                && items.is_empty()
 +                && !result.contains('\n')
 +                && !contains_comment(&snippet[open_pos..]) =>
 +            {
 +                result.push_str(" {}");
 +                return Some(result);
 +            }
 +            BraceStyle::AlwaysNextLine => {
 +                result.push_str(&offset.to_string_with_newline(context.config));
 +            }
 +            BraceStyle::PreferSameLine => result.push(' '),
 +            BraceStyle::SameLineWhere => {
 +                if result.contains('\n')
 +                    || (!generics.where_clause.predicates.is_empty() && !items.is_empty())
 +                {
 +                    result.push_str(&offset.to_string_with_newline(context.config));
 +                } else {
 +                    result.push(' ');
 +                }
 +            }
 +        }
 +        result.push('{');
 +
 +        let outer_indent_str = offset.block_only().to_string_with_newline(context.config);
 +
 +        if !items.is_empty() || contains_comment(&snippet[open_pos..]) {
 +            let mut visitor = FmtVisitor::from_context(context);
 +            visitor.block_indent = offset.block_only().block_indent(context.config);
 +            visitor.last_pos = block_span.lo() + BytePos(open_pos as u32);
 +
 +            for item in items {
 +                visitor.visit_trait_item(item);
 +            }
 +
 +            visitor.format_missing(item.span.hi() - BytePos(1));
 +
 +            let inner_indent_str = visitor.block_indent.to_string_with_newline(context.config);
 +
 +            result.push_str(&inner_indent_str);
 +            result.push_str(visitor.buffer.trim());
 +            result.push_str(&outer_indent_str);
 +        } else if result.contains('\n') {
 +            result.push_str(&outer_indent_str);
 +        }
 +
 +        result.push('}');
 +        Some(result)
 +    } else {
 +        unreachable!();
 +    }
 +}
 +
 +pub(crate) struct TraitAliasBounds<'a> {
 +    generic_bounds: &'a ast::GenericBounds,
 +    generics: &'a ast::Generics,
 +}
 +
 +impl<'a> Rewrite for TraitAliasBounds<'a> {
 +    fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
 +        let generic_bounds_str = self.generic_bounds.rewrite(context, shape)?;
 +
 +        let mut option = WhereClauseOption::new(true, WhereClauseSpace::None);
 +        option.allow_single_line();
 +
 +        let where_str = rewrite_where_clause(
 +            context,
 +            &self.generics.where_clause.predicates,
 +            self.generics.where_clause.span,
 +            context.config.brace_style(),
 +            shape,
 +            false,
 +            ";",
 +            None,
 +            self.generics.where_clause.span.lo(),
 +            option,
 +        )?;
 +
 +        let fits_single_line = !generic_bounds_str.contains('\n')
 +            && !where_str.contains('\n')
 +            && generic_bounds_str.len() + where_str.len() < shape.width;
 +        let space = if generic_bounds_str.is_empty() || where_str.is_empty() {
 +            Cow::from("")
 +        } else if fits_single_line {
 +            Cow::from(" ")
 +        } else {
 +            shape.indent.to_string_with_newline(context.config)
 +        };
 +
 +        Some(format!("{}{}{}", generic_bounds_str, space, where_str))
 +    }
 +}
 +
 +pub(crate) fn format_trait_alias(
 +    context: &RewriteContext<'_>,
 +    ident: symbol::Ident,
 +    vis: &ast::Visibility,
 +    generics: &ast::Generics,
 +    generic_bounds: &ast::GenericBounds,
 +    shape: Shape,
 +) -> Option<String> {
 +    let alias = rewrite_ident(context, ident);
 +    // 6 = "trait ", 2 = " ="
 +    let g_shape = shape.offset_left(6)?.sub_width(2)?;
 +    let generics_str = rewrite_generics(context, alias, generics, g_shape)?;
 +    let vis_str = format_visibility(context, vis);
 +    let lhs = format!("{}trait {} =", vis_str, generics_str);
 +    // 1 = ";"
 +    let trait_alias_bounds = TraitAliasBounds {
 +        generic_bounds,
 +        generics,
 +    };
 +    rewrite_assign_rhs(
 +        context,
 +        lhs,
 +        &trait_alias_bounds,
 +        &RhsAssignKind::Bounds,
 +        shape.sub_width(1)?,
 +    )
 +    .map(|s| s + ";")
 +}
 +
 +fn format_unit_struct(
 +    context: &RewriteContext<'_>,
 +    p: &StructParts<'_>,
 +    offset: Indent,
 +) -> Option<String> {
 +    let header_str = format_header(context, p.prefix, p.ident, p.vis, offset);
 +    let generics_str = if let Some(generics) = p.generics {
 +        let hi = context.snippet_provider.span_before(p.span, ";");
 +        format_generics(
 +            context,
 +            generics,
 +            context.config.brace_style(),
 +            BracePos::None,
 +            offset,
 +            // make a span that starts right after `struct Foo`
 +            mk_sp(p.ident.span.hi(), hi),
 +            last_line_width(&header_str),
 +        )?
 +    } else {
 +        String::new()
 +    };
 +    Some(format!("{}{};", header_str, generics_str))
 +}
 +
 +pub(crate) fn format_struct_struct(
 +    context: &RewriteContext<'_>,
 +    struct_parts: &StructParts<'_>,
 +    fields: &[ast::FieldDef],
 +    offset: Indent,
 +    one_line_width: Option<usize>,
 +) -> Option<String> {
 +    let mut result = String::with_capacity(1024);
 +    let span = struct_parts.span;
 +
 +    let header_str = struct_parts.format_header(context, offset);
 +    result.push_str(&header_str);
 +
 +    let header_hi = struct_parts.ident.span.hi();
-     match rewrite_missing_comment(span, Shape::indented(offset, context.config), context) {
++    let body_lo = if let Some(generics) = struct_parts.generics {
++        // Adjust the span to start at the end of the generic arguments before searching for the '{'
++        let span = span.with_lo(generics.span.hi());
++        context.snippet_provider.span_after(span, "{")
++    } else {
++        context.snippet_provider.span_after(span, "{")
++    };
 +
 +    let generics_str = match struct_parts.generics {
 +        Some(g) => format_generics(
 +            context,
 +            g,
 +            context.config.brace_style(),
 +            if fields.is_empty() {
 +                BracePos::ForceSameLine
 +            } else {
 +                BracePos::Auto
 +            },
 +            offset,
 +            // make a span that starts right after `struct Foo`
 +            mk_sp(header_hi, body_lo),
 +            last_line_width(&result),
 +        )?,
 +        None => {
 +            // 3 = ` {}`, 2 = ` {`.
 +            let overhead = if fields.is_empty() { 3 } else { 2 };
 +            if (context.config.brace_style() == BraceStyle::AlwaysNextLine && !fields.is_empty())
 +                || context.config.max_width() < overhead + result.len()
 +            {
 +                format!("\n{}{{", offset.block_only().to_string(context.config))
 +            } else {
 +                " {".to_owned()
 +            }
 +        }
 +    };
 +    // 1 = `}`
 +    let overhead = if fields.is_empty() { 1 } else { 0 };
 +    let total_width = result.len() + generics_str.len() + overhead;
 +    if !generics_str.is_empty()
 +        && !generics_str.contains('\n')
 +        && total_width > context.config.max_width()
 +    {
 +        result.push('\n');
 +        result.push_str(&offset.to_string(context.config));
 +        result.push_str(generics_str.trim_start());
 +    } else {
 +        result.push_str(&generics_str);
 +    }
 +
 +    if fields.is_empty() {
 +        let inner_span = mk_sp(body_lo, span.hi() - BytePos(1));
 +        format_empty_struct_or_tuple(context, inner_span, offset, &mut result, "", "}");
 +        return Some(result);
 +    }
 +
 +    // 3 = ` ` and ` }`
 +    let one_line_budget = context.budget(result.len() + 3 + offset.width());
 +    let one_line_budget =
 +        one_line_width.map_or(0, |one_line_width| min(one_line_width, one_line_budget));
 +
 +    let items_str = rewrite_with_alignment(
 +        fields,
 +        context,
 +        Shape::indented(offset.block_indent(context.config), context.config).sub_width(1)?,
 +        mk_sp(body_lo, span.hi()),
 +        one_line_budget,
 +    )?;
 +
 +    if !items_str.contains('\n')
 +        && !result.contains('\n')
 +        && items_str.len() <= one_line_budget
 +        && !last_line_contains_single_line_comment(&items_str)
 +    {
 +        Some(format!("{} {} }}", result, items_str))
 +    } else {
 +        Some(format!(
 +            "{}\n{}{}\n{}}}",
 +            result,
 +            offset
 +                .block_indent(context.config)
 +                .to_string(context.config),
 +            items_str,
 +            offset.to_string(context.config)
 +        ))
 +    }
 +}
 +
 +fn get_bytepos_after_visibility(vis: &ast::Visibility, default_span: Span) -> BytePos {
 +    match vis.kind {
 +        ast::VisibilityKind::Crate(..) | ast::VisibilityKind::Restricted { .. } => vis.span.hi(),
 +        _ => default_span.lo(),
 +    }
 +}
 +
 +// Format tuple or struct without any fields. We need to make sure that the comments
 +// inside the delimiters are preserved.
 +fn format_empty_struct_or_tuple(
 +    context: &RewriteContext<'_>,
 +    span: Span,
 +    offset: Indent,
 +    result: &mut String,
 +    opener: &str,
 +    closer: &str,
 +) {
 +    // 3 = " {}" or "();"
 +    let used_width = last_line_used_width(result, offset.width()) + 3;
 +    if used_width > context.config.max_width() {
 +        result.push_str(&offset.to_string_with_newline(context.config))
 +    }
 +    result.push_str(opener);
-             if !is_single_line(s) || first_line_contains_single_line_comment(s) {
++
++    // indented shape for proper indenting of multi-line comments
++    let shape = Shape::indented(offset.block_indent(context.config), context.config);
++    match rewrite_missing_comment(span, shape, context) {
 +        Some(ref s) if s.is_empty() => (),
 +        Some(ref s) => {
-             if last_line_contains_single_line_comment(s) {
++            let is_multi_line = !is_single_line(s);
++            if is_multi_line || first_line_contains_single_line_comment(s) {
 +                let nested_indent_str = offset
 +                    .block_indent(context.config)
 +                    .to_string_with_newline(context.config);
 +                result.push_str(&nested_indent_str);
 +            }
 +            result.push_str(s);
-                         &(param_attrs_result + &shape.to_string_with_newline(context.config)),
++            if is_multi_line || last_line_contains_single_line_comment(s) {
 +                result.push_str(&offset.to_string_with_newline(context.config));
 +            }
 +        }
 +        None => result.push_str(context.snippet(span)),
 +    }
 +    result.push_str(closer);
 +}
 +
 +fn format_tuple_struct(
 +    context: &RewriteContext<'_>,
 +    struct_parts: &StructParts<'_>,
 +    fields: &[ast::FieldDef],
 +    offset: Indent,
 +) -> Option<String> {
 +    let mut result = String::with_capacity(1024);
 +    let span = struct_parts.span;
 +
 +    let header_str = struct_parts.format_header(context, offset);
 +    result.push_str(&header_str);
 +
 +    let body_lo = if fields.is_empty() {
 +        let lo = get_bytepos_after_visibility(struct_parts.vis, span);
 +        context
 +            .snippet_provider
 +            .span_after(mk_sp(lo, span.hi()), "(")
 +    } else {
 +        fields[0].span.lo()
 +    };
 +    let body_hi = if fields.is_empty() {
 +        context
 +            .snippet_provider
 +            .span_after(mk_sp(body_lo, span.hi()), ")")
 +    } else {
 +        // This is a dirty hack to work around a missing `)` from the span of the last field.
 +        let last_arg_span = fields[fields.len() - 1].span;
 +        context
 +            .snippet_provider
 +            .opt_span_after(mk_sp(last_arg_span.hi(), span.hi()), ")")
 +            .unwrap_or_else(|| last_arg_span.hi())
 +    };
 +
 +    let where_clause_str = match struct_parts.generics {
 +        Some(generics) => {
 +            let budget = context.budget(last_line_width(&header_str));
 +            let shape = Shape::legacy(budget, offset);
 +            let generics_str = rewrite_generics(context, "", generics, shape)?;
 +            result.push_str(&generics_str);
 +
 +            let where_budget = context.budget(last_line_width(&result));
 +            let option = WhereClauseOption::new(true, WhereClauseSpace::Newline);
 +            rewrite_where_clause(
 +                context,
 +                &generics.where_clause.predicates,
 +                generics.where_clause.span,
 +                context.config.brace_style(),
 +                Shape::legacy(where_budget, offset.block_only()),
 +                false,
 +                ";",
 +                None,
 +                body_hi,
 +                option,
 +            )?
 +        }
 +        None => "".to_owned(),
 +    };
 +
 +    if fields.is_empty() {
 +        let body_hi = context
 +            .snippet_provider
 +            .span_before(mk_sp(body_lo, span.hi()), ")");
 +        let inner_span = mk_sp(body_lo, body_hi);
 +        format_empty_struct_or_tuple(context, inner_span, offset, &mut result, "(", ")");
 +    } else {
 +        let shape = Shape::indented(offset, context.config).sub_width(1)?;
 +        let lo = if let Some(generics) = struct_parts.generics {
 +            generics.span.hi()
 +        } else {
 +            struct_parts.ident.span.hi()
 +        };
 +        result = overflow::rewrite_with_parens(
 +            context,
 +            &result,
 +            fields.iter(),
 +            shape,
 +            mk_sp(lo, span.hi()),
 +            context.config.fn_call_width(),
 +            None,
 +        )?;
 +    }
 +
 +    if !where_clause_str.is_empty()
 +        && !where_clause_str.contains('\n')
 +        && (result.contains('\n')
 +            || offset.block_indent + result.len() + where_clause_str.len() + 1
 +                > context.config.max_width())
 +    {
 +        // We need to put the where-clause on a new line, but we didn't
 +        // know that earlier, so the where-clause will not be indented properly.
 +        result.push('\n');
 +        result.push_str(
 +            &(offset.block_only() + (context.config.tab_spaces() - 1)).to_string(context.config),
 +        );
 +    }
 +    result.push_str(&where_clause_str);
 +
 +    Some(result)
 +}
 +
 +pub(crate) enum ItemVisitorKind<'a> {
 +    Item(&'a ast::Item),
 +    AssocTraitItem(&'a ast::AssocItem),
 +    AssocImplItem(&'a ast::AssocItem),
 +    ForeignItem(&'a ast::ForeignItem),
 +}
 +
 +struct TyAliasRewriteInfo<'c, 'g>(
 +    &'c RewriteContext<'c>,
 +    Indent,
 +    &'g ast::Generics,
 +    (ast::TyAliasWhereClause, ast::TyAliasWhereClause),
 +    usize,
 +    symbol::Ident,
 +    Span,
 +);
 +
 +pub(crate) fn rewrite_type_alias<'a, 'b>(
 +    ty_alias_kind: &ast::TyAlias,
 +    context: &RewriteContext<'a>,
 +    indent: Indent,
 +    visitor_kind: &ItemVisitorKind<'b>,
 +    span: Span,
 +) -> Option<String> {
 +    use ItemVisitorKind::*;
 +
 +    let ast::TyAlias {
 +        defaultness,
 +        ref generics,
 +        ref bounds,
 +        ref ty,
 +        where_clauses,
 +        where_predicates_split,
 +    } = *ty_alias_kind;
 +    let ty_opt = ty.as_ref();
 +    let (ident, vis) = match visitor_kind {
 +        Item(i) => (i.ident, &i.vis),
 +        AssocTraitItem(i) | AssocImplItem(i) => (i.ident, &i.vis),
 +        ForeignItem(i) => (i.ident, &i.vis),
 +    };
 +    let rw_info = &TyAliasRewriteInfo(
 +        context,
 +        indent,
 +        generics,
 +        where_clauses,
 +        where_predicates_split,
 +        ident,
 +        span,
 +    );
 +    let op_ty = opaque_ty(ty);
 +    // Type Aliases are formatted slightly differently depending on the context
 +    // in which they appear, whether they are opaque, and whether they are associated.
 +    // https://rustc-dev-guide.rust-lang.org/opaque-types-type-alias-impl-trait.html
 +    // https://github.com/rust-dev-tools/fmt-rfcs/blob/master/guide/items.md#type-aliases
 +    match (visitor_kind, &op_ty) {
 +        (Item(_) | AssocTraitItem(_) | ForeignItem(_), Some(op_bounds)) => {
 +            let op = OpaqueType { bounds: op_bounds };
 +            rewrite_ty(rw_info, Some(bounds), Some(&op), vis)
 +        }
 +        (Item(_) | AssocTraitItem(_) | ForeignItem(_), None) => {
 +            rewrite_ty(rw_info, Some(bounds), ty_opt, vis)
 +        }
 +        (AssocImplItem(_), _) => {
 +            let result = if let Some(op_bounds) = op_ty {
 +                let op = OpaqueType { bounds: op_bounds };
 +                rewrite_ty(rw_info, Some(bounds), Some(&op), &DEFAULT_VISIBILITY)
 +            } else {
 +                rewrite_ty(rw_info, Some(bounds), ty_opt, vis)
 +            }?;
 +            match defaultness {
 +                ast::Defaultness::Default(..) => Some(format!("default {}", result)),
 +                _ => Some(result),
 +            }
 +        }
 +    }
 +}
 +
 +fn rewrite_ty<R: Rewrite>(
 +    rw_info: &TyAliasRewriteInfo<'_, '_>,
 +    generic_bounds_opt: Option<&ast::GenericBounds>,
 +    rhs: Option<&R>,
 +    vis: &ast::Visibility,
 +) -> Option<String> {
 +    let mut result = String::with_capacity(128);
 +    let TyAliasRewriteInfo(
 +        context,
 +        indent,
 +        generics,
 +        where_clauses,
 +        where_predicates_split,
 +        ident,
 +        span,
 +    ) = *rw_info;
 +    let (before_where_predicates, after_where_predicates) = generics
 +        .where_clause
 +        .predicates
 +        .split_at(where_predicates_split);
 +    if !after_where_predicates.is_empty() {
 +        return None;
 +    }
 +    result.push_str(&format!("{}type ", format_visibility(context, vis)));
 +    let ident_str = rewrite_ident(context, ident);
 +
 +    if generics.params.is_empty() {
 +        result.push_str(ident_str)
 +    } else {
 +        // 2 = `= `
 +        let g_shape = Shape::indented(indent, context.config)
 +            .offset_left(result.len())?
 +            .sub_width(2)?;
 +        let generics_str = rewrite_generics(context, ident_str, generics, g_shape)?;
 +        result.push_str(&generics_str);
 +    }
 +
 +    if let Some(bounds) = generic_bounds_opt {
 +        if !bounds.is_empty() {
 +            // 2 = `: `
 +            let shape = Shape::indented(indent, context.config).offset_left(result.len() + 2)?;
 +            let type_bounds = bounds.rewrite(context, shape).map(|s| format!(": {}", s))?;
 +            result.push_str(&type_bounds);
 +        }
 +    }
 +
 +    let where_budget = context.budget(last_line_width(&result));
 +    let mut option = WhereClauseOption::snuggled(&result);
 +    if rhs.is_none() {
 +        option.suppress_comma();
 +    }
 +    let where_clause_str = rewrite_where_clause(
 +        context,
 +        before_where_predicates,
 +        where_clauses.0.1,
 +        context.config.brace_style(),
 +        Shape::legacy(where_budget, indent),
 +        false,
 +        "=",
 +        None,
 +        generics.span.hi(),
 +        option,
 +    )?;
 +    result.push_str(&where_clause_str);
 +
 +    if let Some(ty) = rhs {
 +        // If there's a where clause, add a newline before the assignment. Otherwise just add a
 +        // space.
 +        let has_where = !before_where_predicates.is_empty();
 +        if has_where {
 +            result.push_str(&indent.to_string_with_newline(context.config));
 +        } else {
 +            result.push(' ');
 +        }
 +
 +        let comment_span = context
 +            .snippet_provider
 +            .opt_span_before(span, "=")
 +            .map(|op_lo| mk_sp(where_clauses.0.1.hi(), op_lo));
 +
 +        let lhs = match comment_span {
 +            Some(comment_span)
 +                if contains_comment(context.snippet_provider.span_to_snippet(comment_span)?) =>
 +            {
 +                let comment_shape = if has_where {
 +                    Shape::indented(indent, context.config)
 +                } else {
 +                    Shape::indented(indent, context.config)
 +                        .block_left(context.config.tab_spaces())?
 +                };
 +
 +                combine_strs_with_missing_comments(
 +                    context,
 +                    result.trim_end(),
 +                    "=",
 +                    comment_span,
 +                    comment_shape,
 +                    true,
 +                )?
 +            }
 +            _ => format!("{}=", result),
 +        };
 +
 +        // 1 = `;`
 +        let shape = Shape::indented(indent, context.config).sub_width(1)?;
 +        rewrite_assign_rhs(context, lhs, &*ty, &RhsAssignKind::Ty, shape).map(|s| s + ";")
 +    } else {
 +        Some(format!("{};", result))
 +    }
 +}
 +
 +fn type_annotation_spacing(config: &Config) -> (&str, &str) {
 +    (
 +        if config.space_before_colon() { " " } else { "" },
 +        if config.space_after_colon() { " " } else { "" },
 +    )
 +}
 +
 +pub(crate) fn rewrite_struct_field_prefix(
 +    context: &RewriteContext<'_>,
 +    field: &ast::FieldDef,
 +) -> Option<String> {
 +    let vis = format_visibility(context, &field.vis);
 +    let type_annotation_spacing = type_annotation_spacing(context.config);
 +    Some(match field.ident {
 +        Some(name) => format!(
 +            "{}{}{}:",
 +            vis,
 +            rewrite_ident(context, name),
 +            type_annotation_spacing.0
 +        ),
 +        None => vis.to_string(),
 +    })
 +}
 +
 +impl Rewrite for ast::FieldDef {
 +    fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
 +        rewrite_struct_field(context, self, shape, 0)
 +    }
 +}
 +
 +pub(crate) fn rewrite_struct_field(
 +    context: &RewriteContext<'_>,
 +    field: &ast::FieldDef,
 +    shape: Shape,
 +    lhs_max_width: usize,
 +) -> Option<String> {
 +    if contains_skip(&field.attrs) {
 +        return Some(context.snippet(field.span()).to_owned());
 +    }
 +
 +    let type_annotation_spacing = type_annotation_spacing(context.config);
 +    let prefix = rewrite_struct_field_prefix(context, field)?;
 +
 +    let attrs_str = field.attrs.rewrite(context, shape)?;
 +    let attrs_extendable = field.ident.is_none() && is_attributes_extendable(&attrs_str);
 +    let missing_span = if field.attrs.is_empty() {
 +        mk_sp(field.span.lo(), field.span.lo())
 +    } else {
 +        mk_sp(field.attrs.last().unwrap().span.hi(), field.span.lo())
 +    };
 +    let mut spacing = String::from(if field.ident.is_some() {
 +        type_annotation_spacing.1
 +    } else {
 +        ""
 +    });
 +    // Try to put everything on a single line.
 +    let attr_prefix = combine_strs_with_missing_comments(
 +        context,
 +        &attrs_str,
 +        &prefix,
 +        missing_span,
 +        shape,
 +        attrs_extendable,
 +    )?;
 +    let overhead = trimmed_last_line_width(&attr_prefix);
 +    let lhs_offset = lhs_max_width.saturating_sub(overhead);
 +    for _ in 0..lhs_offset {
 +        spacing.push(' ');
 +    }
 +    // In this extreme case we will be missing a space between an attribute and a field.
 +    if prefix.is_empty() && !attrs_str.is_empty() && attrs_extendable && spacing.is_empty() {
 +        spacing.push(' ');
 +    }
 +    let orig_ty = shape
 +        .offset_left(overhead + spacing.len())
 +        .and_then(|ty_shape| field.ty.rewrite(context, ty_shape));
 +    if let Some(ref ty) = orig_ty {
 +        if !ty.contains('\n') {
 +            return Some(attr_prefix + &spacing + ty);
 +        }
 +    }
 +
 +    let is_prefix_empty = prefix.is_empty();
 +    // We must use multiline. We are going to put attributes and a field on different lines.
 +    let field_str = rewrite_assign_rhs(context, prefix, &*field.ty, &RhsAssignKind::Ty, shape)?;
 +    // Remove a leading white-space from `rewrite_assign_rhs()` when rewriting a tuple struct.
 +    let field_str = if is_prefix_empty {
 +        field_str.trim_start()
 +    } else {
 +        &field_str
 +    };
 +    combine_strs_with_missing_comments(context, &attrs_str, field_str, missing_span, shape, false)
 +}
 +
 +pub(crate) struct StaticParts<'a> {
 +    prefix: &'a str,
 +    vis: &'a ast::Visibility,
 +    ident: symbol::Ident,
 +    ty: &'a ast::Ty,
 +    mutability: ast::Mutability,
 +    expr_opt: Option<&'a ptr::P<ast::Expr>>,
 +    defaultness: Option<ast::Defaultness>,
 +    span: Span,
 +}
 +
 +impl<'a> StaticParts<'a> {
 +    pub(crate) fn from_item(item: &'a ast::Item) -> Self {
 +        let (defaultness, prefix, ty, mutability, expr) = match item.kind {
 +            ast::ItemKind::Static(ref ty, mutability, ref expr) => {
 +                (None, "static", ty, mutability, expr)
 +            }
 +            ast::ItemKind::Const(defaultness, ref ty, ref expr) => {
 +                (Some(defaultness), "const", ty, ast::Mutability::Not, expr)
 +            }
 +            _ => unreachable!(),
 +        };
 +        StaticParts {
 +            prefix,
 +            vis: &item.vis,
 +            ident: item.ident,
 +            ty,
 +            mutability,
 +            expr_opt: expr.as_ref(),
 +            defaultness,
 +            span: item.span,
 +        }
 +    }
 +
 +    pub(crate) fn from_trait_item(ti: &'a ast::AssocItem) -> Self {
 +        let (defaultness, ty, expr_opt) = match ti.kind {
 +            ast::AssocItemKind::Const(defaultness, ref ty, ref expr_opt) => {
 +                (defaultness, ty, expr_opt)
 +            }
 +            _ => unreachable!(),
 +        };
 +        StaticParts {
 +            prefix: "const",
 +            vis: &ti.vis,
 +            ident: ti.ident,
 +            ty,
 +            mutability: ast::Mutability::Not,
 +            expr_opt: expr_opt.as_ref(),
 +            defaultness: Some(defaultness),
 +            span: ti.span,
 +        }
 +    }
 +
 +    pub(crate) fn from_impl_item(ii: &'a ast::AssocItem) -> Self {
 +        let (defaultness, ty, expr) = match ii.kind {
 +            ast::AssocItemKind::Const(defaultness, ref ty, ref expr) => (defaultness, ty, expr),
 +            _ => unreachable!(),
 +        };
 +        StaticParts {
 +            prefix: "const",
 +            vis: &ii.vis,
 +            ident: ii.ident,
 +            ty,
 +            mutability: ast::Mutability::Not,
 +            expr_opt: expr.as_ref(),
 +            defaultness: Some(defaultness),
 +            span: ii.span,
 +        }
 +    }
 +}
 +
 +fn rewrite_static(
 +    context: &RewriteContext<'_>,
 +    static_parts: &StaticParts<'_>,
 +    offset: Indent,
 +) -> Option<String> {
 +    let colon = colon_spaces(context.config);
 +    let mut prefix = format!(
 +        "{}{}{} {}{}{}",
 +        format_visibility(context, static_parts.vis),
 +        static_parts.defaultness.map_or("", format_defaultness),
 +        static_parts.prefix,
 +        format_mutability(static_parts.mutability),
 +        rewrite_ident(context, static_parts.ident),
 +        colon,
 +    );
 +    // 2 = " =".len()
 +    let ty_shape =
 +        Shape::indented(offset.block_only(), context.config).offset_left(prefix.len() + 2)?;
 +    let ty_str = match static_parts.ty.rewrite(context, ty_shape) {
 +        Some(ty_str) => ty_str,
 +        None => {
 +            if prefix.ends_with(' ') {
 +                prefix.pop();
 +            }
 +            let nested_indent = offset.block_indent(context.config);
 +            let nested_shape = Shape::indented(nested_indent, context.config);
 +            let ty_str = static_parts.ty.rewrite(context, nested_shape)?;
 +            format!(
 +                "{}{}",
 +                nested_indent.to_string_with_newline(context.config),
 +                ty_str
 +            )
 +        }
 +    };
 +
 +    if let Some(expr) = static_parts.expr_opt {
 +        let comments_lo = context.snippet_provider.span_after(static_parts.span, "=");
 +        let expr_lo = expr.span.lo();
 +        let comments_span = mk_sp(comments_lo, expr_lo);
 +
 +        let lhs = format!("{}{} =", prefix, ty_str);
 +
 +        // 1 = ;
 +        let remaining_width = context.budget(offset.block_indent + 1);
 +        rewrite_assign_rhs_with_comments(
 +            context,
 +            &lhs,
 +            &**expr,
 +            Shape::legacy(remaining_width, offset.block_only()),
 +            &RhsAssignKind::Expr(&expr.kind, expr.span),
 +            RhsTactics::Default,
 +            comments_span,
 +            true,
 +        )
 +        .and_then(|res| recover_comment_removed(res, static_parts.span, context))
 +        .map(|s| if s.ends_with(';') { s } else { s + ";" })
 +    } else {
 +        Some(format!("{}{};", prefix, ty_str))
 +    }
 +}
 +
 +// FIXME(calebcartwright) - This is a hack around a bug in the handling of TyKind::ImplTrait.
 +// This should be removed once that bug is resolved, with the type alias formatting using the
 +// defined Ty for the RHS directly.
 +// https://github.com/rust-lang/rustfmt/issues/4373
 +// https://github.com/rust-lang/rustfmt/issues/5027
 +struct OpaqueType<'a> {
 +    bounds: &'a ast::GenericBounds,
 +}
 +
 +impl<'a> Rewrite for OpaqueType<'a> {
 +    fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
 +        let shape = shape.offset_left(5)?; // `impl `
 +        self.bounds
 +            .rewrite(context, shape)
 +            .map(|s| format!("impl {}", s))
 +    }
 +}
 +
 +impl Rewrite for ast::FnRetTy {
 +    fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
 +        match *self {
 +            ast::FnRetTy::Default(_) => Some(String::new()),
 +            ast::FnRetTy::Ty(ref ty) => {
 +                if context.config.version() == Version::One
 +                    || context.config.indent_style() == IndentStyle::Visual
 +                {
 +                    let inner_width = shape.width.checked_sub(3)?;
 +                    return ty
 +                        .rewrite(context, Shape::legacy(inner_width, shape.indent + 3))
 +                        .map(|r| format!("-> {}", r));
 +                }
 +
 +                ty.rewrite(context, shape.offset_left(3)?)
 +                    .map(|s| format!("-> {}", s))
 +            }
 +        }
 +    }
 +}
 +
 +fn is_empty_infer(ty: &ast::Ty, pat_span: Span) -> bool {
 +    match ty.kind {
 +        ast::TyKind::Infer => ty.span.hi() == pat_span.hi(),
 +        _ => false,
 +    }
 +}
 +
 +/// Recover any missing comments between the param and the type.
 +///
 +/// # Returns
 +///
 +/// A 2-len tuple with the comment before the colon in first position, and the comment after the
 +/// colon in second position.
 +fn get_missing_param_comments(
 +    context: &RewriteContext<'_>,
 +    pat_span: Span,
 +    ty_span: Span,
 +    shape: Shape,
 +) -> (String, String) {
 +    let missing_comment_span = mk_sp(pat_span.hi(), ty_span.lo());
 +
 +    let span_before_colon = {
 +        let missing_comment_span_hi = context
 +            .snippet_provider
 +            .span_before(missing_comment_span, ":");
 +        mk_sp(pat_span.hi(), missing_comment_span_hi)
 +    };
 +    let span_after_colon = {
 +        let missing_comment_span_lo = context
 +            .snippet_provider
 +            .span_after(missing_comment_span, ":");
 +        mk_sp(missing_comment_span_lo, ty_span.lo())
 +    };
 +
 +    let comment_before_colon = rewrite_missing_comment(span_before_colon, shape, context)
 +        .filter(|comment| !comment.is_empty())
 +        .map_or(String::new(), |comment| format!(" {}", comment));
 +    let comment_after_colon = rewrite_missing_comment(span_after_colon, shape, context)
 +        .filter(|comment| !comment.is_empty())
 +        .map_or(String::new(), |comment| format!("{} ", comment));
 +    (comment_before_colon, comment_after_colon)
 +}
 +
 +impl Rewrite for ast::Param {
 +    fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
 +        let param_attrs_result = self
 +            .attrs
 +            .rewrite(context, Shape::legacy(shape.width, shape.indent))?;
 +        // N.B. Doc comments aren't typically valid syntax, but could appear
 +        // in the presence of certain macros - https://github.com/rust-lang/rustfmt/issues/4936
 +        let (span, has_multiple_attr_lines, has_doc_comments) = if !self.attrs.is_empty() {
 +            let num_attrs = self.attrs.len();
 +            (
 +                mk_sp(self.attrs[num_attrs - 1].span.hi(), self.pat.span.lo()),
 +                param_attrs_result.contains('\n'),
 +                self.attrs.iter().any(|a| a.is_doc_comment()),
 +            )
 +        } else {
 +            (mk_sp(self.span.lo(), self.span.lo()), false, false)
 +        };
 +
 +        if let Some(ref explicit_self) = self.to_self() {
 +            rewrite_explicit_self(
 +                context,
 +                explicit_self,
 +                &param_attrs_result,
 +                span,
 +                shape,
 +                has_multiple_attr_lines,
 +            )
 +        } else if is_named_param(self) {
 +            let param_name = &self
 +                .pat
 +                .rewrite(context, Shape::legacy(shape.width, shape.indent))?;
 +            let mut result = combine_strs_with_missing_comments(
 +                context,
 +                &param_attrs_result,
 +                param_name,
 +                span,
 +                shape,
 +                !has_multiple_attr_lines && !has_doc_comments,
 +            )?;
 +
 +            if !is_empty_infer(&*self.ty, self.pat.span) {
 +                let (before_comment, after_comment) =
 +                    get_missing_param_comments(context, self.pat.span, self.ty.span, shape);
 +                result.push_str(&before_comment);
 +                result.push_str(colon_spaces(context.config));
 +                result.push_str(&after_comment);
 +                let overhead = last_line_width(&result);
 +                let max_width = shape.width.checked_sub(overhead)?;
 +                if let Some(ty_str) = self
 +                    .ty
 +                    .rewrite(context, Shape::legacy(max_width, shape.indent))
 +                {
 +                    result.push_str(&ty_str);
 +                } else {
++                    let prev_str = if param_attrs_result.is_empty() {
++                        param_attrs_result
++                    } else {
++                        param_attrs_result + &shape.to_string_with_newline(context.config)
++                    };
++
 +                    result = combine_strs_with_missing_comments(
 +                        context,
++                        &prev_str,
 +                        param_name,
 +                        span,
 +                        shape,
 +                        !has_multiple_attr_lines,
 +                    )?;
 +                    result.push_str(&before_comment);
 +                    result.push_str(colon_spaces(context.config));
 +                    result.push_str(&after_comment);
 +                    let overhead = last_line_width(&result);
 +                    let max_width = shape.width.checked_sub(overhead)?;
 +                    let ty_str = self
 +                        .ty
 +                        .rewrite(context, Shape::legacy(max_width, shape.indent))?;
 +                    result.push_str(&ty_str);
 +                }
 +            }
 +
 +            Some(result)
 +        } else {
 +            self.ty.rewrite(context, shape)
 +        }
 +    }
 +}
 +
 +fn rewrite_explicit_self(
 +    context: &RewriteContext<'_>,
 +    explicit_self: &ast::ExplicitSelf,
 +    param_attrs: &str,
 +    span: Span,
 +    shape: Shape,
 +    has_multiple_attr_lines: bool,
 +) -> Option<String> {
 +    match explicit_self.node {
 +        ast::SelfKind::Region(lt, m) => {
 +            let mut_str = format_mutability(m);
 +            match lt {
 +                Some(ref l) => {
 +                    let lifetime_str = l.rewrite(
 +                        context,
 +                        Shape::legacy(context.config.max_width(), Indent::empty()),
 +                    )?;
 +                    Some(combine_strs_with_missing_comments(
 +                        context,
 +                        param_attrs,
 +                        &format!("&{} {}self", lifetime_str, mut_str),
 +                        span,
 +                        shape,
 +                        !has_multiple_attr_lines,
 +                    )?)
 +                }
 +                None => Some(combine_strs_with_missing_comments(
 +                    context,
 +                    param_attrs,
 +                    &format!("&{}self", mut_str),
 +                    span,
 +                    shape,
 +                    !has_multiple_attr_lines,
 +                )?),
 +            }
 +        }
 +        ast::SelfKind::Explicit(ref ty, mutability) => {
 +            let type_str = ty.rewrite(
 +                context,
 +                Shape::legacy(context.config.max_width(), Indent::empty()),
 +            )?;
 +
 +            Some(combine_strs_with_missing_comments(
 +                context,
 +                param_attrs,
 +                &format!("{}self: {}", format_mutability(mutability), type_str),
 +                span,
 +                shape,
 +                !has_multiple_attr_lines,
 +            )?)
 +        }
 +        ast::SelfKind::Value(mutability) => Some(combine_strs_with_missing_comments(
 +            context,
 +            param_attrs,
 +            &format!("{}self", format_mutability(mutability)),
 +            span,
 +            shape,
 +            !has_multiple_attr_lines,
 +        )?),
 +    }
 +}
 +
 +pub(crate) fn span_lo_for_param(param: &ast::Param) -> BytePos {
 +    if param.attrs.is_empty() {
 +        if is_named_param(param) {
 +            param.pat.span.lo()
 +        } else {
 +            param.ty.span.lo()
 +        }
 +    } else {
 +        param.attrs[0].span.lo()
 +    }
 +}
 +
 +pub(crate) fn span_hi_for_param(context: &RewriteContext<'_>, param: &ast::Param) -> BytePos {
 +    match param.ty.kind {
 +        ast::TyKind::Infer if context.snippet(param.ty.span) == "_" => param.ty.span.hi(),
 +        ast::TyKind::Infer if is_named_param(param) => param.pat.span.hi(),
 +        _ => param.ty.span.hi(),
 +    }
 +}
 +
 +pub(crate) fn is_named_param(param: &ast::Param) -> bool {
 +    if let ast::PatKind::Ident(_, ident, _) = param.pat.kind {
 +        ident.name != symbol::kw::Empty
 +    } else {
 +        true
 +    }
 +}
 +
 +#[derive(Copy, Clone, Debug, PartialEq, Eq)]
 +pub(crate) enum FnBraceStyle {
 +    SameLine,
 +    NextLine,
 +    None,
 +}
 +
 +// Return type is (result, force_new_line_for_brace)
 +fn rewrite_fn_base(
 +    context: &RewriteContext<'_>,
 +    indent: Indent,
 +    ident: symbol::Ident,
 +    fn_sig: &FnSig<'_>,
 +    span: Span,
 +    fn_brace_style: FnBraceStyle,
 +) -> Option<(String, bool, bool)> {
 +    let mut force_new_line_for_brace = false;
 +
 +    let where_clause = &fn_sig.generics.where_clause;
 +
 +    let mut result = String::with_capacity(1024);
 +    result.push_str(&fn_sig.to_str(context));
 +
 +    // fn foo
 +    result.push_str("fn ");
 +
 +    // Generics.
 +    let overhead = if let FnBraceStyle::SameLine = fn_brace_style {
 +        // 4 = `() {`
 +        4
 +    } else {
 +        // 2 = `()`
 +        2
 +    };
 +    let used_width = last_line_used_width(&result, indent.width());
 +    let one_line_budget = context.budget(used_width + overhead);
 +    let shape = Shape {
 +        width: one_line_budget,
 +        indent,
 +        offset: used_width,
 +    };
 +    let fd = fn_sig.decl;
 +    let generics_str = rewrite_generics(
 +        context,
 +        rewrite_ident(context, ident),
 +        &fn_sig.generics,
 +        shape,
 +    )?;
 +    result.push_str(&generics_str);
 +
 +    let snuggle_angle_bracket = generics_str
 +        .lines()
 +        .last()
 +        .map_or(false, |l| l.trim_start().len() == 1);
 +
 +    // Note that the width and indent don't really matter, we'll re-layout the
 +    // return type later anyway.
 +    let ret_str = fd
 +        .output
 +        .rewrite(context, Shape::indented(indent, context.config))?;
 +
 +    let multi_line_ret_str = ret_str.contains('\n');
 +    let ret_str_len = if multi_line_ret_str { 0 } else { ret_str.len() };
 +
 +    // Params.
 +    let (one_line_budget, multi_line_budget, mut param_indent) = compute_budgets_for_params(
 +        context,
 +        &result,
 +        indent,
 +        ret_str_len,
 +        fn_brace_style,
 +        multi_line_ret_str,
 +    )?;
 +
 +    debug!(
 +        "rewrite_fn_base: one_line_budget: {}, multi_line_budget: {}, param_indent: {:?}",
 +        one_line_budget, multi_line_budget, param_indent
 +    );
 +
 +    result.push('(');
 +    // Check if vertical layout was forced.
 +    if one_line_budget == 0
 +        && !snuggle_angle_bracket
 +        && context.config.indent_style() == IndentStyle::Visual
 +    {
 +        result.push_str(&param_indent.to_string_with_newline(context.config));
 +    }
 +
 +    let params_end = if fd.inputs.is_empty() {
 +        context
 +            .snippet_provider
 +            .span_after(mk_sp(fn_sig.generics.span.hi(), span.hi()), ")")
 +    } else {
 +        let last_span = mk_sp(fd.inputs[fd.inputs.len() - 1].span().hi(), span.hi());
 +        context.snippet_provider.span_after(last_span, ")")
 +    };
 +    let params_span = mk_sp(
 +        context
 +            .snippet_provider
 +            .span_after(mk_sp(fn_sig.generics.span.hi(), span.hi()), "("),
 +        params_end,
 +    );
 +    let param_str = rewrite_params(
 +        context,
 +        &fd.inputs,
 +        one_line_budget,
 +        multi_line_budget,
 +        indent,
 +        param_indent,
 +        params_span,
 +        fd.c_variadic(),
 +    )?;
 +
 +    let put_params_in_block = match context.config.indent_style() {
 +        IndentStyle::Block => param_str.contains('\n') || param_str.len() > one_line_budget,
 +        _ => false,
 +    } && !fd.inputs.is_empty();
 +
 +    let mut params_last_line_contains_comment = false;
 +    let mut no_params_and_over_max_width = false;
 +
 +    if put_params_in_block {
 +        param_indent = indent.block_indent(context.config);
 +        result.push_str(&param_indent.to_string_with_newline(context.config));
 +        result.push_str(&param_str);
 +        result.push_str(&indent.to_string_with_newline(context.config));
 +        result.push(')');
 +    } else {
 +        result.push_str(&param_str);
 +        let used_width = last_line_used_width(&result, indent.width()) + first_line_width(&ret_str);
 +        // Put the closing brace on the next line if it overflows the max width.
 +        // 1 = `)`
 +        let closing_paren_overflow_max_width =
 +            fd.inputs.is_empty() && used_width + 1 > context.config.max_width();
 +        // If the last line of params contains comment, we cannot put the closing paren
 +        // on the same line.
 +        params_last_line_contains_comment = param_str
 +            .lines()
 +            .last()
 +            .map_or(false, |last_line| last_line.contains("//"));
 +
 +        if context.config.version() == Version::Two {
 +            if closing_paren_overflow_max_width {
 +                result.push(')');
 +                result.push_str(&indent.to_string_with_newline(context.config));
 +                no_params_and_over_max_width = true;
 +            } else if params_last_line_contains_comment {
 +                result.push_str(&indent.to_string_with_newline(context.config));
 +                result.push(')');
 +                no_params_and_over_max_width = true;
 +            } else {
 +                result.push(')');
 +            }
 +        } else {
 +            if closing_paren_overflow_max_width || params_last_line_contains_comment {
 +                result.push_str(&indent.to_string_with_newline(context.config));
 +            }
 +            result.push(')');
 +        }
 +    }
 +
 +    // Return type.
 +    if let ast::FnRetTy::Ty(..) = fd.output {
 +        let ret_should_indent = match context.config.indent_style() {
 +            // If our params are block layout then we surely must have space.
 +            IndentStyle::Block if put_params_in_block || fd.inputs.is_empty() => false,
 +            _ if params_last_line_contains_comment => false,
 +            _ if result.contains('\n') || multi_line_ret_str => true,
 +            _ => {
 +                // If the return type would push over the max width, then put the return type on
 +                // a new line. With the +1 for the signature length an additional space between
 +                // the closing parenthesis of the param and the arrow '->' is considered.
 +                let mut sig_length = result.len() + indent.width() + ret_str_len + 1;
 +
 +                // If there is no where-clause, take into account the space after the return type
 +                // and the brace.
 +                if where_clause.predicates.is_empty() {
 +                    sig_length += 2;
 +                }
 +
 +                sig_length > context.config.max_width()
 +            }
 +        };
 +        let ret_shape = if ret_should_indent {
 +            if context.config.version() == Version::One
 +                || context.config.indent_style() == IndentStyle::Visual
 +            {
 +                let indent = if param_str.is_empty() {
 +                    // Aligning with non-existent params looks silly.
 +                    force_new_line_for_brace = true;
 +                    indent + 4
 +                } else {
 +                    // FIXME: we might want to check that using the param indent
 +                    // doesn't blow our budget, and if it does, then fallback to
 +                    // the where-clause indent.
 +                    param_indent
 +                };
 +
 +                result.push_str(&indent.to_string_with_newline(context.config));
 +                Shape::indented(indent, context.config)
 +            } else {
 +                let mut ret_shape = Shape::indented(indent, context.config);
 +                if param_str.is_empty() {
 +                    // Aligning with non-existent params looks silly.
 +                    force_new_line_for_brace = true;
 +                    ret_shape = if context.use_block_indent() {
 +                        ret_shape.offset_left(4).unwrap_or(ret_shape)
 +                    } else {
 +                        ret_shape.indent = ret_shape.indent + 4;
 +                        ret_shape
 +                    };
 +                }
 +
 +                result.push_str(&ret_shape.indent.to_string_with_newline(context.config));
 +                ret_shape
 +            }
 +        } else {
 +            if context.config.version() == Version::Two {
 +                if !param_str.is_empty() || !no_params_and_over_max_width {
 +                    result.push(' ');
 +                }
 +            } else {
 +                result.push(' ');
 +            }
 +
 +            let ret_shape = Shape::indented(indent, context.config);
 +            ret_shape
 +                .offset_left(last_line_width(&result))
 +                .unwrap_or(ret_shape)
 +        };
 +
 +        if multi_line_ret_str || ret_should_indent {
 +            // Now that we know the proper indent and width, we need to
 +            // re-layout the return type.
 +            let ret_str = fd.output.rewrite(context, ret_shape)?;
 +            result.push_str(&ret_str);
 +        } else {
 +            result.push_str(&ret_str);
 +        }
 +
 +        // Comment between return type and the end of the decl.
 +        let snippet_lo = fd.output.span().hi();
 +        if where_clause.predicates.is_empty() {
 +            let snippet_hi = span.hi();
 +            let snippet = context.snippet(mk_sp(snippet_lo, snippet_hi));
 +            // Try to preserve the layout of the original snippet.
 +            let original_starts_with_newline = snippet
 +                .find(|c| c != ' ')
 +                .map_or(false, |i| starts_with_newline(&snippet[i..]));
 +            let original_ends_with_newline = snippet
 +                .rfind(|c| c != ' ')
 +                .map_or(false, |i| snippet[i..].ends_with('\n'));
 +            let snippet = snippet.trim();
 +            if !snippet.is_empty() {
 +                result.push(if original_starts_with_newline {
 +                    '\n'
 +                } else {
 +                    ' '
 +                });
 +                result.push_str(snippet);
 +                if original_ends_with_newline {
 +                    force_new_line_for_brace = true;
 +                }
 +            }
 +        }
 +    }
 +
 +    let pos_before_where = match fd.output {
 +        ast::FnRetTy::Default(..) => params_span.hi(),
 +        ast::FnRetTy::Ty(ref ty) => ty.span.hi(),
 +    };
 +
 +    let is_params_multi_lined = param_str.contains('\n');
 +
 +    let space = if put_params_in_block && ret_str.is_empty() {
 +        WhereClauseSpace::Space
 +    } else {
 +        WhereClauseSpace::Newline
 +    };
 +    let mut option = WhereClauseOption::new(fn_brace_style == FnBraceStyle::None, space);
 +    if is_params_multi_lined {
 +        option.veto_single_line();
 +    }
 +    let where_clause_str = rewrite_where_clause(
 +        context,
 +        &where_clause.predicates,
 +        where_clause.span,
 +        context.config.brace_style(),
 +        Shape::indented(indent, context.config),
 +        true,
 +        "{",
 +        Some(span.hi()),
 +        pos_before_where,
 +        option,
 +    )?;
 +    // If there are neither where-clause nor return type, we may be missing comments between
 +    // params and `{`.
 +    if where_clause_str.is_empty() {
 +        if let ast::FnRetTy::Default(ret_span) = fd.output {
 +            match recover_missing_comment_in_span(
 +                mk_sp(params_span.hi(), ret_span.hi()),
 +                shape,
 +                context,
 +                last_line_width(&result),
 +            ) {
 +                Some(ref missing_comment) if !missing_comment.is_empty() => {
 +                    result.push_str(missing_comment);
 +                    force_new_line_for_brace = true;
 +                }
 +                _ => (),
 +            }
 +        }
 +    }
 +
 +    result.push_str(&where_clause_str);
 +
 +    let ends_with_comment = last_line_contains_single_line_comment(&result);
 +    force_new_line_for_brace |= ends_with_comment;
 +    force_new_line_for_brace |=
 +        is_params_multi_lined && context.config.where_single_line() && !where_clause_str.is_empty();
 +    Some((result, ends_with_comment, force_new_line_for_brace))
 +}
 +
 +/// Kind of spaces to put before `where`.
 +#[derive(Copy, Clone)]
 +enum WhereClauseSpace {
 +    /// A single space.
 +    Space,
 +    /// A new line.
 +    Newline,
 +    /// Nothing.
 +    None,
 +}
 +
 +#[derive(Copy, Clone)]
 +struct WhereClauseOption {
 +    suppress_comma: bool, // Force no trailing comma
 +    snuggle: WhereClauseSpace,
 +    allow_single_line: bool, // Try single line where-clause instead of vertical layout
 +    veto_single_line: bool,  // Disallow a single-line where-clause.
 +}
 +
 +impl WhereClauseOption {
 +    fn new(suppress_comma: bool, snuggle: WhereClauseSpace) -> WhereClauseOption {
 +        WhereClauseOption {
 +            suppress_comma,
 +            snuggle,
 +            allow_single_line: false,
 +            veto_single_line: false,
 +        }
 +    }
 +
 +    fn snuggled(current: &str) -> WhereClauseOption {
 +        WhereClauseOption {
 +            suppress_comma: false,
 +            snuggle: if last_line_width(current) == 1 {
 +                WhereClauseSpace::Space
 +            } else {
 +                WhereClauseSpace::Newline
 +            },
 +            allow_single_line: false,
 +            veto_single_line: false,
 +        }
 +    }
 +
 +    fn suppress_comma(&mut self) {
 +        self.suppress_comma = true
 +    }
 +
 +    fn allow_single_line(&mut self) {
 +        self.allow_single_line = true
 +    }
 +
 +    fn snuggle(&mut self) {
 +        self.snuggle = WhereClauseSpace::Space
 +    }
 +
 +    fn veto_single_line(&mut self) {
 +        self.veto_single_line = true;
 +    }
 +}
 +
 +fn rewrite_params(
 +    context: &RewriteContext<'_>,
 +    params: &[ast::Param],
 +    one_line_budget: usize,
 +    multi_line_budget: usize,
 +    indent: Indent,
 +    param_indent: Indent,
 +    span: Span,
 +    variadic: bool,
 +) -> Option<String> {
 +    if params.is_empty() {
 +        let comment = context
 +            .snippet(mk_sp(
 +                span.lo(),
 +                // to remove ')'
 +                span.hi() - BytePos(1),
 +            ))
 +            .trim();
 +        return Some(comment.to_owned());
 +    }
 +    let param_items: Vec<_> = itemize_list(
 +        context.snippet_provider,
 +        params.iter(),
 +        ")",
 +        ",",
 +        |param| span_lo_for_param(param),
 +        |param| param.ty.span.hi(),
 +        |param| {
 +            param
 +                .rewrite(context, Shape::legacy(multi_line_budget, param_indent))
 +                .or_else(|| Some(context.snippet(param.span()).to_owned()))
 +        },
 +        span.lo(),
 +        span.hi(),
 +        false,
 +    )
 +    .collect();
 +
 +    let tactic = definitive_tactic(
 +        &param_items,
 +        context
 +            .config
 +            .fn_args_layout()
 +            .to_list_tactic(param_items.len()),
 +        Separator::Comma,
 +        one_line_budget,
 +    );
 +    let budget = match tactic {
 +        DefinitiveListTactic::Horizontal => one_line_budget,
 +        _ => multi_line_budget,
 +    };
 +    let indent = match context.config.indent_style() {
 +        IndentStyle::Block => indent.block_indent(context.config),
 +        IndentStyle::Visual => param_indent,
 +    };
 +    let trailing_separator = if variadic {
 +        SeparatorTactic::Never
 +    } else {
 +        match context.config.indent_style() {
 +            IndentStyle::Block => context.config.trailing_comma(),
 +            IndentStyle::Visual => SeparatorTactic::Never,
 +        }
 +    };
 +    let fmt = ListFormatting::new(Shape::legacy(budget, indent), context.config)
 +        .tactic(tactic)
 +        .trailing_separator(trailing_separator)
 +        .ends_with_newline(tactic.ends_with_newline(context.config.indent_style()))
 +        .preserve_newline(true);
 +    write_list(&param_items, &fmt)
 +}
 +
 +fn compute_budgets_for_params(
 +    context: &RewriteContext<'_>,
 +    result: &str,
 +    indent: Indent,
 +    ret_str_len: usize,
 +    fn_brace_style: FnBraceStyle,
 +    force_vertical_layout: bool,
 +) -> Option<(usize, usize, Indent)> {
 +    debug!(
 +        "compute_budgets_for_params {} {:?}, {}, {:?}",
 +        result.len(),
 +        indent,
 +        ret_str_len,
 +        fn_brace_style,
 +    );
 +    // Try keeping everything on the same line.
 +    if !result.contains('\n') && !force_vertical_layout {
 +        // 2 = `()`, 3 = `() `, space is before ret_string.
 +        let overhead = if ret_str_len == 0 { 2 } else { 3 };
 +        let mut used_space = indent.width() + result.len() + ret_str_len + overhead;
 +        match fn_brace_style {
 +            FnBraceStyle::None => used_space += 1,     // 1 = `;`
 +            FnBraceStyle::SameLine => used_space += 2, // 2 = `{}`
 +            FnBraceStyle::NextLine => (),
 +        }
 +        let one_line_budget = context.budget(used_space);
 +
 +        if one_line_budget > 0 {
 +            // 4 = "() {".len()
 +            let (indent, multi_line_budget) = match context.config.indent_style() {
 +                IndentStyle::Block => {
 +                    let indent = indent.block_indent(context.config);
 +                    (indent, context.budget(indent.width() + 1))
 +                }
 +                IndentStyle::Visual => {
 +                    let indent = indent + result.len() + 1;
 +                    let multi_line_overhead = match fn_brace_style {
 +                        FnBraceStyle::SameLine => 4,
 +                        _ => 2,
 +                    } + indent.width();
 +                    (indent, context.budget(multi_line_overhead))
 +                }
 +            };
 +
 +            return Some((one_line_budget, multi_line_budget, indent));
 +        }
 +    }
 +
 +    // Didn't work. we must force vertical layout and put params on a newline.
 +    let new_indent = indent.block_indent(context.config);
 +    let used_space = match context.config.indent_style() {
 +        // 1 = `,`
 +        IndentStyle::Block => new_indent.width() + 1,
 +        // Account for `)` and possibly ` {`.
 +        IndentStyle::Visual => new_indent.width() + if ret_str_len == 0 { 1 } else { 3 },
 +    };
 +    Some((0, context.budget(used_space), new_indent))
 +}
 +
 +fn newline_for_brace(config: &Config, where_clause: &ast::WhereClause) -> FnBraceStyle {
 +    let predicate_count = where_clause.predicates.len();
 +
 +    if config.where_single_line() && predicate_count == 1 {
 +        return FnBraceStyle::SameLine;
 +    }
 +    let brace_style = config.brace_style();
 +
 +    let use_next_line = brace_style == BraceStyle::AlwaysNextLine
 +        || (brace_style == BraceStyle::SameLineWhere && predicate_count > 0);
 +    if use_next_line {
 +        FnBraceStyle::NextLine
 +    } else {
 +        FnBraceStyle::SameLine
 +    }
 +}
 +
 +fn rewrite_generics(
 +    context: &RewriteContext<'_>,
 +    ident: &str,
 +    generics: &ast::Generics,
 +    shape: Shape,
 +) -> Option<String> {
 +    // FIXME: convert bounds to where-clauses where they get too big or if
 +    // there is a where-clause at all.
 +
 +    if generics.params.is_empty() {
 +        return Some(ident.to_owned());
 +    }
 +
 +    let params = generics.params.iter();
 +    overflow::rewrite_with_angle_brackets(context, ident, params, shape, generics.span)
 +}
 +
 +fn generics_shape_from_config(config: &Config, shape: Shape, offset: usize) -> Option<Shape> {
 +    match config.indent_style() {
 +        IndentStyle::Visual => shape.visual_indent(1 + offset).sub_width(offset + 2),
 +        IndentStyle::Block => {
 +            // 1 = ","
 +            shape
 +                .block()
 +                .block_indent(config.tab_spaces())
 +                .with_max_width(config)
 +                .sub_width(1)
 +        }
 +    }
 +}
 +
 +fn rewrite_where_clause_rfc_style(
 +    context: &RewriteContext<'_>,
 +    predicates: &[ast::WherePredicate],
 +    where_span: Span,
 +    shape: Shape,
 +    terminator: &str,
 +    span_end: Option<BytePos>,
 +    span_end_before_where: BytePos,
 +    where_clause_option: WhereClauseOption,
 +) -> Option<String> {
 +    let (where_keyword, allow_single_line) = rewrite_where_keyword(
 +        context,
 +        predicates,
 +        where_span,
 +        shape,
 +        span_end_before_where,
 +        where_clause_option,
 +    )?;
 +
 +    // 1 = `,`
 +    let clause_shape = shape
 +        .block()
 +        .with_max_width(context.config)
 +        .block_left(context.config.tab_spaces())?
 +        .sub_width(1)?;
 +    let force_single_line = context.config.where_single_line()
 +        && predicates.len() == 1
 +        && !where_clause_option.veto_single_line;
 +
 +    let preds_str = rewrite_bounds_on_where_clause(
 +        context,
 +        predicates,
 +        clause_shape,
 +        terminator,
 +        span_end,
 +        where_clause_option,
 +        force_single_line,
 +    )?;
 +
 +    // 6 = `where `
 +    let clause_sep =
 +        if allow_single_line && !preds_str.contains('\n') && 6 + preds_str.len() <= shape.width
 +            || force_single_line
 +        {
 +            Cow::from(" ")
 +        } else {
 +            clause_shape.indent.to_string_with_newline(context.config)
 +        };
 +
 +    Some(format!("{}{}{}", where_keyword, clause_sep, preds_str))
 +}
 +
 +/// Rewrite `where` and comment around it.
 +fn rewrite_where_keyword(
 +    context: &RewriteContext<'_>,
 +    predicates: &[ast::WherePredicate],
 +    where_span: Span,
 +    shape: Shape,
 +    span_end_before_where: BytePos,
 +    where_clause_option: WhereClauseOption,
 +) -> Option<(String, bool)> {
 +    let block_shape = shape.block().with_max_width(context.config);
 +    // 1 = `,`
 +    let clause_shape = block_shape
 +        .block_left(context.config.tab_spaces())?
 +        .sub_width(1)?;
 +
 +    let comment_separator = |comment: &str, shape: Shape| {
 +        if comment.is_empty() {
 +            Cow::from("")
 +        } else {
 +            shape.indent.to_string_with_newline(context.config)
 +        }
 +    };
 +
 +    let (span_before, span_after) =
 +        missing_span_before_after_where(span_end_before_where, predicates, where_span);
 +    let (comment_before, comment_after) =
 +        rewrite_comments_before_after_where(context, span_before, span_after, shape)?;
 +
 +    let starting_newline = match where_clause_option.snuggle {
 +        WhereClauseSpace::Space if comment_before.is_empty() => Cow::from(" "),
 +        WhereClauseSpace::None => Cow::from(""),
 +        _ => block_shape.indent.to_string_with_newline(context.config),
 +    };
 +
 +    let newline_before_where = comment_separator(&comment_before, shape);
 +    let newline_after_where = comment_separator(&comment_after, clause_shape);
 +    let result = format!(
 +        "{}{}{}where{}{}",
 +        starting_newline, comment_before, newline_before_where, newline_after_where, comment_after
 +    );
 +    let allow_single_line = where_clause_option.allow_single_line
 +        && comment_before.is_empty()
 +        && comment_after.is_empty();
 +
 +    Some((result, allow_single_line))
 +}
 +
 +/// Rewrite bounds on a where clause.
 +fn rewrite_bounds_on_where_clause(
 +    context: &RewriteContext<'_>,
 +    predicates: &[ast::WherePredicate],
 +    shape: Shape,
 +    terminator: &str,
 +    span_end: Option<BytePos>,
 +    where_clause_option: WhereClauseOption,
 +    force_single_line: bool,
 +) -> Option<String> {
 +    let span_start = predicates[0].span().lo();
 +    // If we don't have the start of the next span, then use the end of the
 +    // predicates, but that means we miss comments.
 +    let len = predicates.len();
 +    let end_of_preds = predicates[len - 1].span().hi();
 +    let span_end = span_end.unwrap_or(end_of_preds);
 +    let items = itemize_list(
 +        context.snippet_provider,
 +        predicates.iter(),
 +        terminator,
 +        ",",
 +        |pred| pred.span().lo(),
 +        |pred| pred.span().hi(),
 +        |pred| pred.rewrite(context, shape),
 +        span_start,
 +        span_end,
 +        false,
 +    );
 +    let comma_tactic = if where_clause_option.suppress_comma || force_single_line {
 +        SeparatorTactic::Never
 +    } else {
 +        context.config.trailing_comma()
 +    };
 +
 +    // shape should be vertical only and only if we have `force_single_line` option enabled
 +    // and the number of items of the where-clause is equal to 1
 +    let shape_tactic = if force_single_line {
 +        DefinitiveListTactic::Horizontal
 +    } else {
 +        DefinitiveListTactic::Vertical
 +    };
 +
 +    let fmt = ListFormatting::new(shape, context.config)
 +        .tactic(shape_tactic)
 +        .trailing_separator(comma_tactic)
 +        .preserve_newline(true);
 +    write_list(&items.collect::<Vec<_>>(), &fmt)
 +}
 +
 +fn rewrite_where_clause(
 +    context: &RewriteContext<'_>,
 +    predicates: &[ast::WherePredicate],
 +    where_span: Span,
 +    brace_style: BraceStyle,
 +    shape: Shape,
 +    on_new_line: bool,
 +    terminator: &str,
 +    span_end: Option<BytePos>,
 +    span_end_before_where: BytePos,
 +    where_clause_option: WhereClauseOption,
 +) -> Option<String> {
 +    if predicates.is_empty() {
 +        return Some(String::new());
 +    }
 +
 +    if context.config.indent_style() == IndentStyle::Block {
 +        return rewrite_where_clause_rfc_style(
 +            context,
 +            predicates,
 +            where_span,
 +            shape,
 +            terminator,
 +            span_end,
 +            span_end_before_where,
 +            where_clause_option,
 +        );
 +    }
 +
 +    let extra_indent = Indent::new(context.config.tab_spaces(), 0);
 +
 +    let offset = match context.config.indent_style() {
 +        IndentStyle::Block => shape.indent + extra_indent.block_indent(context.config),
 +        // 6 = "where ".len()
 +        IndentStyle::Visual => shape.indent + extra_indent + 6,
 +    };
 +    // FIXME: if indent_style != Visual, then the budgets below might
 +    // be out by a char or two.
 +
 +    let budget = context.config.max_width() - offset.width();
 +    let span_start = predicates[0].span().lo();
 +    // If we don't have the start of the next span, then use the end of the
 +    // predicates, but that means we miss comments.
 +    let len = predicates.len();
 +    let end_of_preds = predicates[len - 1].span().hi();
 +    let span_end = span_end.unwrap_or(end_of_preds);
 +    let items = itemize_list(
 +        context.snippet_provider,
 +        predicates.iter(),
 +        terminator,
 +        ",",
 +        |pred| pred.span().lo(),
 +        |pred| pred.span().hi(),
 +        |pred| pred.rewrite(context, Shape::legacy(budget, offset)),
 +        span_start,
 +        span_end,
 +        false,
 +    );
 +    let item_vec = items.collect::<Vec<_>>();
 +    // FIXME: we don't need to collect here
 +    let tactic = definitive_tactic(&item_vec, ListTactic::Vertical, Separator::Comma, budget);
 +
 +    let mut comma_tactic = context.config.trailing_comma();
 +    // Kind of a hack because we don't usually have trailing commas in where-clauses.
 +    if comma_tactic == SeparatorTactic::Vertical || where_clause_option.suppress_comma {
 +        comma_tactic = SeparatorTactic::Never;
 +    }
 +
 +    let fmt = ListFormatting::new(Shape::legacy(budget, offset), context.config)
 +        .tactic(tactic)
 +        .trailing_separator(comma_tactic)
 +        .ends_with_newline(tactic.ends_with_newline(context.config.indent_style()))
 +        .preserve_newline(true);
 +    let preds_str = write_list(&item_vec, &fmt)?;
 +
 +    let end_length = if terminator == "{" {
 +        // If the brace is on the next line we don't need to count it otherwise it needs two
 +        // characters " {"
 +        match brace_style {
 +            BraceStyle::AlwaysNextLine | BraceStyle::SameLineWhere => 0,
 +            BraceStyle::PreferSameLine => 2,
 +        }
 +    } else if terminator == "=" {
 +        2
 +    } else {
 +        terminator.len()
 +    };
 +    if on_new_line
 +        || preds_str.contains('\n')
 +        || shape.indent.width() + " where ".len() + preds_str.len() + end_length > shape.width
 +    {
 +        Some(format!(
 +            "\n{}where {}",
 +            (shape.indent + extra_indent).to_string(context.config),
 +            preds_str
 +        ))
 +    } else {
 +        Some(format!(" where {}", preds_str))
 +    }
 +}
 +
 +fn missing_span_before_after_where(
 +    before_item_span_end: BytePos,
 +    predicates: &[ast::WherePredicate],
 +    where_span: Span,
 +) -> (Span, Span) {
 +    let missing_span_before = mk_sp(before_item_span_end, where_span.lo());
 +    // 5 = `where`
 +    let pos_after_where = where_span.lo() + BytePos(5);
 +    let missing_span_after = mk_sp(pos_after_where, predicates[0].span().lo());
 +    (missing_span_before, missing_span_after)
 +}
 +
 +fn rewrite_comments_before_after_where(
 +    context: &RewriteContext<'_>,
 +    span_before_where: Span,
 +    span_after_where: Span,
 +    shape: Shape,
 +) -> Option<(String, String)> {
 +    let before_comment = rewrite_missing_comment(span_before_where, shape, context)?;
 +    let after_comment = rewrite_missing_comment(
 +        span_after_where,
 +        shape.block_indent(context.config.tab_spaces()),
 +        context,
 +    )?;
 +    Some((before_comment, after_comment))
 +}
 +
 +fn format_header(
 +    context: &RewriteContext<'_>,
 +    item_name: &str,
 +    ident: symbol::Ident,
 +    vis: &ast::Visibility,
 +    offset: Indent,
 +) -> String {
 +    let mut result = String::with_capacity(128);
 +    let shape = Shape::indented(offset, context.config);
 +
 +    result.push_str(format_visibility(context, vis).trim());
 +
 +    // Check for a missing comment between the visibility and the item name.
 +    let after_vis = vis.span.hi();
 +    if let Some(before_item_name) = context
 +        .snippet_provider
 +        .opt_span_before(mk_sp(vis.span.lo(), ident.span.hi()), item_name.trim())
 +    {
 +        let missing_span = mk_sp(after_vis, before_item_name);
 +        if let Some(result_with_comment) = combine_strs_with_missing_comments(
 +            context,
 +            &result,
 +            item_name,
 +            missing_span,
 +            shape,
 +            /* allow_extend */ true,
 +        ) {
 +            result = result_with_comment;
 +        }
 +    }
 +
 +    result.push_str(rewrite_ident(context, ident));
 +
 +    result
 +}
 +
 +#[derive(PartialEq, Eq, Clone, Copy)]
 +enum BracePos {
 +    None,
 +    Auto,
 +    ForceSameLine,
 +}
 +
 +fn format_generics(
 +    context: &RewriteContext<'_>,
 +    generics: &ast::Generics,
 +    brace_style: BraceStyle,
 +    brace_pos: BracePos,
 +    offset: Indent,
 +    span: Span,
 +    used_width: usize,
 +) -> Option<String> {
 +    let shape = Shape::legacy(context.budget(used_width + offset.width()), offset);
 +    let mut result = rewrite_generics(context, "", generics, shape)?;
 +
 +    // If the generics are not parameterized then generics.span.hi() == 0,
 +    // so we use span.lo(), which is the position after `struct Foo`.
 +    let span_end_before_where = if !generics.params.is_empty() {
 +        generics.span.hi()
 +    } else {
 +        span.lo()
 +    };
 +    let (same_line_brace, missed_comments) = if !generics.where_clause.predicates.is_empty() {
 +        let budget = context.budget(last_line_used_width(&result, offset.width()));
 +        let mut option = WhereClauseOption::snuggled(&result);
 +        if brace_pos == BracePos::None {
 +            option.suppress_comma = true;
 +        }
 +        let where_clause_str = rewrite_where_clause(
 +            context,
 +            &generics.where_clause.predicates,
 +            generics.where_clause.span,
 +            brace_style,
 +            Shape::legacy(budget, offset.block_only()),
 +            true,
 +            "{",
 +            Some(span.hi()),
 +            span_end_before_where,
 +            option,
 +        )?;
 +        result.push_str(&where_clause_str);
 +        (
 +            brace_pos == BracePos::ForceSameLine || brace_style == BraceStyle::PreferSameLine,
 +            // missed comments are taken care of in #rewrite_where_clause
 +            None,
 +        )
 +    } else {
 +        (
 +            brace_pos == BracePos::ForceSameLine
 +                || (result.contains('\n') && brace_style == BraceStyle::PreferSameLine
 +                    || brace_style != BraceStyle::AlwaysNextLine)
 +                || trimmed_last_line_width(&result) == 1,
 +            rewrite_missing_comment(
 +                mk_sp(
 +                    span_end_before_where,
 +                    if brace_pos == BracePos::None {
 +                        span.hi()
 +                    } else {
 +                        context.snippet_provider.span_before(span, "{")
 +                    },
 +                ),
 +                shape,
 +                context,
 +            ),
 +        )
 +    };
 +    // add missing comments
 +    let missed_line_comments = missed_comments
 +        .filter(|missed_comments| !missed_comments.is_empty())
 +        .map_or(false, |missed_comments| {
 +            let is_block = is_last_comment_block(&missed_comments);
 +            let sep = if is_block { " " } else { "\n" };
 +            result.push_str(sep);
 +            result.push_str(&missed_comments);
 +            !is_block
 +        });
 +    if brace_pos == BracePos::None {
 +        return Some(result);
 +    }
 +    let total_used_width = last_line_used_width(&result, used_width);
 +    let remaining_budget = context.budget(total_used_width);
 +    // If the same line brace if forced, it indicates that we are rewriting an item with empty body,
 +    // and hence we take the closer into account as well for one line budget.
 +    // We assume that the closer has the same length as the opener.
 +    let overhead = if brace_pos == BracePos::ForceSameLine {
 +        // 3 = ` {}`
 +        3
 +    } else {
 +        // 2 = ` {`
 +        2
 +    };
 +    let forbid_same_line_brace = missed_line_comments || overhead > remaining_budget;
 +    if !forbid_same_line_brace && same_line_brace {
 +        result.push(' ');
 +    } else {
 +        result.push('\n');
 +        result.push_str(&offset.block_only().to_string(context.config));
 +    }
 +    result.push('{');
 +
 +    Some(result)
 +}
 +
 +impl Rewrite for ast::ForeignItem {
 +    fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
 +        let attrs_str = self.attrs.rewrite(context, shape)?;
 +        // Drop semicolon or it will be interpreted as comment.
 +        // FIXME: this may be a faulty span from libsyntax.
 +        let span = mk_sp(self.span.lo(), self.span.hi() - BytePos(1));
 +
 +        let item_str = match self.kind {
 +            ast::ForeignItemKind::Fn(ref fn_kind) => {
 +                let ast::Fn {
 +                    defaultness,
 +                    ref sig,
 +                    ref generics,
 +                    ref body,
 +                } = **fn_kind;
 +                if let Some(ref body) = body {
 +                    let mut visitor = FmtVisitor::from_context(context);
 +                    visitor.block_indent = shape.indent;
 +                    visitor.last_pos = self.span.lo();
 +                    let inner_attrs = inner_attributes(&self.attrs);
 +                    let fn_ctxt = visit::FnCtxt::Foreign;
 +                    visitor.visit_fn(
 +                        visit::FnKind::Fn(fn_ctxt, self.ident, sig, &self.vis, Some(body)),
 +                        generics,
 +                        &sig.decl,
 +                        self.span,
 +                        defaultness,
 +                        Some(&inner_attrs),
 +                    );
 +                    Some(visitor.buffer.to_owned())
 +                } else {
 +                    rewrite_fn_base(
 +                        context,
 +                        shape.indent,
 +                        self.ident,
 +                        &FnSig::from_method_sig(sig, generics, &self.vis),
 +                        span,
 +                        FnBraceStyle::None,
 +                    )
 +                    .map(|(s, _, _)| format!("{};", s))
 +                }
 +            }
 +            ast::ForeignItemKind::Static(ref ty, mutability, _) => {
 +                // FIXME(#21): we're dropping potential comments in between the
 +                // function kw here.
 +                let vis = format_visibility(context, &self.vis);
 +                let mut_str = format_mutability(mutability);
 +                let prefix = format!(
 +                    "{}static {}{}:",
 +                    vis,
 +                    mut_str,
 +                    rewrite_ident(context, self.ident)
 +                );
 +                // 1 = ;
 +                rewrite_assign_rhs(
 +                    context,
 +                    prefix,
 +                    &**ty,
 +                    &RhsAssignKind::Ty,
 +                    shape.sub_width(1)?,
 +                )
 +                .map(|s| s + ";")
 +            }
 +            ast::ForeignItemKind::TyAlias(ref ty_alias) => {
 +                let (kind, span) = (&ItemVisitorKind::ForeignItem(self), self.span);
 +                rewrite_type_alias(ty_alias, context, shape.indent, kind, span)
 +            }
 +            ast::ForeignItemKind::MacCall(ref mac) => {
 +                rewrite_macro(mac, None, context, shape, MacroPosition::Item)
 +            }
 +        }?;
 +
 +        let missing_span = if self.attrs.is_empty() {
 +            mk_sp(self.span.lo(), self.span.lo())
 +        } else {
 +            mk_sp(self.attrs[self.attrs.len() - 1].span.hi(), self.span.lo())
 +        };
 +        combine_strs_with_missing_comments(
 +            context,
 +            &attrs_str,
 +            &item_str,
 +            missing_span,
 +            shape,
 +            false,
 +        )
 +    }
 +}
 +
 +/// Rewrite the attributes of an item.
 +fn rewrite_attrs(
 +    context: &RewriteContext<'_>,
 +    item: &ast::Item,
 +    item_str: &str,
 +    shape: Shape,
 +) -> Option<String> {
 +    let attrs = filter_inline_attrs(&item.attrs, item.span());
 +    let attrs_str = attrs.rewrite(context, shape)?;
 +
 +    let missed_span = if attrs.is_empty() {
 +        mk_sp(item.span.lo(), item.span.lo())
 +    } else {
 +        mk_sp(attrs[attrs.len() - 1].span.hi(), item.span.lo())
 +    };
 +
 +    let allow_extend = if attrs.len() == 1 {
 +        let line_len = attrs_str.len() + 1 + item_str.len();
 +        !attrs.first().unwrap().is_doc_comment()
 +            && context.config.inline_attribute_width() >= line_len
 +    } else {
 +        false
 +    };
 +
 +    combine_strs_with_missing_comments(
 +        context,
 +        &attrs_str,
 +        item_str,
 +        missed_span,
 +        shape,
 +        allow_extend,
 +    )
 +}
 +
 +/// Rewrite an inline mod.
 +/// The given shape is used to format the mod's attributes.
 +pub(crate) fn rewrite_mod(
 +    context: &RewriteContext<'_>,
 +    item: &ast::Item,
 +    attrs_shape: Shape,
 +) -> Option<String> {
 +    let mut result = String::with_capacity(32);
 +    result.push_str(&*format_visibility(context, &item.vis));
 +    result.push_str("mod ");
 +    result.push_str(rewrite_ident(context, item.ident));
 +    result.push(';');
 +    rewrite_attrs(context, item, &result, attrs_shape)
 +}
 +
 +/// Rewrite `extern crate foo;`.
 +/// The given shape is used to format the extern crate's attributes.
 +pub(crate) fn rewrite_extern_crate(
 +    context: &RewriteContext<'_>,
 +    item: &ast::Item,
 +    attrs_shape: Shape,
 +) -> Option<String> {
 +    assert!(is_extern_crate(item));
 +    let new_str = context.snippet(item.span);
 +    let item_str = if contains_comment(new_str) {
 +        new_str.to_owned()
 +    } else {
 +        let no_whitespace = &new_str.split_whitespace().collect::<Vec<&str>>().join(" ");
 +        String::from(&*Regex::new(r"\s;").unwrap().replace(no_whitespace, ";"))
 +    };
 +    rewrite_attrs(context, item, &item_str, attrs_shape)
 +}
 +
 +/// Returns `true` for `mod foo;`, false for `mod foo { .. }`.
 +pub(crate) fn is_mod_decl(item: &ast::Item) -> bool {
 +    !matches!(
 +        item.kind,
 +        ast::ItemKind::Mod(_, ast::ModKind::Loaded(_, ast::Inline::Yes, _))
 +    )
 +}
 +
 +pub(crate) fn is_use_item(item: &ast::Item) -> bool {
 +    matches!(item.kind, ast::ItemKind::Use(_))
 +}
 +
 +pub(crate) fn is_extern_crate(item: &ast::Item) -> bool {
 +    matches!(item.kind, ast::ItemKind::ExternCrate(..))
 +}
index 29d75585eb7252355f4969f341fc25072ed383a7,0000000000000000000000000000000000000000..e87850507824f03b7907f738bd5c0f8251643795
mode 100644,000000..100644
--- /dev/null
@@@ -1,930 -1,0 +1,943 @@@
-             let comment_end = get_comment_end(
-                 post_snippet,
-                 self.separator,
-                 self.terminator,
-                 self.inner.peek().is_none(),
-             );
 +//! Format list-like expressions and items.
 +
 +use std::cmp;
 +use std::iter::Peekable;
 +
 +use rustc_span::BytePos;
 +
 +use crate::comment::{find_comment_end, rewrite_comment, FindUncommented};
 +use crate::config::lists::*;
 +use crate::config::{Config, IndentStyle};
 +use crate::rewrite::RewriteContext;
 +use crate::shape::{Indent, Shape};
 +use crate::utils::{
 +    count_newlines, first_line_width, last_line_width, mk_sp, starts_with_newline,
 +    unicode_str_width,
 +};
 +use crate::visitor::SnippetProvider;
 +
 +pub(crate) struct ListFormatting<'a> {
 +    tactic: DefinitiveListTactic,
 +    separator: &'a str,
 +    trailing_separator: SeparatorTactic,
 +    separator_place: SeparatorPlace,
 +    shape: Shape,
 +    // Non-expressions, e.g., items, will have a new line at the end of the list.
 +    // Important for comment styles.
 +    ends_with_newline: bool,
 +    // Remove newlines between list elements for expressions.
 +    preserve_newline: bool,
 +    // Nested import lists get some special handling for the "Mixed" list type
 +    nested: bool,
 +    // Whether comments should be visually aligned.
 +    align_comments: bool,
 +    config: &'a Config,
 +}
 +
 +impl<'a> ListFormatting<'a> {
 +    pub(crate) fn new(shape: Shape, config: &'a Config) -> Self {
 +        ListFormatting {
 +            tactic: DefinitiveListTactic::Vertical,
 +            separator: ",",
 +            trailing_separator: SeparatorTactic::Never,
 +            separator_place: SeparatorPlace::Back,
 +            shape,
 +            ends_with_newline: true,
 +            preserve_newline: false,
 +            nested: false,
 +            align_comments: true,
 +            config,
 +        }
 +    }
 +
 +    pub(crate) fn tactic(mut self, tactic: DefinitiveListTactic) -> Self {
 +        self.tactic = tactic;
 +        self
 +    }
 +
 +    pub(crate) fn separator(mut self, separator: &'a str) -> Self {
 +        self.separator = separator;
 +        self
 +    }
 +
 +    pub(crate) fn trailing_separator(mut self, trailing_separator: SeparatorTactic) -> Self {
 +        self.trailing_separator = trailing_separator;
 +        self
 +    }
 +
 +    pub(crate) fn separator_place(mut self, separator_place: SeparatorPlace) -> Self {
 +        self.separator_place = separator_place;
 +        self
 +    }
 +
 +    pub(crate) fn ends_with_newline(mut self, ends_with_newline: bool) -> Self {
 +        self.ends_with_newline = ends_with_newline;
 +        self
 +    }
 +
 +    pub(crate) fn preserve_newline(mut self, preserve_newline: bool) -> Self {
 +        self.preserve_newline = preserve_newline;
 +        self
 +    }
 +
 +    pub(crate) fn nested(mut self, nested: bool) -> Self {
 +        self.nested = nested;
 +        self
 +    }
 +
 +    pub(crate) fn align_comments(mut self, align_comments: bool) -> Self {
 +        self.align_comments = align_comments;
 +        self
 +    }
 +
 +    pub(crate) fn needs_trailing_separator(&self) -> bool {
 +        match self.trailing_separator {
 +            // We always put separator in front.
 +            SeparatorTactic::Always => true,
 +            SeparatorTactic::Vertical => self.tactic == DefinitiveListTactic::Vertical,
 +            SeparatorTactic::Never => {
 +                self.tactic == DefinitiveListTactic::Vertical && self.separator_place.is_front()
 +            }
 +        }
 +    }
 +}
 +
 +impl AsRef<ListItem> for ListItem {
 +    fn as_ref(&self) -> &ListItem {
 +        self
 +    }
 +}
 +
 +#[derive(PartialEq, Eq, Debug, Copy, Clone)]
 +pub(crate) enum ListItemCommentStyle {
 +    // Try to keep the comment on the same line with the item.
 +    SameLine,
 +    // Put the comment on the previous or the next line of the item.
 +    DifferentLine,
 +    // No comment available.
 +    None,
 +}
 +
 +#[derive(Debug, Clone)]
 +pub(crate) struct ListItem {
 +    // None for comments mean that they are not present.
 +    pub(crate) pre_comment: Option<String>,
 +    pub(crate) pre_comment_style: ListItemCommentStyle,
 +    // Item should include attributes and doc comments. None indicates a failed
 +    // rewrite.
 +    pub(crate) item: Option<String>,
 +    pub(crate) post_comment: Option<String>,
 +    // Whether there is extra whitespace before this item.
 +    pub(crate) new_lines: bool,
 +}
 +
 +impl ListItem {
 +    pub(crate) fn empty() -> ListItem {
 +        ListItem {
 +            pre_comment: None,
 +            pre_comment_style: ListItemCommentStyle::None,
 +            item: None,
 +            post_comment: None,
 +            new_lines: false,
 +        }
 +    }
 +
 +    pub(crate) fn inner_as_ref(&self) -> &str {
 +        self.item.as_ref().map_or("", |s| s)
 +    }
 +
 +    pub(crate) fn is_different_group(&self) -> bool {
 +        self.inner_as_ref().contains('\n')
 +            || self.pre_comment.is_some()
 +            || self
 +                .post_comment
 +                .as_ref()
 +                .map_or(false, |s| s.contains('\n'))
 +    }
 +
 +    pub(crate) fn is_multiline(&self) -> bool {
 +        self.inner_as_ref().contains('\n')
 +            || self
 +                .pre_comment
 +                .as_ref()
 +                .map_or(false, |s| s.contains('\n'))
 +            || self
 +                .post_comment
 +                .as_ref()
 +                .map_or(false, |s| s.contains('\n'))
 +    }
 +
 +    pub(crate) fn has_single_line_comment(&self) -> bool {
 +        self.pre_comment
 +            .as_ref()
 +            .map_or(false, |comment| comment.trim_start().starts_with("//"))
 +            || self
 +                .post_comment
 +                .as_ref()
 +                .map_or(false, |comment| comment.trim_start().starts_with("//"))
 +    }
 +
 +    pub(crate) fn has_comment(&self) -> bool {
 +        self.pre_comment.is_some() || self.post_comment.is_some()
 +    }
 +
 +    pub(crate) fn from_str<S: Into<String>>(s: S) -> ListItem {
 +        ListItem {
 +            pre_comment: None,
 +            pre_comment_style: ListItemCommentStyle::None,
 +            item: Some(s.into()),
 +            post_comment: None,
 +            new_lines: false,
 +        }
 +    }
 +
 +    // Returns `true` if the item causes something to be written.
 +    fn is_substantial(&self) -> bool {
 +        fn empty(s: &Option<String>) -> bool {
 +            !matches!(*s, Some(ref s) if !s.is_empty())
 +        }
 +
 +        !(empty(&self.pre_comment) && empty(&self.item) && empty(&self.post_comment))
 +    }
 +}
 +
 +/// The type of separator for lists.
 +#[derive(Copy, Clone, Eq, PartialEq, Debug)]
 +pub(crate) enum Separator {
 +    Comma,
 +    VerticalBar,
 +}
 +
 +impl Separator {
 +    pub(crate) fn len(self) -> usize {
 +        match self {
 +            // 2 = `, `
 +            Separator::Comma => 2,
 +            // 3 = ` | `
 +            Separator::VerticalBar => 3,
 +        }
 +    }
 +}
 +
 +pub(crate) fn definitive_tactic<I, T>(
 +    items: I,
 +    tactic: ListTactic,
 +    sep: Separator,
 +    width: usize,
 +) -> DefinitiveListTactic
 +where
 +    I: IntoIterator<Item = T> + Clone,
 +    T: AsRef<ListItem>,
 +{
 +    let pre_line_comments = items
 +        .clone()
 +        .into_iter()
 +        .any(|item| item.as_ref().has_single_line_comment());
 +
 +    let limit = match tactic {
 +        _ if pre_line_comments => return DefinitiveListTactic::Vertical,
 +        ListTactic::Horizontal => return DefinitiveListTactic::Horizontal,
 +        ListTactic::Vertical => return DefinitiveListTactic::Vertical,
 +        ListTactic::LimitedHorizontalVertical(limit) => ::std::cmp::min(width, limit),
 +        ListTactic::Mixed | ListTactic::HorizontalVertical => width,
 +    };
 +
 +    let (sep_count, total_width) = calculate_width(items.clone());
 +    let total_sep_len = sep.len() * sep_count.saturating_sub(1);
 +    let real_total = total_width + total_sep_len;
 +
 +    if real_total <= limit && !items.into_iter().any(|item| item.as_ref().is_multiline()) {
 +        DefinitiveListTactic::Horizontal
 +    } else {
 +        match tactic {
 +            ListTactic::Mixed => DefinitiveListTactic::Mixed,
 +            _ => DefinitiveListTactic::Vertical,
 +        }
 +    }
 +}
 +
 +// Format a list of commented items into a string.
 +pub(crate) fn write_list<I, T>(items: I, formatting: &ListFormatting<'_>) -> Option<String>
 +where
 +    I: IntoIterator<Item = T> + Clone,
 +    T: AsRef<ListItem>,
 +{
 +    let tactic = formatting.tactic;
 +    let sep_len = formatting.separator.len();
 +
 +    // Now that we know how we will layout, we can decide for sure if there
 +    // will be a trailing separator.
 +    let mut trailing_separator = formatting.needs_trailing_separator();
 +    let mut result = String::with_capacity(128);
 +    let cloned_items = items.clone();
 +    let mut iter = items.into_iter().enumerate().peekable();
 +    let mut item_max_width: Option<usize> = None;
 +    let sep_place =
 +        SeparatorPlace::from_tactic(formatting.separator_place, tactic, formatting.separator);
 +    let mut prev_item_had_post_comment = false;
 +    let mut prev_item_is_nested_import = false;
 +
 +    let mut line_len = 0;
 +    let indent_str = &formatting.shape.indent.to_string(formatting.config);
 +    while let Some((i, item)) = iter.next() {
 +        let item = item.as_ref();
 +        let inner_item = item.item.as_ref()?;
 +        let first = i == 0;
 +        let last = iter.peek().is_none();
 +        let mut separate = match sep_place {
 +            SeparatorPlace::Front => !first,
 +            SeparatorPlace::Back => !last || trailing_separator,
 +        };
 +        let item_sep_len = if separate { sep_len } else { 0 };
 +
 +        // Item string may be multi-line. Its length (used for block comment alignment)
 +        // should be only the length of the last line.
 +        let item_last_line = if item.is_multiline() {
 +            inner_item.lines().last().unwrap_or("")
 +        } else {
 +            inner_item.as_ref()
 +        };
 +        let mut item_last_line_width = item_last_line.len() + item_sep_len;
 +        if item_last_line.starts_with(&**indent_str) {
 +            item_last_line_width -= indent_str.len();
 +        }
 +
 +        if !item.is_substantial() {
 +            continue;
 +        }
 +
 +        match tactic {
 +            DefinitiveListTactic::Horizontal if !first => {
 +                result.push(' ');
 +            }
 +            DefinitiveListTactic::SpecialMacro(num_args_before) => {
 +                if i == 0 {
 +                    // Nothing
 +                } else if i < num_args_before {
 +                    result.push(' ');
 +                } else if i <= num_args_before + 1 {
 +                    result.push('\n');
 +                    result.push_str(indent_str);
 +                } else {
 +                    result.push(' ');
 +                }
 +            }
 +            DefinitiveListTactic::Vertical
 +                if !first && !inner_item.is_empty() && !result.is_empty() =>
 +            {
 +                result.push('\n');
 +                result.push_str(indent_str);
 +            }
 +            DefinitiveListTactic::Mixed => {
 +                let total_width = total_item_width(item) + item_sep_len;
 +
 +                // 1 is space between separator and item.
 +                if (line_len > 0 && line_len + 1 + total_width > formatting.shape.width)
 +                    || prev_item_had_post_comment
 +                    || (formatting.nested
 +                        && (prev_item_is_nested_import || (!first && inner_item.contains("::"))))
 +                {
 +                    result.push('\n');
 +                    result.push_str(indent_str);
 +                    line_len = 0;
 +                    if formatting.ends_with_newline {
 +                        trailing_separator = true;
 +                    }
 +                } else if line_len > 0 {
 +                    result.push(' ');
 +                    line_len += 1;
 +                }
 +
 +                if last && formatting.ends_with_newline {
 +                    separate = formatting.trailing_separator != SeparatorTactic::Never;
 +                }
 +
 +                line_len += total_width;
 +            }
 +            _ => {}
 +        }
 +
 +        // Pre-comments
 +        if let Some(ref comment) = item.pre_comment {
 +            // Block style in non-vertical mode.
 +            let block_mode = tactic == DefinitiveListTactic::Horizontal;
 +            // Width restriction is only relevant in vertical mode.
 +            let comment =
 +                rewrite_comment(comment, block_mode, formatting.shape, formatting.config)?;
 +            result.push_str(&comment);
 +
 +            if !inner_item.is_empty() {
 +                use DefinitiveListTactic::*;
 +                if matches!(tactic, Vertical | Mixed | SpecialMacro(_)) {
 +                    // We cannot keep pre-comments on the same line if the comment is normalized.
 +                    let keep_comment = if formatting.config.normalize_comments()
 +                        || item.pre_comment_style == ListItemCommentStyle::DifferentLine
 +                    {
 +                        false
 +                    } else {
 +                        // We will try to keep the comment on the same line with the item here.
 +                        // 1 = ` `
 +                        let total_width = total_item_width(item) + item_sep_len + 1;
 +                        total_width <= formatting.shape.width
 +                    };
 +                    if keep_comment {
 +                        result.push(' ');
 +                    } else {
 +                        result.push('\n');
 +                        result.push_str(indent_str);
 +                        // This is the width of the item (without comments).
 +                        line_len = item.item.as_ref().map_or(0, |s| unicode_str_width(s));
 +                    }
 +                } else {
 +                    result.push(' ')
 +                }
 +            }
 +            item_max_width = None;
 +        }
 +
 +        if separate && sep_place.is_front() && !first {
 +            result.push_str(formatting.separator.trim());
 +            result.push(' ');
 +        }
 +        result.push_str(inner_item);
 +
 +        // Post-comments
 +        if tactic == DefinitiveListTactic::Horizontal && item.post_comment.is_some() {
 +            let comment = item.post_comment.as_ref().unwrap();
 +            let formatted_comment = rewrite_comment(
 +                comment,
 +                true,
 +                Shape::legacy(formatting.shape.width, Indent::empty()),
 +                formatting.config,
 +            )?;
 +
 +            result.push(' ');
 +            result.push_str(&formatted_comment);
 +        }
 +
 +        if separate && sep_place.is_back() {
 +            result.push_str(formatting.separator);
 +        }
 +
 +        if tactic != DefinitiveListTactic::Horizontal && item.post_comment.is_some() {
 +            let comment = item.post_comment.as_ref().unwrap();
 +            let overhead = last_line_width(&result) + first_line_width(comment.trim());
 +
 +            let rewrite_post_comment = |item_max_width: &mut Option<usize>| {
 +                if item_max_width.is_none() && !last && !inner_item.contains('\n') {
 +                    *item_max_width = Some(max_width_of_item_with_post_comment(
 +                        &cloned_items,
 +                        i,
 +                        overhead,
 +                        formatting.config.max_width(),
 +                    ));
 +                }
 +                let overhead = if starts_with_newline(comment) {
 +                    0
 +                } else if let Some(max_width) = *item_max_width {
 +                    max_width + 2
 +                } else {
 +                    // 1 = space between item and comment.
 +                    item_last_line_width + 1
 +                };
 +                let width = formatting.shape.width.checked_sub(overhead).unwrap_or(1);
 +                let offset = formatting.shape.indent + overhead;
 +                let comment_shape = Shape::legacy(width, offset);
 +
 +                let block_style = if !formatting.ends_with_newline && last {
 +                    true
 +                } else if starts_with_newline(comment) {
 +                    false
 +                } else {
 +                    comment.trim().contains('\n') || comment.trim().len() > width
 +                };
 +
 +                rewrite_comment(
 +                    comment.trim_start(),
 +                    block_style,
 +                    comment_shape,
 +                    formatting.config,
 +                )
 +            };
 +
 +            let mut formatted_comment = rewrite_post_comment(&mut item_max_width)?;
 +
 +            if !starts_with_newline(comment) {
 +                if formatting.align_comments {
 +                    let mut comment_alignment =
 +                        post_comment_alignment(item_max_width, inner_item.len());
 +                    if first_line_width(&formatted_comment)
 +                        + last_line_width(&result)
 +                        + comment_alignment
 +                        + 1
 +                        > formatting.config.max_width()
 +                    {
 +                        item_max_width = None;
 +                        formatted_comment = rewrite_post_comment(&mut item_max_width)?;
 +                        comment_alignment =
 +                            post_comment_alignment(item_max_width, inner_item.len());
 +                    }
 +                    for _ in 0..=comment_alignment {
 +                        result.push(' ');
 +                    }
 +                }
 +                // An additional space for the missing trailing separator (or
 +                // if we skipped alignment above).
 +                if !formatting.align_comments
 +                    || (last
 +                        && item_max_width.is_some()
 +                        && !separate
 +                        && !formatting.separator.is_empty())
 +                {
 +                    result.push(' ');
 +                }
 +            } else {
 +                result.push('\n');
 +                result.push_str(indent_str);
 +            }
 +            if formatted_comment.contains('\n') {
 +                item_max_width = None;
 +            }
 +            result.push_str(&formatted_comment);
 +        } else {
 +            item_max_width = None;
 +        }
 +
 +        if formatting.preserve_newline
 +            && !last
 +            && tactic == DefinitiveListTactic::Vertical
 +            && item.new_lines
 +        {
 +            item_max_width = None;
 +            result.push('\n');
 +        }
 +
 +        prev_item_had_post_comment = item.post_comment.is_some();
 +        prev_item_is_nested_import = inner_item.contains("::");
 +    }
 +
 +    Some(result)
 +}
 +
 +fn max_width_of_item_with_post_comment<I, T>(
 +    items: &I,
 +    i: usize,
 +    overhead: usize,
 +    max_budget: usize,
 +) -> usize
 +where
 +    I: IntoIterator<Item = T> + Clone,
 +    T: AsRef<ListItem>,
 +{
 +    let mut max_width = 0;
 +    let mut first = true;
 +    for item in items.clone().into_iter().skip(i) {
 +        let item = item.as_ref();
 +        let inner_item_width = item.inner_as_ref().len();
 +        if !first
 +            && (item.is_different_group()
 +                || item.post_comment.is_none()
 +                || inner_item_width + overhead > max_budget)
 +        {
 +            return max_width;
 +        }
 +        if max_width < inner_item_width {
 +            max_width = inner_item_width;
 +        }
 +        if item.new_lines {
 +            return max_width;
 +        }
 +        first = false;
 +    }
 +    max_width
 +}
 +
 +fn post_comment_alignment(item_max_width: Option<usize>, inner_item_len: usize) -> usize {
 +    item_max_width.unwrap_or(0).saturating_sub(inner_item_len)
 +}
 +
 +pub(crate) struct ListItems<'a, I, F1, F2, F3>
 +where
 +    I: Iterator,
 +{
 +    snippet_provider: &'a SnippetProvider,
 +    inner: Peekable<I>,
 +    get_lo: F1,
 +    get_hi: F2,
 +    get_item_string: F3,
 +    prev_span_end: BytePos,
 +    next_span_start: BytePos,
 +    terminator: &'a str,
 +    separator: &'a str,
 +    leave_last: bool,
 +}
 +
 +pub(crate) fn extract_pre_comment(pre_snippet: &str) -> (Option<String>, ListItemCommentStyle) {
 +    let trimmed_pre_snippet = pre_snippet.trim();
 +    // Both start and end are checked to support keeping a block comment inline with
 +    // the item, even if there are preceding line comments, while still supporting
 +    // a snippet that starts with a block comment but also contains one or more
 +    // trailing single line comments.
 +    // https://github.com/rust-lang/rustfmt/issues/3025
 +    // https://github.com/rust-lang/rustfmt/pull/3048
 +    // https://github.com/rust-lang/rustfmt/issues/3839
 +    let starts_with_block_comment = trimmed_pre_snippet.starts_with("/*");
 +    let ends_with_block_comment = trimmed_pre_snippet.ends_with("*/");
 +    let starts_with_single_line_comment = trimmed_pre_snippet.starts_with("//");
 +    if ends_with_block_comment {
 +        let comment_end = pre_snippet.rfind(|c| c == '/').unwrap();
 +        if pre_snippet[comment_end..].contains('\n') {
 +            (
 +                Some(trimmed_pre_snippet.to_owned()),
 +                ListItemCommentStyle::DifferentLine,
 +            )
 +        } else {
 +            (
 +                Some(trimmed_pre_snippet.to_owned()),
 +                ListItemCommentStyle::SameLine,
 +            )
 +        }
 +    } else if starts_with_single_line_comment || starts_with_block_comment {
 +        (
 +            Some(trimmed_pre_snippet.to_owned()),
 +            ListItemCommentStyle::DifferentLine,
 +        )
 +    } else {
 +        (None, ListItemCommentStyle::None)
 +    }
 +}
 +
 +pub(crate) fn extract_post_comment(
 +    post_snippet: &str,
 +    comment_end: usize,
 +    separator: &str,
++    is_last: bool,
 +) -> Option<String> {
 +    let white_space: &[_] = &[' ', '\t'];
 +
 +    // Cleanup post-comment: strip separators and whitespace.
 +    let post_snippet = post_snippet[..comment_end].trim();
++
++    let last_inline_comment_ends_with_separator = if is_last {
++        if let Some(line) = post_snippet.lines().last() {
++            line.ends_with(separator) && line.trim().starts_with("//")
++        } else {
++            false
++        }
++    } else {
++        false
++    };
++
 +    let post_snippet_trimmed = if post_snippet.starts_with(|c| c == ',' || c == ':') {
 +        post_snippet[1..].trim_matches(white_space)
 +    } else if let Some(stripped) = post_snippet.strip_prefix(separator) {
 +        stripped.trim_matches(white_space)
++    } else if last_inline_comment_ends_with_separator {
++        // since we're on the last item it's fine to keep any trailing separators in comments
++        post_snippet.trim_matches(white_space)
 +    }
 +    // not comment or over two lines
 +    else if post_snippet.ends_with(',')
 +        && (!post_snippet.trim().starts_with("//") || post_snippet.trim().contains('\n'))
 +    {
 +        post_snippet[..(post_snippet.len() - 1)].trim_matches(white_space)
 +    } else {
 +        post_snippet
 +    };
 +    // FIXME(#3441): post_snippet includes 'const' now
 +    // it should not include here
 +    let removed_newline_snippet = post_snippet_trimmed.trim();
 +    if !post_snippet_trimmed.is_empty()
 +        && (removed_newline_snippet.starts_with("//") || removed_newline_snippet.starts_with("/*"))
 +    {
 +        Some(post_snippet_trimmed.to_owned())
 +    } else {
 +        None
 +    }
 +}
 +
 +pub(crate) fn get_comment_end(
 +    post_snippet: &str,
 +    separator: &str,
 +    terminator: &str,
 +    is_last: bool,
 +) -> usize {
 +    if is_last {
 +        return post_snippet
 +            .find_uncommented(terminator)
 +            .unwrap_or_else(|| post_snippet.len());
 +    }
 +
 +    let mut block_open_index = post_snippet.find("/*");
 +    // check if it really is a block comment (and not `//*` or a nested comment)
 +    if let Some(i) = block_open_index {
 +        match post_snippet.find('/') {
 +            Some(j) if j < i => block_open_index = None,
 +            _ if post_snippet[..i].ends_with('/') => block_open_index = None,
 +            _ => (),
 +        }
 +    }
 +    let newline_index = post_snippet.find('\n');
 +    if let Some(separator_index) = post_snippet.find_uncommented(separator) {
 +        match (block_open_index, newline_index) {
 +            // Separator before comment, with the next item on same line.
 +            // Comment belongs to next item.
 +            (Some(i), None) if i > separator_index => separator_index + 1,
 +            // Block-style post-comment before the separator.
 +            (Some(i), None) => cmp::max(
 +                find_comment_end(&post_snippet[i..]).unwrap() + i,
 +                separator_index + 1,
 +            ),
 +            // Block-style post-comment. Either before or after the separator.
 +            (Some(i), Some(j)) if i < j => cmp::max(
 +                find_comment_end(&post_snippet[i..]).unwrap() + i,
 +                separator_index + 1,
 +            ),
 +            // Potential *single* line comment.
 +            (_, Some(j)) if j > separator_index => j + 1,
 +            _ => post_snippet.len(),
 +        }
 +    } else if let Some(newline_index) = newline_index {
 +        // Match arms may not have trailing comma. In any case, for match arms,
 +        // we will assume that the post comment belongs to the next arm if they
 +        // do not end with trailing comma.
 +        newline_index + 1
 +    } else {
 +        0
 +    }
 +}
 +
 +// Account for extra whitespace between items. This is fiddly
 +// because of the way we divide pre- and post- comments.
 +pub(crate) fn has_extra_newline(post_snippet: &str, comment_end: usize) -> bool {
 +    if post_snippet.is_empty() || comment_end == 0 {
 +        return false;
 +    }
 +
 +    let len_last = post_snippet[..comment_end]
 +        .chars()
 +        .last()
 +        .unwrap()
 +        .len_utf8();
 +    // Everything from the separator to the next item.
 +    let test_snippet = &post_snippet[comment_end - len_last..];
 +    let first_newline = test_snippet
 +        .find('\n')
 +        .unwrap_or_else(|| test_snippet.len());
 +    // From the end of the first line of comments.
 +    let test_snippet = &test_snippet[first_newline..];
 +    let first = test_snippet
 +        .find(|c: char| !c.is_whitespace())
 +        .unwrap_or_else(|| test_snippet.len());
 +    // From the end of the first line of comments to the next non-whitespace char.
 +    let test_snippet = &test_snippet[..first];
 +
 +    // There were multiple line breaks which got trimmed to nothing.
 +    count_newlines(test_snippet) > 1
 +}
 +
 +impl<'a, T, I, F1, F2, F3> Iterator for ListItems<'a, I, F1, F2, F3>
 +where
 +    I: Iterator<Item = T>,
 +    F1: Fn(&T) -> BytePos,
 +    F2: Fn(&T) -> BytePos,
 +    F3: Fn(&T) -> Option<String>,
 +{
 +    type Item = ListItem;
 +
 +    fn next(&mut self) -> Option<Self::Item> {
 +        self.inner.next().map(|item| {
 +            // Pre-comment
 +            let pre_snippet = self
 +                .snippet_provider
 +                .span_to_snippet(mk_sp(self.prev_span_end, (self.get_lo)(&item)))
 +                .unwrap_or("");
 +            let (pre_comment, pre_comment_style) = extract_pre_comment(pre_snippet);
 +
 +            // Post-comment
 +            let next_start = match self.inner.peek() {
 +                Some(next_item) => (self.get_lo)(next_item),
 +                None => self.next_span_start,
 +            };
 +            let post_snippet = self
 +                .snippet_provider
 +                .span_to_snippet(mk_sp((self.get_hi)(&item), next_start))
 +                .unwrap_or("");
-             let post_comment = extract_post_comment(post_snippet, comment_end, self.separator);
++            let is_last = self.inner.peek().is_none();
++            let comment_end =
++                get_comment_end(post_snippet, self.separator, self.terminator, is_last);
 +            let new_lines = has_extra_newline(post_snippet, comment_end);
++            let post_comment =
++                extract_post_comment(post_snippet, comment_end, self.separator, is_last);
 +
 +            self.prev_span_end = (self.get_hi)(&item) + BytePos(comment_end as u32);
 +
 +            ListItem {
 +                pre_comment,
 +                pre_comment_style,
 +                item: if self.inner.peek().is_none() && self.leave_last {
 +                    None
 +                } else {
 +                    (self.get_item_string)(&item)
 +                },
 +                post_comment,
 +                new_lines,
 +            }
 +        })
 +    }
 +}
 +
 +#[allow(clippy::too_many_arguments)]
 +// Creates an iterator over a list's items with associated comments.
 +pub(crate) fn itemize_list<'a, T, I, F1, F2, F3>(
 +    snippet_provider: &'a SnippetProvider,
 +    inner: I,
 +    terminator: &'a str,
 +    separator: &'a str,
 +    get_lo: F1,
 +    get_hi: F2,
 +    get_item_string: F3,
 +    prev_span_end: BytePos,
 +    next_span_start: BytePos,
 +    leave_last: bool,
 +) -> ListItems<'a, I, F1, F2, F3>
 +where
 +    I: Iterator<Item = T>,
 +    F1: Fn(&T) -> BytePos,
 +    F2: Fn(&T) -> BytePos,
 +    F3: Fn(&T) -> Option<String>,
 +{
 +    ListItems {
 +        snippet_provider,
 +        inner: inner.peekable(),
 +        get_lo,
 +        get_hi,
 +        get_item_string,
 +        prev_span_end,
 +        next_span_start,
 +        terminator,
 +        separator,
 +        leave_last,
 +    }
 +}
 +
 +/// Returns the count and total width of the list items.
 +fn calculate_width<I, T>(items: I) -> (usize, usize)
 +where
 +    I: IntoIterator<Item = T>,
 +    T: AsRef<ListItem>,
 +{
 +    items
 +        .into_iter()
 +        .map(|item| total_item_width(item.as_ref()))
 +        .fold((0, 0), |acc, l| (acc.0 + 1, acc.1 + l))
 +}
 +
 +pub(crate) fn total_item_width(item: &ListItem) -> usize {
 +    comment_len(item.pre_comment.as_ref().map(|x| &(*x)[..]))
 +        + comment_len(item.post_comment.as_ref().map(|x| &(*x)[..]))
 +        + item.item.as_ref().map_or(0, |s| unicode_str_width(s))
 +}
 +
 +fn comment_len(comment: Option<&str>) -> usize {
 +    match comment {
 +        Some(s) => {
 +            let text_len = s.trim().len();
 +            if text_len > 0 {
 +                // We'll put " /*" before and " */" after inline comments.
 +                text_len + 6
 +            } else {
 +                text_len
 +            }
 +        }
 +        None => 0,
 +    }
 +}
 +
 +// Compute horizontal and vertical shapes for a struct-lit-like thing.
 +pub(crate) fn struct_lit_shape(
 +    shape: Shape,
 +    context: &RewriteContext<'_>,
 +    prefix_width: usize,
 +    suffix_width: usize,
 +) -> Option<(Option<Shape>, Shape)> {
 +    let v_shape = match context.config.indent_style() {
 +        IndentStyle::Visual => shape
 +            .visual_indent(0)
 +            .shrink_left(prefix_width)?
 +            .sub_width(suffix_width)?,
 +        IndentStyle::Block => {
 +            let shape = shape.block_indent(context.config.tab_spaces());
 +            Shape {
 +                width: context.budget(shape.indent.width()),
 +                ..shape
 +            }
 +        }
 +    };
 +    let shape_width = shape.width.checked_sub(prefix_width + suffix_width);
 +    if let Some(w) = shape_width {
 +        let shape_width = cmp::min(w, context.config.struct_lit_width());
 +        Some((Some(Shape::legacy(shape_width, shape.indent)), v_shape))
 +    } else {
 +        Some((None, v_shape))
 +    }
 +}
 +
 +// Compute the tactic for the internals of a struct-lit-like thing.
 +pub(crate) fn struct_lit_tactic(
 +    h_shape: Option<Shape>,
 +    context: &RewriteContext<'_>,
 +    items: &[ListItem],
 +) -> DefinitiveListTactic {
 +    if let Some(h_shape) = h_shape {
 +        let prelim_tactic = match (context.config.indent_style(), items.len()) {
 +            (IndentStyle::Visual, 1) => ListTactic::HorizontalVertical,
 +            _ if context.config.struct_lit_single_line() => ListTactic::HorizontalVertical,
 +            _ => ListTactic::Vertical,
 +        };
 +        definitive_tactic(items, prelim_tactic, Separator::Comma, h_shape.width)
 +    } else {
 +        DefinitiveListTactic::Vertical
 +    }
 +}
 +
 +// Given a tactic and possible shapes for horizontal and vertical layout,
 +// come up with the actual shape to use.
 +pub(crate) fn shape_for_tactic(
 +    tactic: DefinitiveListTactic,
 +    h_shape: Option<Shape>,
 +    v_shape: Shape,
 +) -> Shape {
 +    match tactic {
 +        DefinitiveListTactic::Horizontal => h_shape.unwrap(),
 +        _ => v_shape,
 +    }
 +}
 +
 +// Create a ListFormatting object for formatting the internals of a
 +// struct-lit-like thing, that is a series of fields.
 +pub(crate) fn struct_lit_formatting<'a>(
 +    shape: Shape,
 +    tactic: DefinitiveListTactic,
 +    context: &'a RewriteContext<'_>,
 +    force_no_trailing_comma: bool,
 +) -> ListFormatting<'a> {
 +    let ends_with_newline = context.config.indent_style() != IndentStyle::Visual
 +        && tactic == DefinitiveListTactic::Vertical;
 +    ListFormatting {
 +        tactic,
 +        separator: ",",
 +        trailing_separator: if force_no_trailing_comma {
 +            SeparatorTactic::Never
 +        } else {
 +            context.config.trailing_comma()
 +        },
 +        separator_place: SeparatorPlace::Back,
 +        shape,
 +        ends_with_newline,
 +        preserve_newline: true,
 +        nested: false,
 +        align_comments: true,
 +        config: context.config,
 +    }
 +}
index f29552caf8d87efb2c585ced695eb065c302c9d6,0000000000000000000000000000000000000000..664f152e8be1d3ba2e7586b1acdbc6afcdcc42af
mode 100644,000000..100644
--- /dev/null
@@@ -1,1378 -1,0 +1,1388 @@@
-     Some(context.snippet(span).to_owned())
 +// Format list-like macro invocations. These are invocations whose token trees
 +// can be interpreted as expressions and separated by commas.
 +// Note that these token trees do not actually have to be interpreted as
 +// expressions by the compiler. An example of an invocation we would reformat is
 +// foo!( x, y, z ). The token x may represent an identifier in the code, but we
 +// interpreted as an expression.
 +// Macro uses which are not-list like, such as bar!(key => val), will not be
 +// reformatted.
 +// List-like invocations with parentheses will be formatted as function calls,
 +// and those with brackets will be formatted as array literals.
 +
 +use std::collections::HashMap;
 +use std::panic::{catch_unwind, AssertUnwindSafe};
 +
 +use rustc_ast::token::{BinOpToken, DelimToken, Token, TokenKind};
 +use rustc_ast::tokenstream::{Cursor, Spacing, TokenStream, TokenTree};
 +use rustc_ast::{ast, ptr};
 +use rustc_ast_pretty::pprust;
 +use rustc_span::{
 +    symbol::{self, kw},
 +    BytePos, Span, Symbol, DUMMY_SP,
 +};
 +
 +use crate::comment::{
 +    contains_comment, CharClasses, FindUncommented, FullCodeCharKind, LineClasses,
 +};
 +use crate::config::lists::*;
 +use crate::expr::{rewrite_array, rewrite_assign_rhs, RhsAssignKind};
 +use crate::lists::{itemize_list, write_list, ListFormatting};
 +use crate::overflow;
 +use crate::parse::macros::lazy_static::parse_lazy_static;
 +use crate::parse::macros::{parse_expr, parse_macro_args, ParsedMacroArgs};
 +use crate::rewrite::{Rewrite, RewriteContext};
 +use crate::shape::{Indent, Shape};
 +use crate::source_map::SpanUtils;
 +use crate::spanned::Spanned;
 +use crate::utils::{
 +    format_visibility, indent_next_line, is_empty_line, mk_sp, remove_trailing_white_spaces,
 +    rewrite_ident, trim_left_preserve_layout, wrap_str, NodeIdExt,
 +};
 +use crate::visitor::FmtVisitor;
 +
 +const FORCED_BRACKET_MACROS: &[&str] = &["vec!"];
 +
 +#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 +pub(crate) enum MacroPosition {
 +    Item,
 +    Statement,
 +    Expression,
 +    Pat,
 +}
 +
 +#[derive(Debug)]
 +pub(crate) enum MacroArg {
 +    Expr(ptr::P<ast::Expr>),
 +    Ty(ptr::P<ast::Ty>),
 +    Pat(ptr::P<ast::Pat>),
 +    Item(ptr::P<ast::Item>),
 +    Keyword(symbol::Ident, Span),
 +}
 +
 +impl MacroArg {
 +    pub(crate) fn is_item(&self) -> bool {
 +        match self {
 +            MacroArg::Item(..) => true,
 +            _ => false,
 +        }
 +    }
 +}
 +
 +impl Rewrite for ast::Item {
 +    fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
 +        let mut visitor = crate::visitor::FmtVisitor::from_context(context);
 +        visitor.block_indent = shape.indent;
 +        visitor.last_pos = self.span().lo();
 +        visitor.visit_item(self);
 +        Some(visitor.buffer.to_owned())
 +    }
 +}
 +
 +impl Rewrite for MacroArg {
 +    fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
 +        match *self {
 +            MacroArg::Expr(ref expr) => expr.rewrite(context, shape),
 +            MacroArg::Ty(ref ty) => ty.rewrite(context, shape),
 +            MacroArg::Pat(ref pat) => pat.rewrite(context, shape),
 +            MacroArg::Item(ref item) => item.rewrite(context, shape),
 +            MacroArg::Keyword(ident, _) => Some(ident.name.to_string()),
 +        }
 +    }
 +}
 +
 +/// Rewrite macro name without using pretty-printer if possible.
 +fn rewrite_macro_name(
 +    context: &RewriteContext<'_>,
 +    path: &ast::Path,
 +    extra_ident: Option<symbol::Ident>,
 +) -> String {
 +    let name = if path.segments.len() == 1 {
 +        // Avoid using pretty-printer in the common case.
 +        format!("{}!", rewrite_ident(context, path.segments[0].ident))
 +    } else {
 +        format!("{}!", pprust::path_to_string(path))
 +    };
 +    match extra_ident {
 +        Some(ident) if ident.name != kw::Empty => format!("{} {}", name, ident),
 +        _ => name,
 +    }
 +}
 +
 +// Use this on failing to format the macro call.
 +fn return_macro_parse_failure_fallback(
 +    context: &RewriteContext<'_>,
 +    indent: Indent,
++    position: MacroPosition,
 +    span: Span,
 +) -> Option<String> {
 +    // Mark this as a failure however we format it
 +    context.macro_rewrite_failure.replace(true);
 +
 +    // Heuristically determine whether the last line of the macro uses "Block" style
 +    // rather than using "Visual" style, or another indentation style.
 +    let is_like_block_indent_style = context
 +        .snippet(span)
 +        .lines()
 +        .last()
 +        .map(|closing_line| {
 +            closing_line
 +                .trim()
 +                .chars()
 +                .all(|ch| matches!(ch, '}' | ')' | ']'))
 +        })
 +        .unwrap_or(false);
 +    if is_like_block_indent_style {
 +        return trim_left_preserve_layout(context.snippet(span), indent, context.config);
 +    }
 +
 +    context.skipped_range.borrow_mut().push((
 +        context.parse_sess.line_of_byte_pos(span.lo()),
 +        context.parse_sess.line_of_byte_pos(span.hi()),
 +    ));
 +
 +    // Return the snippet unmodified if the macro is not block-like
-         .skip_macro(&context.snippet(mac.path.span).to_owned());
++    let mut snippet = context.snippet(span).to_owned();
++    if position == MacroPosition::Item {
++        snippet.push(';');
++    }
++    Some(snippet)
 +}
 +
 +pub(crate) fn rewrite_macro(
 +    mac: &ast::MacCall,
 +    extra_ident: Option<symbol::Ident>,
 +    context: &RewriteContext<'_>,
 +    shape: Shape,
 +    position: MacroPosition,
 +) -> Option<String> {
 +    let should_skip = context
 +        .skip_context
-             return return_macro_parse_failure_fallback(context, shape.indent, mac.span());
++        .skip_macro(context.snippet(mac.path.span));
 +    if should_skip {
 +        None
 +    } else {
 +        let guard = context.enter_macro();
 +        let result = catch_unwind(AssertUnwindSafe(|| {
 +            rewrite_macro_inner(
 +                mac,
 +                extra_ident,
 +                context,
 +                shape,
 +                position,
 +                guard.is_nested(),
 +            )
 +        }));
 +        match result {
 +            Err(..) | Ok(None) => {
 +                context.macro_rewrite_failure.replace(true);
 +                None
 +            }
 +            Ok(rw) => rw,
 +        }
 +    }
 +}
 +
 +fn rewrite_macro_inner(
 +    mac: &ast::MacCall,
 +    extra_ident: Option<symbol::Ident>,
 +    context: &RewriteContext<'_>,
 +    shape: Shape,
 +    position: MacroPosition,
 +    is_nested_macro: bool,
 +) -> Option<String> {
 +    if context.config.use_try_shorthand() {
 +        if let Some(expr) = convert_try_mac(mac, context) {
 +            context.leave_macro();
 +            return expr.rewrite(context, shape);
 +        }
 +    }
 +
 +    let original_style = macro_style(mac, context);
 +
 +    let macro_name = rewrite_macro_name(context, &mac.path, extra_ident);
 +    let is_forced_bracket = FORCED_BRACKET_MACROS.contains(&&macro_name[..]);
 +
 +    let style = if is_forced_bracket && !is_nested_macro {
 +        DelimToken::Bracket
 +    } else {
 +        original_style
 +    };
 +
 +    let ts = mac.args.inner_tokens();
 +    let has_comment = contains_comment(context.snippet(mac.span()));
 +    if ts.is_empty() && !has_comment {
 +        return match style {
 +            DelimToken::Paren if position == MacroPosition::Item => {
 +                Some(format!("{}();", macro_name))
 +            }
 +            DelimToken::Bracket if position == MacroPosition::Item => {
 +                Some(format!("{}[];", macro_name))
 +            }
 +            DelimToken::Paren => Some(format!("{}()", macro_name)),
 +            DelimToken::Bracket => Some(format!("{}[]", macro_name)),
 +            DelimToken::Brace => Some(format!("{} {{}}", macro_name)),
 +            _ => unreachable!(),
 +        };
 +    }
 +    // Format well-known macros which cannot be parsed as a valid AST.
 +    if macro_name == "lazy_static!" && !has_comment {
 +        if let success @ Some(..) = format_lazy_static(context, shape, ts.trees().collect()) {
 +            return success;
 +        }
 +    }
 +
 +    let ParsedMacroArgs {
 +        args: arg_vec,
 +        vec_with_semi,
 +        trailing_comma,
 +    } = match parse_macro_args(context, ts, style, is_forced_bracket) {
 +        Some(args) => args,
 +        None => {
++            return return_macro_parse_failure_fallback(
++                context,
++                shape.indent,
++                position,
++                mac.span(),
++            );
 +        }
 +    };
 +
 +    if !arg_vec.is_empty() && arg_vec.iter().all(MacroArg::is_item) {
 +        return rewrite_macro_with_items(
 +            context,
 +            &arg_vec,
 +            &macro_name,
 +            shape,
 +            style,
 +            position,
 +            mac.span(),
 +        );
 +    }
 +
 +    match style {
 +        DelimToken::Paren => {
 +            // Handle special case: `vec!(expr; expr)`
 +            if vec_with_semi {
 +                handle_vec_semi(context, shape, arg_vec, macro_name, style)
 +            } else {
 +                // Format macro invocation as function call, preserve the trailing
 +                // comma because not all macros support them.
 +                overflow::rewrite_with_parens(
 +                    context,
 +                    &macro_name,
 +                    arg_vec.iter(),
 +                    shape,
 +                    mac.span(),
 +                    context.config.fn_call_width(),
 +                    if trailing_comma {
 +                        Some(SeparatorTactic::Always)
 +                    } else {
 +                        Some(SeparatorTactic::Never)
 +                    },
 +                )
 +                .map(|rw| match position {
 +                    MacroPosition::Item => format!("{};", rw),
 +                    _ => rw,
 +                })
 +            }
 +        }
 +        DelimToken::Bracket => {
 +            // Handle special case: `vec![expr; expr]`
 +            if vec_with_semi {
 +                handle_vec_semi(context, shape, arg_vec, macro_name, style)
 +            } else {
 +                // If we are rewriting `vec!` macro or other special macros,
 +                // then we can rewrite this as a usual array literal.
 +                // Otherwise, we must preserve the original existence of trailing comma.
 +                let macro_name = &macro_name.as_str();
 +                let mut force_trailing_comma = if trailing_comma {
 +                    Some(SeparatorTactic::Always)
 +                } else {
 +                    Some(SeparatorTactic::Never)
 +                };
 +                if FORCED_BRACKET_MACROS.contains(macro_name) && !is_nested_macro {
 +                    context.leave_macro();
 +                    if context.use_block_indent() {
 +                        force_trailing_comma = Some(SeparatorTactic::Vertical);
 +                    };
 +                }
 +                let rewrite = rewrite_array(
 +                    macro_name,
 +                    arg_vec.iter(),
 +                    mac.span(),
 +                    context,
 +                    shape,
 +                    force_trailing_comma,
 +                    Some(original_style),
 +                )?;
 +                let comma = match position {
 +                    MacroPosition::Item => ";",
 +                    _ => "",
 +                };
 +
 +                Some(format!("{}{}", rewrite, comma))
 +            }
 +        }
 +        DelimToken::Brace => {
 +            // For macro invocations with braces, always put a space between
 +            // the `macro_name!` and `{ /* macro_body */ }` but skip modifying
 +            // anything in between the braces (for now).
 +            let snippet = context.snippet(mac.span()).trim_start_matches(|c| c != '{');
 +            match trim_left_preserve_layout(snippet, shape.indent, context.config) {
 +                Some(macro_body) => Some(format!("{} {}", macro_name, macro_body)),
 +                None => Some(format!("{} {}", macro_name, snippet)),
 +            }
 +        }
 +        _ => unreachable!(),
 +    }
 +}
 +
 +fn handle_vec_semi(
 +    context: &RewriteContext<'_>,
 +    shape: Shape,
 +    arg_vec: Vec<MacroArg>,
 +    macro_name: String,
 +    delim_token: DelimToken,
 +) -> Option<String> {
 +    let (left, right) = match delim_token {
 +        DelimToken::Paren => ("(", ")"),
 +        DelimToken::Bracket => ("[", "]"),
 +        _ => unreachable!(),
 +    };
 +
 +    let mac_shape = shape.offset_left(macro_name.len())?;
 +    // 8 = `vec![]` + `; ` or `vec!()` + `; `
 +    let total_overhead = 8;
 +    let nested_shape = mac_shape.block_indent(context.config.tab_spaces());
 +    let lhs = arg_vec[0].rewrite(context, nested_shape)?;
 +    let rhs = arg_vec[1].rewrite(context, nested_shape)?;
 +    if !lhs.contains('\n')
 +        && !rhs.contains('\n')
 +        && lhs.len() + rhs.len() + total_overhead <= shape.width
 +    {
 +        // macro_name(lhs; rhs) or macro_name[lhs; rhs]
 +        Some(format!("{}{}{}; {}{}", macro_name, left, lhs, rhs, right))
 +    } else {
 +        // macro_name(\nlhs;\nrhs\n) or macro_name[\nlhs;\nrhs\n]
 +        Some(format!(
 +            "{}{}{}{};{}{}{}{}",
 +            macro_name,
 +            left,
 +            nested_shape.indent.to_string_with_newline(context.config),
 +            lhs,
 +            nested_shape.indent.to_string_with_newline(context.config),
 +            rhs,
 +            shape.indent.to_string_with_newline(context.config),
 +            right
 +        ))
 +    }
 +}
 +
 +pub(crate) fn rewrite_macro_def(
 +    context: &RewriteContext<'_>,
 +    shape: Shape,
 +    indent: Indent,
 +    def: &ast::MacroDef,
 +    ident: symbol::Ident,
 +    vis: &ast::Visibility,
 +    span: Span,
 +) -> Option<String> {
 +    let snippet = Some(remove_trailing_white_spaces(context.snippet(span)));
 +    if snippet.as_ref().map_or(true, |s| s.ends_with(';')) {
 +        return snippet;
 +    }
 +
 +    let ts = def.body.inner_tokens();
 +    let mut parser = MacroParser::new(ts.into_trees());
 +    let parsed_def = match parser.parse() {
 +        Some(def) => def,
 +        None => return snippet,
 +    };
 +
 +    let mut result = if def.macro_rules {
 +        String::from("macro_rules!")
 +    } else {
 +        format!("{}macro", format_visibility(context, vis))
 +    };
 +
 +    result += " ";
 +    result += rewrite_ident(context, ident);
 +
 +    let multi_branch_style = def.macro_rules || parsed_def.branches.len() != 1;
 +
 +    let arm_shape = if multi_branch_style {
 +        shape
 +            .block_indent(context.config.tab_spaces())
 +            .with_max_width(context.config)
 +    } else {
 +        shape
 +    };
 +
 +    let branch_items = itemize_list(
 +        context.snippet_provider,
 +        parsed_def.branches.iter(),
 +        "}",
 +        ";",
 +        |branch| branch.span.lo(),
 +        |branch| branch.span.hi(),
 +        |branch| match branch.rewrite(context, arm_shape, multi_branch_style) {
 +            Some(v) => Some(v),
 +            // if the rewrite returned None because a macro could not be rewritten, then return the
 +            // original body
 +            None if context.macro_rewrite_failure.get() => {
 +                Some(context.snippet(branch.body).trim().to_string())
 +            }
 +            None => None,
 +        },
 +        context.snippet_provider.span_after(span, "{"),
 +        span.hi(),
 +        false,
 +    )
 +    .collect::<Vec<_>>();
 +
 +    let fmt = ListFormatting::new(arm_shape, context.config)
 +        .separator(if def.macro_rules { ";" } else { "" })
 +        .trailing_separator(SeparatorTactic::Always)
 +        .preserve_newline(true);
 +
 +    if multi_branch_style {
 +        result += " {";
 +        result += &arm_shape.indent.to_string_with_newline(context.config);
 +    }
 +
 +    match write_list(&branch_items, &fmt) {
 +        Some(ref s) => result += s,
 +        None => return snippet,
 +    }
 +
 +    if multi_branch_style {
 +        result += &indent.to_string_with_newline(context.config);
 +        result += "}";
 +    }
 +
 +    Some(result)
 +}
 +
 +fn register_metavariable(
 +    map: &mut HashMap<String, String>,
 +    result: &mut String,
 +    name: &str,
 +    dollar_count: usize,
 +) {
 +    let mut new_name = "$".repeat(dollar_count - 1);
 +    let mut old_name = "$".repeat(dollar_count);
 +
 +    new_name.push('z');
 +    new_name.push_str(name);
 +    old_name.push_str(name);
 +
 +    result.push_str(&new_name);
 +    map.insert(old_name, new_name);
 +}
 +
 +// Replaces `$foo` with `zfoo`. We must check for name overlap to ensure we
 +// aren't causing problems.
 +// This should also work for escaped `$` variables, where we leave earlier `$`s.
 +fn replace_names(input: &str) -> Option<(String, HashMap<String, String>)> {
 +    // Each substitution will require five or six extra bytes.
 +    let mut result = String::with_capacity(input.len() + 64);
 +    let mut substs = HashMap::new();
 +    let mut dollar_count = 0;
 +    let mut cur_name = String::new();
 +
 +    for (kind, c) in CharClasses::new(input.chars()) {
 +        if kind != FullCodeCharKind::Normal {
 +            result.push(c);
 +        } else if c == '$' {
 +            dollar_count += 1;
 +        } else if dollar_count == 0 {
 +            result.push(c);
 +        } else if !c.is_alphanumeric() && !cur_name.is_empty() {
 +            // Terminates a name following one or more dollars.
 +            register_metavariable(&mut substs, &mut result, &cur_name, dollar_count);
 +
 +            result.push(c);
 +            dollar_count = 0;
 +            cur_name.clear();
 +        } else if c == '(' && cur_name.is_empty() {
 +            // FIXME: Support macro def with repeat.
 +            return None;
 +        } else if c.is_alphanumeric() || c == '_' {
 +            cur_name.push(c);
 +        }
 +    }
 +
 +    if !cur_name.is_empty() {
 +        register_metavariable(&mut substs, &mut result, &cur_name, dollar_count);
 +    }
 +
 +    debug!("replace_names `{}` {:?}", result, substs);
 +
 +    Some((result, substs))
 +}
 +
 +#[derive(Debug, Clone)]
 +enum MacroArgKind {
 +    /// e.g., `$x: expr`.
 +    MetaVariable(Symbol, String),
 +    /// e.g., `$($foo: expr),*`
 +    Repeat(
 +        /// `()`, `[]` or `{}`.
 +        DelimToken,
 +        /// Inner arguments inside delimiters.
 +        Vec<ParsedMacroArg>,
 +        /// Something after the closing delimiter and the repeat token, if available.
 +        Option<Box<ParsedMacroArg>>,
 +        /// The repeat token. This could be one of `*`, `+` or `?`.
 +        Token,
 +    ),
 +    /// e.g., `[derive(Debug)]`
 +    Delimited(DelimToken, Vec<ParsedMacroArg>),
 +    /// A possible separator. e.g., `,` or `;`.
 +    Separator(String, String),
 +    /// Other random stuff that does not fit to other kinds.
 +    /// e.g., `== foo` in `($x: expr == foo)`.
 +    Other(String, String),
 +}
 +
 +fn delim_token_to_str(
 +    context: &RewriteContext<'_>,
 +    delim_token: DelimToken,
 +    shape: Shape,
 +    use_multiple_lines: bool,
 +    inner_is_empty: bool,
 +) -> (String, String) {
 +    let (lhs, rhs) = match delim_token {
 +        DelimToken::Paren => ("(", ")"),
 +        DelimToken::Bracket => ("[", "]"),
 +        DelimToken::Brace => {
 +            if inner_is_empty || use_multiple_lines {
 +                ("{", "}")
 +            } else {
 +                ("{ ", " }")
 +            }
 +        }
 +        DelimToken::NoDelim => ("", ""),
 +    };
 +    if use_multiple_lines {
 +        let indent_str = shape.indent.to_string_with_newline(context.config);
 +        let nested_indent_str = shape
 +            .indent
 +            .block_indent(context.config)
 +            .to_string_with_newline(context.config);
 +        (
 +            format!("{}{}", lhs, nested_indent_str),
 +            format!("{}{}", indent_str, rhs),
 +        )
 +    } else {
 +        (lhs.to_owned(), rhs.to_owned())
 +    }
 +}
 +
 +impl MacroArgKind {
 +    fn starts_with_brace(&self) -> bool {
 +        matches!(
 +            *self,
 +            MacroArgKind::Repeat(DelimToken::Brace, _, _, _)
 +                | MacroArgKind::Delimited(DelimToken::Brace, _)
 +        )
 +    }
 +
 +    fn starts_with_dollar(&self) -> bool {
 +        matches!(
 +            *self,
 +            MacroArgKind::Repeat(..) | MacroArgKind::MetaVariable(..)
 +        )
 +    }
 +
 +    fn ends_with_space(&self) -> bool {
 +        matches!(*self, MacroArgKind::Separator(..))
 +    }
 +
 +    fn has_meta_var(&self) -> bool {
 +        match *self {
 +            MacroArgKind::MetaVariable(..) => true,
 +            MacroArgKind::Repeat(_, ref args, _, _) => args.iter().any(|a| a.kind.has_meta_var()),
 +            _ => false,
 +        }
 +    }
 +
 +    fn rewrite(
 +        &self,
 +        context: &RewriteContext<'_>,
 +        shape: Shape,
 +        use_multiple_lines: bool,
 +    ) -> Option<String> {
 +        let rewrite_delimited_inner = |delim_tok, args| -> Option<(String, String, String)> {
 +            let inner = wrap_macro_args(context, args, shape)?;
 +            let (lhs, rhs) = delim_token_to_str(context, delim_tok, shape, false, inner.is_empty());
 +            if lhs.len() + inner.len() + rhs.len() <= shape.width {
 +                return Some((lhs, inner, rhs));
 +            }
 +
 +            let (lhs, rhs) = delim_token_to_str(context, delim_tok, shape, true, false);
 +            let nested_shape = shape
 +                .block_indent(context.config.tab_spaces())
 +                .with_max_width(context.config);
 +            let inner = wrap_macro_args(context, args, nested_shape)?;
 +            Some((lhs, inner, rhs))
 +        };
 +
 +        match *self {
 +            MacroArgKind::MetaVariable(ty, ref name) => Some(format!("${}:{}", name, ty)),
 +            MacroArgKind::Repeat(delim_tok, ref args, ref another, ref tok) => {
 +                let (lhs, inner, rhs) = rewrite_delimited_inner(delim_tok, args)?;
 +                let another = another
 +                    .as_ref()
 +                    .and_then(|a| a.rewrite(context, shape, use_multiple_lines))
 +                    .unwrap_or_else(|| "".to_owned());
 +                let repeat_tok = pprust::token_to_string(tok);
 +
 +                Some(format!("${}{}{}{}{}", lhs, inner, rhs, another, repeat_tok))
 +            }
 +            MacroArgKind::Delimited(delim_tok, ref args) => {
 +                rewrite_delimited_inner(delim_tok, args)
 +                    .map(|(lhs, inner, rhs)| format!("{}{}{}", lhs, inner, rhs))
 +            }
 +            MacroArgKind::Separator(ref sep, ref prefix) => Some(format!("{}{} ", prefix, sep)),
 +            MacroArgKind::Other(ref inner, ref prefix) => Some(format!("{}{}", prefix, inner)),
 +        }
 +    }
 +}
 +
 +#[derive(Debug, Clone)]
 +struct ParsedMacroArg {
 +    kind: MacroArgKind,
 +}
 +
 +impl ParsedMacroArg {
 +    fn rewrite(
 +        &self,
 +        context: &RewriteContext<'_>,
 +        shape: Shape,
 +        use_multiple_lines: bool,
 +    ) -> Option<String> {
 +        self.kind.rewrite(context, shape, use_multiple_lines)
 +    }
 +}
 +
 +/// Parses macro arguments on macro def.
 +struct MacroArgParser {
 +    /// Either a name of the next metavariable, a separator, or junk.
 +    buf: String,
 +    /// The first token of the current buffer.
 +    start_tok: Token,
 +    /// `true` if we are parsing a metavariable or a repeat.
 +    is_meta_var: bool,
 +    /// The last token parsed.
 +    last_tok: Token,
 +    /// Holds the parsed arguments.
 +    result: Vec<ParsedMacroArg>,
 +}
 +
 +fn last_tok(tt: &TokenTree) -> Token {
 +    match *tt {
 +        TokenTree::Token(ref t) => t.clone(),
 +        TokenTree::Delimited(delim_span, delim, _) => Token {
 +            kind: TokenKind::CloseDelim(delim),
 +            span: delim_span.close,
 +        },
 +    }
 +}
 +
 +impl MacroArgParser {
 +    fn new() -> MacroArgParser {
 +        MacroArgParser {
 +            buf: String::new(),
 +            is_meta_var: false,
 +            last_tok: Token {
 +                kind: TokenKind::Eof,
 +                span: DUMMY_SP,
 +            },
 +            start_tok: Token {
 +                kind: TokenKind::Eof,
 +                span: DUMMY_SP,
 +            },
 +            result: vec![],
 +        }
 +    }
 +
 +    fn set_last_tok(&mut self, tok: &TokenTree) {
 +        self.last_tok = last_tok(tok);
 +    }
 +
 +    fn add_separator(&mut self) {
 +        let prefix = if self.need_space_prefix() {
 +            " ".to_owned()
 +        } else {
 +            "".to_owned()
 +        };
 +        self.result.push(ParsedMacroArg {
 +            kind: MacroArgKind::Separator(self.buf.clone(), prefix),
 +        });
 +        self.buf.clear();
 +    }
 +
 +    fn add_other(&mut self) {
 +        let prefix = if self.need_space_prefix() {
 +            " ".to_owned()
 +        } else {
 +            "".to_owned()
 +        };
 +        self.result.push(ParsedMacroArg {
 +            kind: MacroArgKind::Other(self.buf.clone(), prefix),
 +        });
 +        self.buf.clear();
 +    }
 +
 +    fn add_meta_variable(&mut self, iter: &mut Cursor) -> Option<()> {
 +        match iter.next() {
 +            Some(TokenTree::Token(Token {
 +                kind: TokenKind::Ident(name, _),
 +                ..
 +            })) => {
 +                self.result.push(ParsedMacroArg {
 +                    kind: MacroArgKind::MetaVariable(name, self.buf.clone()),
 +                });
 +
 +                self.buf.clear();
 +                self.is_meta_var = false;
 +                Some(())
 +            }
 +            _ => None,
 +        }
 +    }
 +
 +    fn add_delimited(&mut self, inner: Vec<ParsedMacroArg>, delim: DelimToken) {
 +        self.result.push(ParsedMacroArg {
 +            kind: MacroArgKind::Delimited(delim, inner),
 +        });
 +    }
 +
 +    // $($foo: expr),?
 +    fn add_repeat(
 +        &mut self,
 +        inner: Vec<ParsedMacroArg>,
 +        delim: DelimToken,
 +        iter: &mut Cursor,
 +    ) -> Option<()> {
 +        let mut buffer = String::new();
 +        let mut first = true;
 +
 +        // Parse '*', '+' or '?.
 +        for tok in iter {
 +            self.set_last_tok(&tok);
 +            if first {
 +                first = false;
 +            }
 +
 +            match tok {
 +                TokenTree::Token(Token {
 +                    kind: TokenKind::BinOp(BinOpToken::Plus),
 +                    ..
 +                })
 +                | TokenTree::Token(Token {
 +                    kind: TokenKind::Question,
 +                    ..
 +                })
 +                | TokenTree::Token(Token {
 +                    kind: TokenKind::BinOp(BinOpToken::Star),
 +                    ..
 +                }) => {
 +                    break;
 +                }
 +                TokenTree::Token(ref t) => {
 +                    buffer.push_str(&pprust::token_to_string(t));
 +                }
 +                _ => return None,
 +            }
 +        }
 +
 +        // There could be some random stuff between ')' and '*', '+' or '?'.
 +        let another = if buffer.trim().is_empty() {
 +            None
 +        } else {
 +            Some(Box::new(ParsedMacroArg {
 +                kind: MacroArgKind::Other(buffer, "".to_owned()),
 +            }))
 +        };
 +
 +        self.result.push(ParsedMacroArg {
 +            kind: MacroArgKind::Repeat(delim, inner, another, self.last_tok.clone()),
 +        });
 +        Some(())
 +    }
 +
 +    fn update_buffer(&mut self, t: &Token) {
 +        if self.buf.is_empty() {
 +            self.start_tok = t.clone();
 +        } else {
 +            let needs_space = match next_space(&self.last_tok.kind) {
 +                SpaceState::Ident => ident_like(t),
 +                SpaceState::Punctuation => !ident_like(t),
 +                SpaceState::Always => true,
 +                SpaceState::Never => false,
 +            };
 +            if force_space_before(&t.kind) || needs_space {
 +                self.buf.push(' ');
 +            }
 +        }
 +
 +        self.buf.push_str(&pprust::token_to_string(t));
 +    }
 +
 +    fn need_space_prefix(&self) -> bool {
 +        if self.result.is_empty() {
 +            return false;
 +        }
 +
 +        let last_arg = self.result.last().unwrap();
 +        if let MacroArgKind::MetaVariable(..) = last_arg.kind {
 +            if ident_like(&self.start_tok) {
 +                return true;
 +            }
 +            if self.start_tok.kind == TokenKind::Colon {
 +                return true;
 +            }
 +        }
 +
 +        if force_space_before(&self.start_tok.kind) {
 +            return true;
 +        }
 +
 +        false
 +    }
 +
 +    /// Returns a collection of parsed macro def's arguments.
 +    fn parse(mut self, tokens: TokenStream) -> Option<Vec<ParsedMacroArg>> {
 +        let mut iter = tokens.trees();
 +
 +        while let Some(tok) = iter.next() {
 +            match tok {
 +                TokenTree::Token(Token {
 +                    kind: TokenKind::Dollar,
 +                    span,
 +                }) => {
 +                    // We always want to add a separator before meta variables.
 +                    if !self.buf.is_empty() {
 +                        self.add_separator();
 +                    }
 +
 +                    // Start keeping the name of this metavariable in the buffer.
 +                    self.is_meta_var = true;
 +                    self.start_tok = Token {
 +                        kind: TokenKind::Dollar,
 +                        span,
 +                    };
 +                }
 +                TokenTree::Token(Token {
 +                    kind: TokenKind::Colon,
 +                    ..
 +                }) if self.is_meta_var => {
 +                    self.add_meta_variable(&mut iter)?;
 +                }
 +                TokenTree::Token(ref t) => self.update_buffer(t),
 +                TokenTree::Delimited(_delimited_span, delimited, ref tts) => {
 +                    if !self.buf.is_empty() {
 +                        if next_space(&self.last_tok.kind) == SpaceState::Always {
 +                            self.add_separator();
 +                        } else {
 +                            self.add_other();
 +                        }
 +                    }
 +
 +                    // Parse the stuff inside delimiters.
 +                    let parser = MacroArgParser::new();
 +                    let delimited_arg = parser.parse(tts.clone())?;
 +
 +                    if self.is_meta_var {
 +                        self.add_repeat(delimited_arg, delimited, &mut iter)?;
 +                        self.is_meta_var = false;
 +                    } else {
 +                        self.add_delimited(delimited_arg, delimited);
 +                    }
 +                }
 +            }
 +
 +            self.set_last_tok(&tok);
 +        }
 +
 +        // We are left with some stuff in the buffer. Since there is nothing
 +        // left to separate, add this as `Other`.
 +        if !self.buf.is_empty() {
 +            self.add_other();
 +        }
 +
 +        Some(self.result)
 +    }
 +}
 +
 +fn wrap_macro_args(
 +    context: &RewriteContext<'_>,
 +    args: &[ParsedMacroArg],
 +    shape: Shape,
 +) -> Option<String> {
 +    wrap_macro_args_inner(context, args, shape, false)
 +        .or_else(|| wrap_macro_args_inner(context, args, shape, true))
 +}
 +
 +fn wrap_macro_args_inner(
 +    context: &RewriteContext<'_>,
 +    args: &[ParsedMacroArg],
 +    shape: Shape,
 +    use_multiple_lines: bool,
 +) -> Option<String> {
 +    let mut result = String::with_capacity(128);
 +    let mut iter = args.iter().peekable();
 +    let indent_str = shape.indent.to_string_with_newline(context.config);
 +
 +    while let Some(arg) = iter.next() {
 +        result.push_str(&arg.rewrite(context, shape, use_multiple_lines)?);
 +
 +        if use_multiple_lines
 +            && (arg.kind.ends_with_space() || iter.peek().map_or(false, |a| a.kind.has_meta_var()))
 +        {
 +            if arg.kind.ends_with_space() {
 +                result.pop();
 +            }
 +            result.push_str(&indent_str);
 +        } else if let Some(next_arg) = iter.peek() {
 +            let space_before_dollar =
 +                !arg.kind.ends_with_space() && next_arg.kind.starts_with_dollar();
 +            let space_before_brace = next_arg.kind.starts_with_brace();
 +            if space_before_dollar || space_before_brace {
 +                result.push(' ');
 +            }
 +        }
 +    }
 +
 +    if !use_multiple_lines && result.len() >= shape.width {
 +        None
 +    } else {
 +        Some(result)
 +    }
 +}
 +
 +// This is a bit sketchy. The token rules probably need tweaking, but it works
 +// for some common cases. I hope the basic logic is sufficient. Note that the
 +// meaning of some tokens is a bit different here from usual Rust, e.g., `*`
 +// and `(`/`)` have special meaning.
 +//
 +// We always try and format on one line.
 +// FIXME: Use multi-line when every thing does not fit on one line.
 +fn format_macro_args(
 +    context: &RewriteContext<'_>,
 +    token_stream: TokenStream,
 +    shape: Shape,
 +) -> Option<String> {
 +    if !context.config.format_macro_matchers() {
 +        let span = span_for_token_stream(&token_stream);
 +        return Some(match span {
 +            Some(span) => context.snippet(span).to_owned(),
 +            None => String::new(),
 +        });
 +    }
 +    let parsed_args = MacroArgParser::new().parse(token_stream)?;
 +    wrap_macro_args(context, &parsed_args, shape)
 +}
 +
 +fn span_for_token_stream(token_stream: &TokenStream) -> Option<Span> {
 +    token_stream.trees().next().map(|tt| tt.span())
 +}
 +
 +// We should insert a space if the next token is a:
 +#[derive(Copy, Clone, PartialEq)]
 +enum SpaceState {
 +    Never,
 +    Punctuation,
 +    Ident, // Or ident/literal-like thing.
 +    Always,
 +}
 +
 +fn force_space_before(tok: &TokenKind) -> bool {
 +    debug!("tok: force_space_before {:?}", tok);
 +
 +    match tok {
 +        TokenKind::Eq
 +        | TokenKind::Lt
 +        | TokenKind::Le
 +        | TokenKind::EqEq
 +        | TokenKind::Ne
 +        | TokenKind::Ge
 +        | TokenKind::Gt
 +        | TokenKind::AndAnd
 +        | TokenKind::OrOr
 +        | TokenKind::Not
 +        | TokenKind::Tilde
 +        | TokenKind::BinOpEq(_)
 +        | TokenKind::At
 +        | TokenKind::RArrow
 +        | TokenKind::LArrow
 +        | TokenKind::FatArrow
 +        | TokenKind::BinOp(_)
 +        | TokenKind::Pound
 +        | TokenKind::Dollar => true,
 +        _ => false,
 +    }
 +}
 +
 +fn ident_like(tok: &Token) -> bool {
 +    matches!(
 +        tok.kind,
 +        TokenKind::Ident(..) | TokenKind::Literal(..) | TokenKind::Lifetime(_)
 +    )
 +}
 +
 +fn next_space(tok: &TokenKind) -> SpaceState {
 +    debug!("next_space: {:?}", tok);
 +
 +    match tok {
 +        TokenKind::Not
 +        | TokenKind::BinOp(BinOpToken::And)
 +        | TokenKind::Tilde
 +        | TokenKind::At
 +        | TokenKind::Comma
 +        | TokenKind::Dot
 +        | TokenKind::DotDot
 +        | TokenKind::DotDotDot
 +        | TokenKind::DotDotEq
 +        | TokenKind::Question => SpaceState::Punctuation,
 +
 +        TokenKind::ModSep
 +        | TokenKind::Pound
 +        | TokenKind::Dollar
 +        | TokenKind::OpenDelim(_)
 +        | TokenKind::CloseDelim(_) => SpaceState::Never,
 +
 +        TokenKind::Literal(..) | TokenKind::Ident(..) | TokenKind::Lifetime(_) => SpaceState::Ident,
 +
 +        _ => SpaceState::Always,
 +    }
 +}
 +
 +/// Tries to convert a macro use into a short hand try expression. Returns `None`
 +/// when the macro is not an instance of `try!` (or parsing the inner expression
 +/// failed).
 +pub(crate) fn convert_try_mac(
 +    mac: &ast::MacCall,
 +    context: &RewriteContext<'_>,
 +) -> Option<ast::Expr> {
 +    let path = &pprust::path_to_string(&mac.path);
 +    if path == "try" || path == "r#try" {
 +        let ts = mac.args.inner_tokens();
 +
 +        Some(ast::Expr {
 +            id: ast::NodeId::root(), // dummy value
 +            kind: ast::ExprKind::Try(parse_expr(context, ts)?),
 +            span: mac.span(), // incorrect span, but shouldn't matter too much
 +            attrs: ast::AttrVec::new(),
 +            tokens: None,
 +        })
 +    } else {
 +        None
 +    }
 +}
 +
 +pub(crate) fn macro_style(mac: &ast::MacCall, context: &RewriteContext<'_>) -> DelimToken {
 +    let snippet = context.snippet(mac.span());
 +    let paren_pos = snippet.find_uncommented("(").unwrap_or(usize::max_value());
 +    let bracket_pos = snippet.find_uncommented("[").unwrap_or(usize::max_value());
 +    let brace_pos = snippet.find_uncommented("{").unwrap_or(usize::max_value());
 +
 +    if paren_pos < bracket_pos && paren_pos < brace_pos {
 +        DelimToken::Paren
 +    } else if bracket_pos < brace_pos {
 +        DelimToken::Bracket
 +    } else {
 +        DelimToken::Brace
 +    }
 +}
 +
 +// A very simple parser that just parses a macros 2.0 definition into its branches.
 +// Currently we do not attempt to parse any further than that.
 +#[derive(new)]
 +struct MacroParser {
 +    toks: Cursor,
 +}
 +
 +impl MacroParser {
 +    // (`(` ... `)` `=>` `{` ... `}`)*
 +    fn parse(&mut self) -> Option<Macro> {
 +        let mut branches = vec![];
 +        while self.toks.look_ahead(1).is_some() {
 +            branches.push(self.parse_branch()?);
 +        }
 +
 +        Some(Macro { branches })
 +    }
 +
 +    // `(` ... `)` `=>` `{` ... `}`
 +    fn parse_branch(&mut self) -> Option<MacroBranch> {
 +        let tok = self.toks.next()?;
 +        let (lo, args_paren_kind) = match tok {
 +            TokenTree::Token(..) => return None,
 +            TokenTree::Delimited(delimited_span, d, _) => (delimited_span.open.lo(), d),
 +        };
 +        let args = TokenStream::new(vec![(tok, Spacing::Joint)]);
 +        match self.toks.next()? {
 +            TokenTree::Token(Token {
 +                kind: TokenKind::FatArrow,
 +                ..
 +            }) => {}
 +            _ => return None,
 +        }
 +        let (mut hi, body, whole_body) = match self.toks.next()? {
 +            TokenTree::Token(..) => return None,
 +            TokenTree::Delimited(delimited_span, ..) => {
 +                let data = delimited_span.entire().data();
 +                (
 +                    data.hi,
 +                    Span::new(
 +                        data.lo + BytePos(1),
 +                        data.hi - BytePos(1),
 +                        data.ctxt,
 +                        data.parent,
 +                    ),
 +                    delimited_span.entire(),
 +                )
 +            }
 +        };
 +        if let Some(TokenTree::Token(Token {
 +            kind: TokenKind::Semi,
 +            span,
 +        })) = self.toks.look_ahead(0)
 +        {
 +            hi = span.hi();
 +            self.toks.next();
 +        }
 +        Some(MacroBranch {
 +            span: mk_sp(lo, hi),
 +            args_paren_kind,
 +            args,
 +            body,
 +            whole_body,
 +        })
 +    }
 +}
 +
 +// A parsed macros 2.0 macro definition.
 +struct Macro {
 +    branches: Vec<MacroBranch>,
 +}
 +
 +// FIXME: it would be more efficient to use references to the token streams
 +// rather than clone them, if we can make the borrowing work out.
 +struct MacroBranch {
 +    span: Span,
 +    args_paren_kind: DelimToken,
 +    args: TokenStream,
 +    body: Span,
 +    whole_body: Span,
 +}
 +
 +impl MacroBranch {
 +    fn rewrite(
 +        &self,
 +        context: &RewriteContext<'_>,
 +        shape: Shape,
 +        multi_branch_style: bool,
 +    ) -> Option<String> {
 +        // Only attempt to format function-like macros.
 +        if self.args_paren_kind != DelimToken::Paren {
 +            // FIXME(#1539): implement for non-sugared macros.
 +            return None;
 +        }
 +
 +        // 5 = " => {"
 +        let mut result = format_macro_args(context, self.args.clone(), shape.sub_width(5)?)?;
 +
 +        if multi_branch_style {
 +            result += " =>";
 +        }
 +
 +        if !context.config.format_macro_bodies() {
 +            result += " ";
 +            result += context.snippet(self.whole_body);
 +            return Some(result);
 +        }
 +
 +        // The macro body is the most interesting part. It might end up as various
 +        // AST nodes, but also has special variables (e.g, `$foo`) which can't be
 +        // parsed as regular Rust code (and note that these can be escaped using
 +        // `$$`). We'll try and format like an AST node, but we'll substitute
 +        // variables for new names with the same length first.
 +
 +        let old_body = context.snippet(self.body).trim();
 +        let (body_str, substs) = replace_names(old_body)?;
 +        let has_block_body = old_body.starts_with('{');
 +
 +        let mut config = context.config.clone();
 +        config.set().hide_parse_errors(true);
 +
 +        result += " {";
 +
 +        let body_indent = if has_block_body {
 +            shape.indent
 +        } else {
 +            shape.indent.block_indent(&config)
 +        };
 +        let new_width = config.max_width() - body_indent.width();
 +        config.set().max_width(new_width);
 +
 +        // First try to format as items, then as statements.
 +        let new_body_snippet = match crate::format_snippet(&body_str, &config, true) {
 +            Some(new_body) => new_body,
 +            None => {
 +                let new_width = new_width + config.tab_spaces();
 +                config.set().max_width(new_width);
 +                match crate::format_code_block(&body_str, &config, true) {
 +                    Some(new_body) => new_body,
 +                    None => return None,
 +                }
 +            }
 +        };
 +        let new_body = wrap_str(
 +            new_body_snippet.snippet.to_string(),
 +            config.max_width(),
 +            shape,
 +        )?;
 +
 +        // Indent the body since it is in a block.
 +        let indent_str = body_indent.to_string(&config);
 +        let mut new_body = LineClasses::new(new_body.trim_end())
 +            .enumerate()
 +            .fold(
 +                (String::new(), true),
 +                |(mut s, need_indent), (i, (kind, ref l))| {
 +                    if !is_empty_line(l)
 +                        && need_indent
 +                        && !new_body_snippet.is_line_non_formatted(i + 1)
 +                    {
 +                        s += &indent_str;
 +                    }
 +                    (s + l + "\n", indent_next_line(kind, l, &config))
 +                },
 +            )
 +            .0;
 +
 +        // Undo our replacement of macro variables.
 +        // FIXME: this could be *much* more efficient.
 +        for (old, new) in &substs {
 +            if old_body.contains(new) {
 +                debug!("rewrite_macro_def: bailing matching variable: `{}`", new);
 +                return None;
 +            }
 +            new_body = new_body.replace(new, old);
 +        }
 +
 +        if has_block_body {
 +            result += new_body.trim();
 +        } else if !new_body.is_empty() {
 +            result += "\n";
 +            result += &new_body;
 +            result += &shape.indent.to_string(&config);
 +        }
 +
 +        result += "}";
 +
 +        Some(result)
 +    }
 +}
 +
 +/// Format `lazy_static!` from <https://crates.io/crates/lazy_static>.
 +///
 +/// # Expected syntax
 +///
 +/// ```text
 +/// lazy_static! {
 +///     [pub] static ref NAME_1: TYPE_1 = EXPR_1;
 +///     [pub] static ref NAME_2: TYPE_2 = EXPR_2;
 +///     ...
 +///     [pub] static ref NAME_N: TYPE_N = EXPR_N;
 +/// }
 +/// ```
 +fn format_lazy_static(
 +    context: &RewriteContext<'_>,
 +    shape: Shape,
 +    ts: TokenStream,
 +) -> Option<String> {
 +    let mut result = String::with_capacity(1024);
 +    let nested_shape = shape
 +        .block_indent(context.config.tab_spaces())
 +        .with_max_width(context.config);
 +
 +    result.push_str("lazy_static! {");
 +    result.push_str(&nested_shape.indent.to_string_with_newline(context.config));
 +
 +    let parsed_elems = parse_lazy_static(context, ts)?;
 +    let last = parsed_elems.len() - 1;
 +    for (i, (vis, id, ty, expr)) in parsed_elems.iter().enumerate() {
 +        // Rewrite as a static item.
 +        let vis = crate::utils::format_visibility(context, vis);
 +        let mut stmt = String::with_capacity(128);
 +        stmt.push_str(&format!(
 +            "{}static ref {}: {} =",
 +            vis,
 +            id,
 +            ty.rewrite(context, nested_shape)?
 +        ));
 +        result.push_str(&rewrite_assign_rhs(
 +            context,
 +            stmt,
 +            &*expr,
 +            &RhsAssignKind::Expr(&expr.kind, expr.span),
 +            nested_shape.sub_width(1)?,
 +        )?);
 +        result.push(';');
 +        if i != last {
 +            result.push_str(&nested_shape.indent.to_string_with_newline(context.config));
 +        }
 +    }
 +
 +    result.push_str(&shape.indent.to_string_with_newline(context.config));
 +    result.push('}');
 +
 +    Some(result)
 +}
 +
 +fn rewrite_macro_with_items(
 +    context: &RewriteContext<'_>,
 +    items: &[MacroArg],
 +    macro_name: &str,
 +    shape: Shape,
 +    style: DelimToken,
 +    position: MacroPosition,
 +    span: Span,
 +) -> Option<String> {
 +    let (opener, closer) = match style {
 +        DelimToken::Paren => ("(", ")"),
 +        DelimToken::Bracket => ("[", "]"),
 +        DelimToken::Brace => (" {", "}"),
 +        _ => return None,
 +    };
 +    let trailing_semicolon = match style {
 +        DelimToken::Paren | DelimToken::Bracket if position == MacroPosition::Item => ";",
 +        _ => "",
 +    };
 +
 +    let mut visitor = FmtVisitor::from_context(context);
 +    visitor.block_indent = shape.indent.block_indent(context.config);
 +    visitor.last_pos = context.snippet_provider.span_after(span, opener.trim());
 +    for item in items {
 +        let item = match item {
 +            MacroArg::Item(item) => item,
 +            _ => return None,
 +        };
 +        visitor.visit_item(item);
 +    }
 +
 +    let mut result = String::with_capacity(256);
 +    result.push_str(macro_name);
 +    result.push_str(opener);
 +    result.push_str(&visitor.block_indent.to_string_with_newline(context.config));
 +    result.push_str(visitor.buffer.trim());
 +    result.push_str(&shape.indent.to_string_with_newline(context.config));
 +    result.push_str(closer);
 +    result.push_str(trailing_semicolon);
 +    Some(result)
 +}
index 64d96a5c6a6e4c7ef3186209e2eceefe2748f80b,0000000000000000000000000000000000000000..a65dc66f7972e06f3648025b18f5998cff209d64
mode 100644,000000..100644
--- /dev/null
@@@ -1,554 -1,0 +1,587 @@@
-             Err(_) => Err(ModuleResolutionError {
-                 module: mod_name.to_string(),
-                 kind: ModuleResolutionErrorKind::NotFound {
-                     file: self.directory.path.clone(),
-                 },
-             }),
 +use std::borrow::Cow;
 +use std::collections::BTreeMap;
 +use std::path::{Path, PathBuf};
 +
 +use rustc_ast::ast;
 +use rustc_ast::visit::Visitor;
 +use rustc_ast::AstLike;
 +use rustc_span::symbol::{self, sym, Symbol};
 +use rustc_span::Span;
 +use thiserror::Error;
 +
 +use crate::attr::MetaVisitor;
 +use crate::config::FileName;
 +use crate::items::is_mod_decl;
 +use crate::parse::parser::{
 +    Directory, DirectoryOwnership, ModError, ModulePathSuccess, Parser, ParserError,
 +};
 +use crate::parse::session::ParseSess;
 +use crate::utils::{contains_skip, mk_sp};
 +
 +mod visitor;
 +
 +type FileModMap<'ast> = BTreeMap<FileName, Module<'ast>>;
 +
 +/// Represents module with its inner attributes.
 +#[derive(Debug, Clone)]
 +pub(crate) struct Module<'a> {
 +    ast_mod_kind: Option<Cow<'a, ast::ModKind>>,
 +    pub(crate) items: Cow<'a, Vec<rustc_ast::ptr::P<ast::Item>>>,
 +    inner_attr: Vec<ast::Attribute>,
 +    pub(crate) span: Span,
 +}
 +
 +impl<'a> Module<'a> {
 +    pub(crate) fn new(
 +        mod_span: Span,
 +        ast_mod_kind: Option<Cow<'a, ast::ModKind>>,
 +        mod_items: Cow<'a, Vec<rustc_ast::ptr::P<ast::Item>>>,
 +        mod_attrs: Cow<'a, Vec<ast::Attribute>>,
 +    ) -> Self {
 +        let inner_attr = mod_attrs
 +            .iter()
 +            .filter(|attr| attr.style == ast::AttrStyle::Inner)
 +            .cloned()
 +            .collect();
 +        Module {
 +            items: mod_items,
 +            inner_attr,
 +            span: mod_span,
 +            ast_mod_kind,
 +        }
 +    }
 +}
 +
 +impl<'a> AstLike for Module<'a> {
 +    const SUPPORTS_CUSTOM_INNER_ATTRS: bool = true;
 +    fn attrs(&self) -> &[ast::Attribute] {
 +        &self.inner_attr
 +    }
 +    fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<ast::Attribute>)) {
 +        f(&mut self.inner_attr)
 +    }
 +    fn tokens_mut(&mut self) -> Option<&mut Option<rustc_ast::tokenstream::LazyTokenStream>> {
 +        unimplemented!()
 +    }
 +}
 +
 +/// Maps each module to the corresponding file.
 +pub(crate) struct ModResolver<'ast, 'sess> {
 +    parse_sess: &'sess ParseSess,
 +    directory: Directory,
 +    file_map: FileModMap<'ast>,
 +    recursive: bool,
 +}
 +
 +/// Represents errors while trying to resolve modules.
 +#[derive(Debug, Error)]
 +#[error("failed to resolve mod `{module}`: {kind}")]
 +pub struct ModuleResolutionError {
 +    pub(crate) module: String,
 +    pub(crate) kind: ModuleResolutionErrorKind,
 +}
 +
++/// Defines variants similar to those of [rustc_expand::module::ModError]
 +#[derive(Debug, Error)]
 +pub(crate) enum ModuleResolutionErrorKind {
 +    /// Find a file that cannot be parsed.
 +    #[error("cannot parse {file}")]
 +    ParseError { file: PathBuf },
 +    /// File cannot be found.
 +    #[error("{file} does not exist")]
 +    NotFound { file: PathBuf },
++    /// File a.rs and a/mod.rs both exist
++    #[error("file for module found at both {default_path:?} and {secondary_path:?}")]
++    MultipleCandidates {
++        default_path: PathBuf,
++        secondary_path: PathBuf,
++    },
 +}
 +
 +#[derive(Clone)]
 +enum SubModKind<'a, 'ast> {
 +    /// `mod foo;`
 +    External(PathBuf, DirectoryOwnership, Module<'ast>),
 +    /// `mod foo;` with multiple sources.
 +    MultiExternal(Vec<(PathBuf, DirectoryOwnership, Module<'ast>)>),
 +    /// `mod foo {}`
 +    Internal(&'a ast::Item),
 +}
 +
 +impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
 +    /// Creates a new `ModResolver`.
 +    pub(crate) fn new(
 +        parse_sess: &'sess ParseSess,
 +        directory_ownership: DirectoryOwnership,
 +        recursive: bool,
 +    ) -> Self {
 +        ModResolver {
 +            directory: Directory {
 +                path: PathBuf::new(),
 +                ownership: directory_ownership,
 +            },
 +            file_map: BTreeMap::new(),
 +            parse_sess,
 +            recursive,
 +        }
 +    }
 +
 +    /// Creates a map that maps a file name to the module in AST.
 +    pub(crate) fn visit_crate(
 +        mut self,
 +        krate: &'ast ast::Crate,
 +    ) -> Result<FileModMap<'ast>, ModuleResolutionError> {
 +        let root_filename = self.parse_sess.span_to_filename(krate.spans.inner_span);
 +        self.directory.path = match root_filename {
 +            FileName::Real(ref p) => p.parent().unwrap_or(Path::new("")).to_path_buf(),
 +            _ => PathBuf::new(),
 +        };
 +
 +        // Skip visiting sub modules when the input is from stdin.
 +        if self.recursive {
 +            self.visit_mod_from_ast(&krate.items)?;
 +        }
 +
 +        let snippet_provider = self.parse_sess.snippet_provider(krate.spans.inner_span);
 +
 +        self.file_map.insert(
 +            root_filename,
 +            Module::new(
 +                mk_sp(snippet_provider.start_pos(), snippet_provider.end_pos()),
 +                None,
 +                Cow::Borrowed(&krate.items),
 +                Cow::Borrowed(&krate.attrs),
 +            ),
 +        );
 +        Ok(self.file_map)
 +    }
 +
 +    /// Visit `cfg_if` macro and look for module declarations.
 +    fn visit_cfg_if(&mut self, item: Cow<'ast, ast::Item>) -> Result<(), ModuleResolutionError> {
 +        let mut visitor = visitor::CfgIfVisitor::new(self.parse_sess);
 +        visitor.visit_item(&item);
 +        for module_item in visitor.mods() {
 +            if let ast::ItemKind::Mod(_, ref sub_mod_kind) = module_item.item.kind {
 +                self.visit_sub_mod(
 +                    &module_item.item,
 +                    Module::new(
 +                        module_item.item.span,
 +                        Some(Cow::Owned(sub_mod_kind.clone())),
 +                        Cow::Owned(vec![]),
 +                        Cow::Owned(vec![]),
 +                    ),
 +                )?;
 +            }
 +        }
 +        Ok(())
 +    }
 +
 +    /// Visit modules defined inside macro calls.
 +    fn visit_mod_outside_ast(
 +        &mut self,
 +        items: Vec<rustc_ast::ptr::P<ast::Item>>,
 +    ) -> Result<(), ModuleResolutionError> {
 +        for item in items {
 +            if is_cfg_if(&item) {
 +                self.visit_cfg_if(Cow::Owned(item.into_inner()))?;
 +                continue;
 +            }
 +
 +            if let ast::ItemKind::Mod(_, ref sub_mod_kind) = item.kind {
 +                let span = item.span;
 +                self.visit_sub_mod(
 +                    &item,
 +                    Module::new(
 +                        span,
 +                        Some(Cow::Owned(sub_mod_kind.clone())),
 +                        Cow::Owned(vec![]),
 +                        Cow::Owned(vec![]),
 +                    ),
 +                )?;
 +            }
 +        }
 +        Ok(())
 +    }
 +
 +    /// Visit modules from AST.
 +    fn visit_mod_from_ast(
 +        &mut self,
 +        items: &'ast [rustc_ast::ptr::P<ast::Item>],
 +    ) -> Result<(), ModuleResolutionError> {
 +        for item in items {
 +            if is_cfg_if(item) {
 +                self.visit_cfg_if(Cow::Borrowed(item))?;
 +            }
 +
 +            if let ast::ItemKind::Mod(_, ref sub_mod_kind) = item.kind {
 +                let span = item.span;
 +                self.visit_sub_mod(
 +                    item,
 +                    Module::new(
 +                        span,
 +                        Some(Cow::Borrowed(sub_mod_kind)),
 +                        Cow::Owned(vec![]),
 +                        Cow::Borrowed(&item.attrs),
 +                    ),
 +                )?;
 +            }
 +        }
 +        Ok(())
 +    }
 +
 +    fn visit_sub_mod(
 +        &mut self,
 +        item: &'c ast::Item,
 +        sub_mod: Module<'ast>,
 +    ) -> Result<(), ModuleResolutionError> {
 +        let old_directory = self.directory.clone();
 +        let sub_mod_kind = self.peek_sub_mod(item, &sub_mod)?;
 +        if let Some(sub_mod_kind) = sub_mod_kind {
 +            self.insert_sub_mod(sub_mod_kind.clone())?;
 +            self.visit_sub_mod_inner(sub_mod, sub_mod_kind)?;
 +        }
 +        self.directory = old_directory;
 +        Ok(())
 +    }
 +
 +    /// Inspect the given sub-module which we are about to visit and returns its kind.
 +    fn peek_sub_mod(
 +        &self,
 +        item: &'c ast::Item,
 +        sub_mod: &Module<'ast>,
 +    ) -> Result<Option<SubModKind<'c, 'ast>>, ModuleResolutionError> {
 +        if contains_skip(&item.attrs) {
 +            return Ok(None);
 +        }
 +
 +        if is_mod_decl(item) {
 +            // mod foo;
 +            // Look for an extern file.
 +            self.find_external_module(item.ident, &item.attrs, sub_mod)
 +        } else {
 +            // An internal module (`mod foo { /* ... */ }`);
 +            Ok(Some(SubModKind::Internal(item)))
 +        }
 +    }
 +
 +    fn insert_sub_mod(
 +        &mut self,
 +        sub_mod_kind: SubModKind<'c, 'ast>,
 +    ) -> Result<(), ModuleResolutionError> {
 +        match sub_mod_kind {
 +            SubModKind::External(mod_path, _, sub_mod) => {
 +                self.file_map
 +                    .entry(FileName::Real(mod_path))
 +                    .or_insert(sub_mod);
 +            }
 +            SubModKind::MultiExternal(mods) => {
 +                for (mod_path, _, sub_mod) in mods {
 +                    self.file_map
 +                        .entry(FileName::Real(mod_path))
 +                        .or_insert(sub_mod);
 +                }
 +            }
 +            _ => (),
 +        }
 +        Ok(())
 +    }
 +
 +    fn visit_sub_mod_inner(
 +        &mut self,
 +        sub_mod: Module<'ast>,
 +        sub_mod_kind: SubModKind<'c, 'ast>,
 +    ) -> Result<(), ModuleResolutionError> {
 +        match sub_mod_kind {
 +            SubModKind::External(mod_path, directory_ownership, sub_mod) => {
 +                let directory = Directory {
 +                    path: mod_path.parent().unwrap().to_path_buf(),
 +                    ownership: directory_ownership,
 +                };
 +                self.visit_sub_mod_after_directory_update(sub_mod, Some(directory))
 +            }
 +            SubModKind::Internal(item) => {
 +                self.push_inline_mod_directory(item.ident, &item.attrs);
 +                self.visit_sub_mod_after_directory_update(sub_mod, None)
 +            }
 +            SubModKind::MultiExternal(mods) => {
 +                for (mod_path, directory_ownership, sub_mod) in mods {
 +                    let directory = Directory {
 +                        path: mod_path.parent().unwrap().to_path_buf(),
 +                        ownership: directory_ownership,
 +                    };
 +                    self.visit_sub_mod_after_directory_update(sub_mod, Some(directory))?;
 +                }
 +                Ok(())
 +            }
 +        }
 +    }
 +
 +    fn visit_sub_mod_after_directory_update(
 +        &mut self,
 +        sub_mod: Module<'ast>,
 +        directory: Option<Directory>,
 +    ) -> Result<(), ModuleResolutionError> {
 +        if let Some(directory) = directory {
 +            self.directory = directory;
 +        }
 +        match (sub_mod.ast_mod_kind, sub_mod.items) {
 +            (Some(Cow::Borrowed(ast::ModKind::Loaded(items, _, _))), _) => {
 +                self.visit_mod_from_ast(items)
 +            }
 +            (Some(Cow::Owned(ast::ModKind::Loaded(items, _, _))), _) | (_, Cow::Owned(items)) => {
 +                self.visit_mod_outside_ast(items)
 +            }
 +            (_, _) => Ok(()),
 +        }
 +    }
 +
 +    /// Find a file path in the filesystem which corresponds to the given module.
 +    fn find_external_module(
 +        &self,
 +        mod_name: symbol::Ident,
 +        attrs: &[ast::Attribute],
 +        sub_mod: &Module<'ast>,
 +    ) -> Result<Option<SubModKind<'c, 'ast>>, ModuleResolutionError> {
 +        let relative = match self.directory.ownership {
 +            DirectoryOwnership::Owned { relative } => relative,
 +            DirectoryOwnership::UnownedViaBlock => None,
 +        };
 +        if let Some(path) = Parser::submod_path_from_attr(attrs, &self.directory.path) {
 +            if self.parse_sess.is_file_parsed(&path) {
 +                return Ok(None);
 +            }
 +            return match Parser::parse_file_as_module(self.parse_sess, &path, sub_mod.span) {
 +                Ok((ref attrs, _, _)) if contains_skip(attrs) => Ok(None),
 +                Ok((attrs, items, span)) => Ok(Some(SubModKind::External(
 +                    path,
 +                    DirectoryOwnership::Owned { relative: None },
 +                    Module::new(
 +                        span,
 +                        Some(Cow::Owned(ast::ModKind::Unloaded)),
 +                        Cow::Owned(items),
 +                        Cow::Owned(attrs),
 +                    ),
 +                ))),
 +                Err(ParserError::ParseError) => Err(ModuleResolutionError {
 +                    module: mod_name.to_string(),
 +                    kind: ModuleResolutionErrorKind::ParseError { file: path },
 +                }),
 +                Err(..) => Err(ModuleResolutionError {
 +                    module: mod_name.to_string(),
 +                    kind: ModuleResolutionErrorKind::NotFound { file: path },
 +                }),
 +            };
 +        }
 +
 +        // Look for nested path, like `#[cfg_attr(feature = "foo", path = "bar.rs")]`.
 +        let mut mods_outside_ast = self.find_mods_outside_of_ast(attrs, sub_mod);
 +
 +        match self
 +            .parse_sess
 +            .default_submod_path(mod_name, relative, &self.directory.path)
 +        {
 +            Ok(ModulePathSuccess {
 +                file_path,
 +                dir_ownership,
 +                ..
 +            }) => {
 +                let outside_mods_empty = mods_outside_ast.is_empty();
 +                let should_insert = !mods_outside_ast
 +                    .iter()
 +                    .any(|(outside_path, _, _)| outside_path == &file_path);
 +                if self.parse_sess.is_file_parsed(&file_path) {
 +                    if outside_mods_empty {
 +                        return Ok(None);
 +                    } else {
 +                        if should_insert {
 +                            mods_outside_ast.push((file_path, dir_ownership, sub_mod.clone()));
 +                        }
 +                        return Ok(Some(SubModKind::MultiExternal(mods_outside_ast)));
 +                    }
 +                }
 +                match Parser::parse_file_as_module(self.parse_sess, &file_path, sub_mod.span) {
 +                    Ok((ref attrs, _, _)) if contains_skip(attrs) => Ok(None),
 +                    Ok((attrs, items, span)) if outside_mods_empty => {
 +                        Ok(Some(SubModKind::External(
 +                            file_path,
 +                            dir_ownership,
 +                            Module::new(
 +                                span,
 +                                Some(Cow::Owned(ast::ModKind::Unloaded)),
 +                                Cow::Owned(items),
 +                                Cow::Owned(attrs),
 +                            ),
 +                        )))
 +                    }
 +                    Ok((attrs, items, span)) => {
 +                        mods_outside_ast.push((
 +                            file_path.clone(),
 +                            dir_ownership,
 +                            Module::new(
 +                                span,
 +                                Some(Cow::Owned(ast::ModKind::Unloaded)),
 +                                Cow::Owned(items),
 +                                Cow::Owned(attrs),
 +                            ),
 +                        ));
 +                        if should_insert {
 +                            mods_outside_ast.push((file_path, dir_ownership, sub_mod.clone()));
 +                        }
 +                        Ok(Some(SubModKind::MultiExternal(mods_outside_ast)))
 +                    }
 +                    Err(ParserError::ParseError) => Err(ModuleResolutionError {
 +                        module: mod_name.to_string(),
 +                        kind: ModuleResolutionErrorKind::ParseError { file: file_path },
 +                    }),
 +                    Err(..) if outside_mods_empty => Err(ModuleResolutionError {
 +                        module: mod_name.to_string(),
 +                        kind: ModuleResolutionErrorKind::NotFound { file: file_path },
 +                    }),
 +                    Err(..) => {
 +                        if should_insert {
 +                            mods_outside_ast.push((file_path, dir_ownership, sub_mod.clone()));
 +                        }
 +                        Ok(Some(SubModKind::MultiExternal(mods_outside_ast)))
 +                    }
 +                }
 +            }
 +            Err(mod_err) if !mods_outside_ast.is_empty() => {
 +                if let ModError::ParserError(e) = mod_err {
 +                    e.cancel();
 +                }
 +                Ok(Some(SubModKind::MultiExternal(mods_outside_ast)))
 +            }
-             self.directory.path.push(id.as_str());
++            Err(e) => match e {
++                ModError::FileNotFound(_, default_path, _secondary_path) => {
++                    Err(ModuleResolutionError {
++                        module: mod_name.to_string(),
++                        kind: ModuleResolutionErrorKind::NotFound { file: default_path },
++                    })
++                }
++                ModError::MultipleCandidates(_, default_path, secondary_path) => {
++                    Err(ModuleResolutionError {
++                        module: mod_name.to_string(),
++                        kind: ModuleResolutionErrorKind::MultipleCandidates {
++                            default_path,
++                            secondary_path,
++                        },
++                    })
++                }
++                ModError::ParserError(_)
++                | ModError::CircularInclusion(_)
++                | ModError::ModInBlock(_) => Err(ModuleResolutionError {
++                    module: mod_name.to_string(),
++                    kind: ModuleResolutionErrorKind::ParseError {
++                        file: self.directory.path.clone(),
++                    },
++                }),
++            },
 +        }
 +    }
 +
 +    fn push_inline_mod_directory(&mut self, id: symbol::Ident, attrs: &[ast::Attribute]) {
 +        if let Some(path) = find_path_value(attrs) {
 +            self.directory.path.push(path.as_str());
 +            self.directory.ownership = DirectoryOwnership::Owned { relative: None };
 +        } else {
++            let id = id.as_str();
 +            // We have to push on the current module name in the case of relative
 +            // paths in order to ensure that any additional module paths from inline
 +            // `mod x { ... }` come after the relative extension.
 +            //
 +            // For example, a `mod z { ... }` inside `x/y.rs` should set the current
 +            // directory path to `/x/y/z`, not `/x/z` with a relative offset of `y`.
 +            if let DirectoryOwnership::Owned { relative } = &mut self.directory.ownership {
 +                if let Some(ident) = relative.take() {
 +                    // remove the relative offset
 +                    self.directory.path.push(ident.as_str());
++
++                    // In the case where there is an x.rs and an ./x directory we want
++                    // to prevent adding x twice. For example, ./x/x
++                    if self.directory.path.exists() && !self.directory.path.join(id).exists() {
++                        return;
++                    }
 +                }
 +            }
++            self.directory.path.push(id);
 +        }
 +    }
 +
 +    fn find_mods_outside_of_ast(
 +        &self,
 +        attrs: &[ast::Attribute],
 +        sub_mod: &Module<'ast>,
 +    ) -> Vec<(PathBuf, DirectoryOwnership, Module<'ast>)> {
 +        // Filter nested path, like `#[cfg_attr(feature = "foo", path = "bar.rs")]`.
 +        let mut path_visitor = visitor::PathVisitor::default();
 +        for attr in attrs.iter() {
 +            if let Some(meta) = attr.meta() {
 +                path_visitor.visit_meta_item(&meta)
 +            }
 +        }
 +        let mut result = vec![];
 +        for path in path_visitor.paths() {
 +            let mut actual_path = self.directory.path.clone();
 +            actual_path.push(&path);
 +            if !actual_path.exists() {
 +                continue;
 +            }
 +            if self.parse_sess.is_file_parsed(&actual_path) {
 +                // If the specified file is already parsed, then we just use that.
 +                result.push((
 +                    actual_path,
 +                    DirectoryOwnership::Owned { relative: None },
 +                    sub_mod.clone(),
 +                ));
 +                continue;
 +            }
 +            let (attrs, items, span) =
 +                match Parser::parse_file_as_module(self.parse_sess, &actual_path, sub_mod.span) {
 +                    Ok((ref attrs, _, _)) if contains_skip(attrs) => continue,
 +                    Ok(m) => m,
 +                    Err(..) => continue,
 +                };
 +
 +            result.push((
 +                actual_path,
 +                DirectoryOwnership::Owned { relative: None },
 +                Module::new(
 +                    span,
 +                    Some(Cow::Owned(ast::ModKind::Unloaded)),
 +                    Cow::Owned(items),
 +                    Cow::Owned(attrs),
 +                ),
 +            ))
 +        }
 +        result
 +    }
 +}
 +
 +fn path_value(attr: &ast::Attribute) -> Option<Symbol> {
 +    if attr.has_name(sym::path) {
 +        attr.value_str()
 +    } else {
 +        None
 +    }
 +}
 +
 +// N.B., even when there are multiple `#[path = ...]` attributes, we just need to
 +// examine the first one, since rustc ignores the second and the subsequent ones
 +// as unused attributes.
 +fn find_path_value(attrs: &[ast::Attribute]) -> Option<Symbol> {
 +    attrs.iter().flat_map(path_value).next()
 +}
 +
 +fn is_cfg_if(item: &ast::Item) -> bool {
 +    match item.kind {
 +        ast::ItemKind::MacCall(ref mac) => {
 +            if let Some(first_segment) = mac.path.segments.first() {
 +                if first_segment.ident.name == Symbol::intern("cfg_if") {
 +                    return true;
 +                }
 +            }
 +            false
 +        }
 +        _ => false,
 +    }
 +}
index 3475f5c378cd2fbde554e4a79d0c191bf99d0a97,0000000000000000000000000000000000000000..80aed998d7377d83c16925e2d0fe55ef5fa2f943
mode 100644,000000..100644
--- /dev/null
@@@ -1,782 -1,0 +1,785 @@@
- const SHORT_ITEM_THRESHOLD: usize = 10;
 +//! Rewrite a list some items with overflow.
 +
 +use std::cmp::min;
 +
 +use itertools::Itertools;
 +use rustc_ast::token::DelimToken;
 +use rustc_ast::{ast, ptr};
 +use rustc_span::Span;
 +
 +use crate::closures;
 +use crate::config::lists::*;
 +use crate::config::Version;
 +use crate::expr::{
 +    can_be_overflowed_expr, is_every_expr_simple, is_method_call, is_nested_call, is_simple_expr,
 +    rewrite_cond,
 +};
 +use crate::lists::{
 +    definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator,
 +};
 +use crate::macros::MacroArg;
 +use crate::patterns::{can_be_overflowed_pat, TuplePatField};
 +use crate::rewrite::{Rewrite, RewriteContext};
 +use crate::shape::Shape;
 +use crate::source_map::SpanUtils;
 +use crate::spanned::Spanned;
 +use crate::types::{can_be_overflowed_type, SegmentParam};
 +use crate::utils::{count_newlines, extra_offset, first_line_width, last_line_width, mk_sp};
 +
-                         } else if is_every_expr_simple(&self.items) && no_long_items(list_items) {
 +/// A list of `format!`-like macros, that take a long format string and a list of arguments to
 +/// format.
 +///
 +/// Organized as a list of `(&str, usize)` tuples, giving the name of the macro and the number of
 +/// arguments before the format string (none for `format!("format", ...)`, one for `assert!(result,
 +/// "format", ...)`, two for `assert_eq!(left, right, "format", ...)`).
 +const SPECIAL_MACRO_WHITELIST: &[(&str, usize)] = &[
 +    // format! like macros
 +    // From the Rust Standard Library.
 +    ("eprint!", 0),
 +    ("eprintln!", 0),
 +    ("format!", 0),
 +    ("format_args!", 0),
 +    ("print!", 0),
 +    ("println!", 0),
 +    ("panic!", 0),
 +    ("unreachable!", 0),
 +    // From the `log` crate.
 +    ("debug!", 0),
 +    ("error!", 0),
 +    ("info!", 0),
 +    ("warn!", 0),
 +    // write! like macros
 +    ("assert!", 1),
 +    ("debug_assert!", 1),
 +    ("write!", 1),
 +    ("writeln!", 1),
 +    // assert_eq! like macros
 +    ("assert_eq!", 2),
 +    ("assert_ne!", 2),
 +    ("debug_assert_eq!", 2),
 +    ("debug_assert_ne!", 2),
 +];
 +
 +const SPECIAL_ATTR_WHITELIST: &[(&str, usize)] = &[
 +    // From the `failure` crate.
 +    ("fail", 0),
 +];
 +
 +#[derive(Debug)]
 +pub(crate) enum OverflowableItem<'a> {
 +    Expr(&'a ast::Expr),
 +    GenericParam(&'a ast::GenericParam),
 +    MacroArg(&'a MacroArg),
 +    NestedMetaItem(&'a ast::NestedMetaItem),
 +    SegmentParam(&'a SegmentParam<'a>),
 +    FieldDef(&'a ast::FieldDef),
 +    TuplePatField(&'a TuplePatField<'a>),
 +    Ty(&'a ast::Ty),
 +    Pat(&'a ast::Pat),
 +}
 +
 +impl<'a> Rewrite for OverflowableItem<'a> {
 +    fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
 +        self.map(|item| item.rewrite(context, shape))
 +    }
 +}
 +
 +impl<'a> Spanned for OverflowableItem<'a> {
 +    fn span(&self) -> Span {
 +        self.map(|item| item.span())
 +    }
 +}
 +
 +impl<'a> OverflowableItem<'a> {
 +    fn has_attrs(&self) -> bool {
 +        match self {
 +            OverflowableItem::Expr(ast::Expr { attrs, .. })
 +            | OverflowableItem::GenericParam(ast::GenericParam { attrs, .. }) => !attrs.is_empty(),
 +            OverflowableItem::FieldDef(ast::FieldDef { attrs, .. }) => !attrs.is_empty(),
 +            OverflowableItem::MacroArg(MacroArg::Expr(expr)) => !expr.attrs.is_empty(),
 +            OverflowableItem::MacroArg(MacroArg::Item(item)) => !item.attrs.is_empty(),
 +            _ => false,
 +        }
 +    }
 +
 +    pub(crate) fn map<F, T>(&self, f: F) -> T
 +    where
 +        F: Fn(&dyn IntoOverflowableItem<'a>) -> T,
 +    {
 +        match self {
 +            OverflowableItem::Expr(expr) => f(*expr),
 +            OverflowableItem::GenericParam(gp) => f(*gp),
 +            OverflowableItem::MacroArg(macro_arg) => f(*macro_arg),
 +            OverflowableItem::NestedMetaItem(nmi) => f(*nmi),
 +            OverflowableItem::SegmentParam(sp) => f(*sp),
 +            OverflowableItem::FieldDef(sf) => f(*sf),
 +            OverflowableItem::TuplePatField(pat) => f(*pat),
 +            OverflowableItem::Ty(ty) => f(*ty),
 +            OverflowableItem::Pat(pat) => f(*pat),
 +        }
 +    }
 +
 +    pub(crate) fn is_simple(&self) -> bool {
 +        match self {
 +            OverflowableItem::Expr(expr) => is_simple_expr(expr),
 +            OverflowableItem::MacroArg(MacroArg::Keyword(..)) => true,
 +            OverflowableItem::MacroArg(MacroArg::Expr(expr)) => is_simple_expr(expr),
 +            OverflowableItem::NestedMetaItem(nested_meta_item) => match nested_meta_item {
 +                ast::NestedMetaItem::Literal(..) => true,
 +                ast::NestedMetaItem::MetaItem(ref meta_item) => {
 +                    matches!(meta_item.kind, ast::MetaItemKind::Word)
 +                }
 +            },
 +            _ => false,
 +        }
 +    }
 +
 +    pub(crate) fn is_expr(&self) -> bool {
 +        matches!(
 +            self,
 +            OverflowableItem::Expr(..) | OverflowableItem::MacroArg(MacroArg::Expr(..))
 +        )
 +    }
 +
 +    pub(crate) fn is_nested_call(&self) -> bool {
 +        match self {
 +            OverflowableItem::Expr(expr) => is_nested_call(expr),
 +            OverflowableItem::MacroArg(MacroArg::Expr(expr)) => is_nested_call(expr),
 +            _ => false,
 +        }
 +    }
 +
 +    pub(crate) fn to_expr(&self) -> Option<&'a ast::Expr> {
 +        match self {
 +            OverflowableItem::Expr(expr) => Some(expr),
 +            OverflowableItem::MacroArg(MacroArg::Expr(ref expr)) => Some(expr),
 +            _ => None,
 +        }
 +    }
 +
 +    pub(crate) fn can_be_overflowed(&self, context: &RewriteContext<'_>, len: usize) -> bool {
 +        match self {
 +            OverflowableItem::Expr(expr) => can_be_overflowed_expr(context, expr, len),
 +            OverflowableItem::MacroArg(macro_arg) => match macro_arg {
 +                MacroArg::Expr(ref expr) => can_be_overflowed_expr(context, expr, len),
 +                MacroArg::Ty(ref ty) => can_be_overflowed_type(context, ty, len),
 +                MacroArg::Pat(..) => false,
 +                MacroArg::Item(..) => len == 1,
 +                MacroArg::Keyword(..) => false,
 +            },
 +            OverflowableItem::NestedMetaItem(nested_meta_item) if len == 1 => {
 +                match nested_meta_item {
 +                    ast::NestedMetaItem::Literal(..) => false,
 +                    ast::NestedMetaItem::MetaItem(..) => true,
 +                }
 +            }
 +            OverflowableItem::SegmentParam(SegmentParam::Type(ty)) => {
 +                can_be_overflowed_type(context, ty, len)
 +            }
 +            OverflowableItem::TuplePatField(pat) => can_be_overflowed_pat(context, pat, len),
 +            OverflowableItem::Ty(ty) => can_be_overflowed_type(context, ty, len),
 +            _ => false,
 +        }
 +    }
 +
 +    fn whitelist(&self) -> &'static [(&'static str, usize)] {
 +        match self {
 +            OverflowableItem::MacroArg(..) => SPECIAL_MACRO_WHITELIST,
 +            OverflowableItem::NestedMetaItem(..) => SPECIAL_ATTR_WHITELIST,
 +            _ => &[],
 +        }
 +    }
 +}
 +
 +pub(crate) trait IntoOverflowableItem<'a>: Rewrite + Spanned {
 +    fn into_overflowable_item(&'a self) -> OverflowableItem<'a>;
 +}
 +
 +impl<'a, T: 'a + IntoOverflowableItem<'a>> IntoOverflowableItem<'a> for ptr::P<T> {
 +    fn into_overflowable_item(&'a self) -> OverflowableItem<'a> {
 +        (**self).into_overflowable_item()
 +    }
 +}
 +
 +macro_rules! impl_into_overflowable_item_for_ast_node {
 +    ($($ast_node:ident),*) => {
 +        $(
 +            impl<'a> IntoOverflowableItem<'a> for ast::$ast_node {
 +                fn into_overflowable_item(&'a self) -> OverflowableItem<'a> {
 +                    OverflowableItem::$ast_node(self)
 +                }
 +            }
 +        )*
 +    }
 +}
 +
 +macro_rules! impl_into_overflowable_item_for_rustfmt_types {
 +    ([$($ty:ident),*], [$($ty_with_lifetime:ident),*]) => {
 +        $(
 +            impl<'a> IntoOverflowableItem<'a> for $ty {
 +                fn into_overflowable_item(&'a self) -> OverflowableItem<'a> {
 +                    OverflowableItem::$ty(self)
 +                }
 +            }
 +        )*
 +        $(
 +            impl<'a> IntoOverflowableItem<'a> for $ty_with_lifetime<'a> {
 +                fn into_overflowable_item(&'a self) -> OverflowableItem<'a> {
 +                    OverflowableItem::$ty_with_lifetime(self)
 +                }
 +            }
 +        )*
 +    }
 +}
 +
 +impl_into_overflowable_item_for_ast_node!(Expr, GenericParam, NestedMetaItem, FieldDef, Ty, Pat);
 +impl_into_overflowable_item_for_rustfmt_types!([MacroArg], [SegmentParam, TuplePatField]);
 +
 +pub(crate) fn into_overflowable_list<'a, T>(
 +    iter: impl Iterator<Item = &'a T>,
 +) -> impl Iterator<Item = OverflowableItem<'a>>
 +where
 +    T: 'a + IntoOverflowableItem<'a>,
 +{
 +    iter.map(|x| IntoOverflowableItem::into_overflowable_item(x))
 +}
 +
 +pub(crate) fn rewrite_with_parens<'a, T: 'a + IntoOverflowableItem<'a>>(
 +    context: &'a RewriteContext<'_>,
 +    ident: &'a str,
 +    items: impl Iterator<Item = &'a T>,
 +    shape: Shape,
 +    span: Span,
 +    item_max_width: usize,
 +    force_separator_tactic: Option<SeparatorTactic>,
 +) -> Option<String> {
 +    Context::new(
 +        context,
 +        items,
 +        ident,
 +        shape,
 +        span,
 +        "(",
 +        ")",
 +        item_max_width,
 +        force_separator_tactic,
 +        None,
 +    )
 +    .rewrite(shape)
 +}
 +
 +pub(crate) fn rewrite_with_angle_brackets<'a, T: 'a + IntoOverflowableItem<'a>>(
 +    context: &'a RewriteContext<'_>,
 +    ident: &'a str,
 +    items: impl Iterator<Item = &'a T>,
 +    shape: Shape,
 +    span: Span,
 +) -> Option<String> {
 +    Context::new(
 +        context,
 +        items,
 +        ident,
 +        shape,
 +        span,
 +        "<",
 +        ">",
 +        context.config.max_width(),
 +        None,
 +        None,
 +    )
 +    .rewrite(shape)
 +}
 +
 +pub(crate) fn rewrite_with_square_brackets<'a, T: 'a + IntoOverflowableItem<'a>>(
 +    context: &'a RewriteContext<'_>,
 +    name: &'a str,
 +    items: impl Iterator<Item = &'a T>,
 +    shape: Shape,
 +    span: Span,
 +    force_separator_tactic: Option<SeparatorTactic>,
 +    delim_token: Option<DelimToken>,
 +) -> Option<String> {
 +    let (lhs, rhs) = match delim_token {
 +        Some(DelimToken::Paren) => ("(", ")"),
 +        Some(DelimToken::Brace) => ("{", "}"),
 +        _ => ("[", "]"),
 +    };
 +    Context::new(
 +        context,
 +        items,
 +        name,
 +        shape,
 +        span,
 +        lhs,
 +        rhs,
 +        context.config.array_width(),
 +        force_separator_tactic,
 +        Some(("[", "]")),
 +    )
 +    .rewrite(shape)
 +}
 +
 +struct Context<'a> {
 +    context: &'a RewriteContext<'a>,
 +    items: Vec<OverflowableItem<'a>>,
 +    ident: &'a str,
 +    prefix: &'static str,
 +    suffix: &'static str,
 +    one_line_shape: Shape,
 +    nested_shape: Shape,
 +    span: Span,
 +    item_max_width: usize,
 +    one_line_width: usize,
 +    force_separator_tactic: Option<SeparatorTactic>,
 +    custom_delims: Option<(&'a str, &'a str)>,
 +}
 +
 +impl<'a> Context<'a> {
 +    fn new<T: 'a + IntoOverflowableItem<'a>>(
 +        context: &'a RewriteContext<'_>,
 +        items: impl Iterator<Item = &'a T>,
 +        ident: &'a str,
 +        shape: Shape,
 +        span: Span,
 +        prefix: &'static str,
 +        suffix: &'static str,
 +        item_max_width: usize,
 +        force_separator_tactic: Option<SeparatorTactic>,
 +        custom_delims: Option<(&'a str, &'a str)>,
 +    ) -> Context<'a> {
 +        let used_width = extra_offset(ident, shape);
 +        // 1 = `()`
 +        let one_line_width = shape.width.saturating_sub(used_width + 2);
 +
 +        // 1 = "(" or ")"
 +        let one_line_shape = shape
 +            .offset_left(last_line_width(ident) + 1)
 +            .and_then(|shape| shape.sub_width(1))
 +            .unwrap_or(Shape { width: 0, ..shape });
 +        let nested_shape = shape_from_indent_style(context, shape, used_width + 2, used_width + 1);
 +        Context {
 +            context,
 +            items: into_overflowable_list(items).collect(),
 +            ident,
 +            one_line_shape,
 +            nested_shape,
 +            span,
 +            prefix,
 +            suffix,
 +            item_max_width,
 +            one_line_width,
 +            force_separator_tactic,
 +            custom_delims,
 +        }
 +    }
 +
 +    fn last_item(&self) -> Option<&OverflowableItem<'_>> {
 +        self.items.last()
 +    }
 +
 +    fn items_span(&self) -> Span {
 +        let span_lo = self
 +            .context
 +            .snippet_provider
 +            .span_after(self.span, self.prefix);
 +        mk_sp(span_lo, self.span.hi())
 +    }
 +
 +    fn rewrite_last_item_with_overflow(
 +        &self,
 +        last_list_item: &mut ListItem,
 +        shape: Shape,
 +    ) -> Option<String> {
 +        let last_item = self.last_item()?;
 +        let rewrite = match last_item {
 +            OverflowableItem::Expr(expr) => {
 +                match expr.kind {
 +                    // When overflowing the closure which consists of a single control flow
 +                    // expression, force to use block if its condition uses multi line.
 +                    ast::ExprKind::Closure(..) => {
 +                        // If the argument consists of multiple closures, we do not overflow
 +                        // the last closure.
 +                        if closures::args_have_many_closure(&self.items) {
 +                            None
 +                        } else {
 +                            closures::rewrite_last_closure(self.context, expr, shape)
 +                        }
 +                    }
 +
 +                    // When overflowing the expressions which consists of a control flow
 +                    // expression, avoid condition to use multi line.
 +                    ast::ExprKind::If(..)
 +                    | ast::ExprKind::ForLoop(..)
 +                    | ast::ExprKind::Loop(..)
 +                    | ast::ExprKind::While(..)
 +                    | ast::ExprKind::Match(..) => {
 +                        let multi_line = rewrite_cond(self.context, expr, shape)
 +                            .map_or(false, |cond| cond.contains('\n'));
 +
 +                        if multi_line {
 +                            None
 +                        } else {
 +                            expr.rewrite(self.context, shape)
 +                        }
 +                    }
 +
 +                    _ => expr.rewrite(self.context, shape),
 +                }
 +            }
 +            item => item.rewrite(self.context, shape),
 +        };
 +
 +        if let Some(rewrite) = rewrite {
 +            // splitn(2, *).next().unwrap() is always safe.
 +            let rewrite_first_line = Some(rewrite.splitn(2, '\n').next().unwrap().to_owned());
 +            last_list_item.item = rewrite_first_line;
 +            Some(rewrite)
 +        } else {
 +            None
 +        }
 +    }
 +
 +    fn default_tactic(&self, list_items: &[ListItem]) -> DefinitiveListTactic {
 +        definitive_tactic(
 +            list_items,
 +            ListTactic::LimitedHorizontalVertical(self.item_max_width),
 +            Separator::Comma,
 +            self.one_line_width,
 +        )
 +    }
 +
 +    fn try_overflow_last_item(&self, list_items: &mut Vec<ListItem>) -> DefinitiveListTactic {
 +        // 1 = "("
 +        let combine_arg_with_callee = self.items.len() == 1
 +            && self.items[0].is_expr()
 +            && !self.items[0].has_attrs()
 +            && self.ident.len() < self.context.config.tab_spaces();
 +        let overflow_last = combine_arg_with_callee || can_be_overflowed(self.context, &self.items);
 +
 +        // Replace the last item with its first line to see if it fits with
 +        // first arguments.
 +        let placeholder = if overflow_last {
 +            let old_value = self.context.force_one_line_chain.get();
 +            match self.last_item() {
 +                Some(OverflowableItem::Expr(expr))
 +                    if !combine_arg_with_callee && is_method_call(expr) =>
 +                {
 +                    self.context.force_one_line_chain.replace(true);
 +                }
 +                Some(OverflowableItem::MacroArg(MacroArg::Expr(expr)))
 +                    if !combine_arg_with_callee
 +                        && is_method_call(expr)
 +                        && self.context.config.version() == Version::Two =>
 +                {
 +                    self.context.force_one_line_chain.replace(true);
 +                }
 +                _ => (),
 +            }
 +            let result = last_item_shape(
 +                &self.items,
 +                list_items,
 +                self.one_line_shape,
 +                self.item_max_width,
 +            )
 +            .and_then(|arg_shape| {
 +                self.rewrite_last_item_with_overflow(
 +                    &mut list_items[self.items.len() - 1],
 +                    arg_shape,
 +                )
 +            });
 +            self.context.force_one_line_chain.replace(old_value);
 +            result
 +        } else {
 +            None
 +        };
 +
 +        let mut tactic = definitive_tactic(
 +            &*list_items,
 +            ListTactic::LimitedHorizontalVertical(self.item_max_width),
 +            Separator::Comma,
 +            self.one_line_width,
 +        );
 +
 +        // Replace the stub with the full overflowing last argument if the rewrite
 +        // succeeded and its first line fits with the other arguments.
 +        match (overflow_last, tactic, placeholder) {
 +            (true, DefinitiveListTactic::Horizontal, Some(ref overflowed))
 +                if self.items.len() == 1 =>
 +            {
 +                // When we are rewriting a nested function call, we restrict the
 +                // budget for the inner function to avoid them being deeply nested.
 +                // However, when the inner function has a prefix or a suffix
 +                // (e.g., `foo() as u32`), this budget reduction may produce poorly
 +                // formatted code, where a prefix or a suffix being left on its own
 +                // line. Here we explicitlly check those cases.
 +                if count_newlines(overflowed) == 1 {
 +                    let rw = self
 +                        .items
 +                        .last()
 +                        .and_then(|last_item| last_item.rewrite(self.context, self.nested_shape));
 +                    let no_newline = rw.as_ref().map_or(false, |s| !s.contains('\n'));
 +                    if no_newline {
 +                        list_items[self.items.len() - 1].item = rw;
 +                    } else {
 +                        list_items[self.items.len() - 1].item = Some(overflowed.to_owned());
 +                    }
 +                } else {
 +                    list_items[self.items.len() - 1].item = Some(overflowed.to_owned());
 +                }
 +            }
 +            (true, DefinitiveListTactic::Horizontal, placeholder @ Some(..)) => {
 +                list_items[self.items.len() - 1].item = placeholder;
 +            }
 +            _ if !self.items.is_empty() => {
 +                list_items[self.items.len() - 1].item = self
 +                    .items
 +                    .last()
 +                    .and_then(|last_item| last_item.rewrite(self.context, self.nested_shape));
 +
 +                // Use horizontal layout for a function with a single argument as long as
 +                // everything fits in a single line.
 +                // `self.one_line_width == 0` means vertical layout is forced.
 +                if self.items.len() == 1
 +                    && self.one_line_width != 0
 +                    && !list_items[0].has_comment()
 +                    && !list_items[0].inner_as_ref().contains('\n')
 +                    && crate::lists::total_item_width(&list_items[0]) <= self.one_line_width
 +                {
 +                    tactic = DefinitiveListTactic::Horizontal;
 +                } else {
 +                    tactic = self.default_tactic(list_items);
 +
 +                    if tactic == DefinitiveListTactic::Vertical {
 +                        if let Some((all_simple, num_args_before)) =
 +                            maybe_get_args_offset(self.ident, &self.items)
 +                        {
 +                            let one_line = all_simple
 +                                && definitive_tactic(
 +                                    &list_items[..num_args_before],
 +                                    ListTactic::HorizontalVertical,
 +                                    Separator::Comma,
 +                                    self.nested_shape.width,
 +                                ) == DefinitiveListTactic::Horizontal
 +                                && definitive_tactic(
 +                                    &list_items[num_args_before + 1..],
 +                                    ListTactic::HorizontalVertical,
 +                                    Separator::Comma,
 +                                    self.nested_shape.width,
 +                                ) == DefinitiveListTactic::Horizontal;
 +
 +                            if one_line {
 +                                tactic = DefinitiveListTactic::SpecialMacro(num_args_before);
 +                            };
- fn no_long_items(list: &[ListItem]) -> bool {
++                        } else if is_every_expr_simple(&self.items)
++                            && no_long_items(
++                                list_items,
++                                self.context.config.short_array_element_width_threshold(),
++                            )
++                        {
 +                            tactic = DefinitiveListTactic::Mixed;
 +                        }
 +                    }
 +                }
 +            }
 +            _ => (),
 +        }
 +
 +        tactic
 +    }
 +
 +    fn rewrite_items(&self) -> Option<(bool, String)> {
 +        let span = self.items_span();
 +        let items = itemize_list(
 +            self.context.snippet_provider,
 +            self.items.iter(),
 +            self.suffix,
 +            ",",
 +            |item| item.span().lo(),
 +            |item| item.span().hi(),
 +            |item| item.rewrite(self.context, self.nested_shape),
 +            span.lo(),
 +            span.hi(),
 +            true,
 +        );
 +        let mut list_items: Vec<_> = items.collect();
 +
 +        // Try letting the last argument overflow to the next line with block
 +        // indentation. If its first line fits on one line with the other arguments,
 +        // we format the function arguments horizontally.
 +        let tactic = self.try_overflow_last_item(&mut list_items);
 +        let trailing_separator = if let Some(tactic) = self.force_separator_tactic {
 +            tactic
 +        } else if !self.context.use_block_indent() {
 +            SeparatorTactic::Never
 +        } else {
 +            self.context.config.trailing_comma()
 +        };
 +        let ends_with_newline = match tactic {
 +            DefinitiveListTactic::Vertical | DefinitiveListTactic::Mixed => {
 +                self.context.use_block_indent()
 +            }
 +            _ => false,
 +        };
 +
 +        let fmt = ListFormatting::new(self.nested_shape, self.context.config)
 +            .tactic(tactic)
 +            .trailing_separator(trailing_separator)
 +            .ends_with_newline(ends_with_newline);
 +
 +        write_list(&list_items, &fmt)
 +            .map(|items_str| (tactic == DefinitiveListTactic::Horizontal, items_str))
 +    }
 +
 +    fn wrap_items(&self, items_str: &str, shape: Shape, is_extendable: bool) -> String {
 +        let shape = Shape {
 +            width: shape.width.saturating_sub(last_line_width(self.ident)),
 +            ..shape
 +        };
 +
 +        let (prefix, suffix) = match self.custom_delims {
 +            Some((lhs, rhs)) => (lhs, rhs),
 +            _ => (self.prefix, self.suffix),
 +        };
 +
 +        let extend_width = if items_str.is_empty() {
 +            2
 +        } else {
 +            first_line_width(items_str) + 1
 +        };
 +        let nested_indent_str = self
 +            .nested_shape
 +            .indent
 +            .to_string_with_newline(self.context.config);
 +        let indent_str = shape
 +            .block()
 +            .indent
 +            .to_string_with_newline(self.context.config);
 +        let mut result = String::with_capacity(
 +            self.ident.len() + items_str.len() + 2 + indent_str.len() + nested_indent_str.len(),
 +        );
 +        result.push_str(self.ident);
 +        result.push_str(prefix);
 +        let force_single_line = if self.context.config.version() == Version::Two {
 +            !self.context.use_block_indent() || (is_extendable && extend_width <= shape.width)
 +        } else {
 +            // 2 = `()`
 +            let fits_one_line = items_str.len() + 2 <= shape.width;
 +            !self.context.use_block_indent()
 +                || (self.context.inside_macro() && !items_str.contains('\n') && fits_one_line)
 +                || (is_extendable && extend_width <= shape.width)
 +        };
 +        if force_single_line {
 +            result.push_str(items_str);
 +        } else {
 +            if !items_str.is_empty() {
 +                result.push_str(&nested_indent_str);
 +                result.push_str(items_str);
 +            }
 +            result.push_str(&indent_str);
 +        }
 +        result.push_str(suffix);
 +        result
 +    }
 +
 +    fn rewrite(&self, shape: Shape) -> Option<String> {
 +        let (extendable, items_str) = self.rewrite_items()?;
 +
 +        // If we are using visual indent style and failed to format, retry with block indent.
 +        if !self.context.use_block_indent()
 +            && need_block_indent(&items_str, self.nested_shape)
 +            && !extendable
 +        {
 +            self.context.use_block.replace(true);
 +            let result = self.rewrite(shape);
 +            self.context.use_block.replace(false);
 +            return result;
 +        }
 +
 +        Some(self.wrap_items(&items_str, shape, extendable))
 +    }
 +}
 +
 +fn need_block_indent(s: &str, shape: Shape) -> bool {
 +    s.lines().skip(1).any(|s| {
 +        s.find(|c| !char::is_whitespace(c))
 +            .map_or(false, |w| w + 1 < shape.indent.width())
 +    })
 +}
 +
 +fn can_be_overflowed(context: &RewriteContext<'_>, items: &[OverflowableItem<'_>]) -> bool {
 +    items
 +        .last()
 +        .map_or(false, |x| x.can_be_overflowed(context, items.len()))
 +}
 +
 +/// Returns a shape for the last argument which is going to be overflowed.
 +fn last_item_shape(
 +    lists: &[OverflowableItem<'_>],
 +    items: &[ListItem],
 +    shape: Shape,
 +    args_max_width: usize,
 +) -> Option<Shape> {
 +    if items.len() == 1 && !lists.get(0)?.is_nested_call() {
 +        return Some(shape);
 +    }
 +    let offset = items
 +        .iter()
 +        .dropping_back(1)
 +        .map(|i| {
 +            // 2 = ", "
 +            2 + i.inner_as_ref().len()
 +        })
 +        .sum();
 +    Shape {
 +        width: min(args_max_width, shape.width),
 +        ..shape
 +    }
 +    .offset_left(offset)
 +}
 +
 +fn shape_from_indent_style(
 +    context: &RewriteContext<'_>,
 +    shape: Shape,
 +    overhead: usize,
 +    offset: usize,
 +) -> Shape {
 +    let (shape, overhead) = if context.use_block_indent() {
 +        let shape = shape
 +            .block()
 +            .block_indent(context.config.tab_spaces())
 +            .with_max_width(context.config);
 +        (shape, 1) // 1 = ","
 +    } else {
 +        (shape.visual_indent(offset), overhead)
 +    };
 +    Shape {
 +        width: shape.width.saturating_sub(overhead),
 +        ..shape
 +    }
 +}
 +
-         .all(|item| item.inner_as_ref().len() <= SHORT_ITEM_THRESHOLD)
++fn no_long_items(list: &[ListItem], short_array_element_width_threshold: usize) -> bool {
 +    list.iter()
++        .all(|item| item.inner_as_ref().len() <= short_array_element_width_threshold)
 +}
 +
 +/// In case special-case style is required, returns an offset from which we start horizontal layout.
 +pub(crate) fn maybe_get_args_offset(
 +    callee_str: &str,
 +    args: &[OverflowableItem<'_>],
 +) -> Option<(bool, usize)> {
 +    if let Some(&(_, num_args_before)) = args
 +        .get(0)?
 +        .whitelist()
 +        .iter()
 +        .find(|&&(s, _)| s == callee_str)
 +    {
 +        let all_simple = args.len() > num_args_before
 +            && is_every_expr_simple(&args[0..num_args_before])
 +            && is_every_expr_simple(&args[num_args_before + 1..]);
 +
 +        Some((all_simple, num_args_before))
 +    } else {
 +        None
 +    }
 +}
index 412f4434b9ead51f2ea5412ab16df06266aa11e9,0000000000000000000000000000000000000000..7571e6d078a7bfe029767be0a33f99b413f24489
mode 100644,000000..100644
--- /dev/null
@@@ -1,452 -1,0 +1,482 @@@
-     ) -> Result<rustc_expand::module::ModulePathSuccess, rustc_expand::module::ModError<'_>> {
-         rustc_expand::module::default_submod_path(&self.parse_sess, id, relative, dir_path)
 +use std::path::Path;
 +use std::sync::atomic::{AtomicBool, Ordering};
 +
 +use rustc_data_structures::sync::{Lrc, Send};
 +use rustc_errors::emitter::{Emitter, EmitterWriter};
 +use rustc_errors::{ColorConfig, Diagnostic, Handler, Level as DiagnosticLevel};
 +use rustc_session::parse::ParseSess as RawParseSess;
 +use rustc_span::{
 +    source_map::{FilePathMapping, SourceMap},
 +    symbol, BytePos, Span,
 +};
 +
 +use crate::config::file_lines::LineRange;
 +use crate::ignore_path::IgnorePathSet;
++use crate::parse::parser::{ModError, ModulePathSuccess};
 +use crate::source_map::LineRangeUtils;
 +use crate::utils::starts_with_newline;
 +use crate::visitor::SnippetProvider;
 +use crate::{Config, ErrorKind, FileName};
 +
 +/// ParseSess holds structs necessary for constructing a parser.
 +pub(crate) struct ParseSess {
 +    parse_sess: RawParseSess,
 +    ignore_path_set: Lrc<IgnorePathSet>,
 +    can_reset_errors: Lrc<AtomicBool>,
 +}
 +
 +/// Emitter which discards every error.
 +struct SilentEmitter;
 +
 +impl Emitter for SilentEmitter {
 +    fn source_map(&self) -> Option<&Lrc<SourceMap>> {
 +        None
 +    }
 +    fn emit_diagnostic(&mut self, _db: &Diagnostic) {}
 +}
 +
 +fn silent_emitter() -> Box<dyn Emitter + Send> {
 +    Box::new(SilentEmitter {})
 +}
 +
 +/// Emit errors against every files expect ones specified in the `ignore_path_set`.
 +struct SilentOnIgnoredFilesEmitter {
 +    ignore_path_set: Lrc<IgnorePathSet>,
 +    source_map: Lrc<SourceMap>,
 +    emitter: Box<dyn Emitter + Send>,
 +    has_non_ignorable_parser_errors: bool,
 +    can_reset: Lrc<AtomicBool>,
 +}
 +
 +impl SilentOnIgnoredFilesEmitter {
 +    fn handle_non_ignoreable_error(&mut self, db: &Diagnostic) {
 +        self.has_non_ignorable_parser_errors = true;
 +        self.can_reset.store(false, Ordering::Release);
 +        self.emitter.emit_diagnostic(db);
 +    }
 +}
 +
 +impl Emitter for SilentOnIgnoredFilesEmitter {
 +    fn source_map(&self) -> Option<&Lrc<SourceMap>> {
 +        None
 +    }
 +    fn emit_diagnostic(&mut self, db: &Diagnostic) {
 +        if db.level() == DiagnosticLevel::Fatal {
 +            return self.handle_non_ignoreable_error(db);
 +        }
 +        if let Some(primary_span) = &db.span.primary_span() {
 +            let file_name = self.source_map.span_to_filename(*primary_span);
 +            if let rustc_span::FileName::Real(rustc_span::RealFileName::LocalPath(ref path)) =
 +                file_name
 +            {
 +                if self
 +                    .ignore_path_set
 +                    .is_match(&FileName::Real(path.to_path_buf()))
 +                {
 +                    if !self.has_non_ignorable_parser_errors {
 +                        self.can_reset.store(true, Ordering::Release);
 +                    }
 +                    return;
 +                }
 +            };
 +        }
 +        self.handle_non_ignoreable_error(db);
 +    }
 +}
 +
 +fn default_handler(
 +    source_map: Lrc<SourceMap>,
 +    ignore_path_set: Lrc<IgnorePathSet>,
 +    can_reset: Lrc<AtomicBool>,
 +    hide_parse_errors: bool,
 +) -> Handler {
 +    let supports_color = term::stderr().map_or(false, |term| term.supports_color());
 +    let color_cfg = if supports_color {
 +        ColorConfig::Auto
 +    } else {
 +        ColorConfig::Never
 +    };
 +
 +    let emitter = if hide_parse_errors {
 +        silent_emitter()
 +    } else {
 +        Box::new(EmitterWriter::stderr(
 +            color_cfg,
 +            Some(source_map.clone()),
 +            false,
 +            false,
 +            None,
 +            false,
 +        ))
 +    };
 +    Handler::with_emitter(
 +        true,
 +        None,
 +        Box::new(SilentOnIgnoredFilesEmitter {
 +            has_non_ignorable_parser_errors: false,
 +            source_map,
 +            emitter,
 +            ignore_path_set,
 +            can_reset,
 +        }),
 +    )
 +}
 +
 +impl ParseSess {
 +    pub(crate) fn new(config: &Config) -> Result<ParseSess, ErrorKind> {
 +        let ignore_path_set = match IgnorePathSet::from_ignore_list(&config.ignore()) {
 +            Ok(ignore_path_set) => Lrc::new(ignore_path_set),
 +            Err(e) => return Err(ErrorKind::InvalidGlobPattern(e)),
 +        };
 +        let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
 +        let can_reset_errors = Lrc::new(AtomicBool::new(false));
 +
 +        let handler = default_handler(
 +            Lrc::clone(&source_map),
 +            Lrc::clone(&ignore_path_set),
 +            Lrc::clone(&can_reset_errors),
 +            config.hide_parse_errors(),
 +        );
 +        let parse_sess = RawParseSess::with_span_handler(handler, source_map);
 +
 +        Ok(ParseSess {
 +            parse_sess,
 +            ignore_path_set,
 +            can_reset_errors,
 +        })
 +    }
 +
++    /// Determine the submodule path for the given module identifier.
++    ///
++    /// * `id` - The name of the module
++    /// * `relative` - If Some(symbol), the symbol name is a directory relative to the dir_path.
++    ///   If relative is Some, resolve the submodle at {dir_path}/{symbol}/{id}.rs
++    ///   or {dir_path}/{symbol}/{id}/mod.rs. if None, resolve the module at {dir_path}/{id}.rs.
++    /// *  `dir_path` - Module resolution will occur relative to this direcotry.
 +    pub(crate) fn default_submod_path(
 +        &self,
 +        id: symbol::Ident,
 +        relative: Option<symbol::Ident>,
 +        dir_path: &Path,
++    ) -> Result<ModulePathSuccess, ModError<'_>> {
++        rustc_expand::module::default_submod_path(&self.parse_sess, id, relative, dir_path).or_else(
++            |e| {
++                // If resloving a module relative to {dir_path}/{symbol} fails because a file
++                // could not be found, then try to resolve the module relative to {dir_path}.
++                // If we still can't find the module after searching for it in {dir_path},
++                // surface the original error.
++                if matches!(e, ModError::FileNotFound(..)) && relative.is_some() {
++                    rustc_expand::module::default_submod_path(&self.parse_sess, id, None, dir_path)
++                        .map_err(|_| e)
++                } else {
++                    Err(e)
++                }
++            },
++        )
 +    }
 +
 +    pub(crate) fn is_file_parsed(&self, path: &Path) -> bool {
 +        self.parse_sess
 +            .source_map()
 +            .get_source_file(&rustc_span::FileName::Real(
 +                rustc_span::RealFileName::LocalPath(path.to_path_buf()),
 +            ))
 +            .is_some()
 +    }
 +
 +    pub(crate) fn ignore_file(&self, path: &FileName) -> bool {
 +        self.ignore_path_set.as_ref().is_match(path)
 +    }
 +
 +    pub(crate) fn set_silent_emitter(&mut self) {
 +        self.parse_sess.span_diagnostic = Handler::with_emitter(true, None, silent_emitter());
 +    }
 +
 +    pub(crate) fn span_to_filename(&self, span: Span) -> FileName {
 +        self.parse_sess.source_map().span_to_filename(span).into()
 +    }
 +
 +    pub(crate) fn span_to_file_contents(&self, span: Span) -> Lrc<rustc_span::SourceFile> {
 +        self.parse_sess
 +            .source_map()
 +            .lookup_source_file(span.data().lo)
 +    }
 +
 +    pub(crate) fn span_to_first_line_string(&self, span: Span) -> String {
 +        let file_lines = self.parse_sess.source_map().span_to_lines(span).ok();
 +
 +        match file_lines {
 +            Some(fl) => fl
 +                .file
 +                .get_line(fl.lines[0].line_index)
 +                .map_or_else(String::new, |s| s.to_string()),
 +            None => String::new(),
 +        }
 +    }
 +
 +    pub(crate) fn line_of_byte_pos(&self, pos: BytePos) -> usize {
 +        self.parse_sess.source_map().lookup_char_pos(pos).line
 +    }
 +
++    // TODO(calebcartwright): Preemptive, currently unused addition
++    // that will be used to support formatting scenarios that take original
++    // positions into account
++    /// Determines whether two byte positions are in the same source line.
++    #[allow(dead_code)]
++    pub(crate) fn byte_pos_same_line(&self, a: BytePos, b: BytePos) -> bool {
++        self.line_of_byte_pos(a) == self.line_of_byte_pos(b)
++    }
++
 +    pub(crate) fn span_to_debug_info(&self, span: Span) -> String {
 +        self.parse_sess.source_map().span_to_diagnostic_string(span)
 +    }
 +
 +    pub(crate) fn inner(&self) -> &RawParseSess {
 +        &self.parse_sess
 +    }
 +
 +    pub(crate) fn snippet_provider(&self, span: Span) -> SnippetProvider {
 +        let source_file = self.parse_sess.source_map().lookup_char_pos(span.lo()).file;
 +        SnippetProvider::new(
 +            source_file.start_pos,
 +            source_file.end_pos,
 +            Lrc::clone(source_file.src.as_ref().unwrap()),
 +        )
 +    }
 +
 +    pub(crate) fn get_original_snippet(&self, file_name: &FileName) -> Option<Lrc<String>> {
 +        self.parse_sess
 +            .source_map()
 +            .get_source_file(&file_name.into())
 +            .and_then(|source_file| source_file.src.clone())
 +    }
 +}
 +
 +// Methods that should be restricted within the parse module.
 +impl ParseSess {
 +    pub(super) fn emit_diagnostics(&self, diagnostics: Vec<Diagnostic>) {
 +        for mut diagnostic in diagnostics {
 +            self.parse_sess
 +                .span_diagnostic
 +                .emit_diagnostic(&mut diagnostic);
 +        }
 +    }
 +
 +    pub(super) fn can_reset_errors(&self) -> bool {
 +        self.can_reset_errors.load(Ordering::Acquire)
 +    }
 +
 +    pub(super) fn has_errors(&self) -> bool {
 +        self.parse_sess.span_diagnostic.has_errors().is_some()
 +    }
 +
 +    pub(super) fn reset_errors(&self) {
 +        self.parse_sess.span_diagnostic.reset_err_count();
 +    }
 +}
 +
 +impl LineRangeUtils for ParseSess {
 +    fn lookup_line_range(&self, span: Span) -> LineRange {
 +        let snippet = self
 +            .parse_sess
 +            .source_map()
 +            .span_to_snippet(span)
 +            .unwrap_or_default();
 +        let lo = self.parse_sess.source_map().lookup_line(span.lo()).unwrap();
 +        let hi = self.parse_sess.source_map().lookup_line(span.hi()).unwrap();
 +
 +        debug_assert_eq!(
 +            lo.sf.name, hi.sf.name,
 +            "span crossed file boundary: lo: {:?}, hi: {:?}",
 +            lo, hi
 +        );
 +
 +        // in case the span starts with a newline, the line range is off by 1 without the
 +        // adjustment below
 +        let offset = 1 + if starts_with_newline(&snippet) { 1 } else { 0 };
 +        // Line numbers start at 1
 +        LineRange {
 +            file: lo.sf.clone(),
 +            lo: lo.line + offset,
 +            hi: hi.line + offset,
 +        }
 +    }
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use super::*;
 +
 +    use rustfmt_config_proc_macro::nightly_only_test;
 +
 +    mod emitter {
 +        use super::*;
 +        use crate::config::IgnoreList;
 +        use crate::utils::mk_sp;
 +        use rustc_span::{FileName as SourceMapFileName, MultiSpan, RealFileName};
 +        use std::path::PathBuf;
 +        use std::sync::atomic::AtomicU32;
 +
 +        struct TestEmitter {
 +            num_emitted_errors: Lrc<AtomicU32>,
 +        }
 +
 +        impl Emitter for TestEmitter {
 +            fn source_map(&self) -> Option<&Lrc<SourceMap>> {
 +                None
 +            }
 +            fn emit_diagnostic(&mut self, _db: &Diagnostic) {
 +                self.num_emitted_errors.fetch_add(1, Ordering::Release);
 +            }
 +        }
 +
 +        fn build_diagnostic(level: DiagnosticLevel, span: Option<MultiSpan>) -> Diagnostic {
 +            let mut diag = Diagnostic::new(level, "");
 +            diag.message.clear();
 +            if let Some(span) = span {
 +                diag.span = span;
 +            }
 +            diag
 +        }
 +
 +        fn build_emitter(
 +            num_emitted_errors: Lrc<AtomicU32>,
 +            can_reset: Lrc<AtomicBool>,
 +            source_map: Option<Lrc<SourceMap>>,
 +            ignore_list: Option<IgnoreList>,
 +        ) -> SilentOnIgnoredFilesEmitter {
 +            let emitter_writer = TestEmitter { num_emitted_errors };
 +            let source_map =
 +                source_map.unwrap_or_else(|| Lrc::new(SourceMap::new(FilePathMapping::empty())));
 +            let ignore_path_set = Lrc::new(
 +                IgnorePathSet::from_ignore_list(&ignore_list.unwrap_or_default()).unwrap(),
 +            );
 +            SilentOnIgnoredFilesEmitter {
 +                has_non_ignorable_parser_errors: false,
 +                source_map,
 +                emitter: Box::new(emitter_writer),
 +                ignore_path_set,
 +                can_reset,
 +            }
 +        }
 +
 +        fn get_ignore_list(config: &str) -> IgnoreList {
 +            Config::from_toml(config, Path::new("")).unwrap().ignore()
 +        }
 +
 +        #[test]
 +        fn handles_fatal_parse_error_in_ignored_file() {
 +            let num_emitted_errors = Lrc::new(AtomicU32::new(0));
 +            let can_reset_errors = Lrc::new(AtomicBool::new(false));
 +            let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
 +            let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
 +            let source =
 +                String::from(r#"extern "system" fn jni_symbol!( funcName ) ( ... ) -> {} "#);
 +            source_map.new_source_file(
 +                SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))),
 +                source,
 +            );
 +            let mut emitter = build_emitter(
 +                Lrc::clone(&num_emitted_errors),
 +                Lrc::clone(&can_reset_errors),
 +                Some(Lrc::clone(&source_map)),
 +                Some(ignore_list),
 +            );
 +            let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
 +            let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, Some(span));
 +            emitter.emit_diagnostic(&fatal_diagnostic);
 +            assert_eq!(num_emitted_errors.load(Ordering::Acquire), 1);
 +            assert_eq!(can_reset_errors.load(Ordering::Acquire), false);
 +        }
 +
 +        #[nightly_only_test]
 +        #[test]
 +        fn handles_recoverable_parse_error_in_ignored_file() {
 +            let num_emitted_errors = Lrc::new(AtomicU32::new(0));
 +            let can_reset_errors = Lrc::new(AtomicBool::new(false));
 +            let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
 +            let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
 +            let source = String::from(r#"pub fn bar() { 1x; }"#);
 +            source_map.new_source_file(
 +                SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))),
 +                source,
 +            );
 +            let mut emitter = build_emitter(
 +                Lrc::clone(&num_emitted_errors),
 +                Lrc::clone(&can_reset_errors),
 +                Some(Lrc::clone(&source_map)),
 +                Some(ignore_list),
 +            );
 +            let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
 +            let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span));
 +            emitter.emit_diagnostic(&non_fatal_diagnostic);
 +            assert_eq!(num_emitted_errors.load(Ordering::Acquire), 0);
 +            assert_eq!(can_reset_errors.load(Ordering::Acquire), true);
 +        }
 +
 +        #[nightly_only_test]
 +        #[test]
 +        fn handles_recoverable_parse_error_in_non_ignored_file() {
 +            let num_emitted_errors = Lrc::new(AtomicU32::new(0));
 +            let can_reset_errors = Lrc::new(AtomicBool::new(false));
 +            let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
 +            let source = String::from(r#"pub fn bar() { 1x; }"#);
 +            source_map.new_source_file(
 +                SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))),
 +                source,
 +            );
 +            let mut emitter = build_emitter(
 +                Lrc::clone(&num_emitted_errors),
 +                Lrc::clone(&can_reset_errors),
 +                Some(Lrc::clone(&source_map)),
 +                None,
 +            );
 +            let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
 +            let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span));
 +            emitter.emit_diagnostic(&non_fatal_diagnostic);
 +            assert_eq!(num_emitted_errors.load(Ordering::Acquire), 1);
 +            assert_eq!(can_reset_errors.load(Ordering::Acquire), false);
 +        }
 +
 +        #[nightly_only_test]
 +        #[test]
 +        fn handles_mix_of_recoverable_parse_error() {
 +            let num_emitted_errors = Lrc::new(AtomicU32::new(0));
 +            let can_reset_errors = Lrc::new(AtomicBool::new(false));
 +            let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
 +            let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
 +            let bar_source = String::from(r#"pub fn bar() { 1x; }"#);
 +            let foo_source = String::from(r#"pub fn foo() { 1x; }"#);
 +            let fatal_source =
 +                String::from(r#"extern "system" fn jni_symbol!( funcName ) ( ... ) -> {} "#);
 +            source_map.new_source_file(
 +                SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("bar.rs"))),
 +                bar_source,
 +            );
 +            source_map.new_source_file(
 +                SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))),
 +                foo_source,
 +            );
 +            source_map.new_source_file(
 +                SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("fatal.rs"))),
 +                fatal_source,
 +            );
 +            let mut emitter = build_emitter(
 +                Lrc::clone(&num_emitted_errors),
 +                Lrc::clone(&can_reset_errors),
 +                Some(Lrc::clone(&source_map)),
 +                Some(ignore_list),
 +            );
 +            let bar_span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
 +            let foo_span = MultiSpan::from_span(mk_sp(BytePos(21), BytePos(22)));
 +            let bar_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(bar_span));
 +            let foo_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(foo_span));
 +            let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, None);
 +            emitter.emit_diagnostic(&bar_diagnostic);
 +            emitter.emit_diagnostic(&foo_diagnostic);
 +            emitter.emit_diagnostic(&fatal_diagnostic);
 +            assert_eq!(num_emitted_errors.load(Ordering::Acquire), 2);
 +            assert_eq!(can_reset_errors.load(Ordering::Acquire), false);
 +        }
 +    }
 +}
index 8e6c75a3744ac6a8e4b1e0c6a7460f664ff09b0d,0000000000000000000000000000000000000000..2136cfeae1af107592aabbdf92d4aef98d104835
mode 100644,000000..100644
--- /dev/null
@@@ -1,206 -1,0 +1,199 @@@
-         let lo = if let ast::GenericParamKind::Const {
-             ty: _,
-             kw_span,
-             default: _,
-         } = self.kind
-         {
-             kw_span.lo()
-         } else if self.attrs.is_empty() {
-             self.ident.span.lo()
-         } else {
-             self.attrs[0].span.lo()
 +use std::cmp::max;
 +
 +use rustc_ast::{ast, ptr};
 +use rustc_span::{source_map, Span};
 +
 +use crate::macros::MacroArg;
 +use crate::utils::{mk_sp, outer_attributes};
 +
 +/// Spanned returns a span including attributes, if available.
 +pub(crate) trait Spanned {
 +    fn span(&self) -> Span;
 +}
 +
 +impl<T: Spanned> Spanned for ptr::P<T> {
 +    fn span(&self) -> Span {
 +        (**self).span()
 +    }
 +}
 +
 +impl<T> Spanned for source_map::Spanned<T> {
 +    fn span(&self) -> Span {
 +        self.span
 +    }
 +}
 +
 +macro_rules! span_with_attrs_lo_hi {
 +    ($this:ident, $lo:expr, $hi:expr) => {{
 +        let attrs = outer_attributes(&$this.attrs);
 +        if attrs.is_empty() {
 +            mk_sp($lo, $hi)
 +        } else {
 +            mk_sp(attrs[0].span.lo(), $hi)
 +        }
 +    }};
 +}
 +
 +macro_rules! span_with_attrs {
 +    ($this:ident) => {
 +        span_with_attrs_lo_hi!($this, $this.span.lo(), $this.span.hi())
 +    };
 +}
 +
 +macro_rules! implement_spanned {
 +    ($this:ty) => {
 +        impl Spanned for $this {
 +            fn span(&self) -> Span {
 +                span_with_attrs!(self)
 +            }
 +        }
 +    };
 +}
 +
 +// Implement `Spanned` for structs with `attrs` field.
 +implement_spanned!(ast::AssocItem);
 +implement_spanned!(ast::Expr);
 +implement_spanned!(ast::ExprField);
 +implement_spanned!(ast::ForeignItem);
 +implement_spanned!(ast::Item);
 +implement_spanned!(ast::Local);
 +
 +impl Spanned for ast::Stmt {
 +    fn span(&self) -> Span {
 +        match self.kind {
 +            ast::StmtKind::Local(ref local) => mk_sp(local.span().lo(), self.span.hi()),
 +            ast::StmtKind::Item(ref item) => mk_sp(item.span().lo(), self.span.hi()),
 +            ast::StmtKind::Expr(ref expr) | ast::StmtKind::Semi(ref expr) => {
 +                mk_sp(expr.span().lo(), self.span.hi())
 +            }
 +            ast::StmtKind::MacCall(ref mac_stmt) => {
 +                if mac_stmt.attrs.is_empty() {
 +                    self.span
 +                } else {
 +                    mk_sp(mac_stmt.attrs[0].span.lo(), self.span.hi())
 +                }
 +            }
 +            ast::StmtKind::Empty => self.span,
 +        }
 +    }
 +}
 +
 +impl Spanned for ast::Pat {
 +    fn span(&self) -> Span {
 +        self.span
 +    }
 +}
 +
 +impl Spanned for ast::Ty {
 +    fn span(&self) -> Span {
 +        self.span
 +    }
 +}
 +
 +impl Spanned for ast::Arm {
 +    fn span(&self) -> Span {
 +        let lo = if self.attrs.is_empty() {
 +            self.pat.span.lo()
 +        } else {
 +            self.attrs[0].span.lo()
 +        };
 +        span_with_attrs_lo_hi!(self, lo, self.body.span.hi())
 +    }
 +}
 +
 +impl Spanned for ast::Param {
 +    fn span(&self) -> Span {
 +        if crate::items::is_named_param(self) {
 +            mk_sp(crate::items::span_lo_for_param(self), self.ty.span.hi())
 +        } else {
 +            self.ty.span
 +        }
 +    }
 +}
 +
 +impl Spanned for ast::GenericParam {
 +    fn span(&self) -> Span {
++        let lo = match self.kind {
++            _ if !self.attrs.is_empty() => self.attrs[0].span.lo(),
++            ast::GenericParamKind::Const { kw_span, .. } => kw_span.lo(),
++            _ => self.ident.span.lo(),
 +        };
 +        let hi = if self.bounds.is_empty() {
 +            self.ident.span.hi()
 +        } else {
 +            self.bounds.last().unwrap().span().hi()
 +        };
 +        let ty_hi = if let ast::GenericParamKind::Type {
 +            default: Some(ref ty),
 +        }
 +        | ast::GenericParamKind::Const { ref ty, .. } = self.kind
 +        {
 +            ty.span().hi()
 +        } else {
 +            hi
 +        };
 +        mk_sp(lo, max(hi, ty_hi))
 +    }
 +}
 +
 +impl Spanned for ast::FieldDef {
 +    fn span(&self) -> Span {
 +        span_with_attrs_lo_hi!(self, self.span.lo(), self.ty.span.hi())
 +    }
 +}
 +
 +impl Spanned for ast::WherePredicate {
 +    fn span(&self) -> Span {
 +        match *self {
 +            ast::WherePredicate::BoundPredicate(ref p) => p.span,
 +            ast::WherePredicate::RegionPredicate(ref p) => p.span,
 +            ast::WherePredicate::EqPredicate(ref p) => p.span,
 +        }
 +    }
 +}
 +
 +impl Spanned for ast::FnRetTy {
 +    fn span(&self) -> Span {
 +        match *self {
 +            ast::FnRetTy::Default(span) => span,
 +            ast::FnRetTy::Ty(ref ty) => ty.span,
 +        }
 +    }
 +}
 +
 +impl Spanned for ast::GenericArg {
 +    fn span(&self) -> Span {
 +        match *self {
 +            ast::GenericArg::Lifetime(ref lt) => lt.ident.span,
 +            ast::GenericArg::Type(ref ty) => ty.span(),
 +            ast::GenericArg::Const(ref _const) => _const.value.span(),
 +        }
 +    }
 +}
 +
 +impl Spanned for ast::GenericBound {
 +    fn span(&self) -> Span {
 +        match *self {
 +            ast::GenericBound::Trait(ref ptr, _) => ptr.span,
 +            ast::GenericBound::Outlives(ref l) => l.ident.span,
 +        }
 +    }
 +}
 +
 +impl Spanned for MacroArg {
 +    fn span(&self) -> Span {
 +        match *self {
 +            MacroArg::Expr(ref expr) => expr.span(),
 +            MacroArg::Ty(ref ty) => ty.span(),
 +            MacroArg::Pat(ref pat) => pat.span(),
 +            MacroArg::Item(ref item) => item.span(),
 +            MacroArg::Keyword(_, span) => span,
 +        }
 +    }
 +}
 +
 +impl Spanned for ast::NestedMetaItem {
 +    fn span(&self) -> Span {
 +        self.span()
 +    }
 +}
index 64ae15672df8ff1c88f3d749d6d2de14bff2f9c3,0000000000000000000000000000000000000000..b65aa5b33b241b4a16f38719d9d39863e16626aa
mode 100644,000000..100644
--- /dev/null
@@@ -1,691 -1,0 +1,694 @@@
-             return SnippetState::LineEnd(input[..=index_plus_ws].concat(), index_plus_ws + 1);
 +// Format string literals.
 +
 +use regex::Regex;
 +use unicode_categories::UnicodeCategories;
 +use unicode_segmentation::UnicodeSegmentation;
 +
 +use crate::config::Config;
 +use crate::shape::Shape;
 +use crate::utils::{unicode_str_width, wrap_str};
 +
 +const MIN_STRING: usize = 10;
 +
 +/// Describes the layout of a piece of text.
 +pub(crate) struct StringFormat<'a> {
 +    /// The opening sequence of characters for the piece of text
 +    pub(crate) opener: &'a str,
 +    /// The closing sequence of characters for the piece of text
 +    pub(crate) closer: &'a str,
 +    /// The opening sequence of characters for a line
 +    pub(crate) line_start: &'a str,
 +    /// The closing sequence of characters for a line
 +    pub(crate) line_end: &'a str,
 +    /// The allocated box to fit the text into
 +    pub(crate) shape: Shape,
 +    /// Trim trailing whitespaces
 +    pub(crate) trim_end: bool,
 +    pub(crate) config: &'a Config,
 +}
 +
 +impl<'a> StringFormat<'a> {
 +    pub(crate) fn new(shape: Shape, config: &'a Config) -> StringFormat<'a> {
 +        StringFormat {
 +            opener: "\"",
 +            closer: "\"",
 +            line_start: " ",
 +            line_end: "\\",
 +            shape,
 +            trim_end: false,
 +            config,
 +        }
 +    }
 +
 +    /// Returns the maximum number of graphemes that is possible on a line while taking the
 +    /// indentation into account.
 +    ///
 +    /// If we cannot put at least a single character per line, the rewrite won't succeed.
 +    fn max_width_with_indent(&self) -> Option<usize> {
 +        Some(
 +            self.shape
 +                .width
 +                .checked_sub(self.opener.len() + self.line_end.len() + 1)?
 +                + 1,
 +        )
 +    }
 +
 +    /// Like max_width_with_indent but the indentation is not subtracted.
 +    /// This allows to fit more graphemes from the string on a line when
 +    /// SnippetState::EndWithLineFeed.
 +    fn max_width_without_indent(&self) -> Option<usize> {
 +        self.config.max_width().checked_sub(self.line_end.len())
 +    }
 +}
 +
 +pub(crate) fn rewrite_string<'a>(
 +    orig: &str,
 +    fmt: &StringFormat<'a>,
 +    newline_max_chars: usize,
 +) -> Option<String> {
 +    let max_width_with_indent = fmt.max_width_with_indent()?;
 +    let max_width_without_indent = fmt.max_width_without_indent()?;
 +    let indent_with_newline = fmt.shape.indent.to_string_with_newline(fmt.config);
 +    let indent_without_newline = fmt.shape.indent.to_string(fmt.config);
 +
 +    // Strip line breaks.
 +    // With this regex applied, all remaining whitespaces are significant
 +    let strip_line_breaks_re = Regex::new(r"([^\\](\\\\)*)\\[\n\r][[:space:]]*").unwrap();
 +    let stripped_str = strip_line_breaks_re.replace_all(orig, "$1");
 +
 +    let graphemes = UnicodeSegmentation::graphemes(&*stripped_str, false).collect::<Vec<&str>>();
 +
 +    // `cur_start` is the position in `orig` of the start of the current line.
 +    let mut cur_start = 0;
 +    let mut result = String::with_capacity(
 +        stripped_str
 +            .len()
 +            .checked_next_power_of_two()
 +            .unwrap_or(usize::max_value()),
 +    );
 +    result.push_str(fmt.opener);
 +
 +    // Snip a line at a time from `stripped_str` until it is used up. Push the snippet
 +    // onto result.
 +    let mut cur_max_width = max_width_with_indent;
 +    let is_bareline_ok = fmt.line_start.is_empty() || is_whitespace(fmt.line_start);
 +    loop {
 +        // All the input starting at cur_start fits on the current line
 +        if graphemes_width(&graphemes[cur_start..]) <= cur_max_width {
 +            for (i, grapheme) in graphemes[cur_start..].iter().enumerate() {
 +                if is_new_line(grapheme) {
 +                    // take care of blank lines
 +                    result = trim_end_but_line_feed(fmt.trim_end, result);
 +                    result.push('\n');
 +                    if !is_bareline_ok && cur_start + i + 1 < graphemes.len() {
 +                        result.push_str(&indent_without_newline);
 +                        result.push_str(fmt.line_start);
 +                    }
 +                } else {
 +                    result.push_str(grapheme);
 +                }
 +            }
 +            result = trim_end_but_line_feed(fmt.trim_end, result);
 +            break;
 +        }
 +
 +        // The input starting at cur_start needs to be broken
 +        match break_string(
 +            cur_max_width,
 +            fmt.trim_end,
 +            fmt.line_end,
 +            &graphemes[cur_start..],
 +        ) {
 +            SnippetState::LineEnd(line, len) => {
 +                result.push_str(&line);
 +                result.push_str(fmt.line_end);
 +                result.push_str(&indent_with_newline);
 +                result.push_str(fmt.line_start);
 +                cur_max_width = newline_max_chars;
 +                cur_start += len;
 +            }
 +            SnippetState::EndWithLineFeed(line, len) => {
 +                if line == "\n" && fmt.trim_end {
 +                    result = result.trim_end().to_string();
 +                }
 +                result.push_str(&line);
 +                if is_bareline_ok {
 +                    // the next line can benefit from the full width
 +                    cur_max_width = max_width_without_indent;
 +                } else {
 +                    result.push_str(&indent_without_newline);
 +                    result.push_str(fmt.line_start);
 +                    cur_max_width = max_width_with_indent;
 +                }
 +                cur_start += len;
 +            }
 +            SnippetState::EndOfInput(line) => {
 +                result.push_str(&line);
 +                break;
 +            }
 +        }
 +    }
 +
 +    result.push_str(fmt.closer);
 +    wrap_str(result, fmt.config.max_width(), fmt.shape)
 +}
 +
 +/// Returns the index to the end of the URL if the split at index of the given string includes a
 +/// URL or alike. Otherwise, returns `None`.
 +fn detect_url(s: &[&str], index: usize) -> Option<usize> {
 +    let start = match s[..=index].iter().rposition(|g| is_whitespace(g)) {
 +        Some(pos) => pos + 1,
 +        None => 0,
 +    };
 +    // 8 = minimum length for a string to contain a URL
 +    if s.len() < start + 8 {
 +        return None;
 +    }
 +    let split = s[start..].concat();
 +    if split.contains("https://")
 +        || split.contains("http://")
 +        || split.contains("ftp://")
 +        || split.contains("file://")
 +    {
 +        match s[index..].iter().position(|g| is_whitespace(g)) {
 +            Some(pos) => Some(index + pos - 1),
 +            None => Some(s.len() - 1),
 +        }
 +    } else {
 +        None
 +    }
 +}
 +
 +/// Trims whitespaces to the right except for the line feed character.
 +fn trim_end_but_line_feed(trim_end: bool, result: String) -> String {
 +    let whitespace_except_line_feed = |c: char| c.is_whitespace() && c != '\n';
 +    if trim_end && result.ends_with(whitespace_except_line_feed) {
 +        result
 +            .trim_end_matches(whitespace_except_line_feed)
 +            .to_string()
 +    } else {
 +        result
 +    }
 +}
 +
 +/// Result of breaking a string so it fits in a line and the state it ended in.
 +/// The state informs about what to do with the snippet and how to continue the breaking process.
 +#[derive(Debug, PartialEq)]
 +enum SnippetState {
 +    /// The input could not be broken and so rewriting the string is finished.
 +    EndOfInput(String),
 +    /// The input could be broken and the returned snippet should be ended with a
 +    /// `[StringFormat::line_end]`. The next snippet needs to be indented.
 +    ///
 +    /// The returned string is the line to print out and the number is the length that got read in
 +    /// the text being rewritten. That length may be greater than the returned string if trailing
 +    /// whitespaces got trimmed.
 +    LineEnd(String, usize),
 +    /// The input could be broken but a newline is present that cannot be trimmed. The next snippet
 +    /// to be rewritten *could* use more width than what is specified by the given shape. For
 +    /// example with a multiline string, the next snippet does not need to be indented, allowing
 +    /// more characters to be fit within a line.
 +    ///
 +    /// The returned string is the line to print out and the number is the length that got read in
 +    /// the text being rewritten.
 +    EndWithLineFeed(String, usize),
 +}
 +
 +fn not_whitespace_except_line_feed(g: &str) -> bool {
 +    is_new_line(g) || !is_whitespace(g)
 +}
 +
 +/// Break the input string at a boundary character around the offset `max_width`. A boundary
 +/// character is either a punctuation or a whitespace.
 +/// FIXME(issue#3281): We must follow UAX#14 algorithm instead of this.
 +fn break_string(max_width: usize, trim_end: bool, line_end: &str, input: &[&str]) -> SnippetState {
 +    let break_at = |index /* grapheme at index is included */| {
 +        // Take in any whitespaces to the left/right of `input[index]` while
 +        // preserving line feeds
 +        let index_minus_ws = input[0..=index]
 +            .iter()
 +            .rposition(|grapheme| not_whitespace_except_line_feed(grapheme))
 +            .unwrap_or(index);
 +        // Take into account newlines occurring in input[0..=index], i.e., the possible next new
 +        // line. If there is one, then text after it could be rewritten in a way that the available
 +        // space is fully used.
 +        for (i, grapheme) in input[0..=index].iter().enumerate() {
 +            if is_new_line(grapheme) {
 +                if i <= index_minus_ws {
 +                    let mut line = &input[0..i].concat()[..];
 +                    if trim_end {
 +                        line = line.trim_end();
 +                    }
 +                    return SnippetState::EndWithLineFeed(format!("{}\n", line), i + 1);
 +                }
 +                break;
 +            }
 +        }
 +
 +        let mut index_plus_ws = index;
 +        for (i, grapheme) in input[index + 1..].iter().enumerate() {
 +            if !trim_end && is_new_line(grapheme) {
 +                return SnippetState::EndWithLineFeed(
 +                    input[0..=index + 1 + i].concat(),
 +                    index + 2 + i,
 +                );
 +            } else if not_whitespace_except_line_feed(grapheme) {
 +                index_plus_ws = index + i;
 +                break;
 +            }
 +        }
 +
 +        if trim_end {
 +            SnippetState::LineEnd(input[0..=index_minus_ws].concat(), index_plus_ws + 1)
 +        } else {
 +            SnippetState::LineEnd(input[0..=index_plus_ws].concat(), index_plus_ws + 1)
 +        }
 +    };
 +
 +    // find a first index where the unicode width of input[0..x] become > max_width
 +    let max_width_index_in_input = {
 +        let mut cur_width = 0;
 +        let mut cur_index = 0;
 +        for (i, grapheme) in input.iter().enumerate() {
 +            cur_width += unicode_str_width(grapheme);
 +            cur_index = i;
 +            if cur_width > max_width {
 +                break;
 +            }
 +        }
 +        cur_index
 +    };
++    if max_width_index_in_input == 0 {
++        return SnippetState::EndOfInput(input.concat());
++    }
 +
 +    // Find the position in input for breaking the string
 +    if line_end.is_empty()
 +        && trim_end
 +        && !is_whitespace(input[max_width_index_in_input - 1])
 +        && is_whitespace(input[max_width_index_in_input])
 +    {
 +        // At a breaking point already
 +        // The line won't invalidate the rewriting because:
 +        // - no extra space needed for the line_end character
 +        // - extra whitespaces to the right can be trimmed
 +        return break_at(max_width_index_in_input - 1);
 +    }
 +    if let Some(url_index_end) = detect_url(input, max_width_index_in_input) {
 +        let index_plus_ws = url_index_end
 +            + input[url_index_end..]
 +                .iter()
 +                .skip(1)
 +                .position(|grapheme| not_whitespace_except_line_feed(grapheme))
 +                .unwrap_or(0);
 +        return if trim_end {
 +            SnippetState::LineEnd(input[..=url_index_end].concat(), index_plus_ws + 1)
 +        } else {
++            SnippetState::LineEnd(input[..=index_plus_ws].concat(), index_plus_ws + 1)
 +        };
 +    }
 +
 +    match input[0..max_width_index_in_input]
 +        .iter()
 +        .rposition(|grapheme| is_whitespace(grapheme))
 +    {
 +        // Found a whitespace and what is on its left side is big enough.
 +        Some(index) if index >= MIN_STRING => break_at(index),
 +        // No whitespace found, try looking for a punctuation instead
 +        _ => match input[0..max_width_index_in_input]
 +            .iter()
 +            .rposition(|grapheme| is_punctuation(grapheme))
 +        {
 +            // 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))
 +            {
 +                // A boundary was found after the line limit
 +                Some(index) => break_at(max_width_index_in_input + index),
 +                // No boundary to the right, the input cannot be broken
 +                None => SnippetState::EndOfInput(input.concat()),
 +            },
 +        },
 +    }
 +}
 +
 +fn is_new_line(grapheme: &str) -> bool {
 +    let bytes = grapheme.as_bytes();
 +    bytes.starts_with(b"\n") || bytes.starts_with(b"\r\n")
 +}
 +
 +fn is_whitespace(grapheme: &str) -> bool {
 +    grapheme.chars().all(char::is_whitespace)
 +}
 +
 +fn is_punctuation(grapheme: &str) -> bool {
 +    grapheme
 +        .chars()
 +        .all(UnicodeCategories::is_punctuation_other)
 +}
 +
 +fn graphemes_width(graphemes: &[&str]) -> usize {
 +    graphemes.iter().map(|s| unicode_str_width(s)).sum()
 +}
 +
 +#[cfg(test)]
 +mod test {
 +    use super::{break_string, detect_url, rewrite_string, SnippetState, StringFormat};
 +    use crate::config::Config;
 +    use crate::shape::{Indent, Shape};
 +    use unicode_segmentation::UnicodeSegmentation;
 +
 +    #[test]
 +    fn issue343() {
 +        let config = Default::default();
 +        let fmt = StringFormat::new(Shape::legacy(2, Indent::empty()), &config);
 +        rewrite_string("eq_", &fmt, 2);
 +    }
 +
 +    #[test]
 +    fn should_break_on_whitespace() {
 +        let string = "Placerat felis. Mauris porta ante sagittis purus.";
 +        let graphemes = UnicodeSegmentation::graphemes(&*string, false).collect::<Vec<&str>>();
 +        assert_eq!(
 +            break_string(20, false, "", &graphemes[..]),
 +            SnippetState::LineEnd("Placerat felis. ".to_string(), 16)
 +        );
 +        assert_eq!(
 +            break_string(20, true, "", &graphemes[..]),
 +            SnippetState::LineEnd("Placerat felis.".to_string(), 16)
 +        );
 +    }
 +
 +    #[test]
 +    fn should_break_on_punctuation() {
 +        let string = "Placerat_felis._Mauris_porta_ante_sagittis_purus.";
 +        let graphemes = UnicodeSegmentation::graphemes(&*string, false).collect::<Vec<&str>>();
 +        assert_eq!(
 +            break_string(20, false, "", &graphemes[..]),
 +            SnippetState::LineEnd("Placerat_felis.".to_string(), 15)
 +        );
 +    }
 +
 +    #[test]
 +    fn should_break_forward() {
 +        let string = "Venenatis_tellus_vel_tellus. Aliquam aliquam dolor at justo.";
 +        let graphemes = UnicodeSegmentation::graphemes(&*string, false).collect::<Vec<&str>>();
 +        assert_eq!(
 +            break_string(20, false, "", &graphemes[..]),
 +            SnippetState::LineEnd("Venenatis_tellus_vel_tellus. ".to_string(), 29)
 +        );
 +        assert_eq!(
 +            break_string(20, true, "", &graphemes[..]),
 +            SnippetState::LineEnd("Venenatis_tellus_vel_tellus.".to_string(), 29)
 +        );
 +    }
 +
 +    #[test]
 +    fn nothing_to_break() {
 +        let string = "Venenatis_tellus_vel_tellus";
 +        let graphemes = UnicodeSegmentation::graphemes(&*string, false).collect::<Vec<&str>>();
 +        assert_eq!(
 +            break_string(20, false, "", &graphemes[..]),
 +            SnippetState::EndOfInput("Venenatis_tellus_vel_tellus".to_string())
 +        );
 +    }
 +
 +    #[test]
 +    fn significant_whitespaces() {
 +        let string = "Neque in sem.      \n      Pellentesque tellus augue.";
 +        let graphemes = UnicodeSegmentation::graphemes(&*string, false).collect::<Vec<&str>>();
 +        assert_eq!(
 +            break_string(15, false, "", &graphemes[..]),
 +            SnippetState::EndWithLineFeed("Neque in sem.      \n".to_string(), 20)
 +        );
 +        assert_eq!(
 +            break_string(25, false, "", &graphemes[..]),
 +            SnippetState::EndWithLineFeed("Neque in sem.      \n".to_string(), 20)
 +        );
 +
 +        assert_eq!(
 +            break_string(15, true, "", &graphemes[..]),
 +            SnippetState::LineEnd("Neque in sem.".to_string(), 19)
 +        );
 +        assert_eq!(
 +            break_string(25, true, "", &graphemes[..]),
 +            SnippetState::EndWithLineFeed("Neque in sem.\n".to_string(), 20)
 +        );
 +    }
 +
 +    #[test]
 +    fn big_whitespace() {
 +        let string = "Neque in sem.            Pellentesque tellus augue.";
 +        let graphemes = UnicodeSegmentation::graphemes(&*string, false).collect::<Vec<&str>>();
 +        assert_eq!(
 +            break_string(20, false, "", &graphemes[..]),
 +            SnippetState::LineEnd("Neque in sem.            ".to_string(), 25)
 +        );
 +        assert_eq!(
 +            break_string(20, true, "", &graphemes[..]),
 +            SnippetState::LineEnd("Neque in sem.".to_string(), 25)
 +        );
 +    }
 +
 +    #[test]
 +    fn newline_in_candidate_line() {
 +        let string = "Nulla\nconsequat erat at massa. Vivamus id mi.";
 +
 +        let graphemes = UnicodeSegmentation::graphemes(&*string, false).collect::<Vec<&str>>();
 +        assert_eq!(
 +            break_string(25, false, "", &graphemes[..]),
 +            SnippetState::EndWithLineFeed("Nulla\n".to_string(), 6)
 +        );
 +        assert_eq!(
 +            break_string(25, true, "", &graphemes[..]),
 +            SnippetState::EndWithLineFeed("Nulla\n".to_string(), 6)
 +        );
 +
 +        let mut config: Config = Default::default();
 +        config.set().max_width(27);
 +        let fmt = StringFormat::new(Shape::legacy(25, Indent::empty()), &config);
 +        let rewritten_string = rewrite_string(string, &fmt, 27);
 +        assert_eq!(
 +            rewritten_string,
 +            Some("\"Nulla\nconsequat erat at massa. \\\n Vivamus id mi.\"".to_string())
 +        );
 +    }
 +
 +    #[test]
 +    fn last_line_fit_with_trailing_whitespaces() {
 +        let string = "Vivamus id mi.  ";
 +        let config: Config = Default::default();
 +        let mut fmt = StringFormat::new(Shape::legacy(25, Indent::empty()), &config);
 +
 +        fmt.trim_end = true;
 +        let rewritten_string = rewrite_string(string, &fmt, 25);
 +        assert_eq!(rewritten_string, Some("\"Vivamus id mi.\"".to_string()));
 +
 +        fmt.trim_end = false; // default value of trim_end
 +        let rewritten_string = rewrite_string(string, &fmt, 25);
 +        assert_eq!(rewritten_string, Some("\"Vivamus id mi.  \"".to_string()));
 +    }
 +
 +    #[test]
 +    fn last_line_fit_with_newline() {
 +        let string = "Vivamus id mi.\nVivamus id mi.";
 +        let config: Config = Default::default();
 +        let fmt = StringFormat {
 +            opener: "",
 +            closer: "",
 +            line_start: "// ",
 +            line_end: "",
 +            shape: Shape::legacy(100, Indent::from_width(&config, 4)),
 +            trim_end: true,
 +            config: &config,
 +        };
 +
 +        let rewritten_string = rewrite_string(string, &fmt, 100);
 +        assert_eq!(
 +            rewritten_string,
 +            Some("Vivamus id mi.\n    // Vivamus id mi.".to_string())
 +        );
 +    }
 +
 +    #[test]
 +    fn overflow_in_non_string_content() {
 +        let comment = "Aenean metus.\nVestibulum ac lacus. Vivamus porttitor";
 +        let config: Config = Default::default();
 +        let fmt = StringFormat {
 +            opener: "",
 +            closer: "",
 +            line_start: "// ",
 +            line_end: "",
 +            shape: Shape::legacy(30, Indent::from_width(&config, 8)),
 +            trim_end: true,
 +            config: &config,
 +        };
 +
 +        assert_eq!(
 +            rewrite_string(comment, &fmt, 30),
 +            Some(
 +                "Aenean metus.\n        // Vestibulum ac lacus. Vivamus\n        // porttitor"
 +                    .to_string()
 +            )
 +        );
 +    }
 +
 +    #[test]
 +    fn overflow_in_non_string_content_with_line_end() {
 +        let comment = "Aenean metus.\nVestibulum ac lacus. Vivamus porttitor";
 +        let config: Config = Default::default();
 +        let fmt = StringFormat {
 +            opener: "",
 +            closer: "",
 +            line_start: "// ",
 +            line_end: "@",
 +            shape: Shape::legacy(30, Indent::from_width(&config, 8)),
 +            trim_end: true,
 +            config: &config,
 +        };
 +
 +        assert_eq!(
 +            rewrite_string(comment, &fmt, 30),
 +            Some(
 +                "Aenean metus.\n        // Vestibulum ac lacus. Vivamus@\n        // porttitor"
 +                    .to_string()
 +            )
 +        );
 +    }
 +
 +    #[test]
 +    fn blank_line_with_non_empty_line_start() {
 +        let config: Config = Default::default();
 +        let mut fmt = StringFormat {
 +            opener: "",
 +            closer: "",
 +            line_start: "// ",
 +            line_end: "",
 +            shape: Shape::legacy(30, Indent::from_width(&config, 4)),
 +            trim_end: true,
 +            config: &config,
 +        };
 +
 +        let comment = "Aenean metus. Vestibulum\n\nac lacus. Vivamus porttitor";
 +        assert_eq!(
 +            rewrite_string(comment, &fmt, 30),
 +            Some(
 +                "Aenean metus. Vestibulum\n    //\n    // ac lacus. Vivamus porttitor".to_string()
 +            )
 +        );
 +
 +        fmt.shape = Shape::legacy(15, Indent::from_width(&config, 4));
 +        let comment = "Aenean\n\nmetus. Vestibulum ac lacus. Vivamus porttitor";
 +        assert_eq!(
 +            rewrite_string(comment, &fmt, 15),
 +            Some(
 +                r#"Aenean
 +    //
 +    // metus. Vestibulum
 +    // ac lacus. Vivamus
 +    // porttitor"#
 +                    .to_string()
 +            )
 +        );
 +    }
 +
 +    #[test]
 +    fn retain_blank_lines() {
 +        let config: Config = Default::default();
 +        let fmt = StringFormat {
 +            opener: "",
 +            closer: "",
 +            line_start: "// ",
 +            line_end: "",
 +            shape: Shape::legacy(20, Indent::from_width(&config, 4)),
 +            trim_end: true,
 +            config: &config,
 +        };
 +
 +        let comment = "Aenean\n\nmetus. Vestibulum ac lacus.\n\n";
 +        assert_eq!(
 +            rewrite_string(comment, &fmt, 20),
 +            Some(
 +                "Aenean\n    //\n    // metus. Vestibulum ac\n    // lacus.\n    //\n".to_string()
 +            )
 +        );
 +
 +        let comment = "Aenean\n\nmetus. Vestibulum ac lacus.\n";
 +        assert_eq!(
 +            rewrite_string(comment, &fmt, 20),
 +            Some("Aenean\n    //\n    // metus. Vestibulum ac\n    // lacus.\n".to_string())
 +        );
 +
 +        let comment = "Aenean\n        \nmetus. Vestibulum ac lacus.";
 +        assert_eq!(
 +            rewrite_string(comment, &fmt, 20),
 +            Some("Aenean\n    //\n    // metus. Vestibulum ac\n    // lacus.".to_string())
 +        );
 +    }
 +
 +    #[test]
 +    fn boundary_on_edge() {
 +        let config: Config = Default::default();
 +        let mut fmt = StringFormat {
 +            opener: "",
 +            closer: "",
 +            line_start: "// ",
 +            line_end: "",
 +            shape: Shape::legacy(13, Indent::from_width(&config, 4)),
 +            trim_end: true,
 +            config: &config,
 +        };
 +
 +        let comment = "Aenean metus. Vestibulum ac lacus.";
 +        assert_eq!(
 +            rewrite_string(comment, &fmt, 13),
 +            Some("Aenean metus.\n    // Vestibulum ac\n    // lacus.".to_string())
 +        );
 +
 +        fmt.trim_end = false;
 +        let comment = "Vestibulum ac lacus.";
 +        assert_eq!(
 +            rewrite_string(comment, &fmt, 13),
 +            Some("Vestibulum \n    // ac lacus.".to_string())
 +        );
 +
 +        fmt.trim_end = true;
 +        fmt.line_end = "\\";
 +        let comment = "Vestibulum ac lacus.";
 +        assert_eq!(
 +            rewrite_string(comment, &fmt, 13),
 +            Some("Vestibulum\\\n    // ac lacus.".to_string())
 +        );
 +    }
 +
 +    #[test]
 +    fn detect_urls() {
 +        let string = "aaa http://example.org something";
 +        let graphemes = UnicodeSegmentation::graphemes(&*string, false).collect::<Vec<&str>>();
 +        assert_eq!(detect_url(&graphemes, 8), Some(21));
 +
 +        let string = "https://example.org something";
 +        let graphemes = UnicodeSegmentation::graphemes(&*string, false).collect::<Vec<&str>>();
 +        assert_eq!(detect_url(&graphemes, 0), Some(18));
 +
 +        let string = "aaa ftp://example.org something";
 +        let graphemes = UnicodeSegmentation::graphemes(&*string, false).collect::<Vec<&str>>();
 +        assert_eq!(detect_url(&graphemes, 8), Some(20));
 +
 +        let string = "aaa file://example.org something";
 +        let graphemes = UnicodeSegmentation::graphemes(&*string, false).collect::<Vec<&str>>();
 +        assert_eq!(detect_url(&graphemes, 8), Some(21));
 +
 +        let string = "aaa http not an url";
 +        let graphemes = UnicodeSegmentation::graphemes(&*string, false).collect::<Vec<&str>>();
 +        assert_eq!(detect_url(&graphemes, 6), None);
 +
 +        let string = "aaa file://example.org";
 +        let graphemes = UnicodeSegmentation::graphemes(&*string, false).collect::<Vec<&str>>();
 +        assert_eq!(detect_url(&graphemes, 8), Some(21));
 +    }
 +}
index 92949ab576a6bffd4c0d546bdab19db91ad40ed3,0000000000000000000000000000000000000000..c8fda7c8556db9ea4f42704db5ad34dbb24d48d1
mode 100644,000000..100644
--- /dev/null
@@@ -1,292 -1,0 +1,322 @@@
 +use std::collections::{HashMap, HashSet};
 +use std::fs;
 +use std::io::{BufRead, BufReader, Write};
 +use std::iter::Enumerate;
 +use std::path::{Path, PathBuf};
 +
 +use super::{print_mismatches, write_message, DIFF_CONTEXT_SIZE};
 +use crate::config::{Config, EmitMode, Verbosity};
 +use crate::rustfmt_diff::{make_diff, Mismatch};
 +use crate::{Input, Session};
 +
 +const CONFIGURATIONS_FILE_NAME: &str = "Configurations.md";
 +
 +// This enum is used to represent one of three text features in Configurations.md: a block of code
 +// with its starting line number, the name of a rustfmt configuration option, or the value of a
 +// rustfmt configuration option.
 +enum ConfigurationSection {
 +    CodeBlock((String, u32)), // (String: block of code, u32: line number of code block start)
 +    ConfigName(String),
 +    ConfigValue(String),
 +}
 +
 +impl ConfigurationSection {
 +    fn get_section<I: Iterator<Item = String>>(
 +        file: &mut Enumerate<I>,
 +    ) -> Option<ConfigurationSection> {
 +        lazy_static! {
 +            static ref CONFIG_NAME_REGEX: regex::Regex =
 +                regex::Regex::new(r"^## `([^`]+)`").expect("failed creating configuration pattern");
 +            static ref CONFIG_VALUE_REGEX: regex::Regex =
 +                regex::Regex::new(r#"^#### `"?([^`"]+)"?`"#)
 +                    .expect("failed creating configuration value pattern");
 +        }
 +
 +        loop {
 +            match file.next() {
 +                Some((i, line)) => {
 +                    if line.starts_with("```rust") {
 +                        // Get the lines of the code block.
 +                        let lines: Vec<String> = file
 +                            .map(|(_i, l)| l)
 +                            .take_while(|l| !l.starts_with("```"))
 +                            .collect();
 +                        let block = format!("{}\n", lines.join("\n"));
 +
 +                        // +1 to translate to one-based indexing
 +                        // +1 to get to first line of code (line after "```")
 +                        let start_line = (i + 2) as u32;
 +
 +                        return Some(ConfigurationSection::CodeBlock((block, start_line)));
 +                    } else if let Some(c) = CONFIG_NAME_REGEX.captures(&line) {
 +                        return Some(ConfigurationSection::ConfigName(String::from(&c[1])));
 +                    } else if let Some(c) = CONFIG_VALUE_REGEX.captures(&line) {
 +                        return Some(ConfigurationSection::ConfigValue(String::from(&c[1])));
 +                    }
 +                }
 +                None => return None, // reached the end of the file
 +            }
 +        }
 +    }
 +}
 +
 +// This struct stores the information about code blocks in the configurations
 +// file, formats the code blocks, and prints formatting errors.
 +struct ConfigCodeBlock {
 +    config_name: Option<String>,
 +    config_value: Option<String>,
 +    code_block: Option<String>,
 +    code_block_start: Option<u32>,
 +}
 +
 +impl ConfigCodeBlock {
 +    fn new() -> ConfigCodeBlock {
 +        ConfigCodeBlock {
 +            config_name: None,
 +            config_value: None,
 +            code_block: None,
 +            code_block_start: None,
 +        }
 +    }
 +
 +    fn set_config_name(&mut self, name: Option<String>) {
 +        self.config_name = name;
 +        self.config_value = None;
 +    }
 +
 +    fn set_config_value(&mut self, value: Option<String>) {
 +        self.config_value = value;
 +    }
 +
 +    fn set_code_block(&mut self, code_block: String, code_block_start: u32) {
 +        self.code_block = Some(code_block);
 +        self.code_block_start = Some(code_block_start);
 +    }
 +
 +    fn get_block_config(&self) -> Config {
 +        let mut config = Config::default();
 +        config.set().verbose(Verbosity::Quiet);
 +        if self.config_name.is_some() && self.config_value.is_some() {
 +            config.override_value(
 +                self.config_name.as_ref().unwrap(),
 +                self.config_value.as_ref().unwrap(),
 +            );
 +        }
 +        config
 +    }
 +
 +    fn code_block_valid(&self) -> bool {
 +        // We never expect to not have a code block.
 +        assert!(self.code_block.is_some() && self.code_block_start.is_some());
 +
 +        // See if code block begins with #![rustfmt::skip].
 +        let fmt_skip = self.fmt_skip();
 +
 +        if self.config_name.is_none() && !fmt_skip {
 +            write_message(&format!(
 +                "No configuration name for {}:{}",
 +                CONFIGURATIONS_FILE_NAME,
 +                self.code_block_start.unwrap()
 +            ));
 +            return false;
 +        }
 +        if self.config_value.is_none() && !fmt_skip {
 +            write_message(&format!(
 +                "No configuration value for {}:{}",
 +                CONFIGURATIONS_FILE_NAME,
 +                self.code_block_start.unwrap()
 +            ));
 +            return false;
 +        }
 +        true
 +    }
 +
 +    /// True if the code block starts with #![rustfmt::skip]
 +    fn fmt_skip(&self) -> bool {
 +        self.code_block
 +            .as_ref()
 +            .unwrap()
 +            .lines()
 +            .nth(0)
 +            .unwrap_or("")
 +            == "#![rustfmt::skip]"
 +    }
 +
 +    fn has_parsing_errors<T: Write>(&self, session: &Session<'_, T>) -> bool {
 +        if session.has_parsing_errors() {
 +            write_message(&format!(
 +                "\u{261d}\u{1f3fd} Cannot format {}:{}",
 +                CONFIGURATIONS_FILE_NAME,
 +                self.code_block_start.unwrap()
 +            ));
 +            return true;
 +        }
 +
 +        false
 +    }
 +
 +    fn print_diff(&self, compare: Vec<Mismatch>) {
 +        let mut mismatches = HashMap::new();
 +        mismatches.insert(PathBuf::from(CONFIGURATIONS_FILE_NAME), compare);
 +        print_mismatches(mismatches, |line_num| {
 +            format!(
 +                "\nMismatch at {}:{}:",
 +                CONFIGURATIONS_FILE_NAME,
 +                line_num + self.code_block_start.unwrap() - 1
 +            )
 +        });
 +    }
 +
 +    fn formatted_has_diff(&self, text: &str) -> bool {
 +        let compare = make_diff(self.code_block.as_ref().unwrap(), text, DIFF_CONTEXT_SIZE);
 +        if !compare.is_empty() {
 +            self.print_diff(compare);
 +            return true;
 +        }
 +
 +        false
 +    }
 +
 +    // Return a bool indicating if formatting this code block is an idempotent
 +    // operation. This function also triggers printing any formatting failure
 +    // messages.
 +    fn formatted_is_idempotent(&self) -> bool {
 +        // Verify that we have all of the expected information.
 +        if !self.code_block_valid() {
 +            return false;
 +        }
 +
 +        let input = Input::Text(self.code_block.as_ref().unwrap().to_owned());
 +        let mut config = self.get_block_config();
 +        config.set().emit_mode(EmitMode::Stdout);
 +        let mut buf: Vec<u8> = vec![];
 +
 +        {
 +            let mut session = Session::new(config, Some(&mut buf));
 +            session.format(input).unwrap();
 +            if self.has_parsing_errors(&session) {
 +                return false;
 +            }
 +        }
 +
 +        !self.formatted_has_diff(&String::from_utf8(buf).unwrap())
 +    }
 +
 +    // Extract a code block from the iterator. Behavior:
 +    // - Rust code blocks are identifed by lines beginning with "```rust".
 +    // - One explicit configuration setting is supported per code block.
 +    // - Rust code blocks with no configuration setting are illegal and cause an
 +    //   assertion failure, unless the snippet begins with #![rustfmt::skip].
 +    // - Configuration names in Configurations.md must be in the form of
 +    //   "## `NAME`".
 +    // - Configuration values in Configurations.md must be in the form of
 +    //   "#### `VALUE`".
 +    fn extract<I: Iterator<Item = String>>(
 +        file: &mut Enumerate<I>,
 +        prev: Option<&ConfigCodeBlock>,
 +        hash_set: &mut HashSet<String>,
 +    ) -> Option<ConfigCodeBlock> {
 +        let mut code_block = ConfigCodeBlock::new();
 +        code_block.config_name = prev.and_then(|cb| cb.config_name.clone());
 +
 +        loop {
 +            match ConfigurationSection::get_section(file) {
 +                Some(ConfigurationSection::CodeBlock((block, start_line))) => {
 +                    code_block.set_code_block(block, start_line);
 +                    break;
 +                }
 +                Some(ConfigurationSection::ConfigName(name)) => {
 +                    assert!(
 +                        Config::is_valid_name(&name),
 +                        "an unknown configuration option was found: {}",
 +                        name
 +                    );
 +                    assert!(
 +                        hash_set.remove(&name),
 +                        "multiple configuration guides found for option {}",
 +                        name
 +                    );
 +                    code_block.set_config_name(Some(name));
 +                }
 +                Some(ConfigurationSection::ConfigValue(value)) => {
 +                    code_block.set_config_value(Some(value));
 +                }
 +                None => return None, // end of file was reached
 +            }
 +        }
 +
 +        Some(code_block)
 +    }
 +}
 +
 +#[test]
 +fn configuration_snippet_tests() {
 +    super::init_log();
 +    let blocks = get_code_blocks();
 +    let failures = blocks
 +        .iter()
 +        .filter(|block| !block.fmt_skip())
 +        .map(ConfigCodeBlock::formatted_is_idempotent)
 +        .fold(0, |acc, r| acc + (!r as u32));
 +
 +    // Display results.
 +    println!("Ran {} configurations tests.", blocks.len());
 +    assert_eq!(failures, 0, "{} configurations tests failed", failures);
 +}
 +
 +// Read Configurations.md and build a `Vec` of `ConfigCodeBlock` structs with one
 +// entry for each Rust code block found.
 +fn get_code_blocks() -> Vec<ConfigCodeBlock> {
 +    let mut file_iter = BufReader::new(
 +        fs::File::open(Path::new(CONFIGURATIONS_FILE_NAME))
 +            .unwrap_or_else(|_| panic!("couldn't read file {}", CONFIGURATIONS_FILE_NAME)),
 +    )
 +    .lines()
 +    .map(Result::unwrap)
 +    .enumerate();
 +    let mut code_blocks: Vec<ConfigCodeBlock> = Vec::new();
 +    let mut hash_set = Config::hash_set();
 +
 +    while let Some(cb) = ConfigCodeBlock::extract(&mut file_iter, code_blocks.last(), &mut hash_set)
 +    {
 +        code_blocks.push(cb);
 +    }
 +
 +    for name in hash_set {
 +        if !Config::is_hidden_option(&name) {
 +            panic!("{} does not have a configuration guide", name);
 +        }
 +    }
 +
 +    code_blocks
 +}
++
++#[test]
++fn check_unstable_option_tracking_issue_numbers() {
++    // Ensure that tracking issue links point to the correct issue number
++    let tracking_issue =
++        regex::Regex::new(r"\(tracking issue: \[#(?P<number>\d+)\]\((?P<link>\S+)\)\)")
++            .expect("failed creating configuration pattern");
++
++    let lines = BufReader::new(
++        fs::File::open(Path::new(CONFIGURATIONS_FILE_NAME))
++            .unwrap_or_else(|_| panic!("couldn't read file {}", CONFIGURATIONS_FILE_NAME)),
++    )
++    .lines()
++    .map(Result::unwrap)
++    .enumerate();
++
++    for (idx, line) in lines {
++        if let Some(capture) = tracking_issue.captures(&line) {
++            let number = capture.name("number").unwrap().as_str();
++            let link = capture.name("link").unwrap().as_str();
++            assert!(
++                link.ends_with(number),
++                "{} on line {} does not point to issue #{}",
++                link,
++                idx + 1,
++                number,
++            );
++        }
++    }
++}
index c50d18644b0910e3d3e968f6db904e3626da27fa,0000000000000000000000000000000000000000..ab966d4a36075168684fea3103a6271694c33ca7
mode 100644,000000..100644
--- /dev/null
@@@ -1,916 -1,0 +1,1038 @@@
-     assert_eq!(buf, "stdin:\n\nfn main() {}\n".as_bytes());
 +use std::collections::HashMap;
 +use std::env;
 +use std::fs;
 +use std::io::{self, BufRead, BufReader, Read, Write};
 +use std::iter::Peekable;
 +use std::mem;
 +use std::path::{Path, PathBuf};
 +use std::process::{Command, Stdio};
 +use std::str::Chars;
 +use std::thread;
 +
 +use crate::config::{Color, Config, EmitMode, FileName, NewlineStyle, ReportTactic};
 +use crate::formatting::{ReportedErrors, SourceFile};
 +use crate::rustfmt_diff::{make_diff, print_diff, DiffLine, Mismatch, ModifiedChunk, OutputWriter};
 +use crate::source_file;
 +use crate::{is_nightly_channel, FormatReport, FormatReportFormatterBuilder, Input, Session};
 +
 +use rustfmt_config_proc_macro::nightly_only_test;
 +
 +mod configuration_snippet;
 +mod mod_resolver;
 +mod parser;
 +
 +const DIFF_CONTEXT_SIZE: usize = 3;
 +
 +// A list of files on which we want to skip testing.
 +const SKIP_FILE_WHITE_LIST: &[&str] = &[
 +    // We want to make sure that the `skip_children` is correctly working,
 +    // so we do not want to test this file directly.
 +    "configs/skip_children/foo/mod.rs",
 +    "issue-3434/no_entry.rs",
 +    "issue-3665/sub_mod.rs",
 +    // Testing for issue-3779
 +    "issue-3779/ice.rs",
 +    // These files and directory are a part of modules defined inside `cfg_if!`.
 +    "cfg_if/mod.rs",
 +    "cfg_if/detect",
 +    "issue-3253/foo.rs",
 +    "issue-3253/bar.rs",
 +    "issue-3253/paths",
 +    // These files and directory are a part of modules defined inside `cfg_attr(..)`.
 +    "cfg_mod/dir",
 +    "cfg_mod/bar.rs",
 +    "cfg_mod/foo.rs",
 +    "cfg_mod/wasm32.rs",
 +    "skip/foo.rs",
 +];
 +
 +fn init_log() {
 +    let _ = env_logger::builder().is_test(true).try_init();
 +}
 +
 +struct TestSetting {
 +    /// The size of the stack of the thread that run tests.
 +    stack_size: usize,
 +}
 +
 +impl Default for TestSetting {
 +    fn default() -> Self {
 +        TestSetting {
 +            stack_size: 8_388_608, // 8MB
 +        }
 +    }
 +}
 +
 +fn run_test_with<F>(test_setting: &TestSetting, f: F)
 +where
 +    F: FnOnce(),
 +    F: Send + 'static,
 +{
 +    thread::Builder::new()
 +        .stack_size(test_setting.stack_size)
 +        .spawn(f)
 +        .expect("Failed to create a test thread")
 +        .join()
 +        .expect("Failed to join a test thread")
 +}
 +
 +fn is_subpath<P>(path: &Path, subpath: &P) -> bool
 +where
 +    P: AsRef<Path>,
 +{
 +    (0..path.components().count())
 +        .map(|i| {
 +            path.components()
 +                .skip(i)
 +                .take(subpath.as_ref().components().count())
 +        })
 +        .any(|c| c.zip(subpath.as_ref().components()).all(|(a, b)| a == b))
 +}
 +
 +fn is_file_skip(path: &Path) -> bool {
 +    SKIP_FILE_WHITE_LIST
 +        .iter()
 +        .any(|file_path| is_subpath(path, file_path))
 +}
 +
 +// Returns a `Vec` containing `PathBuf`s of files with an  `rs` extension in the
 +// given path. The `recursive` argument controls if files from subdirectories
 +// are also returned.
 +fn get_test_files(path: &Path, recursive: bool) -> Vec<PathBuf> {
 +    let mut files = vec![];
 +    if path.is_dir() {
 +        for entry in fs::read_dir(path).expect(&format!(
 +            "couldn't read directory {}",
 +            path.to_str().unwrap()
 +        )) {
 +            let entry = entry.expect("couldn't get `DirEntry`");
 +            let path = entry.path();
 +            if path.is_dir() && recursive {
 +                files.append(&mut get_test_files(&path, recursive));
 +            } else if path.extension().map_or(false, |f| f == "rs") && !is_file_skip(&path) {
 +                files.push(path);
 +            }
 +        }
 +    }
 +    files
 +}
 +
 +fn verify_config_used(path: &Path, config_name: &str) {
 +    for entry in fs::read_dir(path).expect(&format!(
 +        "couldn't read {} directory",
 +        path.to_str().unwrap()
 +    )) {
 +        let entry = entry.expect("couldn't get directory entry");
 +        let path = entry.path();
 +        if path.extension().map_or(false, |f| f == "rs") {
 +            // check if "// rustfmt-<config_name>:" appears in the file.
 +            let filebuf = BufReader::new(
 +                fs::File::open(&path)
 +                    .unwrap_or_else(|_| panic!("couldn't read file {}", path.display())),
 +            );
 +            assert!(
 +                filebuf
 +                    .lines()
 +                    .map(Result::unwrap)
 +                    .take_while(|l| l.starts_with("//"))
 +                    .any(|l| l.starts_with(&format!("// rustfmt-{}", config_name))),
 +                "config option file {} does not contain expected config name",
 +                path.display()
 +            );
 +        }
 +    }
 +}
 +
 +#[test]
 +fn verify_config_test_names() {
 +    init_log();
 +    for path in &[
 +        Path::new("tests/source/configs"),
 +        Path::new("tests/target/configs"),
 +    ] {
 +        for entry in fs::read_dir(path).expect("couldn't read configs directory") {
 +            let entry = entry.expect("couldn't get directory entry");
 +            let path = entry.path();
 +            if path.is_dir() {
 +                let config_name = path.file_name().unwrap().to_str().unwrap();
 +
 +                // Make sure that config name is used in the files in the directory.
 +                verify_config_used(&path, config_name);
 +            }
 +        }
 +    }
 +}
 +
 +// This writes to the terminal using the same approach (via `term::stdout` or
 +// `println!`) that is used by `rustfmt::rustfmt_diff::print_diff`. Writing
 +// using only one or the other will cause the output order to differ when
 +// `print_diff` selects the approach not used.
 +fn write_message(msg: &str) {
 +    let mut writer = OutputWriter::new(Color::Auto);
 +    writer.writeln(msg, None);
 +}
 +
 +// Integration tests. The files in `tests/source` are formatted and compared
 +// to their equivalent in `tests/target`. The target file and config can be
 +// overridden by annotations in the source file. The input and output must match
 +// exactly.
 +#[test]
 +fn system_tests() {
 +    init_log();
 +    run_test_with(&TestSetting::default(), || {
 +        // Get all files in the tests/source directory.
 +        let files = get_test_files(Path::new("tests/source"), true);
 +        let (_reports, count, fails) = check_files(files, &None);
 +
 +        // Display results.
 +        println!("Ran {} system tests.", count);
 +        assert_eq!(fails, 0, "{} system tests failed", fails);
 +        assert!(
 +            count >= 300,
 +            "Expected a minimum of {} system tests to be executed",
 +            300
 +        )
 +    });
 +}
 +
 +// Do the same for tests/coverage-source directory.
 +// The only difference is the coverage mode.
 +#[test]
 +fn coverage_tests() {
 +    init_log();
 +    let files = get_test_files(Path::new("tests/coverage/source"), true);
 +    let (_reports, count, fails) = check_files(files, &None);
 +
 +    println!("Ran {} tests in coverage mode.", count);
 +    assert_eq!(fails, 0, "{} tests failed", fails);
 +}
 +
 +#[test]
 +fn checkstyle_test() {
 +    init_log();
 +    let filename = "tests/writemode/source/fn-single-line.rs";
 +    let expected_filename = "tests/writemode/target/checkstyle.xml";
 +    assert_output(Path::new(filename), Path::new(expected_filename));
 +}
 +
 +#[test]
 +fn json_test() {
 +    init_log();
 +    let filename = "tests/writemode/source/json.rs";
 +    let expected_filename = "tests/writemode/target/output.json";
 +    assert_output(Path::new(filename), Path::new(expected_filename));
 +}
 +
 +#[test]
 +fn modified_test() {
 +    init_log();
 +    use std::io::BufRead;
 +
 +    // Test "modified" output
 +    let filename = "tests/writemode/source/modified.rs";
 +    let mut data = Vec::new();
 +    let mut config = Config::default();
 +    config
 +        .set()
 +        .emit_mode(crate::config::EmitMode::ModifiedLines);
 +
 +    {
 +        let mut session = Session::new(config, Some(&mut data));
 +        session.format(Input::File(filename.into())).unwrap();
 +    }
 +
 +    let mut lines = data.lines();
 +    let mut chunks = Vec::new();
 +    while let Some(Ok(header)) = lines.next() {
 +        // Parse the header line
 +        let values: Vec<_> = header
 +            .split(' ')
 +            .map(|s| s.parse::<u32>().unwrap())
 +            .collect();
 +        assert_eq!(values.len(), 3);
 +        let line_number_orig = values[0];
 +        let lines_removed = values[1];
 +        let num_added = values[2];
 +        let mut added_lines = Vec::new();
 +        for _ in 0..num_added {
 +            added_lines.push(lines.next().unwrap().unwrap());
 +        }
 +        chunks.push(ModifiedChunk {
 +            line_number_orig,
 +            lines_removed,
 +            lines: added_lines,
 +        });
 +    }
 +
 +    assert_eq!(
 +        chunks,
 +        vec![
 +            ModifiedChunk {
 +                line_number_orig: 4,
 +                lines_removed: 4,
 +                lines: vec!["fn blah() {}".into()],
 +            },
 +            ModifiedChunk {
 +                line_number_orig: 9,
 +                lines_removed: 6,
 +                lines: vec!["#[cfg(a, b)]".into(), "fn main() {}".into()],
 +            },
 +        ],
 +    );
 +}
 +
 +// Helper function for comparing the results of rustfmt
 +// to a known output file generated by one of the write modes.
 +fn assert_output(source: &Path, expected_filename: &Path) {
 +    let config = read_config(source);
 +    let (_, source_file, _) = format_file(source, config.clone());
 +
 +    // Populate output by writing to a vec.
 +    let mut out = vec![];
 +    let _ = source_file::write_all_files(&source_file, &mut out, &config);
 +    let output = String::from_utf8(out).unwrap();
 +
 +    let mut expected_file = fs::File::open(&expected_filename).expect("couldn't open target");
 +    let mut expected_text = String::new();
 +    expected_file
 +        .read_to_string(&mut expected_text)
 +        .expect("Failed reading target");
 +
 +    let compare = make_diff(&expected_text, &output, DIFF_CONTEXT_SIZE);
 +    if !compare.is_empty() {
 +        let mut failures = HashMap::new();
 +        failures.insert(source.to_owned(), compare);
 +        print_mismatches_default_message(failures);
 +        panic!("Text does not match expected output");
 +    }
 +}
 +
++// Helper function for comparing the results of rustfmt
++// to a known output generated by one of the write modes.
++fn assert_stdin_output(
++    source: &Path,
++    expected_filename: &Path,
++    emit_mode: EmitMode,
++    has_diff: bool,
++) {
++    let mut config = Config::default();
++    config.set().newline_style(NewlineStyle::Unix);
++    config.set().emit_mode(emit_mode);
++
++    let mut source_file = fs::File::open(&source).expect("couldn't open source");
++    let mut source_text = String::new();
++    source_file
++        .read_to_string(&mut source_text)
++        .expect("Failed reading target");
++    let input = Input::Text(source_text);
++
++    // Populate output by writing to a vec.
++    let mut buf: Vec<u8> = vec![];
++    {
++        let mut session = Session::new(config, Some(&mut buf));
++        session.format(input).unwrap();
++        let errors = ReportedErrors {
++            has_diff: has_diff,
++            ..Default::default()
++        };
++        assert_eq!(session.errors, errors);
++    }
++
++    let mut expected_file = fs::File::open(&expected_filename).expect("couldn't open target");
++    let mut expected_text = String::new();
++    expected_file
++        .read_to_string(&mut expected_text)
++        .expect("Failed reading target");
++
++    let output = String::from_utf8(buf).unwrap();
++    let compare = make_diff(&expected_text, &output, DIFF_CONTEXT_SIZE);
++    if !compare.is_empty() {
++        let mut failures = HashMap::new();
++        failures.insert(source.to_owned(), compare);
++        print_mismatches_default_message(failures);
++        panic!("Text does not match expected output");
++    }
++}
 +// Idempotence tests. Files in tests/target are checked to be unaltered by
 +// rustfmt.
 +#[nightly_only_test]
 +#[test]
 +fn idempotence_tests() {
 +    init_log();
 +    run_test_with(&TestSetting::default(), || {
 +        // Get all files in the tests/target directory.
 +        let files = get_test_files(Path::new("tests/target"), true);
 +        let (_reports, count, fails) = check_files(files, &None);
 +
 +        // Display results.
 +        println!("Ran {} idempotent tests.", count);
 +        assert_eq!(fails, 0, "{} idempotent tests failed", fails);
 +        assert!(
 +            count >= 400,
 +            "Expected a minimum of {} idempotent tests to be executed",
 +            400
 +        )
 +    });
 +}
 +
 +// Run rustfmt on itself. This operation must be idempotent. We also check that
 +// no warnings are emitted.
 +// Issue-3443: these tests require nightly
 +#[nightly_only_test]
 +#[test]
 +fn self_tests() {
 +    init_log();
 +    let mut files = get_test_files(Path::new("tests"), false);
 +    let bin_directories = vec!["cargo-fmt", "git-rustfmt", "bin", "format-diff"];
 +    for dir in bin_directories {
 +        let mut path = PathBuf::from("src");
 +        path.push(dir);
 +        path.push("main.rs");
 +        files.push(path);
 +    }
 +    files.push(PathBuf::from("src/lib.rs"));
 +
 +    let (reports, count, fails) = check_files(files, &Some(PathBuf::from("rustfmt.toml")));
 +    let mut warnings = 0;
 +
 +    // Display results.
 +    println!("Ran {} self tests.", count);
 +    assert_eq!(fails, 0, "{} self tests failed", fails);
 +
 +    for format_report in reports {
 +        println!(
 +            "{}",
 +            FormatReportFormatterBuilder::new(&format_report).build()
 +        );
 +        warnings += format_report.warning_count();
 +    }
 +
 +    assert_eq!(
 +        warnings, 0,
 +        "Rustfmt's code generated {} warnings",
 +        warnings
 +    );
 +}
 +
 +#[test]
 +fn format_files_find_new_files_via_cfg_if() {
 +    init_log();
 +    run_test_with(&TestSetting::default(), || {
 +        // To repro issue-4656, it is necessary that these files are parsed
 +        // as a part of the same session (hence this separate test runner).
 +        let files = vec![
 +            Path::new("tests/source/issue-4656/lib2.rs"),
 +            Path::new("tests/source/issue-4656/lib.rs"),
 +        ];
 +
 +        let config = Config::default();
 +        let mut session = Session::<io::Stdout>::new(config, None);
 +
 +        let mut write_result = HashMap::new();
 +        for file in files {
 +            assert!(file.exists());
 +            let result = session.format(Input::File(file.into())).unwrap();
 +            assert!(!session.has_formatting_errors());
 +            assert!(!result.has_warnings());
 +            let mut source_file = SourceFile::new();
 +            mem::swap(&mut session.source_file, &mut source_file);
 +
 +            for (filename, text) in source_file {
 +                if let FileName::Real(ref filename) = filename {
 +                    write_result.insert(filename.to_owned(), text);
 +                }
 +            }
 +        }
 +        assert_eq!(
 +            3,
 +            write_result.len(),
 +            "Should have uncovered an extra file (format_me_please.rs) via lib.rs"
 +        );
 +        assert!(handle_result(write_result, None).is_ok());
 +    });
 +}
 +
 +#[test]
 +fn stdin_formatting_smoke_test() {
 +    init_log();
 +    let input = Input::Text("fn main () {}".to_owned());
 +    let mut config = Config::default();
 +    config.set().emit_mode(EmitMode::Stdout);
 +    let mut buf: Vec<u8> = vec![];
 +    {
 +        let mut session = Session::new(config, Some(&mut buf));
 +        session.format(input).unwrap();
 +        assert!(session.has_no_errors());
 +    }
 +
 +    #[cfg(not(windows))]
-     assert_eq!(buf, "stdin:\n\nfn main() {}\r\n".as_bytes());
++    assert_eq!(buf, "<stdin>:\n\nfn main() {}\n".as_bytes());
 +    #[cfg(windows)]
-     assert_eq!(buf, "stdin:\n\n//@generated\nfn main() {}\n".as_bytes());
++    assert_eq!(buf, "<stdin>:\n\nfn main() {}\r\n".as_bytes());
 +}
 +
 +#[test]
 +fn stdin_parser_panic_caught() {
 +    init_log();
 +    // See issue #3239.
 +    for text in ["{", "}"].iter().cloned().map(String::from) {
 +        let mut buf = vec![];
 +        let mut session = Session::new(Default::default(), Some(&mut buf));
 +        let _ = session.format(Input::Text(text));
 +
 +        assert!(session.has_parsing_errors());
 +    }
 +}
 +
 +/// Ensures that `EmitMode::ModifiedLines` works with input from `stdin`. Useful
 +/// when embedding Rustfmt (e.g. inside RLS).
 +#[test]
 +fn stdin_works_with_modified_lines() {
 +    init_log();
 +    let input = "\nfn\n some( )\n{\n}\nfn main () {}\n";
 +    let output = "1 6 2\nfn some() {}\nfn main() {}\n";
 +
 +    let input = Input::Text(input.to_owned());
 +    let mut config = Config::default();
 +    config.set().newline_style(NewlineStyle::Unix);
 +    config.set().emit_mode(EmitMode::ModifiedLines);
 +    let mut buf: Vec<u8> = vec![];
 +    {
 +        let mut session = Session::new(config, Some(&mut buf));
 +        session.format(input).unwrap();
 +        let errors = ReportedErrors {
 +            has_diff: true,
 +            ..Default::default()
 +        };
 +        assert_eq!(session.errors, errors);
 +    }
 +    assert_eq!(buf, output.as_bytes());
 +}
 +
++/// Ensures that `EmitMode::Json` works with input from `stdin`.
++#[test]
++fn stdin_works_with_json() {
++    init_log();
++    assert_stdin_output(
++        Path::new("tests/writemode/source/stdin.rs"),
++        Path::new("tests/writemode/target/stdin.json"),
++        EmitMode::Json,
++        true,
++    );
++}
++
++/// Ensures that `EmitMode::Checkstyle` works with input from `stdin`.
++#[test]
++fn stdin_works_with_checkstyle() {
++    init_log();
++    assert_stdin_output(
++        Path::new("tests/writemode/source/stdin.rs"),
++        Path::new("tests/writemode/target/stdin.xml"),
++        EmitMode::Checkstyle,
++        false,
++    );
++}
++
 +#[test]
 +fn stdin_disable_all_formatting_test() {
 +    init_log();
 +    let input = String::from("fn main() { println!(\"This should not be formatted.\"); }");
 +    let mut child = Command::new(rustfmt().to_str().unwrap())
 +        .stdin(Stdio::piped())
 +        .stdout(Stdio::piped())
 +        .arg("--config-path=./tests/config/disable_all_formatting.toml")
 +        .spawn()
 +        .expect("failed to execute child");
 +
 +    {
 +        let stdin = child.stdin.as_mut().expect("failed to get stdin");
 +        stdin
 +            .write_all(input.as_bytes())
 +            .expect("failed to write stdin");
 +    }
 +
 +    let output = child.wait_with_output().expect("failed to wait on child");
 +    assert!(output.status.success());
 +    assert!(output.stderr.is_empty());
 +    assert_eq!(input, String::from_utf8(output.stdout).unwrap());
 +}
 +
 +#[test]
 +fn stdin_generated_files_issue_5172() {
 +    init_log();
 +    let input = Input::Text("//@generated\nfn   main() {}".to_owned());
 +    let mut config = Config::default();
 +    config.set().emit_mode(EmitMode::Stdout);
 +    config.set().format_generated_files(false);
 +    config.set().newline_style(NewlineStyle::Unix);
 +    let mut buf: Vec<u8> = vec![];
 +    {
 +        let mut session = Session::new(config, Some(&mut buf));
 +        session.format(input).unwrap();
 +        assert!(session.has_no_errors());
 +    }
 +    // N.B. this should be changed once `format_generated_files` is supported with stdin
++    assert_eq!(
++        String::from_utf8(buf).unwrap(),
++        "<stdin>:\n\n//@generated\nfn main() {}\n",
++    );
 +}
 +
 +#[test]
 +fn format_lines_errors_are_reported() {
 +    init_log();
 +    let long_identifier = String::from_utf8(vec![b'a'; 239]).unwrap();
 +    let input = Input::Text(format!("fn {}() {{}}", long_identifier));
 +    let mut config = Config::default();
 +    config.set().error_on_line_overflow(true);
 +    let mut session = Session::<io::Stdout>::new(config, None);
 +    session.format(input).unwrap();
 +    assert!(session.has_formatting_errors());
 +}
 +
 +#[test]
 +fn format_lines_errors_are_reported_with_tabs() {
 +    init_log();
 +    let long_identifier = String::from_utf8(vec![b'a'; 97]).unwrap();
 +    let input = Input::Text(format!("fn a() {{\n\t{}\n}}", long_identifier));
 +    let mut config = Config::default();
 +    config.set().error_on_line_overflow(true);
 +    config.set().hard_tabs(true);
 +    let mut session = Session::<io::Stdout>::new(config, None);
 +    session.format(input).unwrap();
 +    assert!(session.has_formatting_errors());
 +}
 +
 +// For each file, run rustfmt and collect the output.
 +// Returns the number of files checked and the number of failures.
 +fn check_files(files: Vec<PathBuf>, opt_config: &Option<PathBuf>) -> (Vec<FormatReport>, u32, u32) {
 +    let mut count = 0;
 +    let mut fails = 0;
 +    let mut reports = vec![];
 +
 +    for file_name in files {
 +        let sig_comments = read_significant_comments(&file_name);
 +        if sig_comments.contains_key("unstable") && !is_nightly_channel!() {
 +            debug!(
 +                "Skipping '{}' because it requires unstable \
 +                 features which are only available on nightly...",
 +                file_name.display()
 +            );
 +            continue;
 +        }
 +
 +        debug!("Testing '{}'...", file_name.display());
 +
 +        match idempotent_check(&file_name, opt_config) {
 +            Ok(ref report) if report.has_warnings() => {
 +                print!("{}", FormatReportFormatterBuilder::new(report).build());
 +                fails += 1;
 +            }
 +            Ok(report) => reports.push(report),
 +            Err(err) => {
 +                if let IdempotentCheckError::Mismatch(msg) = err {
 +                    print_mismatches_default_message(msg);
 +                }
 +                fails += 1;
 +            }
 +        }
 +
 +        count += 1;
 +    }
 +
 +    (reports, count, fails)
 +}
 +
 +fn print_mismatches_default_message(result: HashMap<PathBuf, Vec<Mismatch>>) {
 +    for (file_name, diff) in result {
 +        let mismatch_msg_formatter =
 +            |line_num| format!("\nMismatch at {}:{}:", file_name.display(), line_num);
 +        print_diff(diff, &mismatch_msg_formatter, &Default::default());
 +    }
 +
 +    if let Some(mut t) = term::stdout() {
 +        t.reset().unwrap_or(());
 +    }
 +}
 +
 +fn print_mismatches<T: Fn(u32) -> String>(
 +    result: HashMap<PathBuf, Vec<Mismatch>>,
 +    mismatch_msg_formatter: T,
 +) {
 +    for (_file_name, diff) in result {
 +        print_diff(diff, &mismatch_msg_formatter, &Default::default());
 +    }
 +
 +    if let Some(mut t) = term::stdout() {
 +        t.reset().unwrap_or(());
 +    }
 +}
 +
 +fn read_config(filename: &Path) -> Config {
 +    let sig_comments = read_significant_comments(filename);
 +    // Look for a config file. If there is a 'config' property in the significant comments, use
 +    // that. Otherwise, if there are no significant comments at all, look for a config file with
 +    // the same name as the test file.
 +    let mut config = if !sig_comments.is_empty() {
 +        get_config(sig_comments.get("config").map(Path::new))
 +    } else {
 +        get_config(filename.with_extension("toml").file_name().map(Path::new))
 +    };
 +
 +    for (key, val) in &sig_comments {
 +        if key != "target" && key != "config" && key != "unstable" {
 +            config.override_value(key, val);
 +            if config.is_default(key) {
 +                warn!("Default value {} used explicitly for {}", val, key);
 +            }
 +        }
 +    }
 +
 +    // Don't generate warnings for to-do items.
 +    config.set().report_todo(ReportTactic::Never);
 +
 +    config
 +}
 +
 +fn format_file<P: Into<PathBuf>>(filepath: P, config: Config) -> (bool, SourceFile, FormatReport) {
 +    let filepath = filepath.into();
 +    let input = Input::File(filepath);
 +    let mut session = Session::<io::Stdout>::new(config, None);
 +    let result = session.format(input).unwrap();
 +    let parsing_errors = session.has_parsing_errors();
 +    let mut source_file = SourceFile::new();
 +    mem::swap(&mut session.source_file, &mut source_file);
 +    (parsing_errors, source_file, result)
 +}
 +
 +enum IdempotentCheckError {
 +    Mismatch(HashMap<PathBuf, Vec<Mismatch>>),
 +    Parse,
 +}
 +
 +fn idempotent_check(
 +    filename: &PathBuf,
 +    opt_config: &Option<PathBuf>,
 +) -> Result<FormatReport, IdempotentCheckError> {
 +    let sig_comments = read_significant_comments(filename);
 +    let config = if let Some(ref config_file_path) = opt_config {
 +        Config::from_toml_path(config_file_path).expect("`rustfmt.toml` not found")
 +    } else {
 +        read_config(filename)
 +    };
 +    let (parsing_errors, source_file, format_report) = format_file(filename, config);
 +    if parsing_errors {
 +        return Err(IdempotentCheckError::Parse);
 +    }
 +
 +    let mut write_result = HashMap::new();
 +    for (filename, text) in source_file {
 +        if let FileName::Real(ref filename) = filename {
 +            write_result.insert(filename.to_owned(), text);
 +        }
 +    }
 +
 +    let target = sig_comments.get("target").map(|x| &(*x)[..]);
 +
 +    handle_result(write_result, target).map(|_| format_report)
 +}
 +
 +// Reads test config file using the supplied (optional) file name. If there's no file name or the
 +// file doesn't exist, just return the default config. Otherwise, the file must be read
 +// successfully.
 +fn get_config(config_file: Option<&Path>) -> Config {
 +    let config_file_name = match config_file {
 +        None => return Default::default(),
 +        Some(file_name) => {
 +            let mut full_path = PathBuf::from("tests/config/");
 +            full_path.push(file_name);
 +            if !full_path.exists() {
 +                return Default::default();
 +            };
 +            full_path
 +        }
 +    };
 +
 +    let mut def_config_file = fs::File::open(config_file_name).expect("couldn't open config");
 +    let mut def_config = String::new();
 +    def_config_file
 +        .read_to_string(&mut def_config)
 +        .expect("Couldn't read config");
 +
 +    Config::from_toml(&def_config, Path::new("tests/config/")).expect("invalid TOML")
 +}
 +
 +// Reads significant comments of the form: `// rustfmt-key: value` into a hash map.
 +fn read_significant_comments(file_name: &Path) -> HashMap<String, String> {
 +    let file = fs::File::open(file_name)
 +        .unwrap_or_else(|_| panic!("couldn't read file {}", file_name.display()));
 +    let reader = BufReader::new(file);
 +    let pattern = r"^\s*//\s*rustfmt-([^:]+):\s*(\S+)";
 +    let regex = regex::Regex::new(pattern).expect("failed creating pattern 1");
 +
 +    // Matches lines containing significant comments or whitespace.
 +    let line_regex = regex::Regex::new(r"(^\s*$)|(^\s*//\s*rustfmt-[^:]+:\s*\S+)")
 +        .expect("failed creating pattern 2");
 +
 +    reader
 +        .lines()
 +        .map(|line| line.expect("failed getting line"))
 +        .filter(|line| line_regex.is_match(line))
 +        .filter_map(|line| {
 +            regex.captures_iter(&line).next().map(|capture| {
 +                (
 +                    capture
 +                        .get(1)
 +                        .expect("couldn't unwrap capture")
 +                        .as_str()
 +                        .to_owned(),
 +                    capture
 +                        .get(2)
 +                        .expect("couldn't unwrap capture")
 +                        .as_str()
 +                        .to_owned(),
 +                )
 +            })
 +        })
 +        .collect()
 +}
 +
 +// Compares output to input.
 +// TODO: needs a better name, more explanation.
 +fn handle_result(
 +    result: HashMap<PathBuf, String>,
 +    target: Option<&str>,
 +) -> Result<(), IdempotentCheckError> {
 +    let mut failures = HashMap::new();
 +
 +    for (file_name, fmt_text) in result {
 +        // If file is in tests/source, compare to file with same name in tests/target.
 +        let target = get_target(&file_name, target);
 +        let open_error = format!("couldn't open target {:?}", target);
 +        let mut f = fs::File::open(&target).expect(&open_error);
 +
 +        let mut text = String::new();
 +        let read_error = format!("failed reading target {:?}", target);
 +        f.read_to_string(&mut text).expect(&read_error);
 +
 +        // Ignore LF and CRLF difference for Windows.
 +        if !string_eq_ignore_newline_repr(&fmt_text, &text) {
 +            let diff = make_diff(&text, &fmt_text, DIFF_CONTEXT_SIZE);
 +            assert!(
 +                !diff.is_empty(),
 +                "Empty diff? Maybe due to a missing a newline at the end of a file?"
 +            );
 +            failures.insert(file_name, diff);
 +        }
 +    }
 +
 +    if failures.is_empty() {
 +        Ok(())
 +    } else {
 +        Err(IdempotentCheckError::Mismatch(failures))
 +    }
 +}
 +
 +// Maps source file paths to their target paths.
 +fn get_target(file_name: &Path, target: Option<&str>) -> PathBuf {
 +    if let Some(n) = file_name
 +        .components()
 +        .position(|c| c.as_os_str() == "source")
 +    {
 +        let mut target_file_name = PathBuf::new();
 +        for (i, c) in file_name.components().enumerate() {
 +            if i == n {
 +                target_file_name.push("target");
 +            } else {
 +                target_file_name.push(c.as_os_str());
 +            }
 +        }
 +        if let Some(replace_name) = target {
 +            target_file_name.with_file_name(replace_name)
 +        } else {
 +            target_file_name
 +        }
 +    } else {
 +        // This is either and idempotence check or a self check.
 +        file_name.to_owned()
 +    }
 +}
 +
 +#[test]
 +fn rustfmt_diff_make_diff_tests() {
 +    init_log();
 +    let diff = make_diff("a\nb\nc\nd", "a\ne\nc\nd", 3);
 +    assert_eq!(
 +        diff,
 +        vec![Mismatch {
 +            line_number: 1,
 +            line_number_orig: 1,
 +            lines: vec![
 +                DiffLine::Context("a".into()),
 +                DiffLine::Resulting("b".into()),
 +                DiffLine::Expected("e".into()),
 +                DiffLine::Context("c".into()),
 +                DiffLine::Context("d".into()),
 +            ],
 +        }]
 +    );
 +}
 +
 +#[test]
 +fn rustfmt_diff_no_diff_test() {
 +    init_log();
 +    let diff = make_diff("a\nb\nc\nd", "a\nb\nc\nd", 3);
 +    assert_eq!(diff, vec![]);
 +}
 +
 +// Compare strings without distinguishing between CRLF and LF
 +fn string_eq_ignore_newline_repr(left: &str, right: &str) -> bool {
 +    let left = CharsIgnoreNewlineRepr(left.chars().peekable());
 +    let right = CharsIgnoreNewlineRepr(right.chars().peekable());
 +    left.eq(right)
 +}
 +
 +struct CharsIgnoreNewlineRepr<'a>(Peekable<Chars<'a>>);
 +
 +impl<'a> Iterator for CharsIgnoreNewlineRepr<'a> {
 +    type Item = char;
 +
 +    fn next(&mut self) -> Option<char> {
 +        self.0.next().map(|c| {
 +            if c == '\r' {
 +                if *self.0.peek().unwrap_or(&'\0') == '\n' {
 +                    self.0.next();
 +                    '\n'
 +                } else {
 +                    '\r'
 +                }
 +            } else {
 +                c
 +            }
 +        })
 +    }
 +}
 +
 +#[test]
 +fn string_eq_ignore_newline_repr_test() {
 +    init_log();
 +    assert!(string_eq_ignore_newline_repr("", ""));
 +    assert!(!string_eq_ignore_newline_repr("", "abc"));
 +    assert!(!string_eq_ignore_newline_repr("abc", ""));
 +    assert!(string_eq_ignore_newline_repr("a\nb\nc\rd", "a\nb\r\nc\rd"));
 +    assert!(string_eq_ignore_newline_repr("a\r\n\r\n\r\nb", "a\n\n\nb"));
 +    assert!(!string_eq_ignore_newline_repr("a\r\nbcd", "a\nbcdefghijk"));
 +}
 +
 +struct TempFile {
 +    path: PathBuf,
 +}
 +
 +fn make_temp_file(file_name: &'static str) -> TempFile {
 +    use std::env::var;
 +    use std::fs::File;
 +
 +    // Used in the Rust build system.
 +    let target_dir = var("RUSTFMT_TEST_DIR").unwrap_or_else(|_| ".".to_owned());
 +    let path = Path::new(&target_dir).join(file_name);
 +
 +    let mut file = File::create(&path).expect("couldn't create temp file");
 +    let content = "fn main() {}\n";
 +    file.write_all(content.as_bytes())
 +        .expect("couldn't write temp file");
 +    TempFile { path }
 +}
 +
 +impl Drop for TempFile {
 +    fn drop(&mut self) {
 +        use std::fs::remove_file;
 +        remove_file(&self.path).expect("couldn't delete temp file");
 +    }
 +}
 +
 +fn rustfmt() -> PathBuf {
 +    let mut me = env::current_exe().expect("failed to get current executable");
 +    // Chop of the test name.
 +    me.pop();
 +    // Chop off `deps`.
 +    me.pop();
 +
 +    // 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(),
 +        "{}",
 +        if cfg!(release) {
 +            "no rustfmt bin, try running `cargo build --release` before testing"
 +        } else {
 +            "no rustfmt bin, try running `cargo build` before testing"
 +        }
 +    );
 +    me
 +}
 +
 +#[test]
 +fn verify_check_works() {
 +    init_log();
 +    let temp_file = make_temp_file("temp_check.rs");
 +
 +    Command::new(rustfmt().to_str().unwrap())
 +        .arg("--check")
 +        .arg(temp_file.path.to_str().unwrap())
 +        .status()
 +        .expect("run with check option failed");
 +}
++
++#[test]
++fn verify_check_works_with_stdin() {
++    init_log();
++
++    let mut child = Command::new(rustfmt().to_str().unwrap())
++        .arg("--check")
++        .stdin(Stdio::piped())
++        .stderr(Stdio::piped())
++        .spawn()
++        .expect("run with check option failed");
++
++    {
++        let stdin = child.stdin.as_mut().expect("Failed to open stdin");
++        stdin
++            .write_all("fn main() {}\n".as_bytes())
++            .expect("Failed to write to rustfmt --check");
++    }
++    let output = child
++        .wait_with_output()
++        .expect("Failed to wait on rustfmt child");
++    assert!(output.status.success());
++}
++
++#[test]
++fn verify_check_l_works_with_stdin() {
++    init_log();
++
++    let mut child = Command::new(rustfmt().to_str().unwrap())
++        .arg("--check")
++        .arg("-l")
++        .stdin(Stdio::piped())
++        .stdout(Stdio::piped())
++        .stderr(Stdio::piped())
++        .spawn()
++        .expect("run with check option failed");
++
++    {
++        let stdin = child.stdin.as_mut().expect("Failed to open stdin");
++        stdin
++            .write_all("fn main()\n{}\n".as_bytes())
++            .expect("Failed to write to rustfmt --check");
++    }
++    let output = child
++        .wait_with_output()
++        .expect("Failed to wait on rustfmt child");
++    assert!(output.status.success());
++    assert_eq!(std::str::from_utf8(&output.stdout).unwrap(), "<stdin>\n");
++}
index ec9ed0f0b8d6249357980d28ddc9955c8a9e850b,0000000000000000000000000000000000000000..aacb2acc684986b3d6b0ceae263a19468a22b99e
mode 100644,000000..100644
--- /dev/null
@@@ -1,52 -1,0 +1,82 @@@
 +use std::io;
 +use std::path::PathBuf;
 +
 +use super::read_config;
 +
 +use crate::{FileName, Input, Session};
 +
 +fn verify_mod_resolution(input_file_name: &str, exp_misformatted_files: &[&str]) {
 +    let input_file = PathBuf::from(input_file_name);
 +    let config = read_config(&input_file);
 +    let mut session = Session::<io::Stdout>::new(config, None);
 +    let report = session
 +        .format(Input::File(input_file_name.into()))
 +        .expect("Should not have had any execution errors");
 +    let errors_by_file = &report.internal.borrow().0;
 +    for exp_file in exp_misformatted_files {
 +        assert!(errors_by_file.contains_key(&FileName::Real(PathBuf::from(exp_file))));
 +    }
 +}
 +
 +#[test]
 +fn nested_out_of_line_mods_loaded() {
 +    // See also https://github.com/rust-lang/rustfmt/issues/4874
 +    verify_mod_resolution(
 +        "tests/mod-resolver/issue-4874/main.rs",
 +        &[
 +            "tests/mod-resolver/issue-4874/bar/baz.rs",
 +            "tests/mod-resolver/issue-4874/foo/qux.rs",
 +        ],
 +    );
 +}
 +
 +#[test]
 +fn out_of_line_nested_inline_within_out_of_line() {
 +    // See also https://github.com/rust-lang/rustfmt/issues/5063
 +    verify_mod_resolution(
 +        "tests/mod-resolver/issue-5063/main.rs",
 +        &[
 +            "tests/mod-resolver/issue-5063/foo/bar/baz.rs",
 +            "tests/mod-resolver/issue-5063/foo.rs",
 +        ],
 +    );
 +}
 +
 +#[test]
 +fn skip_out_of_line_nested_inline_within_out_of_line() {
 +    // See also https://github.com/rust-lang/rustfmt/issues/5065
 +    verify_mod_resolution(
 +        "tests/mod-resolver/skip-files-issue-5065/main.rs",
 +        &["tests/mod-resolver/skip-files-issue-5065/one.rs"],
 +    );
 +}
++
++#[test]
++fn fmt_out_of_line_test_modules() {
++    // See also https://github.com/rust-lang/rustfmt/issues/5119
++    verify_mod_resolution(
++        "tests/mod-resolver/test-submodule-issue-5119/tests/test1.rs",
++        &[
++            "tests/mod-resolver/test-submodule-issue-5119/tests/test1.rs",
++            "tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub1.rs",
++            "tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub2.rs",
++            "tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/sub4.rs",
++        ],
++    )
++}
++
++#[test]
++fn fallback_and_try_to_resolve_external_submod_relative_to_current_dir_path() {
++    // See also https://github.com/rust-lang/rustfmt/issues/5198
++    verify_mod_resolution(
++        "tests/mod-resolver/issue-5198/lib.rs",
++        &[
++            "tests/mod-resolver/issue-5198/a.rs",
++            "tests/mod-resolver/issue-5198/lib/b.rs",
++            "tests/mod-resolver/issue-5198/lib/c/mod.rs",
++            "tests/mod-resolver/issue-5198/lib/c/e.rs",
++            "tests/mod-resolver/issue-5198/lib/c/d/f.rs",
++            "tests/mod-resolver/issue-5198/lib/c/d/g/mod.rs",
++        ],
++    )
++}
index a49d473a13f3fbdc955656174cc1313b51ec6695,0000000000000000000000000000000000000000..64a201e45ddd4fbf7b662b96f1c9c7f8e0da3d42
mode 100644,000000..100644
--- /dev/null
@@@ -1,1077 -1,0 +1,1086 @@@
-             Some(ref rw) if !rw.is_empty() => result.push_str(&format!("{} ", rw)),
 +use std::iter::ExactSizeIterator;
 +use std::ops::Deref;
 +
 +use rustc_ast::ast::{self, FnRetTy, Mutability, Term};
 +use rustc_ast::ptr;
 +use rustc_span::{symbol::kw, BytePos, Pos, Span};
 +
 +use crate::comment::{combine_strs_with_missing_comments, contains_comment};
 +use crate::config::lists::*;
 +use crate::config::{IndentStyle, TypeDensity, Version};
 +use crate::expr::{
 +    format_expr, rewrite_assign_rhs, rewrite_call, rewrite_tuple, rewrite_unary_prefix, ExprType,
 +    RhsAssignKind,
 +};
 +use crate::lists::{
 +    definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator,
 +};
 +use crate::macros::{rewrite_macro, MacroPosition};
 +use crate::overflow;
 +use crate::pairs::{rewrite_pair, PairParts};
 +use crate::rewrite::{Rewrite, RewriteContext};
 +use crate::shape::Shape;
 +use crate::source_map::SpanUtils;
 +use crate::spanned::Spanned;
 +use crate::utils::{
 +    colon_spaces, extra_offset, first_line_width, format_extern, format_mutability,
 +    last_line_extendable, last_line_width, mk_sp, rewrite_ident,
 +};
 +
 +#[derive(Copy, Clone, Debug, Eq, PartialEq)]
 +pub(crate) enum PathContext {
 +    Expr,
 +    Type,
 +    Import,
 +}
 +
 +// Does not wrap on simple segments.
 +pub(crate) fn rewrite_path(
 +    context: &RewriteContext<'_>,
 +    path_context: PathContext,
 +    qself: Option<&ast::QSelf>,
 +    path: &ast::Path,
 +    shape: Shape,
 +) -> Option<String> {
 +    let skip_count = qself.map_or(0, |x| x.position);
 +
 +    let mut result = if path.is_global() && qself.is_none() && path_context != PathContext::Import {
 +        "::".to_owned()
 +    } else {
 +        String::new()
 +    };
 +
 +    let mut span_lo = path.span.lo();
 +
 +    if let Some(qself) = qself {
 +        result.push('<');
 +
 +        let fmt_ty = qself.ty.rewrite(context, shape)?;
 +        result.push_str(&fmt_ty);
 +
 +        if skip_count > 0 {
 +            result.push_str(" as ");
 +            if path.is_global() && path_context != PathContext::Import {
 +                result.push_str("::");
 +            }
 +
 +            // 3 = ">::".len()
 +            let shape = shape.sub_width(3)?;
 +
 +            result = rewrite_path_segments(
 +                PathContext::Type,
 +                result,
 +                path.segments.iter().take(skip_count),
 +                span_lo,
 +                path.span.hi(),
 +                context,
 +                shape,
 +            )?;
 +        }
 +
 +        result.push_str(">::");
 +        span_lo = qself.ty.span.hi() + BytePos(1);
 +    }
 +
 +    rewrite_path_segments(
 +        path_context,
 +        result,
 +        path.segments.iter().skip(skip_count),
 +        span_lo,
 +        path.span.hi(),
 +        context,
 +        shape,
 +    )
 +}
 +
 +fn rewrite_path_segments<'a, I>(
 +    path_context: PathContext,
 +    mut buffer: String,
 +    iter: I,
 +    mut span_lo: BytePos,
 +    span_hi: BytePos,
 +    context: &RewriteContext<'_>,
 +    shape: Shape,
 +) -> Option<String>
 +where
 +    I: Iterator<Item = &'a ast::PathSegment>,
 +{
 +    let mut first = true;
 +    let shape = shape.visual_indent(0);
 +
 +    for segment in iter {
 +        // Indicates a global path, shouldn't be rendered.
 +        if segment.ident.name == kw::PathRoot {
 +            continue;
 +        }
 +        if first {
 +            first = false;
 +        } else {
 +            buffer.push_str("::");
 +        }
 +
 +        let extra_offset = extra_offset(&buffer, shape);
 +        let new_shape = shape.shrink_left(extra_offset)?;
 +        let segment_string = rewrite_segment(
 +            path_context,
 +            segment,
 +            &mut span_lo,
 +            span_hi,
 +            context,
 +            new_shape,
 +        )?;
 +
 +        buffer.push_str(&segment_string);
 +    }
 +
 +    Some(buffer)
 +}
 +
 +#[derive(Debug)]
 +pub(crate) enum SegmentParam<'a> {
 +    Const(&'a ast::AnonConst),
 +    LifeTime(&'a ast::Lifetime),
 +    Type(&'a ast::Ty),
 +    Binding(&'a ast::AssocConstraint),
 +}
 +
 +impl<'a> SegmentParam<'a> {
 +    fn from_generic_arg(arg: &ast::GenericArg) -> SegmentParam<'_> {
 +        match arg {
 +            ast::GenericArg::Lifetime(ref lt) => SegmentParam::LifeTime(lt),
 +            ast::GenericArg::Type(ref ty) => SegmentParam::Type(ty),
 +            ast::GenericArg::Const(const_) => SegmentParam::Const(const_),
 +        }
 +    }
 +}
 +
 +impl<'a> Spanned for SegmentParam<'a> {
 +    fn span(&self) -> Span {
 +        match *self {
 +            SegmentParam::Const(const_) => const_.value.span,
 +            SegmentParam::LifeTime(lt) => lt.ident.span,
 +            SegmentParam::Type(ty) => ty.span,
 +            SegmentParam::Binding(binding) => binding.span,
 +        }
 +    }
 +}
 +
 +impl<'a> Rewrite for SegmentParam<'a> {
 +    fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
 +        match *self {
 +            SegmentParam::Const(const_) => const_.rewrite(context, shape),
 +            SegmentParam::LifeTime(lt) => lt.rewrite(context, shape),
 +            SegmentParam::Type(ty) => ty.rewrite(context, shape),
 +            SegmentParam::Binding(atc) => atc.rewrite(context, shape),
 +        }
 +    }
 +}
 +
 +impl Rewrite for ast::AssocConstraint {
 +    fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
 +        use ast::AssocConstraintKind::{Bound, Equality};
 +
 +        let mut result = String::with_capacity(128);
 +        result.push_str(rewrite_ident(context, self.ident));
 +
 +        if let Some(ref gen_args) = self.gen_args {
 +            let budget = shape.width.checked_sub(result.len())?;
 +            let shape = Shape::legacy(budget, shape.indent + result.len());
 +            let gen_str = rewrite_generic_args(gen_args, context, shape, gen_args.span())?;
 +            result.push_str(&gen_str);
 +        }
 +
 +        let infix = match (&self.kind, context.config.type_punctuation_density()) {
 +            (Bound { .. }, _) => ": ",
 +            (Equality { .. }, TypeDensity::Wide) => " = ",
 +            (Equality { .. }, TypeDensity::Compressed) => "=",
 +        };
 +        result.push_str(infix);
 +
 +        let budget = shape.width.checked_sub(result.len())?;
 +        let shape = Shape::legacy(budget, shape.indent + result.len());
 +        let rewrite = self.kind.rewrite(context, shape)?;
 +        result.push_str(&rewrite);
 +
 +        Some(result)
 +    }
 +}
 +
 +impl Rewrite for ast::AssocConstraintKind {
 +    fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
 +        match self {
 +            ast::AssocConstraintKind::Equality { term } => match term {
 +                Term::Ty(ty) => ty.rewrite(context, shape),
 +                Term::Const(c) => c.rewrite(context, shape),
 +            },
 +            ast::AssocConstraintKind::Bound { bounds } => bounds.rewrite(context, shape),
 +        }
 +    }
 +}
 +
 +// Formats a path segment. There are some hacks involved to correctly determine
 +// the segment's associated span since it's not part of the AST.
 +//
 +// The span_lo is assumed to be greater than the end of any previous segment's
 +// parameters and lesser or equal than the start of current segment.
 +//
 +// span_hi is assumed equal to the end of the entire path.
 +//
 +// When the segment contains a positive number of parameters, we update span_lo
 +// so that invariants described above will hold for the next segment.
 +fn rewrite_segment(
 +    path_context: PathContext,
 +    segment: &ast::PathSegment,
 +    span_lo: &mut BytePos,
 +    span_hi: BytePos,
 +    context: &RewriteContext<'_>,
 +    shape: Shape,
 +) -> Option<String> {
 +    let mut result = String::with_capacity(128);
 +    result.push_str(rewrite_ident(context, segment.ident));
 +
 +    let ident_len = result.len();
 +    let shape = if context.use_block_indent() {
 +        shape.offset_left(ident_len)?
 +    } else {
 +        shape.shrink_left(ident_len)?
 +    };
 +
 +    if let Some(ref args) = segment.args {
 +        let generics_str = rewrite_generic_args(args, context, shape, mk_sp(*span_lo, span_hi))?;
 +        match **args {
 +            ast::GenericArgs::AngleBracketed(ref data) if !data.args.is_empty() => {
 +                // HACK: squeeze out the span between the identifier and the parameters.
 +                // The hack is required so that we don't remove the separator inside macro calls.
 +                // This does not work in the presence of comment, hoping that people are
 +                // sane about where to put their comment.
 +                let separator_snippet = context
 +                    .snippet(mk_sp(segment.ident.span.hi(), data.span.lo()))
 +                    .trim();
 +                let force_separator = context.inside_macro() && separator_snippet.starts_with("::");
 +                let separator = if path_context == PathContext::Expr || force_separator {
 +                    "::"
 +                } else {
 +                    ""
 +                };
 +                result.push_str(separator);
 +
 +                // Update position of last bracket.
 +                *span_lo = context
 +                    .snippet_provider
 +                    .span_after(mk_sp(*span_lo, span_hi), "<");
 +            }
 +            _ => (),
 +        }
 +        result.push_str(&generics_str)
 +    }
 +
 +    Some(result)
 +}
 +
 +fn format_function_type<'a, I>(
 +    inputs: I,
 +    output: &FnRetTy,
 +    variadic: bool,
 +    span: Span,
 +    context: &RewriteContext<'_>,
 +    shape: Shape,
 +) -> Option<String>
 +where
 +    I: ExactSizeIterator,
 +    <I as Iterator>::Item: Deref,
 +    <I::Item as Deref>::Target: Rewrite + Spanned + 'a,
 +{
 +    debug!("format_function_type {:#?}", shape);
 +
 +    let ty_shape = match context.config.indent_style() {
 +        // 4 = " -> "
 +        IndentStyle::Block => shape.offset_left(4)?,
 +        IndentStyle::Visual => shape.block_left(4)?,
 +    };
 +    let output = match *output {
 +        FnRetTy::Ty(ref ty) => {
 +            let type_str = ty.rewrite(context, ty_shape)?;
 +            format!(" -> {}", type_str)
 +        }
 +        FnRetTy::Default(..) => String::new(),
 +    };
 +
 +    let list_shape = if context.use_block_indent() {
 +        Shape::indented(
 +            shape.block().indent.block_indent(context.config),
 +            context.config,
 +        )
 +    } else {
 +        // 2 for ()
 +        let budget = shape.width.checked_sub(2)?;
 +        // 1 for (
 +        let offset = shape.indent + 1;
 +        Shape::legacy(budget, offset)
 +    };
 +
 +    let is_inputs_empty = inputs.len() == 0;
 +    let list_lo = context.snippet_provider.span_after(span, "(");
 +    let (list_str, tactic) = if is_inputs_empty {
 +        let tactic = get_tactics(&[], &output, shape);
 +        let list_hi = context.snippet_provider.span_before(span, ")");
 +        let comment = context
 +            .snippet_provider
 +            .span_to_snippet(mk_sp(list_lo, list_hi))?
 +            .trim();
 +        let comment = if comment.starts_with("//") {
 +            format!(
 +                "{}{}{}",
 +                &list_shape.indent.to_string_with_newline(context.config),
 +                comment,
 +                &shape.block().indent.to_string_with_newline(context.config)
 +            )
 +        } else {
 +            comment.to_string()
 +        };
 +        (comment, tactic)
 +    } else {
 +        let items = itemize_list(
 +            context.snippet_provider,
 +            inputs,
 +            ")",
 +            ",",
 +            |arg| arg.span().lo(),
 +            |arg| arg.span().hi(),
 +            |arg| arg.rewrite(context, list_shape),
 +            list_lo,
 +            span.hi(),
 +            false,
 +        );
 +
 +        let item_vec: Vec<_> = items.collect();
 +        let tactic = get_tactics(&item_vec, &output, shape);
 +        let trailing_separator = if !context.use_block_indent() || variadic {
 +            SeparatorTactic::Never
 +        } else {
 +            context.config.trailing_comma()
 +        };
 +
 +        let fmt = ListFormatting::new(list_shape, context.config)
 +            .tactic(tactic)
 +            .trailing_separator(trailing_separator)
 +            .ends_with_newline(tactic.ends_with_newline(context.config.indent_style()))
 +            .preserve_newline(true);
 +        (write_list(&item_vec, &fmt)?, tactic)
 +    };
 +
 +    let args = if tactic == DefinitiveListTactic::Horizontal
 +        || !context.use_block_indent()
 +        || is_inputs_empty
 +    {
 +        format!("({})", list_str)
 +    } else {
 +        format!(
 +            "({}{}{})",
 +            list_shape.indent.to_string_with_newline(context.config),
 +            list_str,
 +            shape.block().indent.to_string_with_newline(context.config),
 +        )
 +    };
 +    if output.is_empty() || last_line_width(&args) + first_line_width(&output) <= shape.width {
 +        Some(format!("{}{}", args, output))
 +    } else {
 +        Some(format!(
 +            "{}\n{}{}",
 +            args,
 +            list_shape.indent.to_string(context.config),
 +            output.trim_start()
 +        ))
 +    }
 +}
 +
 +fn type_bound_colon(context: &RewriteContext<'_>) -> &'static str {
 +    colon_spaces(context.config)
 +}
 +
 +// If the return type is multi-lined, then force to use multiple lines for
 +// arguments as well.
 +fn get_tactics(item_vec: &[ListItem], output: &str, shape: Shape) -> DefinitiveListTactic {
 +    if output.contains('\n') {
 +        DefinitiveListTactic::Vertical
 +    } else {
 +        definitive_tactic(
 +            item_vec,
 +            ListTactic::HorizontalVertical,
 +            Separator::Comma,
 +            // 2 is for the case of ',\n'
 +            shape.width.saturating_sub(2 + output.len()),
 +        )
 +    }
 +}
 +
 +impl Rewrite for ast::WherePredicate {
 +    fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
 +        // FIXME: dead spans?
 +        let result = match *self {
 +            ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate {
 +                ref bound_generic_params,
 +                ref bounded_ty,
 +                ref bounds,
 +                ..
 +            }) => {
 +                let type_str = bounded_ty.rewrite(context, shape)?;
 +                let colon = type_bound_colon(context).trim_end();
 +                let lhs = if let Some(lifetime_str) =
 +                    rewrite_lifetime_param(context, shape, bound_generic_params)
 +                {
 +                    format!("for<{}> {}{}", lifetime_str, type_str, colon)
 +                } else {
 +                    format!("{}{}", type_str, colon)
 +                };
 +
 +                rewrite_assign_rhs(context, lhs, bounds, &RhsAssignKind::Bounds, shape)?
 +            }
 +            ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate {
 +                ref lifetime,
 +                ref bounds,
 +                ..
 +            }) => rewrite_bounded_lifetime(lifetime, bounds, context, shape)?,
 +            ast::WherePredicate::EqPredicate(ast::WhereEqPredicate {
 +                ref lhs_ty,
 +                ref rhs_ty,
 +                ..
 +            }) => {
 +                let lhs_ty_str = lhs_ty.rewrite(context, shape).map(|lhs| lhs + " =")?;
 +                rewrite_assign_rhs(context, lhs_ty_str, &**rhs_ty, &RhsAssignKind::Ty, shape)?
 +            }
 +        };
 +
 +        Some(result)
 +    }
 +}
 +
 +impl Rewrite for ast::GenericArg {
 +    fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
 +        match *self {
 +            ast::GenericArg::Lifetime(ref lt) => lt.rewrite(context, shape),
 +            ast::GenericArg::Type(ref ty) => ty.rewrite(context, shape),
 +            ast::GenericArg::Const(ref const_) => const_.rewrite(context, shape),
 +        }
 +    }
 +}
 +
 +fn rewrite_generic_args(
 +    gen_args: &ast::GenericArgs,
 +    context: &RewriteContext<'_>,
 +    shape: Shape,
 +    span: Span,
 +) -> Option<String> {
 +    match gen_args {
 +        ast::GenericArgs::AngleBracketed(ref data) if !data.args.is_empty() => {
 +            let args = data
 +                .args
 +                .iter()
 +                .map(|x| match x {
 +                    ast::AngleBracketedArg::Arg(generic_arg) => {
 +                        SegmentParam::from_generic_arg(generic_arg)
 +                    }
 +                    ast::AngleBracketedArg::Constraint(constraint) => {
 +                        SegmentParam::Binding(constraint)
 +                    }
 +                })
 +                .collect::<Vec<_>>();
 +
 +            overflow::rewrite_with_angle_brackets(context, "", args.iter(), shape, span)
 +        }
 +        ast::GenericArgs::Parenthesized(ref data) => format_function_type(
 +            data.inputs.iter().map(|x| &**x),
 +            &data.output,
 +            false,
 +            data.span,
 +            context,
 +            shape,
 +        ),
 +        _ => Some("".to_owned()),
 +    }
 +}
 +
 +fn rewrite_bounded_lifetime(
 +    lt: &ast::Lifetime,
 +    bounds: &[ast::GenericBound],
 +    context: &RewriteContext<'_>,
 +    shape: Shape,
 +) -> Option<String> {
 +    let result = lt.rewrite(context, shape)?;
 +
 +    if bounds.is_empty() {
 +        Some(result)
 +    } else {
 +        let colon = type_bound_colon(context);
 +        let overhead = last_line_width(&result) + colon.len();
 +        let result = format!(
 +            "{}{}{}",
 +            result,
 +            colon,
 +            join_bounds(context, shape.sub_width(overhead)?, bounds, true)?
 +        );
 +        Some(result)
 +    }
 +}
 +
 +impl Rewrite for ast::AnonConst {
 +    fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
 +        format_expr(&self.value, ExprType::SubExpression, context, shape)
 +    }
 +}
 +
 +impl Rewrite for ast::Lifetime {
 +    fn rewrite(&self, context: &RewriteContext<'_>, _: Shape) -> Option<String> {
 +        Some(rewrite_ident(context, self.ident).to_owned())
 +    }
 +}
 +
 +impl Rewrite for ast::GenericBound {
 +    fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
 +        match *self {
 +            ast::GenericBound::Trait(ref poly_trait_ref, trait_bound_modifier) => {
 +                let snippet = context.snippet(self.span());
 +                let has_paren = snippet.starts_with('(') && snippet.ends_with(')');
 +                let rewrite = match trait_bound_modifier {
 +                    ast::TraitBoundModifier::None => poly_trait_ref.rewrite(context, shape),
 +                    ast::TraitBoundModifier::Maybe => poly_trait_ref
 +                        .rewrite(context, shape.offset_left(1)?)
 +                        .map(|s| format!("?{}", s)),
 +                    ast::TraitBoundModifier::MaybeConst => poly_trait_ref
 +                        .rewrite(context, shape.offset_left(7)?)
 +                        .map(|s| format!("~const {}", s)),
 +                    ast::TraitBoundModifier::MaybeConstMaybe => poly_trait_ref
 +                        .rewrite(context, shape.offset_left(8)?)
 +                        .map(|s| format!("~const ?{}", s)),
 +                };
 +                rewrite.map(|s| if has_paren { format!("({})", s) } else { s })
 +            }
 +            ast::GenericBound::Outlives(ref lifetime) => lifetime.rewrite(context, shape),
 +        }
 +    }
 +}
 +
 +impl Rewrite for ast::GenericBounds {
 +    fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
 +        if self.is_empty() {
 +            return Some(String::new());
 +        }
 +
 +        join_bounds(context, shape, self, true)
 +    }
 +}
 +
 +impl Rewrite for ast::GenericParam {
 +    fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
 +        let mut result = String::with_capacity(128);
 +        // FIXME: If there are more than one attributes, this will force multiline.
 +        match self.attrs.rewrite(context, shape) {
++            Some(ref rw) if !rw.is_empty() => {
++                result.push_str(rw);
++                // When rewriting generic params, an extra newline should be put
++                // if the attributes end with a doc comment
++                if let Some(true) = self.attrs.last().map(|a| a.is_doc_comment()) {
++                    result.push_str(&shape.indent.to_string_with_newline(context.config));
++                } else {
++                    result.push(' ');
++                }
++            }
 +            _ => (),
 +        }
 +
 +        if let ast::GenericParamKind::Const {
 +            ref ty,
 +            kw_span: _,
 +            default,
 +        } = &self.kind
 +        {
 +            result.push_str("const ");
 +            result.push_str(rewrite_ident(context, self.ident));
 +            result.push_str(": ");
 +            result.push_str(&ty.rewrite(context, shape)?);
 +            if let Some(default) = default {
 +                let eq_str = match context.config.type_punctuation_density() {
 +                    TypeDensity::Compressed => "=",
 +                    TypeDensity::Wide => " = ",
 +                };
 +                result.push_str(eq_str);
 +                let budget = shape.width.checked_sub(result.len())?;
 +                let rewrite = default.rewrite(context, Shape::legacy(budget, shape.indent))?;
 +                result.push_str(&rewrite);
 +            }
 +        } else {
 +            result.push_str(rewrite_ident(context, self.ident));
 +        }
 +
 +        if !self.bounds.is_empty() {
 +            result.push_str(type_bound_colon(context));
 +            result.push_str(&self.bounds.rewrite(context, shape)?)
 +        }
 +        if let ast::GenericParamKind::Type {
 +            default: Some(ref def),
 +        } = self.kind
 +        {
 +            let eq_str = match context.config.type_punctuation_density() {
 +                TypeDensity::Compressed => "=",
 +                TypeDensity::Wide => " = ",
 +            };
 +            result.push_str(eq_str);
 +            let budget = shape.width.checked_sub(result.len())?;
 +            let rewrite =
 +                def.rewrite(context, Shape::legacy(budget, shape.indent + result.len()))?;
 +            result.push_str(&rewrite);
 +        }
 +
 +        Some(result)
 +    }
 +}
 +
 +impl Rewrite for ast::PolyTraitRef {
 +    fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
 +        if let Some(lifetime_str) =
 +            rewrite_lifetime_param(context, shape, &self.bound_generic_params)
 +        {
 +            // 6 is "for<> ".len()
 +            let extra_offset = lifetime_str.len() + 6;
 +            let path_str = self
 +                .trait_ref
 +                .rewrite(context, shape.offset_left(extra_offset)?)?;
 +
 +            Some(format!("for<{}> {}", lifetime_str, path_str))
 +        } else {
 +            self.trait_ref.rewrite(context, shape)
 +        }
 +    }
 +}
 +
 +impl Rewrite for ast::TraitRef {
 +    fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
 +        rewrite_path(context, PathContext::Type, None, &self.path, shape)
 +    }
 +}
 +
 +impl Rewrite for ast::Ty {
 +    fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
 +        match self.kind {
 +            ast::TyKind::TraitObject(ref bounds, tobj_syntax) => {
 +                // we have to consider 'dyn' keyword is used or not!!!
 +                let is_dyn = tobj_syntax == ast::TraitObjectSyntax::Dyn;
 +                // 4 is length of 'dyn '
 +                let shape = if is_dyn { shape.offset_left(4)? } else { shape };
 +                let mut res = bounds.rewrite(context, shape)?;
 +                // We may have falsely removed a trailing `+` inside macro call.
 +                if context.inside_macro() && bounds.len() == 1 {
 +                    if context.snippet(self.span).ends_with('+') && !res.ends_with('+') {
 +                        res.push('+');
 +                    }
 +                }
 +                if is_dyn {
 +                    Some(format!("dyn {}", res))
 +                } else {
 +                    Some(res)
 +                }
 +            }
 +            ast::TyKind::Ptr(ref mt) => {
 +                let prefix = match mt.mutbl {
 +                    Mutability::Mut => "*mut ",
 +                    Mutability::Not => "*const ",
 +                };
 +
 +                rewrite_unary_prefix(context, prefix, &*mt.ty, shape)
 +            }
 +            ast::TyKind::Rptr(ref lifetime, ref mt) => {
 +                let mut_str = format_mutability(mt.mutbl);
 +                let mut_len = mut_str.len();
 +                let mut result = String::with_capacity(128);
 +                result.push('&');
 +                let ref_hi = context.snippet_provider.span_after(self.span(), "&");
 +                let mut cmnt_lo = ref_hi;
 +
 +                if let Some(ref lifetime) = *lifetime {
 +                    let lt_budget = shape.width.checked_sub(2 + mut_len)?;
 +                    let lt_str = lifetime.rewrite(
 +                        context,
 +                        Shape::legacy(lt_budget, shape.indent + 2 + mut_len),
 +                    )?;
 +                    let before_lt_span = mk_sp(cmnt_lo, lifetime.ident.span.lo());
 +                    if contains_comment(context.snippet(before_lt_span)) {
 +                        result = combine_strs_with_missing_comments(
 +                            context,
 +                            &result,
 +                            &lt_str,
 +                            before_lt_span,
 +                            shape,
 +                            true,
 +                        )?;
 +                    } else {
 +                        result.push_str(&lt_str);
 +                    }
 +                    result.push(' ');
 +                    cmnt_lo = lifetime.ident.span.hi();
 +                }
 +
 +                if ast::Mutability::Mut == mt.mutbl {
 +                    let mut_hi = context.snippet_provider.span_after(self.span(), "mut");
 +                    let before_mut_span = mk_sp(cmnt_lo, mut_hi - BytePos::from_usize(3));
 +                    if contains_comment(context.snippet(before_mut_span)) {
 +                        result = combine_strs_with_missing_comments(
 +                            context,
 +                            result.trim_end(),
 +                            mut_str,
 +                            before_mut_span,
 +                            shape,
 +                            true,
 +                        )?;
 +                    } else {
 +                        result.push_str(mut_str);
 +                    }
 +                    cmnt_lo = mut_hi;
 +                }
 +
 +                let before_ty_span = mk_sp(cmnt_lo, mt.ty.span.lo());
 +                if contains_comment(context.snippet(before_ty_span)) {
 +                    result = combine_strs_with_missing_comments(
 +                        context,
 +                        result.trim_end(),
 +                        &mt.ty.rewrite(context, shape)?,
 +                        before_ty_span,
 +                        shape,
 +                        true,
 +                    )?;
 +                } else {
 +                    let used_width = last_line_width(&result);
 +                    let budget = shape.width.checked_sub(used_width)?;
 +                    let ty_str = mt
 +                        .ty
 +                        .rewrite(context, Shape::legacy(budget, shape.indent + used_width))?;
 +                    result.push_str(&ty_str);
 +                }
 +
 +                Some(result)
 +            }
 +            // FIXME: we drop any comments here, even though it's a silly place to put
 +            // comments.
 +            ast::TyKind::Paren(ref ty) => {
 +                if context.config.version() == Version::One
 +                    || context.config.indent_style() == IndentStyle::Visual
 +                {
 +                    let budget = shape.width.checked_sub(2)?;
 +                    return ty
 +                        .rewrite(context, Shape::legacy(budget, shape.indent + 1))
 +                        .map(|ty_str| format!("({})", ty_str));
 +                }
 +
 +                // 2 = ()
 +                if let Some(sh) = shape.sub_width(2) {
 +                    if let Some(ref s) = ty.rewrite(context, sh) {
 +                        if !s.contains('\n') {
 +                            return Some(format!("({})", s));
 +                        }
 +                    }
 +                }
 +
 +                let indent_str = shape.indent.to_string_with_newline(context.config);
 +                let shape = shape
 +                    .block_indent(context.config.tab_spaces())
 +                    .with_max_width(context.config);
 +                let rw = ty.rewrite(context, shape)?;
 +                Some(format!(
 +                    "({}{}{})",
 +                    shape.to_string_with_newline(context.config),
 +                    rw,
 +                    indent_str
 +                ))
 +            }
 +            ast::TyKind::Slice(ref ty) => {
 +                let budget = shape.width.checked_sub(4)?;
 +                ty.rewrite(context, Shape::legacy(budget, shape.indent + 1))
 +                    .map(|ty_str| format!("[{}]", ty_str))
 +            }
 +            ast::TyKind::Tup(ref items) => {
 +                rewrite_tuple(context, items.iter(), self.span, shape, items.len() == 1)
 +            }
 +            ast::TyKind::Path(ref q_self, ref path) => {
 +                rewrite_path(context, PathContext::Type, q_self.as_ref(), path, shape)
 +            }
 +            ast::TyKind::Array(ref ty, ref repeats) => rewrite_pair(
 +                &**ty,
 +                &*repeats.value,
 +                PairParts::new("[", "; ", "]"),
 +                context,
 +                shape,
 +                SeparatorPlace::Back,
 +            ),
 +            ast::TyKind::Infer => {
 +                if shape.width >= 1 {
 +                    Some("_".to_owned())
 +                } else {
 +                    None
 +                }
 +            }
 +            ast::TyKind::BareFn(ref bare_fn) => rewrite_bare_fn(bare_fn, self.span, context, shape),
 +            ast::TyKind::Never => Some(String::from("!")),
 +            ast::TyKind::MacCall(ref mac) => {
 +                rewrite_macro(mac, None, context, shape, MacroPosition::Expression)
 +            }
 +            ast::TyKind::ImplicitSelf => Some(String::from("")),
 +            ast::TyKind::ImplTrait(_, ref it) => {
 +                // Empty trait is not a parser error.
 +                if it.is_empty() {
 +                    return Some("impl".to_owned());
 +                }
 +                let rw = if context.config.version() == Version::One {
 +                    it.rewrite(context, shape)
 +                } else {
 +                    join_bounds(context, shape, it, false)
 +                };
 +                rw.map(|it_str| {
 +                    let space = if it_str.is_empty() { "" } else { " " };
 +                    format!("impl{}{}", space, it_str)
 +                })
 +            }
 +            ast::TyKind::CVarArgs => Some("...".to_owned()),
 +            ast::TyKind::Err => Some(context.snippet(self.span).to_owned()),
 +            ast::TyKind::Typeof(ref anon_const) => rewrite_call(
 +                context,
 +                "typeof",
 +                &[anon_const.value.clone()],
 +                self.span,
 +                shape,
 +            ),
 +        }
 +    }
 +}
 +
 +fn rewrite_bare_fn(
 +    bare_fn: &ast::BareFnTy,
 +    span: Span,
 +    context: &RewriteContext<'_>,
 +    shape: Shape,
 +) -> Option<String> {
 +    debug!("rewrite_bare_fn {:#?}", shape);
 +
 +    let mut result = String::with_capacity(128);
 +
 +    if let Some(ref lifetime_str) = rewrite_lifetime_param(context, shape, &bare_fn.generic_params)
 +    {
 +        result.push_str("for<");
 +        // 6 = "for<> ".len(), 4 = "for<".
 +        // This doesn't work out so nicely for multiline situation with lots of
 +        // rightward drift. If that is a problem, we could use the list stuff.
 +        result.push_str(lifetime_str);
 +        result.push_str("> ");
 +    }
 +
 +    result.push_str(crate::utils::format_unsafety(bare_fn.unsafety));
 +
 +    result.push_str(&format_extern(
 +        bare_fn.ext,
 +        context.config.force_explicit_abi(),
 +        false,
 +    ));
 +
 +    result.push_str("fn");
 +
 +    let func_ty_shape = if context.use_block_indent() {
 +        shape.offset_left(result.len())?
 +    } else {
 +        shape.visual_indent(result.len()).sub_width(result.len())?
 +    };
 +
 +    let rewrite = format_function_type(
 +        bare_fn.decl.inputs.iter(),
 +        &bare_fn.decl.output,
 +        bare_fn.decl.c_variadic(),
 +        span,
 +        context,
 +        func_ty_shape,
 +    )?;
 +
 +    result.push_str(&rewrite);
 +
 +    Some(result)
 +}
 +
 +fn is_generic_bounds_in_order(generic_bounds: &[ast::GenericBound]) -> bool {
 +    let is_trait = |b: &ast::GenericBound| match b {
 +        ast::GenericBound::Outlives(..) => false,
 +        ast::GenericBound::Trait(..) => true,
 +    };
 +    let is_lifetime = |b: &ast::GenericBound| !is_trait(b);
 +    let last_trait_index = generic_bounds.iter().rposition(is_trait);
 +    let first_lifetime_index = generic_bounds.iter().position(is_lifetime);
 +    match (last_trait_index, first_lifetime_index) {
 +        (Some(last_trait_index), Some(first_lifetime_index)) => {
 +            last_trait_index < first_lifetime_index
 +        }
 +        _ => true,
 +    }
 +}
 +
 +fn join_bounds(
 +    context: &RewriteContext<'_>,
 +    shape: Shape,
 +    items: &[ast::GenericBound],
 +    need_indent: bool,
 +) -> Option<String> {
 +    join_bounds_inner(context, shape, items, need_indent, false)
 +}
 +
 +fn join_bounds_inner(
 +    context: &RewriteContext<'_>,
 +    shape: Shape,
 +    items: &[ast::GenericBound],
 +    need_indent: bool,
 +    force_newline: bool,
 +) -> Option<String> {
 +    debug_assert!(!items.is_empty());
 +
 +    let generic_bounds_in_order = is_generic_bounds_in_order(items);
 +    let is_bound_extendable = |s: &str, b: &ast::GenericBound| match b {
 +        ast::GenericBound::Outlives(..) => true,
 +        ast::GenericBound::Trait(..) => last_line_extendable(s),
 +    };
 +
 +    let result = items.iter().enumerate().try_fold(
 +        (String::new(), None, false),
 +        |(strs, prev_trailing_span, prev_extendable), (i, item)| {
 +            let trailing_span = if i < items.len() - 1 {
 +                let hi = context
 +                    .snippet_provider
 +                    .span_before(mk_sp(items[i + 1].span().lo(), item.span().hi()), "+");
 +
 +                Some(mk_sp(item.span().hi(), hi))
 +            } else {
 +                None
 +            };
 +            let (leading_span, has_leading_comment) = if i > 0 {
 +                let lo = context
 +                    .snippet_provider
 +                    .span_after(mk_sp(items[i - 1].span().hi(), item.span().lo()), "+");
 +
 +                let span = mk_sp(lo, item.span().lo());
 +
 +                let has_comments = contains_comment(context.snippet(span));
 +
 +                (Some(mk_sp(lo, item.span().lo())), has_comments)
 +            } else {
 +                (None, false)
 +            };
 +            let prev_has_trailing_comment = match prev_trailing_span {
 +                Some(ts) => contains_comment(context.snippet(ts)),
 +                _ => false,
 +            };
 +
 +            let shape = if need_indent && force_newline {
 +                shape
 +                    .block_indent(context.config.tab_spaces())
 +                    .with_max_width(context.config)
 +            } else {
 +                shape
 +            };
 +            let whitespace = if force_newline && (!prev_extendable || !generic_bounds_in_order) {
 +                shape
 +                    .indent
 +                    .to_string_with_newline(context.config)
 +                    .to_string()
 +            } else {
 +                String::from(" ")
 +            };
 +
 +            let joiner = match context.config.type_punctuation_density() {
 +                TypeDensity::Compressed => String::from("+"),
 +                TypeDensity::Wide => whitespace + "+ ",
 +            };
 +            let joiner = if has_leading_comment {
 +                joiner.trim_end()
 +            } else {
 +                &joiner
 +            };
 +            let joiner = if prev_has_trailing_comment {
 +                joiner.trim_start()
 +            } else {
 +                joiner
 +            };
 +
 +            let (extendable, trailing_str) = if i == 0 {
 +                let bound_str = item.rewrite(context, shape)?;
 +                (is_bound_extendable(&bound_str, item), bound_str)
 +            } else {
 +                let bound_str = &item.rewrite(context, shape)?;
 +                match leading_span {
 +                    Some(ls) if has_leading_comment => (
 +                        is_bound_extendable(bound_str, item),
 +                        combine_strs_with_missing_comments(
 +                            context, joiner, bound_str, ls, shape, true,
 +                        )?,
 +                    ),
 +                    _ => (
 +                        is_bound_extendable(bound_str, item),
 +                        String::from(joiner) + bound_str,
 +                    ),
 +                }
 +            };
 +            match prev_trailing_span {
 +                Some(ts) if prev_has_trailing_comment => combine_strs_with_missing_comments(
 +                    context,
 +                    &strs,
 +                    &trailing_str,
 +                    ts,
 +                    shape,
 +                    true,
 +                )
 +                .map(|v| (v, trailing_span, extendable)),
 +                _ => Some((strs + &trailing_str, trailing_span, extendable)),
 +            }
 +        },
 +    )?;
 +
 +    if !force_newline
 +        && items.len() > 1
 +        && (result.0.contains('\n') || result.0.len() > shape.width)
 +    {
 +        join_bounds_inner(context, shape, items, need_indent, true)
 +    } else {
 +        Some(result.0)
 +    }
 +}
 +
 +pub(crate) fn opaque_ty(ty: &Option<ptr::P<ast::Ty>>) -> Option<&ast::GenericBounds> {
 +    ty.as_ref().and_then(|t| match &t.kind {
 +        ast::TyKind::ImplTrait(_, bounds) => Some(bounds),
 +        _ => None,
 +    })
 +}
 +
 +pub(crate) fn can_be_overflowed_type(
 +    context: &RewriteContext<'_>,
 +    ty: &ast::Ty,
 +    len: usize,
 +) -> bool {
 +    match ty.kind {
 +        ast::TyKind::Tup(..) => context.use_block_indent() && len == 1,
 +        ast::TyKind::Rptr(_, ref mutty) | ast::TyKind::Ptr(ref mutty) => {
 +            can_be_overflowed_type(context, &*mutty.ty, len)
 +        }
 +        _ => false,
 +    }
 +}
 +
 +/// Returns `None` if there is no `LifetimeDef` in the given generic parameters.
 +fn rewrite_lifetime_param(
 +    context: &RewriteContext<'_>,
 +    shape: Shape,
 +    generic_params: &[ast::GenericParam],
 +) -> Option<String> {
 +    let result = generic_params
 +        .iter()
 +        .filter(|p| matches!(p.kind, ast::GenericParamKind::Lifetime))
 +        .map(|lt| lt.rewrite(context, shape))
 +        .collect::<Option<Vec<_>>>()?
 +        .join(", ");
 +    if result.is_empty() {
 +        None
 +    } else {
 +        Some(result)
 +    }
 +}
index 2428d8cb0fd89ee6b934629ccc5cfed909f4487b,0000000000000000000000000000000000000000..35512e78fa6e29988e58eee9bd427f7eeb4c96a5
mode 100644,000000..100644
--- /dev/null
@@@ -1,704 -1,0 +1,717 @@@
- /// This allows to preserve the indentation of multi-line literals.
- pub(crate) fn indent_next_line(kind: FullCodeCharKind, _line: &str, config: &Config) -> bool {
-     !(kind.is_string() || (config.version() == Version::Two && kind.is_commented_string()))
 +use std::borrow::Cow;
 +
 +use rustc_ast::ast::{
 +    self, Attribute, CrateSugar, MetaItem, MetaItemKind, NestedMetaItem, NodeId, Path, Visibility,
 +    VisibilityKind,
 +};
 +use rustc_ast::ptr;
 +use rustc_ast_pretty::pprust;
 +use rustc_span::{sym, symbol, BytePos, LocalExpnId, Span, Symbol, SyntaxContext};
 +use unicode_width::UnicodeWidthStr;
 +
 +use crate::comment::{filter_normal_code, CharClasses, FullCodeCharKind, LineClasses};
 +use crate::config::{Config, Version};
 +use crate::rewrite::RewriteContext;
 +use crate::shape::{Indent, Shape};
 +
 +#[inline]
 +pub(crate) fn depr_skip_annotation() -> Symbol {
 +    Symbol::intern("rustfmt_skip")
 +}
 +
 +#[inline]
 +pub(crate) fn skip_annotation() -> Symbol {
 +    Symbol::intern("rustfmt::skip")
 +}
 +
 +pub(crate) fn rewrite_ident<'a>(context: &'a RewriteContext<'_>, ident: symbol::Ident) -> &'a str {
 +    context.snippet(ident.span)
 +}
 +
 +// Computes the length of a string's last line, minus offset.
 +pub(crate) fn extra_offset(text: &str, shape: Shape) -> usize {
 +    match text.rfind('\n') {
 +        // 1 for newline character
 +        Some(idx) => text.len().saturating_sub(idx + 1 + shape.used_width()),
 +        None => text.len(),
 +    }
 +}
 +
 +pub(crate) fn is_same_visibility(a: &Visibility, b: &Visibility) -> bool {
 +    match (&a.kind, &b.kind) {
 +        (
 +            VisibilityKind::Restricted { path: p, .. },
 +            VisibilityKind::Restricted { path: q, .. },
 +        ) => pprust::path_to_string(p) == pprust::path_to_string(q),
 +        (VisibilityKind::Public, VisibilityKind::Public)
 +        | (VisibilityKind::Inherited, VisibilityKind::Inherited)
 +        | (
 +            VisibilityKind::Crate(CrateSugar::PubCrate),
 +            VisibilityKind::Crate(CrateSugar::PubCrate),
 +        )
 +        | (
 +            VisibilityKind::Crate(CrateSugar::JustCrate),
 +            VisibilityKind::Crate(CrateSugar::JustCrate),
 +        ) => true,
 +        _ => false,
 +    }
 +}
 +
 +// Uses Cow to avoid allocating in the common cases.
 +pub(crate) fn format_visibility(
 +    context: &RewriteContext<'_>,
 +    vis: &Visibility,
 +) -> Cow<'static, str> {
 +    match vis.kind {
 +        VisibilityKind::Public => Cow::from("pub "),
 +        VisibilityKind::Inherited => Cow::from(""),
 +        VisibilityKind::Crate(CrateSugar::PubCrate) => Cow::from("pub(crate) "),
 +        VisibilityKind::Crate(CrateSugar::JustCrate) => Cow::from("crate "),
 +        VisibilityKind::Restricted { ref path, .. } => {
 +            let Path { ref segments, .. } = **path;
 +            let mut segments_iter = segments.iter().map(|seg| rewrite_ident(context, seg.ident));
 +            if path.is_global() {
 +                segments_iter
 +                    .next()
 +                    .expect("Non-global path in pub(restricted)?");
 +            }
 +            let is_keyword = |s: &str| s == "self" || s == "super";
 +            let path = segments_iter.collect::<Vec<_>>().join("::");
 +            let in_str = if is_keyword(&path) { "" } else { "in " };
 +
 +            Cow::from(format!("pub({}{}) ", in_str, path))
 +        }
 +    }
 +}
 +
 +#[inline]
 +pub(crate) fn format_async(is_async: &ast::Async) -> &'static str {
 +    match is_async {
 +        ast::Async::Yes { .. } => "async ",
 +        ast::Async::No => "",
 +    }
 +}
 +
 +#[inline]
 +pub(crate) fn format_constness(constness: ast::Const) -> &'static str {
 +    match constness {
 +        ast::Const::Yes(..) => "const ",
 +        ast::Const::No => "",
 +    }
 +}
 +
 +#[inline]
 +pub(crate) fn format_constness_right(constness: ast::Const) -> &'static str {
 +    match constness {
 +        ast::Const::Yes(..) => " const",
 +        ast::Const::No => "",
 +    }
 +}
 +
 +#[inline]
 +pub(crate) fn format_defaultness(defaultness: ast::Defaultness) -> &'static str {
 +    match defaultness {
 +        ast::Defaultness::Default(..) => "default ",
 +        ast::Defaultness::Final => "",
 +    }
 +}
 +
 +#[inline]
 +pub(crate) fn format_unsafety(unsafety: ast::Unsafe) -> &'static str {
 +    match unsafety {
 +        ast::Unsafe::Yes(..) => "unsafe ",
 +        ast::Unsafe::No => "",
 +    }
 +}
 +
 +#[inline]
 +pub(crate) fn format_auto(is_auto: ast::IsAuto) -> &'static str {
 +    match is_auto {
 +        ast::IsAuto::Yes => "auto ",
 +        ast::IsAuto::No => "",
 +    }
 +}
 +
 +#[inline]
 +pub(crate) fn format_mutability(mutability: ast::Mutability) -> &'static str {
 +    match mutability {
 +        ast::Mutability::Mut => "mut ",
 +        ast::Mutability::Not => "",
 +    }
 +}
 +
 +#[inline]
 +pub(crate) fn format_extern(
 +    ext: ast::Extern,
 +    explicit_abi: bool,
 +    is_mod: bool,
 +) -> Cow<'static, str> {
 +    let abi = match ext {
 +        ast::Extern::None => "Rust".to_owned(),
 +        ast::Extern::Implicit => "C".to_owned(),
 +        ast::Extern::Explicit(abi) => abi.symbol_unescaped.to_string(),
 +    };
 +
 +    if abi == "Rust" && !is_mod {
 +        Cow::from("")
 +    } else if abi == "C" && !explicit_abi {
 +        Cow::from("extern ")
 +    } else {
 +        Cow::from(format!(r#"extern "{}" "#, abi))
 +    }
 +}
 +
 +#[inline]
 +// Transform `Vec<rustc_ast::ptr::P<T>>` into `Vec<&T>`
 +pub(crate) fn ptr_vec_to_ref_vec<T>(vec: &[ptr::P<T>]) -> Vec<&T> {
 +    vec.iter().map(|x| &**x).collect::<Vec<_>>()
 +}
 +
 +#[inline]
 +pub(crate) fn filter_attributes(
 +    attrs: &[ast::Attribute],
 +    style: ast::AttrStyle,
 +) -> Vec<ast::Attribute> {
 +    attrs
 +        .iter()
 +        .filter(|a| a.style == style)
 +        .cloned()
 +        .collect::<Vec<_>>()
 +}
 +
 +#[inline]
 +pub(crate) fn inner_attributes(attrs: &[ast::Attribute]) -> Vec<ast::Attribute> {
 +    filter_attributes(attrs, ast::AttrStyle::Inner)
 +}
 +
 +#[inline]
 +pub(crate) fn outer_attributes(attrs: &[ast::Attribute]) -> Vec<ast::Attribute> {
 +    filter_attributes(attrs, ast::AttrStyle::Outer)
 +}
 +
 +#[inline]
 +pub(crate) fn is_single_line(s: &str) -> bool {
 +    !s.chars().any(|c| c == '\n')
 +}
 +
 +#[inline]
 +pub(crate) fn first_line_contains_single_line_comment(s: &str) -> bool {
 +    s.lines().next().map_or(false, |l| l.contains("//"))
 +}
 +
 +#[inline]
 +pub(crate) fn last_line_contains_single_line_comment(s: &str) -> bool {
 +    s.lines().last().map_or(false, |l| l.contains("//"))
 +}
 +
 +#[inline]
 +pub(crate) fn is_attributes_extendable(attrs_str: &str) -> bool {
 +    !attrs_str.contains('\n') && !last_line_contains_single_line_comment(attrs_str)
 +}
 +
 +/// The width of the first line in s.
 +#[inline]
 +pub(crate) fn first_line_width(s: &str) -> usize {
 +    unicode_str_width(s.splitn(2, '\n').next().unwrap_or(""))
 +}
 +
 +/// The width of the last line in s.
 +#[inline]
 +pub(crate) fn last_line_width(s: &str) -> usize {
 +    unicode_str_width(s.rsplitn(2, '\n').next().unwrap_or(""))
 +}
 +
 +/// The total used width of the last line.
 +#[inline]
 +pub(crate) fn last_line_used_width(s: &str, offset: usize) -> usize {
 +    if s.contains('\n') {
 +        last_line_width(s)
 +    } else {
 +        offset + unicode_str_width(s)
 +    }
 +}
 +
 +#[inline]
 +pub(crate) fn trimmed_last_line_width(s: &str) -> usize {
 +    unicode_str_width(match s.rfind('\n') {
 +        Some(n) => s[(n + 1)..].trim(),
 +        None => s.trim(),
 +    })
 +}
 +
 +#[inline]
 +pub(crate) fn last_line_extendable(s: &str) -> bool {
 +    if s.ends_with("\"#") {
 +        return true;
 +    }
 +    for c in s.chars().rev() {
 +        match c {
 +            '(' | ')' | ']' | '}' | '?' | '>' => continue,
 +            '\n' => break,
 +            _ if c.is_whitespace() => continue,
 +            _ => return false,
 +        }
 +    }
 +    true
 +}
 +
 +#[inline]
 +fn is_skip(meta_item: &MetaItem) -> bool {
 +    match meta_item.kind {
 +        MetaItemKind::Word => {
 +            let path_str = pprust::path_to_string(&meta_item.path);
 +            path_str == skip_annotation().as_str() || path_str == depr_skip_annotation().as_str()
 +        }
 +        MetaItemKind::List(ref l) => {
 +            meta_item.has_name(sym::cfg_attr) && l.len() == 2 && is_skip_nested(&l[1])
 +        }
 +        _ => false,
 +    }
 +}
 +
 +#[inline]
 +fn is_skip_nested(meta_item: &NestedMetaItem) -> bool {
 +    match meta_item {
 +        NestedMetaItem::MetaItem(ref mi) => is_skip(mi),
 +        NestedMetaItem::Literal(_) => false,
 +    }
 +}
 +
 +#[inline]
 +pub(crate) fn contains_skip(attrs: &[Attribute]) -> bool {
 +    attrs
 +        .iter()
 +        .any(|a| a.meta().map_or(false, |a| is_skip(&a)))
 +}
 +
 +#[inline]
 +pub(crate) fn semicolon_for_expr(context: &RewriteContext<'_>, expr: &ast::Expr) -> bool {
 +    // Never try to insert semicolons on expressions when we're inside
 +    // a macro definition - this can prevent the macro from compiling
 +    // when used in expression position
 +    if context.is_macro_def {
 +        return false;
 +    }
 +
 +    match expr.kind {
 +        ast::ExprKind::Ret(..) | ast::ExprKind::Continue(..) | ast::ExprKind::Break(..) => {
 +            context.config.trailing_semicolon()
 +        }
 +        _ => false,
 +    }
 +}
 +
 +#[inline]
 +pub(crate) fn semicolon_for_stmt(context: &RewriteContext<'_>, stmt: &ast::Stmt) -> bool {
 +    match stmt.kind {
 +        ast::StmtKind::Semi(ref expr) => match expr.kind {
 +            ast::ExprKind::While(..) | ast::ExprKind::Loop(..) | ast::ExprKind::ForLoop(..) => {
 +                false
 +            }
 +            ast::ExprKind::Break(..) | ast::ExprKind::Continue(..) | ast::ExprKind::Ret(..) => {
 +                context.config.trailing_semicolon()
 +            }
 +            _ => true,
 +        },
 +        ast::StmtKind::Expr(..) => false,
 +        _ => true,
 +    }
 +}
 +
 +#[inline]
 +pub(crate) fn stmt_expr(stmt: &ast::Stmt) -> Option<&ast::Expr> {
 +    match stmt.kind {
 +        ast::StmtKind::Expr(ref expr) => Some(expr),
 +        _ => None,
 +    }
 +}
 +
 +/// Returns the number of LF and CRLF respectively.
 +pub(crate) fn count_lf_crlf(input: &str) -> (usize, usize) {
 +    let mut lf = 0;
 +    let mut crlf = 0;
 +    let mut is_crlf = false;
 +    for c in input.as_bytes() {
 +        match c {
 +            b'\r' => is_crlf = true,
 +            b'\n' if is_crlf => crlf += 1,
 +            b'\n' => lf += 1,
 +            _ => is_crlf = false,
 +        }
 +    }
 +    (lf, crlf)
 +}
 +
 +pub(crate) fn count_newlines(input: &str) -> usize {
 +    // Using bytes to omit UTF-8 decoding
 +    bytecount::count(input.as_bytes(), b'\n')
 +}
 +
 +// For format_missing and last_pos, need to use the source callsite (if applicable).
 +// Required as generated code spans aren't guaranteed to follow on from the last span.
 +macro_rules! source {
 +    ($this:ident, $sp:expr) => {
 +        $sp.source_callsite()
 +    };
 +}
 +
 +pub(crate) fn mk_sp(lo: BytePos, hi: BytePos) -> Span {
 +    Span::new(lo, hi, SyntaxContext::root(), None)
 +}
 +
 +pub(crate) fn mk_sp_lo_plus_one(lo: BytePos) -> Span {
 +    Span::new(lo, lo + BytePos(1), SyntaxContext::root(), None)
 +}
 +
 +// Returns `true` if the given span does not intersect with file lines.
 +macro_rules! out_of_file_lines_range {
 +    ($self:ident, $span:expr) => {
 +        !$self.config.file_lines().is_all()
 +            && !$self
 +                .config
 +                .file_lines()
 +                .intersects(&$self.parse_sess.lookup_line_range($span))
 +    };
 +}
 +
 +macro_rules! skip_out_of_file_lines_range {
 +    ($self:ident, $span:expr) => {
 +        if out_of_file_lines_range!($self, $span) {
 +            return None;
 +        }
 +    };
 +}
 +
 +macro_rules! skip_out_of_file_lines_range_visitor {
 +    ($self:ident, $span:expr) => {
 +        if out_of_file_lines_range!($self, $span) {
 +            $self.push_rewrite($span, None);
 +            return;
 +        }
 +    };
 +}
 +
 +// Wraps String in an Option. Returns Some when the string adheres to the
 +// Rewrite constraints defined for the Rewrite trait and None otherwise.
 +pub(crate) fn wrap_str(s: String, max_width: usize, shape: Shape) -> Option<String> {
 +    if is_valid_str(&filter_normal_code(&s), max_width, shape) {
 +        Some(s)
 +    } else {
 +        None
 +    }
 +}
 +
 +fn is_valid_str(snippet: &str, max_width: usize, shape: Shape) -> bool {
 +    if !snippet.is_empty() {
 +        // First line must fits with `shape.width`.
 +        if first_line_width(snippet) > shape.width {
 +            return false;
 +        }
 +        // If the snippet does not include newline, we are done.
 +        if is_single_line(snippet) {
 +            return true;
 +        }
 +        // The other lines must fit within the maximum width.
 +        if snippet
 +            .lines()
 +            .skip(1)
 +            .any(|line| unicode_str_width(line) > max_width)
 +        {
 +            return false;
 +        }
 +        // A special check for the last line, since the caller may
 +        // place trailing characters on this line.
 +        if last_line_width(snippet) > shape.used_width() + shape.width {
 +            return false;
 +        }
 +    }
 +    true
 +}
 +
 +#[inline]
 +pub(crate) fn colon_spaces(config: &Config) -> &'static str {
 +    let before = config.space_before_colon();
 +    let after = config.space_after_colon();
 +    match (before, after) {
 +        (true, true) => " : ",
 +        (true, false) => " :",
 +        (false, true) => ": ",
 +        (false, false) => ":",
 +    }
 +}
 +
 +#[inline]
 +pub(crate) fn left_most_sub_expr(e: &ast::Expr) -> &ast::Expr {
 +    match e.kind {
 +        ast::ExprKind::Call(ref e, _)
 +        | ast::ExprKind::Binary(_, ref e, _)
 +        | ast::ExprKind::Cast(ref e, _)
 +        | ast::ExprKind::Type(ref e, _)
 +        | ast::ExprKind::Assign(ref e, _, _)
 +        | ast::ExprKind::AssignOp(_, ref e, _)
 +        | ast::ExprKind::Field(ref e, _)
 +        | ast::ExprKind::Index(ref e, _)
 +        | ast::ExprKind::Range(Some(ref e), _, _)
 +        | ast::ExprKind::Try(ref e) => left_most_sub_expr(e),
 +        _ => e,
 +    }
 +}
 +
 +#[inline]
 +pub(crate) fn starts_with_newline(s: &str) -> bool {
 +    s.starts_with('\n') || s.starts_with("\r\n")
 +}
 +
 +#[inline]
 +pub(crate) fn first_line_ends_with(s: &str, c: char) -> bool {
 +    s.lines().next().map_or(false, |l| l.ends_with(c))
 +}
 +
 +// States whether an expression's last line exclusively consists of closing
 +// parens, braces, and brackets in its idiomatic formatting.
 +pub(crate) fn is_block_expr(context: &RewriteContext<'_>, expr: &ast::Expr, repr: &str) -> bool {
 +    match expr.kind {
 +        ast::ExprKind::MacCall(..)
 +        | ast::ExprKind::Call(..)
 +        | ast::ExprKind::MethodCall(..)
 +        | ast::ExprKind::Array(..)
 +        | ast::ExprKind::Struct(..)
 +        | ast::ExprKind::While(..)
 +        | ast::ExprKind::If(..)
 +        | ast::ExprKind::Block(..)
 +        | ast::ExprKind::ConstBlock(..)
 +        | ast::ExprKind::Async(..)
 +        | ast::ExprKind::Loop(..)
 +        | ast::ExprKind::ForLoop(..)
 +        | ast::ExprKind::TryBlock(..)
 +        | ast::ExprKind::Match(..) => repr.contains('\n'),
 +        ast::ExprKind::Paren(ref expr)
 +        | ast::ExprKind::Binary(_, _, ref expr)
 +        | ast::ExprKind::Index(_, ref expr)
 +        | ast::ExprKind::Unary(_, ref expr)
 +        | ast::ExprKind::Closure(_, _, _, _, ref expr, _)
 +        | ast::ExprKind::Try(ref expr)
 +        | ast::ExprKind::Yield(Some(ref expr)) => is_block_expr(context, expr, repr),
 +        // This can only be a string lit
 +        ast::ExprKind::Lit(_) => {
 +            repr.contains('\n') && trimmed_last_line_width(repr) <= context.config.tab_spaces()
 +        }
 +        ast::ExprKind::AddrOf(..)
 +        | ast::ExprKind::Assign(..)
 +        | ast::ExprKind::AssignOp(..)
 +        | ast::ExprKind::Await(..)
 +        | ast::ExprKind::Box(..)
 +        | ast::ExprKind::Break(..)
 +        | ast::ExprKind::Cast(..)
 +        | ast::ExprKind::Continue(..)
 +        | ast::ExprKind::Err
 +        | ast::ExprKind::Field(..)
 +        | ast::ExprKind::InlineAsm(..)
 +        | ast::ExprKind::Let(..)
 +        | ast::ExprKind::Path(..)
 +        | ast::ExprKind::Range(..)
 +        | ast::ExprKind::Repeat(..)
 +        | ast::ExprKind::Ret(..)
 +        | ast::ExprKind::Tup(..)
 +        | ast::ExprKind::Type(..)
 +        | ast::ExprKind::Yield(None)
 +        | ast::ExprKind::Underscore => false,
 +    }
 +}
 +
 +/// Removes trailing spaces from the specified snippet. We do not remove spaces
 +/// inside strings or comments.
 +pub(crate) fn remove_trailing_white_spaces(text: &str) -> String {
 +    let mut buffer = String::with_capacity(text.len());
 +    let mut space_buffer = String::with_capacity(128);
 +    for (char_kind, c) in CharClasses::new(text.chars()) {
 +        match c {
 +            '\n' => {
 +                if char_kind == FullCodeCharKind::InString {
 +                    buffer.push_str(&space_buffer);
 +                }
 +                space_buffer.clear();
 +                buffer.push('\n');
 +            }
 +            _ if c.is_whitespace() => {
 +                space_buffer.push(c);
 +            }
 +            _ => {
 +                if !space_buffer.is_empty() {
 +                    buffer.push_str(&space_buffer);
 +                    space_buffer.clear();
 +                }
 +                buffer.push(c);
 +            }
 +        }
 +    }
 +    buffer
 +}
 +
 +/// Indent each line according to the specified `indent`.
 +/// e.g.
 +///
 +/// ```rust,compile_fail
 +/// foo!{
 +/// x,
 +/// y,
 +/// foo(
 +///     a,
 +///     b,
 +///     c,
 +/// ),
 +/// }
 +/// ```
 +///
 +/// will become
 +///
 +/// ```rust,compile_fail
 +/// foo!{
 +///     x,
 +///     y,
 +///     foo(
 +///         a,
 +///         b,
 +///         c,
 +///     ),
 +/// }
 +/// ```
 +pub(crate) fn trim_left_preserve_layout(
 +    orig: &str,
 +    indent: Indent,
 +    config: &Config,
 +) -> Option<String> {
 +    let mut lines = LineClasses::new(orig);
 +    let first_line = lines.next().map(|(_, s)| s.trim_end().to_owned())?;
 +    let mut trimmed_lines = Vec::with_capacity(16);
 +
 +    let mut veto_trim = false;
 +    let min_prefix_space_width = lines
 +        .filter_map(|(kind, line)| {
 +            let mut trimmed = true;
 +            let prefix_space_width = if is_empty_line(&line) {
 +                None
 +            } else {
 +                Some(get_prefix_space_width(config, &line))
 +            };
 +
 +            // just InString{Commented} in order to allow the start of a string to be indented
 +            let new_veto_trim_value = (kind == FullCodeCharKind::InString
 +                || (config.version() == Version::Two
 +                    && kind == FullCodeCharKind::InStringCommented))
 +                && !line.ends_with('\\');
 +            let line = if veto_trim || new_veto_trim_value {
 +                veto_trim = new_veto_trim_value;
 +                trimmed = false;
 +                line
 +            } else {
 +                line.trim().to_owned()
 +            };
 +            trimmed_lines.push((trimmed, line, prefix_space_width));
 +
 +            // Because there is a veto against trimming and indenting lines within a string,
 +            // such lines should not be taken into account when computing the minimum.
 +            match kind {
 +                FullCodeCharKind::InStringCommented | FullCodeCharKind::EndStringCommented
 +                    if config.version() == Version::Two =>
 +                {
 +                    None
 +                }
 +                FullCodeCharKind::InString | FullCodeCharKind::EndString => None,
 +                _ => prefix_space_width,
 +            }
 +        })
 +        .min()?;
 +
 +    Some(
 +        first_line
 +            + "\n"
 +            + &trimmed_lines
 +                .iter()
 +                .map(
 +                    |&(trimmed, ref line, prefix_space_width)| match prefix_space_width {
 +                        _ if !trimmed => line.to_owned(),
 +                        Some(original_indent_width) => {
 +                            let new_indent_width = indent.width()
 +                                + original_indent_width.saturating_sub(min_prefix_space_width);
 +                            let new_indent = Indent::from_width(config, new_indent_width);
 +                            format!("{}{}", new_indent.to_string(config), line)
 +                        }
 +                        None => String::new(),
 +                    },
 +                )
 +                .collect::<Vec<_>>()
 +                .join("\n"),
 +    )
 +}
 +
 +/// Based on the given line, determine if the next line can be indented or not.
++/// This allows to preserve the indentation of multi-line literals when
++/// re-inserted a code block that has been formatted separately from the rest
++/// of the code, such as code in macro defs or code blocks doc comments.
++pub(crate) fn indent_next_line(kind: FullCodeCharKind, line: &str, config: &Config) -> bool {
++    if kind.is_string() {
++        // If the string ends with '\', the string has been wrapped over
++        // multiple lines. If `format_strings = true`, then the indentation of
++        // strings wrapped over multiple lines will have been adjusted while
++        // formatting the code block, therefore the string's indentation needs
++        // to be adjusted for the code surrounding the code block.
++        config.format_strings() && line.ends_with('\\')
++    } else if config.version() == Version::Two {
++        !kind.is_commented_string()
++    } else {
++        true
++    }
 +}
 +
 +pub(crate) fn is_empty_line(s: &str) -> bool {
 +    s.is_empty() || s.chars().all(char::is_whitespace)
 +}
 +
 +fn get_prefix_space_width(config: &Config, s: &str) -> usize {
 +    let mut width = 0;
 +    for c in s.chars() {
 +        match c {
 +            ' ' => width += 1,
 +            '\t' => width += config.tab_spaces(),
 +            _ => return width,
 +        }
 +    }
 +    width
 +}
 +
 +pub(crate) trait NodeIdExt {
 +    fn root() -> Self;
 +}
 +
 +impl NodeIdExt for NodeId {
 +    fn root() -> NodeId {
 +        NodeId::placeholder_from_expn_id(LocalExpnId::ROOT)
 +    }
 +}
 +
 +pub(crate) fn unicode_str_width(s: &str) -> usize {
 +    s.width()
 +}
 +
 +#[cfg(test)]
 +mod test {
 +    use super::*;
 +
 +    #[test]
 +    fn test_remove_trailing_white_spaces() {
 +        let s = "    r#\"\n        test\n    \"#";
 +        assert_eq!(remove_trailing_white_spaces(s), s);
 +    }
 +
 +    #[test]
 +    fn test_trim_left_preserve_layout() {
 +        let s = "aaa\n\tbbb\n    ccc";
 +        let config = Config::default();
 +        let indent = Indent::new(4, 0);
 +        assert_eq!(
 +            trim_left_preserve_layout(s, indent, &config),
 +            Some("aaa\n    bbb\n    ccc".to_string())
 +        );
 +    }
 +}
index c4208848c6c2ab3ad6bbaf5414109ca7f5cba5dd,0000000000000000000000000000000000000000..a06bc995aa55e5885a918ec6a313893f530d1706
mode 100644,000000..100644
--- /dev/null
@@@ -1,285 -1,0 +1,302 @@@
-     let result =
-         rewrite_aligned_items_inner(context, init, init_span, shape.indent, one_line_width)?;
 +// Format with vertical alignment.
 +
 +use std::cmp;
 +
 +use itertools::Itertools;
 +use rustc_ast::ast;
 +use rustc_span::{BytePos, Span};
 +
 +use crate::comment::combine_strs_with_missing_comments;
 +use crate::config::lists::*;
 +use crate::expr::rewrite_field;
 +use crate::items::{rewrite_struct_field, rewrite_struct_field_prefix};
 +use crate::lists::{
 +    definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator,
 +};
 +use crate::rewrite::{Rewrite, RewriteContext};
 +use crate::shape::{Indent, Shape};
 +use crate::source_map::SpanUtils;
 +use crate::spanned::Spanned;
 +use crate::utils::{
 +    contains_skip, is_attributes_extendable, mk_sp, rewrite_ident, trimmed_last_line_width,
 +};
 +
 +pub(crate) trait AlignedItem {
 +    fn skip(&self) -> bool;
 +    fn get_span(&self) -> Span;
 +    fn rewrite_prefix(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String>;
 +    fn rewrite_aligned_item(
 +        &self,
 +        context: &RewriteContext<'_>,
 +        shape: Shape,
 +        prefix_max_width: usize,
 +    ) -> Option<String>;
 +}
 +
 +impl AlignedItem for ast::FieldDef {
 +    fn skip(&self) -> bool {
 +        contains_skip(&self.attrs)
 +    }
 +
 +    fn get_span(&self) -> Span {
 +        self.span()
 +    }
 +
 +    fn rewrite_prefix(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
 +        let attrs_str = self.attrs.rewrite(context, shape)?;
 +        let missing_span = if self.attrs.is_empty() {
 +            mk_sp(self.span.lo(), self.span.lo())
 +        } else {
 +            mk_sp(self.attrs.last().unwrap().span.hi(), self.span.lo())
 +        };
 +        let attrs_extendable = self.ident.is_none() && is_attributes_extendable(&attrs_str);
 +        rewrite_struct_field_prefix(context, self).and_then(|field_str| {
 +            combine_strs_with_missing_comments(
 +                context,
 +                &attrs_str,
 +                &field_str,
 +                missing_span,
 +                shape,
 +                attrs_extendable,
 +            )
 +        })
 +    }
 +
 +    fn rewrite_aligned_item(
 +        &self,
 +        context: &RewriteContext<'_>,
 +        shape: Shape,
 +        prefix_max_width: usize,
 +    ) -> Option<String> {
 +        rewrite_struct_field(context, self, shape, prefix_max_width)
 +    }
 +}
 +
 +impl AlignedItem for ast::ExprField {
 +    fn skip(&self) -> bool {
 +        contains_skip(&self.attrs)
 +    }
 +
 +    fn get_span(&self) -> Span {
 +        self.span()
 +    }
 +
 +    fn rewrite_prefix(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
 +        let attrs_str = self.attrs.rewrite(context, shape)?;
 +        let name = rewrite_ident(context, self.ident);
 +        let missing_span = if self.attrs.is_empty() {
 +            mk_sp(self.span.lo(), self.span.lo())
 +        } else {
 +            mk_sp(self.attrs.last().unwrap().span.hi(), self.span.lo())
 +        };
 +        combine_strs_with_missing_comments(
 +            context,
 +            &attrs_str,
 +            name,
 +            missing_span,
 +            shape,
 +            is_attributes_extendable(&attrs_str),
 +        )
 +    }
 +
 +    fn rewrite_aligned_item(
 +        &self,
 +        context: &RewriteContext<'_>,
 +        shape: Shape,
 +        prefix_max_width: usize,
 +    ) -> Option<String> {
 +        rewrite_field(context, self, shape, prefix_max_width)
 +    }
 +}
 +
 +pub(crate) fn rewrite_with_alignment<T: AlignedItem>(
 +    fields: &[T],
 +    context: &RewriteContext<'_>,
 +    shape: Shape,
 +    span: Span,
 +    one_line_width: usize,
 +) -> Option<String> {
 +    let (spaces, group_index) = if context.config.struct_field_align_threshold() > 0 {
 +        group_aligned_items(context, fields)
 +    } else {
 +        ("", fields.len() - 1)
 +    };
 +    let init = &fields[0..=group_index];
 +    let rest = &fields[group_index + 1..];
 +    let init_last_pos = if rest.is_empty() {
 +        span.hi()
 +    } else {
 +        // Decide whether the missing comments should stick to init or rest.
 +        let init_hi = init[init.len() - 1].get_span().hi();
 +        let rest_lo = rest[0].get_span().lo();
 +        let missing_span = mk_sp(init_hi, rest_lo);
 +        let missing_span = mk_sp(
 +            context.snippet_provider.span_after(missing_span, ","),
 +            missing_span.hi(),
 +        );
 +
 +        let snippet = context.snippet(missing_span);
 +        if snippet.trim_start().starts_with("//") {
 +            let offset = snippet.lines().next().map_or(0, str::len);
 +            // 2 = "," + "\n"
 +            init_hi + BytePos(offset as u32 + 2)
 +        } else if snippet.trim_start().starts_with("/*") {
 +            let comment_lines = snippet
 +                .lines()
 +                .position(|line| line.trim_end().ends_with("*/"))
 +                .unwrap_or(0);
 +
 +            let offset = snippet
 +                .lines()
 +                .take(comment_lines + 1)
 +                .collect::<Vec<_>>()
 +                .join("\n")
 +                .len();
 +
 +            init_hi + BytePos(offset as u32 + 2)
 +        } else {
 +            missing_span.lo()
 +        }
 +    };
 +    let init_span = mk_sp(span.lo(), init_last_pos);
 +    let one_line_width = if rest.is_empty() { one_line_width } else { 0 };
-         .trailing_separator(context.config.trailing_comma())
++
++    // if another group follows, we must force a separator
++    let force_separator = !rest.is_empty();
++
++    let result = rewrite_aligned_items_inner(
++        context,
++        init,
++        init_span,
++        shape.indent,
++        one_line_width,
++        force_separator,
++    )?;
 +    if rest.is_empty() {
 +        Some(result + spaces)
 +    } else {
 +        let rest_span = mk_sp(init_last_pos, span.hi());
 +        let rest_str = rewrite_with_alignment(rest, context, shape, rest_span, one_line_width)?;
 +        Some(format!(
 +            "{}{}\n{}{}",
 +            result,
 +            spaces,
 +            &shape.indent.to_string(context.config),
 +            &rest_str
 +        ))
 +    }
 +}
 +
 +fn struct_field_prefix_max_min_width<T: AlignedItem>(
 +    context: &RewriteContext<'_>,
 +    fields: &[T],
 +    shape: Shape,
 +) -> (usize, usize) {
 +    fields
 +        .iter()
 +        .map(|field| {
 +            field
 +                .rewrite_prefix(context, shape)
 +                .map(|field_str| trimmed_last_line_width(&field_str))
 +        })
 +        .fold_options((0, ::std::usize::MAX), |(max_len, min_len), len| {
 +            (cmp::max(max_len, len), cmp::min(min_len, len))
 +        })
 +        .unwrap_or((0, 0))
 +}
 +
 +fn rewrite_aligned_items_inner<T: AlignedItem>(
 +    context: &RewriteContext<'_>,
 +    fields: &[T],
 +    span: Span,
 +    offset: Indent,
 +    one_line_width: usize,
++    force_trailing_separator: bool,
 +) -> Option<String> {
 +    // 1 = ","
 +    let item_shape = Shape::indented(offset, context.config).sub_width(1)?;
 +    let (mut field_prefix_max_width, field_prefix_min_width) =
 +        struct_field_prefix_max_min_width(context, fields, item_shape);
 +    let max_diff = field_prefix_max_width.saturating_sub(field_prefix_min_width);
 +    if max_diff > context.config.struct_field_align_threshold() {
 +        field_prefix_max_width = 0;
 +    }
 +
 +    let mut items = itemize_list(
 +        context.snippet_provider,
 +        fields.iter(),
 +        "}",
 +        ",",
 +        |field| field.get_span().lo(),
 +        |field| field.get_span().hi(),
 +        |field| field.rewrite_aligned_item(context, item_shape, field_prefix_max_width),
 +        span.lo(),
 +        span.hi(),
 +        false,
 +    )
 +    .collect::<Vec<_>>();
 +
 +    let tactic = definitive_tactic(
 +        &items,
 +        ListTactic::HorizontalVertical,
 +        Separator::Comma,
 +        one_line_width,
 +    );
 +
 +    if tactic == DefinitiveListTactic::Horizontal {
 +        // since the items fits on a line, there is no need to align them
 +        let do_rewrite =
 +            |field: &T| -> Option<String> { field.rewrite_aligned_item(context, item_shape, 0) };
 +        fields
 +            .iter()
 +            .zip(items.iter_mut())
 +            .for_each(|(field, list_item): (&T, &mut ListItem)| {
 +                if list_item.item.is_some() {
 +                    list_item.item = do_rewrite(field);
 +                }
 +            });
 +    }
 +
++    let separator_tactic = if force_trailing_separator {
++        SeparatorTactic::Always
++    } else {
++        context.config.trailing_comma()
++    };
++
 +    let fmt = ListFormatting::new(item_shape, context.config)
 +        .tactic(tactic)
++        .trailing_separator(separator_tactic)
 +        .preserve_newline(true);
 +    write_list(&items, &fmt)
 +}
 +
 +/// Returns the index in `fields` up to which a field belongs to the current group.
 +/// The returned string is the group separator to use when rewriting the fields.
 +/// Groups are defined by blank lines.
 +fn group_aligned_items<T: AlignedItem>(
 +    context: &RewriteContext<'_>,
 +    fields: &[T],
 +) -> (&'static str, usize) {
 +    let mut index = 0;
 +    for i in 0..fields.len() - 1 {
 +        if fields[i].skip() {
 +            return ("", index);
 +        }
 +        let span = mk_sp(fields[i].get_span().hi(), fields[i + 1].get_span().lo());
 +        let snippet = context
 +            .snippet(span)
 +            .lines()
 +            .skip(1)
 +            .collect::<Vec<_>>()
 +            .join("\n");
 +        let has_blank_line = snippet
 +            .lines()
 +            .dropping_back(1)
 +            .any(|l| l.trim().is_empty());
 +        if has_blank_line {
 +            return ("\n", index);
 +        }
 +        index += 1;
 +    }
 +    ("", index)
 +}
index 5493b09e4aa63a177f809837070a353e5ca2a13f,0000000000000000000000000000000000000000..348876cd264fa3a0e0df022a4eb0c7c09cb71fda
mode 100644,000000..100644
--- /dev/null
@@@ -1,73 -1,0 +1,98 @@@
 +// Integration tests for cargo-fmt.
 +
 +use std::env;
++use std::path::Path;
 +use std::process::Command;
 +
 +/// Run the cargo-fmt executable and return its output.
 +fn cargo_fmt(args: &[&str]) -> (String, String) {
 +    let mut bin_dir = env::current_exe().unwrap();
 +    bin_dir.pop(); // chop off test exe name
 +    if bin_dir.ends_with("deps") {
 +        bin_dir.pop();
 +    }
 +    let cmd = bin_dir.join(format!("cargo-fmt{}", env::consts::EXE_SUFFIX));
 +
 +    // Ensure cargo-fmt runs the rustfmt binary from the local target dir.
 +    let path = env::var_os("PATH").unwrap_or_default();
 +    let mut paths = env::split_paths(&path).collect::<Vec<_>>();
 +    paths.insert(0, bin_dir);
 +    let new_path = env::join_paths(paths).unwrap();
 +
 +    match Command::new(&cmd).args(args).env("PATH", new_path).output() {
 +        Ok(output) => (
 +            String::from_utf8(output.stdout).expect("utf-8"),
 +            String::from_utf8(output.stderr).expect("utf-8"),
 +        ),
 +        Err(e) => panic!("failed to run `{:?} {:?}`: {}", cmd, args, e),
 +    }
 +}
 +
 +macro_rules! assert_that {
 +    ($args:expr, $check:ident $check_args:tt) => {
 +        let (stdout, stderr) = cargo_fmt($args);
 +        if !stdout.$check$check_args {
 +            panic!(
 +                "Output not expected for cargo-fmt {:?}\n\
 +                 expected: {}{}\n\
 +                 actual stdout:\n{}\n\
 +                 actual stderr:\n{}",
 +                $args,
 +                stringify!($check),
 +                stringify!($check_args),
 +                stdout,
 +                stderr
 +            );
 +        }
 +    };
 +}
 +
 +#[ignore]
 +#[test]
 +fn version() {
 +    assert_that!(&["--version"], starts_with("rustfmt "));
 +    assert_that!(&["--version"], starts_with("rustfmt "));
 +    assert_that!(&["--", "-V"], starts_with("rustfmt "));
 +    assert_that!(&["--", "--version"], starts_with("rustfmt "));
 +}
 +
 +#[ignore]
 +#[test]
 +fn print_config() {
 +    assert_that!(
 +        &["--", "--print-config", "current", "."],
 +        contains("max_width = ")
 +    );
 +}
 +
 +#[ignore]
 +#[test]
 +fn rustfmt_help() {
 +    assert_that!(&["--", "--help"], contains("Format Rust code"));
 +    assert_that!(&["--", "-h"], contains("Format Rust code"));
 +    assert_that!(&["--", "--help=config"], contains("Configuration Options:"));
 +}
++
++#[ignore]
++#[test]
++fn cargo_fmt_out_of_line_test_modules() {
++    // See also https://github.com/rust-lang/rustfmt/issues/5119
++    let expected_modified_files = [
++        "tests/mod-resolver/test-submodule-issue-5119/src/lib.rs",
++        "tests/mod-resolver/test-submodule-issue-5119/tests/test1.rs",
++        "tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub1.rs",
++        "tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub2.rs",
++        "tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/sub4.rs",
++    ];
++    let args = [
++        "-v",
++        "--check",
++        "--manifest-path",
++        "tests/mod-resolver/test-submodule-issue-5119/Cargo.toml",
++    ];
++    let (stdout, _) = cargo_fmt(&args);
++    for file in expected_modified_files {
++        let path = Path::new(file).canonicalize().unwrap();
++        assert!(stdout.contains(&format!("Diff in {}", path.display())))
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
new file mode 100644 (file)
--- /dev/null
--- /dev/null
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
new file mode 100644 (file)
--- /dev/null
--- /dev/null
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f21af614da057799f1daff3eae932ab1a1505ea6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++mod a;
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cd686f5611690cfd925f399d03360647a4325b11
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++fn main(        ) {   println!("Hello World!")                         }
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..696832913c87931a0548feedac66a7ef85466280
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,3 @@@
++mod a;
++mod b;
++mod c;
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cd686f5611690cfd925f399d03360647a4325b11
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++fn main(        ) {   println!("Hello World!")                         }
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d1604aa23a3cbbb4b4593048ca177187d78009fe
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,3 @@@
++mod e;
++mod f;
++mod g;
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..92c9e3021431f84d78a57e0f2838c145f173a78d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++This file is contained in the './lib/c/d/' directory.
++
++The directory name './lib/c/d/' conflicts with the './lib/c/d.rs' file name.
++
++'./lib/c/d.rs' defines 3 external modules:
++
++    * mod e;
++    * mod f;
++    * mod g;
++
++Module resolution will fail if we look for './lib/c/d/e.rs' or './lib/c/d/e/mod.rs',
++so we should fall back to looking for './lib/c/e.rs', which correctly finds the modlue, that
++rustfmt should format.
++
++'./lib/c/d/f.rs' and './lib/c/d/g/mod.rs' exist at the default submodule paths so we should be able
++to resolve these modules with no problems.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cd686f5611690cfd925f399d03360647a4325b11
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++fn main(        ) {   println!("Hello World!")                         }
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cd686f5611690cfd925f399d03360647a4325b11
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++fn main(        ) {   println!("Hello World!")                         }
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cd686f5611690cfd925f399d03360647a4325b11
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++fn main(        ) {   println!("Hello World!")                         }
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..81904619650f9017626f96c488ffaf52f590b248
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,3 @@@
++mod d;
++
++fn main(        ) {   println!("Hello World!")                         }
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d436a8076cd717accfd288772ca1c2d4e46c41fe
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++This file is contained in the './lib' directory.
++
++The directory name './lib' conflicts with the './lib.rs' file name.
++
++'lib.rs' defines 3 external modules:
++
++    * mod a;
++    * mod b;
++    * mod c;
++
++Module resolution will fail if we look for './lib/a.rs' or './lib/a/mod.rs',
++so we should fall back to looking for './a.rs', which correctly finds the modlue that
++rustfmt should format.
++
++'./lib/b.rs' and './lib/c/mod.rs' exist at the default submodule paths so we should be able
++to resolve these modules with no problems.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2a63c961be8fc3a9b1426c7fe161759c0bc9a5bb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,3 @@@
++// module resolution fails because the path does not exist.
++#[path = "path/to/does_not_exist.rs"]
++mod a;
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4a1eac8965ded13bed593c52e47ea1521b187af2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,2 @@@
++// module resolution fails because `./a/b.rs` does not exist
++mod b;
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f21af614da057799f1daff3eae932ab1a1505ea6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++mod a;
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d9d9e1e3c90877a4a3d73ed258a9120051c69858
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,2 @@@
++// module resolution fails because `./a.rs` does not exist
++mod a;
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0993f12795991d2ec4a75bb396aff7643cfa1d16
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,8 @@@
++[package]
++name = "rustfmt-test-submodule-issue"
++version = "0.1.0"
++edition = "2018"
++
++# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
++
++[dependencies]
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3f7ddba8a288c71986e6735c31f2576befc6cb23
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,7 @@@
++pub fn foo() -> i32 {
++3
++}
++
++pub fn bar() -> i32 {
++4
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..da4e86169ad926dcae7d34de1f480fdd79f32666
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,8 @@@
++mod test1 {
++#[cfg(unix)]
++mod sub1;
++#[cfg(not(unix))]
++mod sub2;
++
++mod sub3;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b760ba23cd2775c3aa55c763f2f86a7791ba50c6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6 @@@
++use rustfmt_test_submodule_issue::foo;
++
++#[test]
++fn test_foo() {
++assert_eq!(3, foo());
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4fd8286eac400a4bc2cc9846aa2341127bf55668
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6 @@@
++use rustfmt_test_submodule_issue::bar;
++
++#[test]
++fn test_bar() {
++assert_eq!(4, bar());
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e029785bc24579303fbcf4f0b5c75b1f880fd0ac
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++mod sub4;
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
new file mode 100644 (file)
--- /dev/null
--- /dev/null
index 8effb1c6fcab640f997c8975b9ce0ed034f96d36,0000000000000000000000000000000000000000..450051d2fec617165fcfe6e02677caf78538782e
mode 100644,000000..100644
--- /dev/null
@@@ -1,108 -1,0 +1,159 @@@
 +//! Integration tests for rustfmt.
 +
 +use std::env;
 +use std::fs::remove_file;
 +use std::path::Path;
 +use std::process::Command;
 +
 +/// Run the rustfmt executable and return its output.
 +fn rustfmt(args: &[&str]) -> (String, String) {
 +    let mut bin_dir = env::current_exe().unwrap();
 +    bin_dir.pop(); // chop off test exe name
 +    if bin_dir.ends_with("deps") {
 +        bin_dir.pop();
 +    }
 +    let cmd = bin_dir.join(format!("rustfmt{}", env::consts::EXE_SUFFIX));
 +
 +    // Ensure the rustfmt binary runs from the local target dir.
 +    let path = env::var_os("PATH").unwrap_or_default();
 +    let mut paths = env::split_paths(&path).collect::<Vec<_>>();
 +    paths.insert(0, bin_dir);
 +    let new_path = env::join_paths(paths).unwrap();
 +
 +    match Command::new(&cmd).args(args).env("PATH", new_path).output() {
 +        Ok(output) => (
 +            String::from_utf8(output.stdout).expect("utf-8"),
 +            String::from_utf8(output.stderr).expect("utf-8"),
 +        ),
 +        Err(e) => panic!("failed to run `{:?} {:?}`: {}", cmd, args, e),
 +    }
 +}
 +
 +macro_rules! assert_that {
 +    ($args:expr, $($check:ident $check_args:tt)&&+) => {
 +        let (stdout, stderr) = rustfmt($args);
 +        if $(!stdout.$check$check_args && !stderr.$check$check_args)||* {
 +            panic!(
 +                "Output not expected for rustfmt {:?}\n\
 +                 expected: {}\n\
 +                 actual stdout:\n{}\n\
 +                 actual stderr:\n{}",
 +                $args,
 +                stringify!($( $check$check_args )&&*),
 +                stdout,
 +                stderr
 +            );
 +        }
 +    };
 +}
 +
 +#[ignore]
 +#[test]
 +fn print_config() {
 +    assert_that!(
 +        &["--print-config", "unknown"],
 +        starts_with("Unknown print-config option")
 +    );
 +    assert_that!(&["--print-config", "default"], contains("max_width = 100"));
 +    assert_that!(&["--print-config", "minimal"], contains("PATH required"));
 +    assert_that!(
 +        &["--print-config", "minimal", "minimal-config"],
 +        contains("doesn't work with standard input.")
 +    );
 +
 +    let (stdout, stderr) = rustfmt(&[
 +        "--print-config",
 +        "minimal",
 +        "minimal-config",
 +        "src/shape.rs",
 +    ]);
 +    assert!(
 +        Path::new("minimal-config").exists(),
 +        "stdout:\n{}\nstderr:\n{}",
 +        stdout,
 +        stderr
 +    );
 +    remove_file("minimal-config").unwrap();
 +}
 +
 +#[ignore]
 +#[test]
 +fn inline_config() {
 +    // single invocation
 +    assert_that!(
 +        &[
 +            "--print-config",
 +            "current",
 +            ".",
 +            "--config=color=Never,edition=2018"
 +        ],
 +        contains("color = \"Never\"") && contains("edition = \"2018\"")
 +    );
 +
 +    // multiple overriding invocations
 +    assert_that!(
 +        &[
 +            "--print-config",
 +            "current",
 +            ".",
 +            "--config",
 +            "color=never,edition=2018",
 +            "--config",
 +            "color=always,format_strings=true"
 +        ],
 +        contains("color = \"Always\"")
 +            && contains("edition = \"2018\"")
 +            && contains("format_strings = true")
 +    );
 +}
++
++#[test]
++fn rustfmt_usage_text() {
++    let args = ["--help"];
++    let (stdout, _) = rustfmt(&args);
++    assert!(stdout.contains("Format Rust code\n\nusage: rustfmt [options] <file>..."));
++}
++
++#[test]
++fn mod_resolution_error_multiple_candidate_files() {
++    // See also https://github.com/rust-lang/rustfmt/issues/5167
++    let default_path = Path::new("tests/mod-resolver/issue-5167/src/a.rs");
++    let secondary_path = Path::new("tests/mod-resolver/issue-5167/src/a/mod.rs");
++    let error_message = format!(
++        "file for module found at both {:?} and {:?}",
++        default_path.canonicalize().unwrap(),
++        secondary_path.canonicalize().unwrap(),
++    );
++
++    let args = ["tests/mod-resolver/issue-5167/src/lib.rs"];
++    let (_stdout, stderr) = rustfmt(&args);
++    assert!(stderr.contains(&error_message))
++}
++
++#[test]
++fn mod_resolution_error_sibling_module_not_found() {
++    let args = ["tests/mod-resolver/module-not-found/sibling_module/lib.rs"];
++    let (_stdout, stderr) = rustfmt(&args);
++    // Module resolution fails because we're unable to find `a.rs` in the same directory as lib.rs
++    assert!(stderr.contains("a.rs does not exist"))
++}
++
++#[test]
++fn mod_resolution_error_relative_module_not_found() {
++    let args = ["tests/mod-resolver/module-not-found/relative_module/lib.rs"];
++    let (_stdout, stderr) = rustfmt(&args);
++    // The file `./a.rs` and directory `./a` both exist.
++    // Module resolution fails becuase we're unable to find `./a/b.rs`
++    #[cfg(not(windows))]
++    assert!(stderr.contains("a/b.rs does not exist"));
++    #[cfg(windows)]
++    assert!(stderr.contains("a\\b.rs does not exist"));
++}
++
++#[test]
++fn mod_resolution_error_path_attribute_does_not_exist() {
++    let args = ["tests/mod-resolver/module-not-found/bad_path_attribute/lib.rs"];
++    let (_stdout, stderr) = rustfmt(&args);
++    // The path attribute points to a file that does not exist
++    assert!(stderr.contains("does_not_exist.rs does not exist"));
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..96a31659022aa95834132e850580115e70e595da
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++// rustfmt-imports_granularity: Crate
++
++use foo::a;
++use foo::a;
++use foo::b;
++use foo::b as b2;
++use foo::b::f;
++use foo::b::g;
++use foo::b::g as g2;
++use foo::c;
++use foo::d::e;
++use qux::h;
++use qux::h as h2;
++use qux::i;
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3e9139177c56ed5df57c2aeb1d73985344581687
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,33 @@@
++// rustfmt-imports_granularity: Module
++
++#![allow(dead_code)]
++
++mod a {
++    pub mod b {
++        pub struct Data {
++            pub a: i32,
++        }
++    }
++
++    use crate::a::b::Data;
++    use crate::a::b::Data as Data2;
++
++    pub fn data(a: i32) -> Data {
++        Data { a }
++    }
++
++    pub fn data2(a: i32) -> Data2 {
++        Data2 { a }
++    }
++
++    #[cfg(test)]
++    mod tests {
++        use super::*;
++
++        #[test]
++        pub fn test() {
++            data(1);
++            data2(1);
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..61ddf13410d4b6f3935e31508e7578b9007c7f39
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++// rustfmt-imports_granularity: One
++
++pub use foo::x;
++pub use foo::x as x2;
++pub use foo::y;
++use bar::a;
++use bar::b;
++use bar::b::f;
++use bar::b::f as f2;
++use bar::b::g;
++use bar::c;
++use bar::d::e;
++use bar::d::e as e2;
++use qux::h;
++use qux::i;
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7d0d70919a607f1126bc52ccd5790ed636e834ff
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++// rustfmt-short_array_element_width_threshold: 10
++
++fn main() {
++    pub const FORMAT_TEST: [u64; 5] = [
++        0x0000000000000000,
++        0xaaaaaaaaaaaaaaaa,
++        0xbbbbbbbbbbbbbbbb,
++        0xcccccccccccccccc,
++        0xdddddddddddddddd,
++    ];
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8a93a51d6a2814a667df2a446c12d672082b7347
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++// rustfmt-short_array_element_width_threshold: 20
++
++fn main() {
++    pub const FORMAT_TEST: [u64; 5] = [
++        0x0000000000000000,
++        0xaaaaaaaaaaaaaaaa,
++        0xbbbbbbbbbbbbbbbb,
++        0xcccccccccccccccc,
++        0xdddddddddddddddd,
++    ];
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..710b6fe7c4ba0c764483e12f539644aca109bf3b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++// rustfmt-max_width: 20
++// rustfmt-short_array_element_width_threshold: 30
++
++fn main() {
++    pub const FORMAT_TEST: [u64; 5] = [
++        0x0000000000000000,
++        0xaaaaaaaaaaaaaaaa,
++        0xbbbbbbbbbbbbbbbb,
++        0xcccccccccccccccc,
++        0xdddddddddddddddd,
++    ];
++}
index d0a033b12432aaa95b55d62f799648a078fde672,0000000000000000000000000000000000000000..f51ba6e98c9f597d7f0c4a080c3269eb11c9e682
mode 100644,000000..100644
--- /dev/null
@@@ -1,79 -1,0 +1,92 @@@
 +// rustfmt-normalize_comments: true
 +
 + extern crate       foo    ;   
 +    extern crate       foo       as bar    ;   
 +
 +extern crate futures;
 +extern crate dotenv;
 +extern crate chrono;
 +
 +extern crate foo;
 +extern crate bar;
 +
 +// #2315
 +extern crate proc_macro2;
 +extern crate proc_macro;
 +
 +// #3128
 +extern crate serde; // 1.0.78
 +extern crate serde_derive; // 1.0.78
 +extern crate serde_json; // 1.0.27
 +
 + extern  "C" {
 +  fn c_func(x: *mut *mut libc::c_void);
 +
 +  fn c_func(x: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX, y: YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY);
 +
 +    #[test123]
 +    fn foo() -> uint64_t;
 +
 +pub fn bar() ;
 +   }
 +
 +extern {
 +        fn DMR_GetDevice(pHDev: *mut HDEV, searchMode: DeviceSearchMode, pSearchString: *const c_char, devNr: c_uint, wildcard: c_char) -> TDMR_ERROR;
 +        
 +    fn quux() -> (); // Post comment
 +
 +  pub      type
 +      Foo;
 +
 +          type   Bar;
 +}
 +
 +extern "Rust" { static ext:  u32;
 +    // Some comment.
 +     pub static  mut var : SomeType ; }
 +
 +extern "C" {
 +            fn syscall(number: libc::c_long /* comment 1 */, /* comm 2 */ ... /* sup? */) -> libc::c_long;
 +
 +    fn  foo  (x:  *const c_char ,    ...   ) ->
 +libc::c_long;
 +        }
 +
 +   extern    {  
 +                       pub fn freopen(filename: *const c_char, mode: *const c_char
 +                               , mode2: *const c_char
 +                               , mode3: *const c_char,
 +                               file: *mut FILE)
 +                              -> *mut FILE;
 +
 +
 +       const fn foo(
 +
 +       ) ->
 +                            *mut Bar;
 +       unsafe fn foo(
 +
 +       ) -> *
 +       mut
 +       Bar;
 +
 +       pub(super) const fn foo() -> *mut Bar;
 +       pub(crate) unsafe fn foo() -> *mut Bar;
 +   }
 +
 +extern {
 +
 +}
++
++macro_rules! x {
++    ($tt:tt) => {};
++}
++
++extern "macros" {
++    x!(ident);
++    x!(#);
++    x![ident];
++    x![#];
++    x! {ident}
++    x! {#}
++}
index 5a4fad5872bdd2cc5fccc3f06137ae98fb1e9f40,0000000000000000000000000000000000000000..2d7bb299aaacef723e33266587af6c1dca936d6a
mode 100644,000000..100644
--- /dev/null
@@@ -1,18 -1,0 +1,19 @@@
 +// rustfmt-imports_granularity: Module
 +
 +use a::{b::c, d::e};
 +use a::{f, g::{h, i}};
 +use a::{j::{self, k::{self, l}, m}, n::{o::p, q}};
 +pub use a::{r::s, t};
++use b::{c::d, self};
 +
 +#[cfg(test)]
 +use foo::{a::b, c::d};
 +use foo::e;
 +
 +use bar::{
 +    // comment
 +    a::b,
 +    // more comment
 +    c::d,
 +    e::f,
 +};
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9f9675f51631a76519d27ba428fa8e724e8e2f0a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++// rustfmt-format_strings: true
++
++macro_rules! test {
++    () => {
++        fn from() {
++            None.expect(
++                "We asserted that `buffer.len()` is exactly `$n` so we can expect `ApInt::from_iter` to be successful.",
++            )
++        }
++    };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e1865dd0868b61ec0613eab13e7ce183f03b2b00
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++// rustfmt-format_strings: true
++// rustfmt-hard_tabs: true
++
++macro_rules! test {
++    () => {
++        fn from() {
++            None.expect(
++                "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
++            )
++        }
++    };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fa54d2e3e09cbe58bcc0d9ba1a7ba1ed3d759005
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++// rustfmt-format_strings: true
++
++macro_rules! test {
++    () => {
++        fn from() {
++            None.expect(
++                "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
++            )
++        }
++    };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4760022eeaf0d7de1497c1b92ac252137f85f8c9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++// rustfmt-struct_field_align_threshold: 30
++// rustfmt-trailing_comma: Never
++
++struct Foo {
++    group_a: u8,
++
++    group_b: u8,
++}
++
++struct Bar {
++    group_a: u8,
++
++    group_b: u8
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c56c70faeae40f4916275883540b10b342fca9da
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++// rustfmt-struct_field_align_threshold: 30
++// rustfmt-trailing_comma: Always
++
++struct Foo {
++    group_a: u8,
++
++    group_b: u8
++}
++
++struct Bar {
++    group_a: u8,
++
++    group_b: u8,
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ae1c723eff76aa9334c8270c2b14b525e3f25d3f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++// rustfmt-wrap_comments: true
++
++/// A comment to test special unicode characters on boundaries
++/// æ˜¯ï¼Œæ˜¯ï¼Œæ˜¯ï¼Œæ˜¯ï¼Œæ˜¯ï¼Œæ˜¯ï¼Œæ˜¯ï¼Œæ˜¯ï¼Œæ˜¯ï¼Œæ˜¯ï¼Œæ˜¯ï¼Œæ˜¯  it should break right here this goes to the next line
++fn main() {
++    if xxx {
++        let xxx = xxx
++            .into_iter()
++            .filter(|(xxx, xxx)| {
++                if let Some(x) = Some(1) {
++                    // xxxxxxxxxxxxxxxxxx, xxxxxxxxxxxx, xxxxxxxxxxxxxxxxxxxx xxx xxxxxxx, xxxxx xxx
++                    // xxxxxxxxxx. xxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxx xxx xxxxxxx
++                    // æ˜¯sdfadsdfxxxxxxxxx,sdfaxxxxxx_xxxxx_masdfaonxxx,
++                    if false {
++                        return true;
++                    }
++                }
++                false
++            })
++            .collect();
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5d171f32a1aea10b1bfdd19915f44690df52eab1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++fn main() {
++    // 5042 deals with trailing commas, not the indentation issue of these comments
++    // When a future PR fixes the inentation issues these test can be updated
++    let _ = std::ops::Add::add(10, 20
++        // ...
++        // ...,
++        );
++
++    let _ = std::ops::Add::add(10, 20
++        /* ... */
++        // ...,
++        );
++
++    let _ = std::ops::Add::add(10, 20
++        // ...,
++        // ...,
++        );
++
++    let _ = std::ops::Add::add(10, 20
++        // ...,
++        /* ...
++        */,
++        );
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b8a824b34b796e32d611c4b024385f5e2959a740
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++fn main() {
++    // 5042 deals with trailing commas, not the indentation issue of these comments
++    // When a future PR fixes the inentation issues these test can be updated
++    let _ = std::ops::Add::add(10, 20
++        // ...
++        // ...
++        );
++
++    let _ = std::ops::Add::add(10, 20
++        /* ... */
++        // ...
++        );
++
++    let _ = std::ops::Add::add(10, 20
++        // ...
++        // ...
++        );
++
++    let _ = std::ops::Add::add(10, 20
++        // ...
++        /* ...
++        */
++        );
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bd765b7b41f4972d29cdec26c8ae4021e4d92d06
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++fn main() {
++    let _ = std::ops::Add::add(10, 20
++        // ...,
++        );
++
++    let _ = std::ops::Add::add(10, 20
++        /* ... */,
++        );
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2ed8de875add8b87722dbda589b108ca26ac8969
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++fn main() {
++    let _ = std::ops::Add::add(10, 20
++        // ...
++        );
++
++    let _ = std::ops::Add::add(10, 20
++        /* ... */
++        );
++}
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5c1d79a74309876f6f04b626bad77e117bd7bcc2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,4 @@@
++// rustfmt-wrap_comments: true
++
++///        > For each sample received, the middleware internally maintains a sample_state relative to each DataReader. The sample_state can either be READ or NOT_READ.
++fn block_quote() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cf200d04e08ef9bdb77717872584a1c86156590a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++// rustfmt-wrap_comments: true
++
++/// > For each sample received, the middleware internally maintains a sample_state relative to each DataReader. The sample_state can either be READ or NOT_READ.
++///
++/// > > For each sample received, the middleware internally maintains a sample_state relative to each DataReader. The sample_state can either be READ or NOT_READ.
++///
++/// > > > For each sample received, the middleware internally maintains a sample_state relative to each DataReader. The sample_state can either be READ or NOT_READ.
++///
++/// > > > > > > > > For each sample received, the middleware internally maintains a sample_state relative to each DataReader. The sample_state can either be READ or NOT_READ.
++fn block_quote() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..eb436402e4e00fd2be93a07da36b6c2529d5a2d6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,4 @@@
++// rustfmt-wrap_comments: true
++
++/// > For each sample received, the middleware internally maintains a sample_state relative to each DataReader. The sample_state can either be READ or NOT_READ.
++fn block_quote() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..229c6e5753d2e994eafdab282ee0015b4b5da4c1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++// rustfmt-wrap_comments: false
++
++/// no markdown header so rustfmt should wrap this comment when `format_code_in_doc_comments = true` and `wrap_comments = true`
++fn not_documented_with_markdown_header() {
++    // This is just a normal inline comment so rustfmt should wrap this comment when `wrap_comments = true`
++}
++
++/// # We're using a markdown header here so rustfmt should refuse to wrap this comment in all circumstances
++fn documented_with_markdown_header() {
++    // # We're using a markdown header in an inline comment. rustfmt should be able to wrap this comment when `wrap_comments = true`
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c547ff35c691bc14155dd9e0b55cbebdf0b0f282
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++// rustfmt-wrap_comments: true
++
++/// no markdown header so rustfmt should wrap this comment when `format_code_in_doc_comments = true` and `wrap_comments = true`
++fn not_documented_with_markdown_header() {
++    // This is just a normal inline comment so rustfmt should wrap this comment when `wrap_comments = true`
++}
++
++/// # We're using a markdown header here so rustfmt should refuse to wrap this comment in all circumstances
++fn documented_with_markdown_header() {
++    // # We're using a markdown header in an inline comment. rustfmt should be able to wrap this comment when `wrap_comments = true`
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b31bbf095e73074b6ad6c9757bd3597114ff06fb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,62 @@@
++// rustfmt-merge_derives:true
++
++#[rustfmt::skip::attributes(derive)]
++#[allow(dead_code)]
++#[derive(StructField)]
++#[derive(Clone)]
++struct DoNotMergeDerives {
++    field: String,
++}
++
++#[allow(dead_code)]
++#[derive(StructField)]
++#[rustfmt::skip::attributes(derive)]
++#[derive(Clone)]
++struct DoNotMergeDerivesSkipInMiddle {
++    field: String,
++}
++
++#[allow(dead_code)]
++#[derive(StructField)]
++#[derive(Clone)]
++#[rustfmt::skip::attributes(derive)]
++struct DoNotMergeDerivesSkipAtEnd {
++    field: String,
++}
++
++#[allow(dead_code)]
++#[derive(StructField)]
++#[derive(Clone)]
++struct MergeDerives {
++    field: String,
++}
++
++mod inner_attribute_derive_skip {
++    #![rustfmt::skip::attributes(derive)]
++
++    #[allow(dead_code)]
++    #[derive(StructField)]
++    #[derive(Clone)]
++    struct DoNotMergeDerives {
++        field: String,
++    }
++}
++
++#[rustfmt::skip::attributes(derive)]
++mod outer_attribute_derive_skip {
++    #[allow(dead_code)]
++    #[derive(StructField)]
++    #[derive(Clone)]
++    struct DoNotMergeDerives {
++        field: String,
++    }
++}
++
++mod no_derive_skip {
++    #[allow(dead_code)]
++    #[derive(StructField)]
++    #[derive(Clone)]
++    struct MergeDerives {
++        field: String,
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..35d6e21affeeb57a21275d234bdbb4259d504356
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,113 @@@
++struct Struct {
++    // Multiline comment
++    // should be formatted
++    // properly.
++}
++
++struct Struct2 {
++    // This formatting
++// Should be changed
++}
++
++struct Struct3(
++    // This
++    // is
++    // correct
++);
++
++struct Struct4(
++    // This
++// is
++// not
++// correct
++);
++
++struct Struct5 {
++    /*
++    Comment block
++    with many lines.
++    */
++}
++
++struct Struct6(
++    /*
++    Comment block
++    with many lines.
++    */
++);
++
++struct Struct7 {
++    /*
++Invalid
++format
++*/
++}
++
++struct Struct8(
++    /*
++Invalid
++format
++*/
++);
++
++struct Struct9 { /* bar */ }
++
++struct Struct10 { /* bar
++baz
++*/ }
++
++mod module {
++    struct Struct {
++        // Multiline comment
++        // should be formatted
++        // properly.
++    }
++
++    struct Struct2 {
++        // This formatting
++// Should be changed
++    }
++
++    struct Struct3(
++        // This
++        // is
++        // correct
++    );
++
++    struct Struct4(
++        // This
++    // is
++    // not
++// correct
++    );
++
++    struct Struct5 {
++        /*
++        Comment block
++        with many lines.
++         */
++    }
++
++    struct Struct6(
++        /*
++        Comment block
++        with many lines.
++        */
++    );
++
++    struct Struct7 {
++        /*
++Invalid
++format
++*/
++    }
++
++    struct Struct8(
++        /*
++Invalid
++format
++*/
++    );
++
++    struct Struct9 { /* bar */ }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..557d66703554614633f3d36c8ef41471113d58a9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++// rustfmt-imports_granularity: Crate
++
++use foo::{
++    a, b, b as b2,
++    b::{f, g, g as g2},
++    c,
++    d::e,
++};
++use qux::{h, h as h2, i};
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..763024d6fa4959d5db34f2fbd19f08d29cc96a9c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,32 @@@
++// rustfmt-imports_granularity: Module
++
++#![allow(dead_code)]
++
++mod a {
++    pub mod b {
++        pub struct Data {
++            pub a: i32,
++        }
++    }
++
++    use crate::a::b::{Data, Data as Data2};
++
++    pub fn data(a: i32) -> Data {
++        Data { a }
++    }
++
++    pub fn data2(a: i32) -> Data2 {
++        Data2 { a }
++    }
++
++    #[cfg(test)]
++    mod tests {
++        use super::*;
++
++        #[test]
++        pub fn test() {
++            data(1);
++            data2(1);
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a086dae5a42218d77867d6d9420a1d8f2fecdabb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++// rustfmt-imports_granularity: One
++
++pub use foo::{x, x as x2, y};
++use {
++    bar::{
++        a,
++        b::{self, f, g},
++        c,
++        d::{e, e as e2},
++    },
++    qux::{h, i},
++};
index be31bf0a33198511f63e93dae7bec3cc09bd0fa1,0000000000000000000000000000000000000000..be4b7a8c42e3e38dbe195d5fddfca2f466aca015
mode 100644,000000..100644
--- /dev/null
@@@ -1,96 -1,0 +1,94 @@@
-     // This can be changed once https://github.com/rust-lang/rustfmt/issues/4854 is fixed
- // Expand as needed, numbers should be ascending according to the stage
- // through the inclusion pipeline, or according to the descriptions
 +// rustfmt-format_code_in_doc_comments: true
 +
 +// https://github.com/rust-lang/rustfmt/issues/4420
 +enum Minimal {
 +    Example,
 +    //[thisisremoved thatsleft
 +    // canbeanything
 +}
 +
 +struct Minimal2 {
 +    Example: usize,
 +    //[thisisremoved thatsleft
 +    // canbeanything
 +}
 +
 +pub enum E {
 +    // Expand as needed, numbers should be ascending according to the stage
 +    // through the inclusion pipeline, or according to the descriptions
 +    Variant1,
 +    // Expand as needed, numbers should be ascending according to the stage
 +    // through the inclusion pipeline, or according to the descriptions
 +    Variant2,
 +    // Expand as needed, numbers should be ascending according to the stage
 +    // through the inclusion pipeline, or according to the descriptions
 +}
 +
 +pub enum E2 {
-     // This can be changed once https://github.com/rust-lang/rustfmt/issues/4854 is fixed
- // Expand as needed, numbers should be ascending according to the stage
- // through the inclusion pipeline, or according to the descriptions
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
 +}
 +
 +pub struct S {
 +    // Expand as needed, numbers should be ascending according to the stage
 +    // through the inclusion pipeline, or according to the descriptions
 +    some_field: usize,
 +    // Expand as needed, numbers should be ascending according to the stage
 +    // through the inclusion pipeline, or according to the descriptions
 +    last_field: usize,
 +    // Expand as needed, numbers should be ascending according to the stage
 +    // through the inclusion pipeline, or according to the descriptions
 +}
 +
 +pub struct S2 {
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
 +}
 +
 +fn foo(
 +    // Expand as needed, numbers should be ascending according to the stage
 +    // through the inclusion pipeline, or according to the descriptions
 +    a: usize,
 +    // Expand as needed, numbers should be ascending according to the stage
 +    // through the inclusion pipeline, or according to the descriptions
 +    b: usize,
 +    // Expand as needed, numbers should be ascending according to the stage
 +    // through the inclusion pipeline, or according to the descriptions
 +) -> usize {
 +    5
 +}
 +
 +fn foo2(// Expand as needed, numbers should be ascending according to the stage
 +    // through the inclusion pipeline, or according to the descriptions
 +) -> usize {
 +    5
 +}
 +
 +fn main() {
 +    let v = vec![
 +        // Expand as needed, numbers should be ascending according to the stage
 +        // through the inclusion pipeline, or according to the descriptions
 +        1,
 +        // Expand as needed, numbers should be ascending according to the stage
 +        // through the inclusion pipeline, or according to the descriptions
 +        2,
 +        // Expand as needed, numbers should be ascending according to the stage
 +        // through the inclusion pipeline, or according to the descriptions
 +    ];
 +
 +    let v2: Vec<i32> = vec![
 +        // Expand as needed, numbers should be ascending according to the stage
 +        // through the inclusion pipeline, or according to the descriptions
 +    ];
 +
 +    match a {
 +        // Expand as needed, numbers should be ascending according to the stage
 +        // through the inclusion pipeline, or according to the descriptions
 +        b => c,
 +        // Expand as needed, numbers should be ascending according to the stage
 +        // through the inclusion pipeline, or according to the descriptions
 +        d => e,
 +        // Expand as needed, numbers should be ascending according to the stage
 +        // through the inclusion pipeline, or according to the descriptions
 +    }
 +}
index 80aea59d1b520982bdec702488733c7dceb9aa1f,0000000000000000000000000000000000000000..db4da6223721c33ae8667aebb049cb704ac38a77
mode 100644,000000..100644
--- /dev/null
@@@ -1,85 -1,0 +1,83 @@@
-     // This can be changed once https://github.com/rust-lang/rustfmt/issues/4854 is fixed
- // Expand as needed, numbers should be ascending according to the stage
- // through the inclusion pipeline, or according to the descriptions
 +// rustfmt-normalize_comments: true
 +
 +// https://github.com/rust-lang/rustfmt/issues/4909
 +pub enum E {
 +    // Expand as needed, numbers should be ascending according to the stage
 +    // through the inclusion pipeline, or according to the descriptions
 +    Variant1,
 +    // Expand as needed, numbers should be ascending according to the stage
 +    // through the inclusion pipeline, or according to the descriptions
 +    Variant2,
 +    // Expand as needed, numbers should be ascending according to the stage
 +    // through the inclusion pipeline, or according to the descriptions
 +}
 +
 +pub enum E2 {
-     // This can be changed once https://github.com/rust-lang/rustfmt/issues/4854 is fixed
- // Expand as needed, numbers should be ascending according to the stage
- // through the inclusion pipeline, or according to the descriptions
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
 +}
 +
 +pub struct S {
 +    // Expand as needed, numbers should be ascending according to the stage
 +    // through the inclusion pipeline, or according to the descriptions
 +    some_field: usize,
 +    // Expand as needed, numbers should be ascending according to the stage
 +    // through the inclusion pipeline, or according to the descriptions
 +    last_field: usize,
 +    // Expand as needed, numbers should be ascending according to the stage
 +    // through the inclusion pipeline, or according to the descriptions
 +}
 +
 +pub struct S2 {
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
 +}
 +
 +fn foo(
 +    // Expand as needed, numbers should be ascending according to the stage
 +    // through the inclusion pipeline, or according to the descriptions
 +    a: usize,
 +    // Expand as needed, numbers should be ascending according to the stage
 +    // through the inclusion pipeline, or according to the descriptions
 +    b: usize,
 +    // Expand as needed, numbers should be ascending according to the stage
 +    // through the inclusion pipeline, or according to the descriptions
 +) -> usize {
 +    5
 +}
 +
 +fn foo2(// Expand as needed, numbers should be ascending according to the stage
 +    // through the inclusion pipeline, or according to the descriptions
 +) -> usize {
 +    5
 +}
 +
 +fn main() {
 +    let v = vec![
 +        // Expand as needed, numbers should be ascending according to the stage
 +        // through the inclusion pipeline, or according to the descriptions
 +        1,
 +        // Expand as needed, numbers should be ascending according to the stage
 +        // through the inclusion pipeline, or according to the descriptions
 +        2,
 +        // Expand as needed, numbers should be ascending according to the stage
 +        // through the inclusion pipeline, or according to the descriptions
 +    ];
 +
 +    let v2: Vec<i32> = vec![
 +        // Expand as needed, numbers should be ascending according to the stage
 +        // through the inclusion pipeline, or according to the descriptions
 +    ];
 +
 +    // https://github.com/rust-lang/rustfmt/issues/4430
 +    match a {
 +        // Expand as needed, numbers should be ascending according to the stage
 +        // through the inclusion pipeline, or according to the descriptions
 +        b => c,
 +        // Expand as needed, numbers should be ascending according to the stage
 +        // through the inclusion pipeline, or according to the descriptions
 +        d => e,
 +        // Expand as needed, numbers should be ascending according to the stage
 +        // through the inclusion pipeline, or according to the descriptions
 +    }
 +}
index 52315f470e4b9d01a53ba86f767aecbf7270290a,0000000000000000000000000000000000000000..9b9147eb1247a4ce383cb2187bc5892995d6617b
mode 100644,000000..100644
--- /dev/null
@@@ -1,142 -1,0 +1,142 @@@
- // Expand as needed, numbers should be ascending according to the stage
- // through the inclusion pipeline, or according to the descriptions
 +// rustfmt-wrap_comments: true
 +
 +// https://github.com/rust-lang/rustfmt/issues/4909
 +pub enum E {
 +    // Expand as needed, numbers should be ascending according to the stage
 +    // through the inclusion pipeline, or according to the descriptions
 +    Variant1,
 +    // Expand as needed, numbers should be ascending according to the stage
 +    // through the inclusion pipeline, or according to the descriptions
 +    Variant2,
 +    // Expand as needed, numbers should be ascending according to the stage
 +    // through the inclusion pipeline, or according to the descriptions
 +}
 +
 +pub enum E2 {
 +    // This can be changed once https://github.com/rust-lang/rustfmt/issues/4854 is fixed
- // Expand as needed, numbers should be ascending according to the stage
- // through the inclusion pipeline, or according to the descriptions
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
 +}
 +
 +pub enum E3 {
 +    // Expand as needed, numbers should be ascending according to the stage through the inclusion
 +    // pipeline, or according to the descriptions
 +    Variant1,
 +    // Expand as needed, numbers should be ascending according to the stage through the inclusion
 +    // pipeline, or according to the descriptions
 +    Variant2,
 +    // Expand as needed, numbers should be ascending according to the stage through the inclusion
 +    // pipeline, or according to the descriptions
 +}
 +
 +pub struct S {
 +    // Expand as needed, numbers should be ascending according to the stage
 +    // through the inclusion pipeline, or according to the descriptions
 +    some_field: usize,
 +    // Expand as needed, numbers should be ascending according to the stage
 +    // through the inclusion pipeline, or according to the descriptions
 +    last_field: usize,
 +    // Expand as needed, numbers should be ascending according to the stage
 +    // through the inclusion pipeline, or according to the descriptions
 +}
 +
 +pub struct S2 {
 +    // This can be changed once https://github.com/rust-lang/rustfmt/issues/4854 is fixed
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
 +}
 +
 +pub struct S3 {
 +    // Expand as needed, numbers should be ascending according to the stage through the inclusion
 +    // pipeline, or according to the descriptions
 +    some_field: usize,
 +    // Expand as needed, numbers should be ascending according to the stage through the inclusion
 +    // pipeline, or according to the descriptions
 +    last_field: usize,
 +    // Expand as needed, numbers should be ascending according to the stage through the inclusion
 +    // pipeline, or according to the descriptions
 +}
 +
 +fn foo(
 +    // Expand as needed, numbers should be ascending according to the stage
 +    // through the inclusion pipeline, or according to the descriptions
 +    a: usize,
 +    // Expand as needed, numbers should be ascending according to the stage
 +    // through the inclusion pipeline, or according to the descriptions
 +    b: usize,
 +    // Expand as needed, numbers should be ascending according to the stage
 +    // through the inclusion pipeline, or according to the descriptions
 +) -> usize {
 +    5
 +}
 +
 +fn foo2(// Expand as needed, numbers should be ascending according to the stage
 +    // through the inclusion pipeline, or according to the descriptions
 +) -> usize {
 +    5
 +}
 +
 +fn foo3(
 +    // Expand as needed, numbers should be ascending according to the stage through the inclusion
 +    // pipeline, or according to the descriptions
 +    a: usize,
 +    // Expand as needed, numbers should be ascending according to the stage through the inclusion
 +    // pipeline, or according to the descriptions
 +    b: usize,
 +    // Expand as needed, numbers should be ascending according to the stage through the inclusion
 +    // pipeline, or according to the descriptions
 +) -> usize {
 +    5
 +}
 +
 +fn main() {
 +    let v = vec![
 +        // Expand as needed, numbers should be ascending according to the stage
 +        // through the inclusion pipeline, or according to the descriptions
 +        1,
 +        // Expand as needed, numbers should be ascending according to the stage
 +        // through the inclusion pipeline, or according to the descriptions
 +        2,
 +        // Expand as needed, numbers should be ascending according to the stage
 +        // through the inclusion pipeline, or according to the descriptions
 +    ];
 +
 +    let v2: Vec<i32> = vec![
 +        // Expand as needed, numbers should be ascending according to the stage
 +        // through the inclusion pipeline, or according to the descriptions
 +    ];
 +
 +    let v3 = vec![
 +        // Expand as needed, numbers should be ascending according to the stage through the
 +        // inclusion pipeline, or according to the descriptions
 +        1,
 +        // Expand as needed, numbers should be ascending according to the stage through the
 +        // inclusion pipeline, or according to the descriptions
 +        2,
 +        // Expand as needed, numbers should be ascending according to the stage through the
 +        // inclusion pipeline, or according to the descriptions
 +    ];
 +
 +    // https://github.com/rust-lang/rustfmt/issues/4430
 +    match a {
 +        // Expand as needed, numbers should be ascending according to the stage
 +        // through the inclusion pipeline, or according to the descriptions
 +        b => c,
 +        // Expand as needed, numbers should be ascending according to the stage
 +        // through the inclusion pipeline, or according to the descriptions
 +        d => e,
 +        // Expand as needed, numbers should be ascending according to the stage
 +        // through the inclusion pipeline, or according to the descriptions
 +    }
 +
 +    match a {
 +        // Expand as needed, numbers should be ascending according to the stage through the
 +        // inclusion pipeline, or according to the descriptions
 +        b => c,
 +        // Expand as needed, numbers should be ascending according to the stage through the
 +        // inclusion pipeline, or according to the descriptions
 +        d => e,
 +        // Expand as needed, numbers should be ascending according to the stage through the
 +        // inclusion pipeline, or according to the descriptions
 +    }
 +}
index e0bfcf0b5007da31ba944a6cf5ddc517e65a1aad,0000000000000000000000000000000000000000..c1531d22a4a70cd29aae2693dd70950ac27a262a
mode 100644,000000..100644
--- /dev/null
@@@ -1,143 -1,0 +1,143 @@@
- // Expand as needed, numbers should be ascending according to the stage
- // through the inclusion pipeline, or according to the descriptions
 +// rustfmt-normalize_comments: true
 +// rustfmt-wrap_comments: true
 +
 +// https://github.com/rust-lang/rustfmt/issues/4909
 +pub enum E {
 +    // Expand as needed, numbers should be ascending according to the stage
 +    // through the inclusion pipeline, or according to the descriptions
 +    Variant1,
 +    // Expand as needed, numbers should be ascending according to the stage
 +    // through the inclusion pipeline, or according to the descriptions
 +    Variant2,
 +    // Expand as needed, numbers should be ascending according to the stage
 +    // through the inclusion pipeline, or according to the descriptions
 +}
 +
 +pub enum E2 {
 +    // This can be changed once https://github.com/rust-lang/rustfmt/issues/4854 is fixed
- // Expand as needed, numbers should be ascending according to the stage
- // through the inclusion pipeline, or according to the descriptions
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
 +}
 +
 +pub enum E3 {
 +    // Expand as needed, numbers should be ascending according to the stage through the inclusion
 +    // pipeline, or according to the descriptions
 +    Variant1,
 +    // Expand as needed, numbers should be ascending according to the stage through the inclusion
 +    // pipeline, or according to the descriptions
 +    Variant2,
 +    // Expand as needed, numbers should be ascending according to the stage through the inclusion
 +    // pipeline, or according to the descriptions
 +}
 +
 +pub struct S {
 +    // Expand as needed, numbers should be ascending according to the stage
 +    // through the inclusion pipeline, or according to the descriptions
 +    some_field: usize,
 +    // Expand as needed, numbers should be ascending according to the stage
 +    // through the inclusion pipeline, or according to the descriptions
 +    last_field: usize,
 +    // Expand as needed, numbers should be ascending according to the stage
 +    // through the inclusion pipeline, or according to the descriptions
 +}
 +
 +pub struct S2 {
 +    // This can be changed once https://github.com/rust-lang/rustfmt/issues/4854 is fixed
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
 +}
 +
 +pub struct S3 {
 +    // Expand as needed, numbers should be ascending according to the stage through the inclusion
 +    // pipeline, or according to the descriptions
 +    some_field: usize,
 +    // Expand as needed, numbers should be ascending according to the stage through the inclusion
 +    // pipeline, or according to the descriptions
 +    last_field: usize,
 +    // Expand as needed, numbers should be ascending according to the stage through the inclusion
 +    // pipeline, or according to the descriptions
 +}
 +
 +fn foo(
 +    // Expand as needed, numbers should be ascending according to the stage
 +    // through the inclusion pipeline, or according to the descriptions
 +    a: usize,
 +    // Expand as needed, numbers should be ascending according to the stage
 +    // through the inclusion pipeline, or according to the descriptions
 +    b: usize,
 +    // Expand as needed, numbers should be ascending according to the stage
 +    // through the inclusion pipeline, or according to the descriptions
 +) -> usize {
 +    5
 +}
 +
 +fn foo2(// Expand as needed, numbers should be ascending according to the stage
 +    // through the inclusion pipeline, or according to the descriptions
 +) -> usize {
 +    5
 +}
 +
 +fn foo3(
 +    // Expand as needed, numbers should be ascending according to the stage through the inclusion
 +    // pipeline, or according to the descriptions
 +    a: usize,
 +    // Expand as needed, numbers should be ascending according to the stage through the inclusion
 +    // pipeline, or according to the descriptions
 +    b: usize,
 +    // Expand as needed, numbers should be ascending according to the stage through the inclusion
 +    // pipeline, or according to the descriptions
 +) -> usize {
 +    5
 +}
 +
 +fn main() {
 +    let v = vec![
 +        // Expand as needed, numbers should be ascending according to the stage
 +        // through the inclusion pipeline, or according to the descriptions
 +        1,
 +        // Expand as needed, numbers should be ascending according to the stage
 +        // through the inclusion pipeline, or according to the descriptions
 +        2,
 +        // Expand as needed, numbers should be ascending according to the stage
 +        // through the inclusion pipeline, or according to the descriptions
 +    ];
 +
 +    let v2: Vec<i32> = vec![
 +        // Expand as needed, numbers should be ascending according to the stage
 +        // through the inclusion pipeline, or according to the descriptions
 +    ];
 +
 +    let v3 = vec![
 +        // Expand as needed, numbers should be ascending according to the stage through the
 +        // inclusion pipeline, or according to the descriptions
 +        1,
 +        // Expand as needed, numbers should be ascending according to the stage through the
 +        // inclusion pipeline, or according to the descriptions
 +        2,
 +        // Expand as needed, numbers should be ascending according to the stage through the
 +        // inclusion pipeline, or according to the descriptions
 +    ];
 +
 +    // https://github.com/rust-lang/rustfmt/issues/4430
 +    match a {
 +        // Expand as needed, numbers should be ascending according to the stage
 +        // through the inclusion pipeline, or according to the descriptions
 +        b => c,
 +        // Expand as needed, numbers should be ascending according to the stage
 +        // through the inclusion pipeline, or according to the descriptions
 +        d => e,
 +        // Expand as needed, numbers should be ascending according to the stage
 +        // through the inclusion pipeline, or according to the descriptions
 +    }
 +
 +    match a {
 +        // Expand as needed, numbers should be ascending according to the stage through the
 +        // inclusion pipeline, or according to the descriptions
 +        b => c,
 +        // Expand as needed, numbers should be ascending according to the stage through the
 +        // inclusion pipeline, or according to the descriptions
 +        d => e,
 +        // Expand as needed, numbers should be ascending according to the stage through the
 +        // inclusion pipeline, or according to the descriptions
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..78c4adba1c1f184a1b099735d429e06aa70627a3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++// rustfmt-short_array_element_width_threshold: 10
++
++fn main() {
++    pub const FORMAT_TEST: [u64; 5] = [
++        0x0000000000000000,
++        0xaaaaaaaaaaaaaaaa,
++        0xbbbbbbbbbbbbbbbb,
++        0xcccccccccccccccc,
++        0xdddddddddddddddd,
++    ];
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6084690652f0682651bc29fcd59ce47d0e861981
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,8 @@@
++// rustfmt-short_array_element_width_threshold: 20
++
++fn main() {
++    pub const FORMAT_TEST: [u64; 5] = [
++        0x0000000000000000, 0xaaaaaaaaaaaaaaaa, 0xbbbbbbbbbbbbbbbb, 0xcccccccccccccccc,
++        0xdddddddddddddddd,
++    ];
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..710b6fe7c4ba0c764483e12f539644aca109bf3b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++// rustfmt-max_width: 20
++// rustfmt-short_array_element_width_threshold: 30
++
++fn main() {
++    pub const FORMAT_TEST: [u64; 5] = [
++        0x0000000000000000,
++        0xaaaaaaaaaaaaaaaa,
++        0xbbbbbbbbbbbbbbbb,
++        0xcccccccccccccccc,
++        0xdddddddddddddddd,
++    ];
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2efc5e09a3d34e684604f5b4994a382cce1cea8a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++// Non-doc pre-comment of Foo
++/// doc of Foo
++// Non-doc post-comment of Foo
++struct Foo<
++    // Non-doc pre-comment of 'a
++    /// doc of 'a
++    'a,
++    // Non-doc pre-comment of T
++    /// doc of T
++    T,
++    // Non-doc pre-comment of N
++    /// doc of N
++    const N: item,
++>;
index 44ed6d4b4756d098ed066cb1ec0b1a055a6acd77,0000000000000000000000000000000000000000..d1741360cfd6437878535bb9bd840517ebab4282
mode 100644,000000..100644
--- /dev/null
@@@ -1,84 -1,0 +1,97 @@@
 +// rustfmt-normalize_comments: true
 +
 +extern crate foo;
 +extern crate foo as bar;
 +
 +extern crate chrono;
 +extern crate dotenv;
 +extern crate futures;
 +
 +extern crate bar;
 +extern crate foo;
 +
 +// #2315
 +extern crate proc_macro;
 +extern crate proc_macro2;
 +
 +// #3128
 +extern crate serde; // 1.0.78
 +extern crate serde_derive; // 1.0.78
 +extern crate serde_json; // 1.0.27
 +
 +extern "C" {
 +    fn c_func(x: *mut *mut libc::c_void);
 +
 +    fn c_func(
 +        x: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,
 +        y: YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY,
 +    );
 +
 +    #[test123]
 +    fn foo() -> uint64_t;
 +
 +    pub fn bar();
 +}
 +
 +extern "C" {
 +    fn DMR_GetDevice(
 +        pHDev: *mut HDEV,
 +        searchMode: DeviceSearchMode,
 +        pSearchString: *const c_char,
 +        devNr: c_uint,
 +        wildcard: c_char,
 +    ) -> TDMR_ERROR;
 +
 +    fn quux() -> (); // Post comment
 +
 +    pub type Foo;
 +
 +    type Bar;
 +}
 +
 +extern "Rust" {
 +    static ext: u32;
 +    // Some comment.
 +    pub static mut var: SomeType;
 +}
 +
 +extern "C" {
 +    fn syscall(
 +        number: libc::c_long, // comment 1
 +        // comm 2
 +        ... // sup?
 +    ) -> libc::c_long;
 +
 +    fn foo(x: *const c_char, ...) -> libc::c_long;
 +}
 +
 +extern "C" {
 +    pub fn freopen(
 +        filename: *const c_char,
 +        mode: *const c_char,
 +        mode2: *const c_char,
 +        mode3: *const c_char,
 +        file: *mut FILE,
 +    ) -> *mut FILE;
 +
 +    const fn foo() -> *mut Bar;
 +    unsafe fn foo() -> *mut Bar;
 +
 +    pub(super) const fn foo() -> *mut Bar;
 +    pub(crate) unsafe fn foo() -> *mut Bar;
 +}
 +
 +extern "C" {}
++
++macro_rules! x {
++    ($tt:tt) => {};
++}
++
++extern "macros" {
++    x!(ident);
++    x!(#);
++    x![ident];
++    x![#];
++    x! {ident}
++    x! {#}
++}
index 9c1387c466afa6e26cff5985912bad5cb92617df,0000000000000000000000000000000000000000..e4e1a299e58669f809ae8f9826776c9a6466f43b
mode 100644,000000..100644
--- /dev/null
@@@ -1,20 -1,0 +1,22 @@@
 +// rustfmt-imports_granularity: Module
 +
 +use a::b::c;
 +use a::d::e;
 +use a::f;
 +use a::g::{h, i};
 +use a::j::k::{self, l};
 +use a::j::{self, m};
 +use a::n::o::p;
 +use a::n::q;
 +pub use a::r::s;
 +pub use a::t;
++use b::c::d;
++use b::{self};
 +
 +use foo::e;
 +#[cfg(test)]
 +use foo::{a::b, c::d};
 +
 +use bar::a::b;
 +use bar::c::d;
 +use bar::e::f;
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..54e490b7fbeae313c0ef898a30dd2f4fbf7374a5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++// rustfmt-format_strings: true
++
++macro_rules! test {
++    () => {
++        fn from() {
++            None.expect(
++                "We asserted that `buffer.len()` is exactly `$n` so we can expect \
++                 `ApInt::from_iter` to be successful.",
++            )
++        }
++    };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..394dc8633f5344926adf82bad1ac4998882e7af4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++// rustfmt-format_strings: true
++// rustfmt-hard_tabs: true
++
++macro_rules! test {
++      () => {
++              fn from() {
++                      None.expect(
++                              "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor \
++                               incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis \
++                               nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. \
++                               Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu \
++                               fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in \
++                               culpa qui officia deserunt mollit anim id est laborum.",
++                      )
++              }
++      };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..01cafa76b68424b9012e16c6ff3a8c11414ddb31
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++// rustfmt-format_strings: true
++
++macro_rules! test {
++    () => {
++        fn from() {
++            None.expect(
++                "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor \
++                 incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis \
++                 nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. \
++                 Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu \
++                 fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in \
++                 culpa qui officia deserunt mollit anim id est laborum.",
++            )
++        }
++    };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fff58be99a5096d8c5ff07de952585aa3d19c6fc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++// rustfmt-struct_field_align_threshold: 30
++// rustfmt-trailing_comma: Never
++
++struct Foo {
++    group_a: u8,
++
++    group_b: u8
++}
++
++struct Bar {
++    group_a: u8,
++
++    group_b: u8
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..588656b535fa1af1746da854cc1e64dbb303f06e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,70 @@@
++// rustfmt-brace_style: SameLineWhere
++// rustfmt-comment_width: 100
++// rustfmt-edition: 2018
++// rustfmt-fn_args_layout: Compressed
++// rustfmt-hard_tabs: false
++// rustfmt-match_block_trailing_comma: true
++// rustfmt-max_width: 100
++// rustfmt-merge_derives: false
++// rustfmt-newline_style: Unix
++// rustfmt-normalize_doc_attributes: true
++// rustfmt-overflow_delimited_expr: true
++// rustfmt-reorder_imports: false
++// rustfmt-reorder_modules: true
++// rustfmt-struct_field_align_threshold: 20
++// rustfmt-tab_spaces: 4
++// rustfmt-trailing_comma: Never
++// rustfmt-use_small_heuristics: Max
++// rustfmt-use_try_shorthand: true
++// rustfmt-wrap_comments: true
++
++/// Lorem ipsum dolor sit amet.
++#[repr(C)]
++#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
++pub struct BufferAttr {
++    /* NOTE: Blah blah blah blah blah. */
++    /// Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
++    /// ut labore et dolore magna aliqua. Morbi quis commodo odio aenean sed adipiscing. Nunc
++    /// congue nisi vitae suscipit tellus mauris a. Consectetur adipiscing elit pellentesque
++    /// habitant morbi tristique senectus.
++    pub foo: u32,
++
++    /// Elit eget gravida cum sociis natoque penatibus et magnis dis. Consequat semper viverra nam
++    /// libero. Accumsan in nisl nisi scelerisque eu. Pellentesque id nibh tortor id aliquet. Sed
++    /// velit dignissim sodales ut. Facilisis sed odio morbi quis commodo odio aenean sed. Et
++    /// ultrices neque ornare aenean euismod elementum. Condimentum lacinia quis vel eros donec ac
++    /// odio tempor.
++    ///
++    /// Lacinia at quis risus sed vulputate odio ut enim. Etiam erat velit scelerisque in dictum.
++    /// Nibh tellus molestie nunc non blandit massa enim nec. Nascetur ridiculus mus mauris vitae.
++    pub bar: u32,
++
++    /// Mi proin sed libero enim sed faucibus turpis. Amet consectetur adipiscing elit duis
++    /// tristique sollicitudin nibh sit amet. Congue quisque egestas diam in arcu cursus euismod
++    /// quis viverra. Cum sociis natoque penatibus et magnis dis parturient montes. Enim sit amet
++    /// venenatis urna cursus eget nunc scelerisque viverra. Cras semper auctor neque vitae tempus
++    /// quam pellentesque. Tortor posuere ac ut consequat semper viverra nam libero justo. Vitae
++    /// auctor eu augue ut lectus arcu bibendum at. Faucibus vitae aliquet nec ullamcorper sit amet
++    /// risus nullam. Maecenas accumsan lacus vel facilisis volutpat. Arcu non odio euismod
++    /// lacinia.
++    ///
++    /// [`FooBar::beep()`]: crate::foobar::FooBar::beep
++    /// [`FooBar::boop()`]: crate::foobar::FooBar::boop
++    /// [`foobar::BazBaq::BEEP_BOOP`]: crate::foobar::BazBaq::BEEP_BOOP
++    pub baz: u32,
++
++    /// Eu consequat ac felis donec et odio pellentesque diam. Ut eu sem integer vitae justo eget.
++    /// Consequat ac felis donec et odio pellentesque diam volutpat.
++    pub baq: u32,
++
++    /// Amet consectetur adipiscing elit pellentesque habitant. Ut morbi tincidunt augue interdum
++    /// velit euismod in pellentesque. Imperdiet sed euismod nisi porta lorem. Nec tincidunt
++    /// praesent semper feugiat. Facilisis leo vel fringilla est. Egestas diam in arcu cursus
++    /// euismod quis viverra. Sagittis eu volutpat odio facilisis mauris sit amet. Posuere morbi
++    /// leo urna molestie at.
++    ///
++    /// Pretium aenean pharetra magna ac. Nisl condimentum id venenatis a condimentum vitae. Semper
++    /// quis lectus nulla at volutpat diam ut venenatis tellus. Egestas tellus rutrum tellus
++    /// pellentesque eu tincidunt tortor aliquam.
++    pub foobar: u32
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4a37163969ae95959865c3b4cd038a11e7c553d5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,8 @@@
++// rustfmt-struct_field_align_threshold: 0
++// rustfmt-trailing_comma: Never
++
++pub struct Baz {
++    group_a: u8,
++
++    group_b: u8
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..29a224b3f6d9686828106cc84cd9bf25913a7214
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++// rustfmt-struct_field_align_threshold: 30
++// rustfmt-trailing_comma: Always
++
++struct Foo {
++    group_a: u8,
++
++    group_b: u8,
++}
++
++struct Bar {
++    group_a: u8,
++
++    group_b: u8,
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4e84c7d98427a03a70f91a4e37d9ef6082f9cbed
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++// rustfmt-wrap_comments: true
++
++/// A comment to test special unicode characters on boundaries
++/// æ˜¯ï¼Œæ˜¯ï¼Œæ˜¯ï¼Œæ˜¯ï¼Œæ˜¯ï¼Œæ˜¯ï¼Œæ˜¯ï¼Œæ˜¯ï¼Œæ˜¯ï¼Œæ˜¯ï¼Œæ˜¯ï¼Œæ˜¯  it should break right here
++/// this goes to the next line
++fn main() {
++    if xxx {
++        let xxx = xxx
++            .into_iter()
++            .filter(|(xxx, xxx)| {
++                if let Some(x) = Some(1) {
++                    // xxxxxxxxxxxxxxxxxx, xxxxxxxxxxxx, xxxxxxxxxxxxxxxxxxxx xxx xxxxxxx, xxxxx xxx
++                    // xxxxxxxxxx. xxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxx xxx xxxxxxx
++                    // æ˜¯sdfadsdfxxxxxxxxx,sdfaxxxxxx_xxxxx_masdfaonxxx,
++                    if false {
++                        return true;
++                    }
++                }
++                false
++            })
++            .collect();
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1ae1212b488d9112a36be37564bf2db2658ab729
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++fn main() {
++    // 5042 deals with trailing commas, not the indentation issue of these comments
++    // When a future PR fixes the inentation issues these test can be updated
++    let _ = std::ops::Add::add(
++        10, 20, // ...
++           // ...,
++    );
++
++    let _ = std::ops::Add::add(
++        10, 20, /* ... */
++           // ...,
++    );
++
++    let _ = std::ops::Add::add(
++        10, 20, // ...,
++           // ...,
++    );
++
++    let _ = std::ops::Add::add(
++        10, 20, // ...,
++           /* ...
++            */
++    );
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..30d174664c9cfbf5fa6579d314a5ef72caea3abc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++fn main() {
++    // 5042 deals with trailing commas, not the indentation issue of these comments
++    // When a future PR fixes the inentation issues these test can be updated
++    let _ = std::ops::Add::add(
++        10, 20, // ...
++           // ...
++    );
++
++    let _ = std::ops::Add::add(
++        10, 20, /* ... */
++           // ...
++    );
++
++    let _ = std::ops::Add::add(
++        10, 20, // ...
++           // ...
++    );
++
++    let _ = std::ops::Add::add(
++        10, 20, // ...
++           /* ...
++            */
++    );
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..87b651dd285efa6c1421993db791e588b23fb259
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,7 @@@
++fn main() {
++    let _ = std::ops::Add::add(
++        10, 20, // ...,
++    );
++
++    let _ = std::ops::Add::add(10, 20 /* ... */);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..116df86a4b55468d48325adbb0f74fe43789be82
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,7 @@@
++fn main() {
++    let _ = std::ops::Add::add(
++        10, 20, // ...
++    );
++
++    let _ = std::ops::Add::add(10, 20 /* ... */);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5d167932828f3f29f32158c1bd88eb86bdfeb855
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6 @@@
++fn foo(
++    #[unused] a: <u16 as intercom::type_system::ExternType<
++        intercom::type_system::AutomationTypeSystem,
++    >>::ForeignType,
++) {
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cab20381ce8f7151359f3b227be6b0f94539ecac
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++fn middle(
++    a: usize,
++    b: <u16 as intercom::type_system::ExternType<
++        intercom::type_system::AutomationTypeSystem,
++    >>::ForeignType,
++    c: bool,
++) {
++}
++
++fn last(
++    a: usize,
++    b: <u16 as intercom::type_system::ExternType<
++        intercom::type_system::AutomationTypeSystem,
++    >>::ForeignType,
++) {
++}
++
++fn first(
++    a: <u16 as intercom::type_system::ExternType<
++        intercom::type_system::AutomationTypeSystem,
++    >>::ForeignType,
++    b: usize,
++) {
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8003e66968c787c7b308b1c4bf5f58f1737a7e04
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6 @@@
++fn foo(
++    a: <u16 as intercom::type_system::ExternType<
++        intercom::type_system::AutomationTypeSystem,
++    >>::ForeignType,
++) {
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2340b2f3472e985ca4c2b85860051de637a07e2f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,7 @@@
++fn foo(
++    // Pre Comment
++    a: <u16 as intercom::type_system::ExternType<
++        intercom::type_system::AutomationTypeSystem,
++    >>::ForeignType, // Inline comment
++) {
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e47677f203903ea10193d24d7f88fa603cb02185
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6 @@@
++// rustfmt-wrap_comments: true
++
++///        > For each sample received, the middleware internally maintains a
++///        > sample_state relative to each DataReader. The sample_state can
++///        > either be READ or NOT_READ.
++fn block_quote() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..079510442b7992ad183b9edaa458f8c4f7b77f97
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++// rustfmt-wrap_comments: true
++
++/// > For each sample received, the middleware internally maintains a
++/// > sample_state relative to each DataReader. The sample_state can either be
++/// > READ or NOT_READ.
++///
++/// > > For each sample received, the middleware internally maintains a
++/// > > sample_state relative to each DataReader. The sample_state can either be
++/// > > READ or NOT_READ.
++///
++/// > > > For each sample received, the middleware internally maintains a
++/// > > > sample_state relative to each DataReader. The sample_state can either
++/// > > > be READ or NOT_READ.
++///
++/// > > > > > > > > For each sample received, the middleware internally
++/// > > > > > > > > maintains a sample_state relative to each DataReader. The
++/// > > > > > > > > sample_state can either be READ or NOT_READ.
++fn block_quote() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..029ee37d22a8ba73722459683bdbe1982302bca3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6 @@@
++// rustfmt-wrap_comments: true
++
++/// > For each sample received, the middleware internally maintains a
++/// > sample_state relative to each DataReader. The sample_state can either be
++/// > READ or NOT_READ.
++fn block_quote() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..229c6e5753d2e994eafdab282ee0015b4b5da4c1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++// rustfmt-wrap_comments: false
++
++/// no markdown header so rustfmt should wrap this comment when `format_code_in_doc_comments = true` and `wrap_comments = true`
++fn not_documented_with_markdown_header() {
++    // This is just a normal inline comment so rustfmt should wrap this comment when `wrap_comments = true`
++}
++
++/// # We're using a markdown header here so rustfmt should refuse to wrap this comment in all circumstances
++fn documented_with_markdown_header() {
++    // # We're using a markdown header in an inline comment. rustfmt should be able to wrap this comment when `wrap_comments = true`
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..87dae58eccd73c97a71c36db1c1997bc979f24cd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++// rustfmt-wrap_comments: true
++
++/// no markdown header so rustfmt should wrap this comment when
++/// `format_code_in_doc_comments = true` and `wrap_comments = true`
++fn not_documented_with_markdown_header() {
++    // This is just a normal inline comment so rustfmt should wrap this comment
++    // when `wrap_comments = true`
++}
++
++/// # We're using a markdown header here so rustfmt should refuse to wrap this comment in all circumstances
++fn documented_with_markdown_header() {
++    // # We're using a markdown header in an inline comment. rustfmt should be
++    // able to wrap this comment when `wrap_comments = true`
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3b6f7e66993c0023678d6ef00d71efd7226a8cdc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,62 @@@
++// rustfmt-merge_derives:false
++
++#[rustfmt::skip::attributes(derive)]
++#[allow(dead_code)]
++#[derive(StructField)]
++#[derive(Clone)]
++struct DoNotMergeDerives {
++    field: String,
++}
++
++#[allow(dead_code)]
++#[derive(StructField)]
++#[rustfmt::skip::attributes(derive)]
++#[derive(Clone)]
++struct DoNotMergeDerivesSkipInMiddle {
++    field: String,
++}
++
++#[allow(dead_code)]
++#[derive(StructField)]
++#[derive(Clone)]
++#[rustfmt::skip::attributes(derive)]
++struct DoNotMergeDerivesSkipAtEnd {
++    field: String,
++}
++
++#[allow(dead_code)]
++#[derive(StructField)]
++#[derive(Clone)]
++struct MergeDerives {
++    field: String,
++}
++
++mod inner_attribute_derive_skip {
++    #![rustfmt::skip::attributes(derive)]
++
++    #[allow(dead_code)]
++    #[derive(StructField)]
++    #[derive(Clone)]
++    struct DoNotMergeDerives {
++        field: String,
++    }
++}
++
++#[rustfmt::skip::attributes(derive)]
++mod outer_attribute_derive_skip {
++    #[allow(dead_code)]
++    #[derive(StructField)]
++    #[derive(Clone)]
++    struct DoNotMergeDerives {
++        field: String,
++    }
++}
++
++mod no_derive_skip {
++    #[allow(dead_code)]
++    #[derive(StructField)]
++    #[derive(Clone)]
++    struct MergeDerives {
++        field: String,
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5f488b4542d0b60877cac5850fc1935e93acbeb8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,60 @@@
++// rustfmt-merge_derives:true
++
++#[rustfmt::skip::attributes(derive)]
++#[allow(dead_code)]
++#[derive(StructField)]
++#[derive(Clone)]
++struct DoNotMergeDerives {
++    field: String,
++}
++
++#[allow(dead_code)]
++#[derive(StructField)]
++#[rustfmt::skip::attributes(derive)]
++#[derive(Clone)]
++struct DoNotMergeDerivesSkipInMiddle {
++    field: String,
++}
++
++#[allow(dead_code)]
++#[derive(StructField)]
++#[derive(Clone)]
++#[rustfmt::skip::attributes(derive)]
++struct DoNotMergeDerivesSkipAtEnd {
++    field: String,
++}
++
++#[allow(dead_code)]
++#[derive(StructField, Clone)]
++struct MergeDerives {
++    field: String,
++}
++
++mod inner_attribute_derive_skip {
++    #![rustfmt::skip::attributes(derive)]
++
++    #[allow(dead_code)]
++    #[derive(StructField)]
++    #[derive(Clone)]
++    struct DoNotMergeDerives {
++        field: String,
++    }
++}
++
++#[rustfmt::skip::attributes(derive)]
++mod outer_attribute_derive_skip {
++    #[allow(dead_code)]
++    #[derive(StructField)]
++    #[derive(Clone)]
++    struct DoNotMergeDerives {
++        field: String,
++    }
++}
++
++mod no_derive_skip {
++    #[allow(dead_code)]
++    #[derive(StructField, Clone)]
++    struct MergeDerives {
++        field: String,
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a81c5a5171fb58544e4ef69575f1690b0da03a4c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,115 @@@
++struct Struct {
++    // Multiline comment
++    // should be formatted
++    // properly.
++}
++
++struct Struct2 {
++    // This formatting
++    // Should be changed
++}
++
++struct Struct3(
++    // This
++    // is
++    // correct
++);
++
++struct Struct4(
++    // This
++    // is
++    // not
++    // correct
++);
++
++struct Struct5 {
++    /*
++    Comment block
++    with many lines.
++    */
++}
++
++struct Struct6(
++    /*
++    Comment block
++    with many lines.
++    */
++);
++
++struct Struct7 {
++    /*
++    Invalid
++    format
++    */
++}
++
++struct Struct8(
++    /*
++    Invalid
++    format
++    */
++);
++
++struct Struct9 {/* bar */}
++
++struct Struct10 {
++    /* bar
++    baz
++    */
++}
++
++mod module {
++    struct Struct {
++        // Multiline comment
++        // should be formatted
++        // properly.
++    }
++
++    struct Struct2 {
++        // This formatting
++        // Should be changed
++    }
++
++    struct Struct3(
++        // This
++        // is
++        // correct
++    );
++
++    struct Struct4(
++        // This
++        // is
++        // not
++        // correct
++    );
++
++    struct Struct5 {
++        /*
++        Comment block
++        with many lines.
++         */
++    }
++
++    struct Struct6(
++        /*
++        Comment block
++        with many lines.
++        */
++    );
++
++    struct Struct7 {
++        /*
++        Invalid
++        format
++        */
++    }
++
++    struct Struct8(
++        /*
++        Invalid
++        format
++        */
++    );
++
++    struct Struct9 {/* bar */}
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3bb9048a5fd3d6a0eae954c7a2baa6543e824011
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,3 @@@
++struct Example<const N: usize = { 1048576 }> {
++    //
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..06f8a0c288d7fadee2d4abb621de66cee7cbb51c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6 @@@
++
++fn
++ some( )
++{
++}
++fn main () {}
index b5f327b0a1ca6ac8d12150e2d2afb236e53e575e,0000000000000000000000000000000000000000..d8b5467ee91ca23f245741e6f1d958262378446e
mode 100644,000000..100644
--- /dev/null
@@@ -1,1 -1,0 +1,1 @@@
- [{"name":"tests/writemode/source/json.rs","mismatches":[{"original_begin_line":5,"original_end_line":7,"expected_begin_line":5,"expected_end_line":5,"original":"fn foo_expr() {\n    1\n}","expected":"fn foo_expr() { 1 }"},{"original_begin_line":9,"original_end_line":11,"expected_begin_line":7,"expected_end_line":7,"original":"fn foo_stmt() {\n    foo();\n}","expected":"fn foo_stmt() { foo(); }"},{"original_begin_line":13,"original_end_line":15,"expected_begin_line":9,"expected_end_line":9,"original":"fn foo_decl_local()  {\n    let z = 5;\n   }","expected":"fn foo_decl_local() { let z = 5; }"},{"original_begin_line":17,"original_end_line":19,"expected_begin_line":11,"expected_end_line":11,"original":"fn    foo_decl_item(x: &mut i32) {\n    x = 3;\n}","expected":"fn foo_decl_item(x: &mut i32) { x = 3; }"},{"original_begin_line":21,"original_end_line":21,"expected_begin_line":13,"expected_end_line":13,"original":"   fn empty()     {","expected":"fn empty() {}"},{"original_begin_line":23,"original_end_line":23,"expected_begin_line":15,"expected_end_line":15,"original":"}","expected":"fn foo_return() -> String { \"yay\" }"},{"original_begin_line":25,"original_end_line":29,"expected_begin_line":17,"expected_end_line":20,"original":"fn foo_return() -> String {\n    \"yay\"\n}\n\nfn foo_where() -> T where T: Sync {","expected":"fn foo_where() -> T\nwhere\n    T: Sync,\n{"},{"original_begin_line":64,"original_end_line":66,"expected_begin_line":55,"expected_end_line":55,"original":"fn lots_of_space                      ()                                                           {\n                           1                 \n}","expected":"fn lots_of_space() { 1 }"},{"original_begin_line":71,"original_end_line":72,"expected_begin_line":60,"expected_end_line":60,"original":"    fn dummy(&self) {\n    }","expected":"    fn dummy(&self) {}"},{"original_begin_line":75,"original_end_line":75,"expected_begin_line":63,"expected_end_line":64,"original":"trait CoolerTypes { fn dummy(&self) { ","expected":"trait CoolerTypes {\n    fn dummy(&self) {}"},{"original_begin_line":77,"original_end_line":77,"expected_begin_line":66,"expected_end_line":66,"original":"}","expected":""},{"original_begin_line":79,"original_end_line":79,"expected_begin_line":67,"expected_end_line":70,"original":"fn Foo<T>() where T: Bar {","expected":"fn Foo<T>()\nwhere\n    T: Bar,\n{"}]}]
++[{"name":"tests/writemode/source/json.rs","mismatches":[{"original_begin_line":5,"original_end_line":7,"expected_begin_line":5,"expected_end_line":5,"original":"fn foo_expr() {\n    1\n}\n","expected":"fn foo_expr() { 1 }\n"},{"original_begin_line":9,"original_end_line":11,"expected_begin_line":7,"expected_end_line":7,"original":"fn foo_stmt() {\n    foo();\n}\n","expected":"fn foo_stmt() { foo(); }\n"},{"original_begin_line":13,"original_end_line":15,"expected_begin_line":9,"expected_end_line":9,"original":"fn foo_decl_local()  {\n    let z = 5;\n   }\n","expected":"fn foo_decl_local() { let z = 5; }\n"},{"original_begin_line":17,"original_end_line":19,"expected_begin_line":11,"expected_end_line":11,"original":"fn    foo_decl_item(x: &mut i32) {\n    x = 3;\n}\n","expected":"fn foo_decl_item(x: &mut i32) { x = 3; }\n"},{"original_begin_line":21,"original_end_line":21,"expected_begin_line":13,"expected_end_line":13,"original":"   fn empty()     {\n","expected":"fn empty() {}\n"},{"original_begin_line":23,"original_end_line":23,"expected_begin_line":15,"expected_end_line":15,"original":"}\n","expected":"fn foo_return() -> String { \"yay\" }\n"},{"original_begin_line":25,"original_end_line":29,"expected_begin_line":17,"expected_end_line":20,"original":"fn foo_return() -> String {\n    \"yay\"\n}\n\nfn foo_where() -> T where T: Sync {\n","expected":"fn foo_where() -> T\nwhere\n    T: Sync,\n{\n"},{"original_begin_line":64,"original_end_line":66,"expected_begin_line":55,"expected_end_line":55,"original":"fn lots_of_space                      ()                                                           {\n                           1                 \n}\n","expected":"fn lots_of_space() { 1 }\n"},{"original_begin_line":71,"original_end_line":72,"expected_begin_line":60,"expected_end_line":60,"original":"    fn dummy(&self) {\n    }\n","expected":"    fn dummy(&self) {}\n"},{"original_begin_line":75,"original_end_line":75,"expected_begin_line":63,"expected_end_line":64,"original":"trait CoolerTypes { fn dummy(&self) { \n","expected":"trait CoolerTypes {\n    fn dummy(&self) {}\n"},{"original_begin_line":77,"original_end_line":77,"expected_begin_line":66,"expected_end_line":66,"original":"}\n","expected":""},{"original_begin_line":79,"original_end_line":79,"expected_begin_line":67,"expected_end_line":70,"original":"fn Foo<T>() where T: Bar {\n","expected":"fn Foo<T>()\nwhere\n    T: Bar,\n{\n"}]}]
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..dbf2c486322957fa44e3d63091efd2120af88bbd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++[{"name":"<stdin>","mismatches":[{"original_begin_line":1,"original_end_line":6,"expected_begin_line":1,"expected_end_line":2,"original":"\nfn\n some( )\n{\n}\nfn main () {}\n","expected":"fn some() {}\nfn main() {}\n"}]}]
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a7301bbc553c39a606824d7759d25f7e7cb60635
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,2 @@@
++<?xml version="1.0" encoding="utf-8"?>\r
++<checkstyle version="4.3"><file name="<stdin>"><error line="1" severity="warning" message="Should be `fn some() {}`" /><error line="2" severity="warning" message="Should be `fn main() {}`" /></file></checkstyle>\r