]> git.lizzy.rs Git - rust.git/commitdiff
Merge commit '8da837185714cefbb261e93e9846afb11c1dc60e' into sync-rustfmt-subtree
authorCaleb Cartwright <caleb.cartwright@outlook.com>
Fri, 3 Dec 2021 03:35:30 +0000 (21:35 -0600)
committerCaleb Cartwright <caleb.cartwright@outlook.com>
Fri, 3 Dec 2021 03:35:30 +0000 (21:35 -0600)
47 files changed:
1  2 
src/tools/rustfmt/.github/workflows/linux.yml
src/tools/rustfmt/.github/workflows/mac.yml
src/tools/rustfmt/.github/workflows/windows.yml
src/tools/rustfmt/Configurations.md
src/tools/rustfmt/README.md
src/tools/rustfmt/config_proc_macro/src/lib.rs
src/tools/rustfmt/src/comment.rs
src/tools/rustfmt/src/config/mod.rs
src/tools/rustfmt/src/expr.rs
src/tools/rustfmt/src/ignore_path.rs
src/tools/rustfmt/src/items.rs
src/tools/rustfmt/src/lists.rs
src/tools/rustfmt/src/macros.rs
src/tools/rustfmt/src/syntux/session.rs
src/tools/rustfmt/src/test/mod.rs
src/tools/rustfmt/src/types.rs
src/tools/rustfmt/src/visitor.rs
src/tools/rustfmt/tests/source/comments-in-lists/wrap-comments-not-normalized.rs
src/tools/rustfmt/tests/source/comments-in-lists/wrap-comments-true.rs
src/tools/rustfmt/tests/source/issue-5088/deeply_nested_long_comment_wrap_comments_true.rs
src/tools/rustfmt/tests/source/issue-5088/start_with_empty_comment_very_long_itemized_block_wrap_comments_true.rs
src/tools/rustfmt/tests/source/issue-5088/very_long_comment_wrap_comments_true.rs
src/tools/rustfmt/tests/source/issue_4823.rs
src/tools/rustfmt/tests/source/issue_5027.rs
src/tools/rustfmt/tests/source/issue_5086.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/issue-5088/deeply_nested_long_comment_wrap_comments_false.rs
src/tools/rustfmt/tests/target/issue-5088/deeply_nested_long_comment_wrap_comments_true.rs
src/tools/rustfmt/tests/target/issue-5088/multi_line_itemized_block_wrap_comments_false.rs
src/tools/rustfmt/tests/target/issue-5088/multi_line_itemized_block_wrap_comments_true.rs
src/tools/rustfmt/tests/target/issue-5088/multi_line_text_with_itemized_block_wrap_comments_false.rs
src/tools/rustfmt/tests/target/issue-5088/multi_line_text_with_itemized_block_wrap_comments_true.rs
src/tools/rustfmt/tests/target/issue-5088/single_line_itemized_block_wrap_comments_false.rs
src/tools/rustfmt/tests/target/issue-5088/single_line_itemized_block_wrap_comments_true.rs
src/tools/rustfmt/tests/target/issue-5088/start_with_empty_comment_very_long_itemized_block_wrap_comments_false.rs
src/tools/rustfmt/tests/target/issue-5088/start_with_empty_comment_very_long_itemized_block_wrap_comments_true.rs
src/tools/rustfmt/tests/target/issue-5088/start_with_empty_comment_wrap_comments_false.rs
src/tools/rustfmt/tests/target/issue-5088/start_with_empty_comment_wrap_comments_true.rs
src/tools/rustfmt/tests/target/issue-5088/very_long_comment_wrap_comments_false.rs
src/tools/rustfmt/tests/target/issue-5088/very_long_comment_wrap_comments_true.rs
src/tools/rustfmt/tests/target/issue-5095.rs
src/tools/rustfmt/tests/target/issue_4823.rs
src/tools/rustfmt/tests/target/issue_5027.rs
src/tools/rustfmt/tests/target/issue_5086.rs

index 6eaae69c708056c56c0e6c8d44883fb8625f241b,0000000000000000000000000000000000000000..db497941642122c107bb3335a7520bcd1761647d
mode 100644,000000..100644
--- /dev/null
@@@ -1,42 -1,0 +1,45 @@@
-     name: (${{ matrix.target }}, nightly)
 +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
 +
 +    - name: test
 +      run: cargo test
index 79e4f69163e030b3a5eae659ab0165cb093790bf,0000000000000000000000000000000000000000..55e1cc9539b8508db5234f9fde51191b40644c43
mode 100644,000000..100644
--- /dev/null
@@@ -1,39 -1,0 +1,42 @@@
-     name: (${{ matrix.target }}, nightly)
 +name: mac
 +on:
 +  push:
 +    branches:
 +      - master
 +  pull_request:
 +
 +jobs:
 +  test:
 +    # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/virtual-environments-for-github-hosted-runners#supported-runners-and-hardware-resources
 +    # macOS Catalina 10.15
 +    runs-on: macos-latest
++    name: (${{ matrix.target }}, ${{ matrix.cfg_release_channel }})
++    env:
++      CFG_RELEASE_CHANNEL: ${{ matrix.cfg_release_channel }}
 +    strategy:
 +      fail-fast: false
 +      matrix:
 +        target: [
 +          x86_64-apple-darwin,
 +        ]
++        cfg_release_channel: [nightly, stable]
 +
 +    steps:
 +    - name: checkout
 +      uses: actions/checkout@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
 +
 +    - name: test
 +      run: cargo test
index c05e8d4896ac707d2c23b24d12eda006355a01be,0000000000000000000000000000000000000000..dcb08b5412ea6c421c6ac3bf1971f4eb3d291f77
mode 100644,000000..100644
--- /dev/null
@@@ -1,66 -1,0 +1,69 @@@
-     name: (${{ matrix.target }}, nightly)
 +name: windows
 +on:
 +  push:
 +    branches:
 +      - master
 +  pull_request:
 +
 +jobs:
 +  test:
 +    runs-on: windows-latest
++    name: (${{ matrix.target }}, ${{ matrix.cfg_release_channel }})
++    env:
++      CFG_RELEASE_CHANNEL: ${{ matrix.cfg_release_channel }}
 +    strategy:
 +      # https://help.github.com/en/actions/getting-started-with-github-actions/about-github-actions#usage-limits
 +      # There's a limit of 60 concurrent jobs across all repos in the rust-lang organization.
 +      # In order to prevent overusing too much of that 60 limit, we throttle the
 +      # number of rustfmt jobs that will run concurrently.
 +      max-parallel: 2
 +      fail-fast: false
 +      matrix:
 +        target: [
 +          i686-pc-windows-gnu,
 +          i686-pc-windows-msvc,
 +          x86_64-pc-windows-gnu,
 +          x86_64-pc-windows-msvc,
 +        ]
++        cfg_release_channel: [nightly, stable]
 +
 +    steps:
 +    # The Windows runners have autocrlf enabled by default
 +    # which causes failures for some of rustfmt's line-ending sensitive tests
 +    - name: disable git eol translation
 +      run: git config --global core.autocrlf false
 +    - name: checkout
 +      uses: actions/checkout@v2
 +
 +      # Run build
 +    - name: Install Rustup using win.rustup.rs
 +      run: |
 +        # Disable the download progress bar which can cause perf issues
 +        $ProgressPreference = "SilentlyContinue"
 +        Invoke-WebRequest https://win.rustup.rs/ -OutFile rustup-init.exe
 +        .\rustup-init.exe -y --default-host=x86_64-pc-windows-msvc --default-toolchain=none
 +        del rustup-init.exe
 +        rustup target add ${{ matrix.target }}
 +      shell: powershell
 +
 +    - name: Add mingw32 to path for i686-gnu
 +      run: |
 +        echo "C:\msys64\mingw32\bin" >> $GITHUB_PATH
 +      if: matrix.target == 'i686-pc-windows-gnu' && matrix.channel == 'nightly'
 +      shell: bash
 +
 +    - name: Add mingw64 to path for x86_64-gnu
 +      run: echo "C:\msys64\mingw64\bin" >> $GITHUB_PATH
 +      if: matrix.target == 'x86_64-pc-windows-gnu' && matrix.channel == 'nightly'
 +      shell: bash
 +
 +    - name: build
 +      run: |
 +        rustc -Vv
 +        cargo -V
 +        cargo build
 +      shell: cmd
 +
 +    - name: test
 +      run: cargo test
 +      shell: cmd
index 13826883d2f4bdf537df1df2a687be215adbaf97,0000000000000000000000000000000000000000..a89fbe863e65283ce47ca0e36f87a0267058eb52
mode 100644,000000..100644
--- /dev/null
@@@ -1,2864 -1,0 +1,2864 @@@
- - **Stable**: No (tracking issue: #3368)
 +# 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: #3382)
++- **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: #3381)
++- **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: #3376)
++- **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: #3385)
++- **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: #3369)
++- **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: #3349)
++- **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: #3384)
++- **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: #3377)
++- **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: #3356)
++- **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: #3372)
++- **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: #3391)
++- **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: #3392)
++- **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: #3358)
++- **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: #3374)
++- **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: #3348)
++- **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
++- **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 contains `@generated` marker.
 +
 +- **Default value**: `false`
 +- **Possible values**: `true`, `false`
- - **Stable**: No (tracking issue: #3354)
++- **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: #3355)
++- **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: #3353)
++- **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
++- **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: #3390)
++- **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: #3395)
++- **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: #3360)
++- **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: #3361)
++- **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: #3346)
++- **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: #3343)
++- **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: #3352)
++- **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: #3373)
++- **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
++- **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: #3350)
++- **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: #3351)
++- **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: #3370)
++- **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: #3363)
++- **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
++- **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: #3394)
++- **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: #3393)
++- **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: #3386)
++- **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: #3389)
++- **Stable**: No (tracking issue: [#3386](https://github.com/rust-lang/rustfmt/issues/3386))
 +
 +## `skip_children`
 +
 +Don't reformat out of line modules
 +
 +- **Default value**: `false`
 +- **Possible values**: `true`, `false`
- - **Stable**: No (tracking issue: #3366)
++- **Stable**: No (tracking issue: [#3389](https://github.com/rust-lang/rustfmt/issues/3386))
 +
 +## `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: #3365)
++- **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: #3367)
++- **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: #3371)
++- **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: #3357)
++- **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: #3379)
++- **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: #3378)
++- **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: #3364)
++- **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: #3387)
++- **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: #3383)
++- **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: #3359)
++- **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: #3347)
++- **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 b3d21e6fb87c79ffa3957b20a4cc405f2787f051,0000000000000000000000000000000000000000..b3a968f0c043e30f10badc3853a3b5652e082eee
mode 100644,000000..100644
--- /dev/null
@@@ -1,253 -1,0 +1,252 @@@
- doesn't even need to compile! As we approach a 1.0 release we are also looking
- to limit areas of instability; in particular, post-1.0, the formatting of most
- code should not change as Rustfmt improves. However, there are some things that
- Rustfmt can't do or can't do well (and thus where formatting might change
- significantly, even post-1.0). We would like to reduce the list of limitations
- over time.
 +# rustfmt [![Build Status](https://travis-ci.com/rust-lang/rustfmt.svg?branch=master)](https://travis-ci.com/rust-lang/rustfmt) [![Build Status](https://ci.appveyor.com/api/projects/status/github/rust-lang/rustfmt?svg=true)](https://ci.appveyor.com/project/rust-lang-libs/rustfmt) [![crates.io](https://img.shields.io/crates/v/rustfmt-nightly.svg)](https://crates.io/crates/rustfmt-nightly) [![Travis Configuration Status](https://img.shields.io/travis/davidalber/rustfmt-travis.svg?label=travis%20example)](https://travis-ci.org/davidalber/rustfmt-travis)
 +
 +A tool for formatting Rust code according to style guidelines.
 +
 +If you'd like to help out (and you should, it's a fun project!), see
 +[Contributing.md](Contributing.md) and our [Code of
 +Conduct](CODE_OF_CONDUCT.md).
 +
 +You can use rustfmt in Travis CI builds. We provide a minimal Travis CI
 +configuration (see [here](#checking-style-on-a-ci-server)) and verify its status
 +using another repository. The status of that repository's build is reported by
 +the "travis example" badge above.
 +
 +## Quick start
 +
 +You can run `rustfmt` with Rust 1.24 and above.
 +
 +### On the Stable toolchain
 +
 +To install:
 +
 +```sh
 +rustup component add rustfmt
 +```
 +
 +To run on a cargo project in the current working directory:
 +
 +```sh
 +cargo fmt
 +```
 +
 +### On the Nightly toolchain
 +
 +For the latest and greatest `rustfmt`, nightly is required.
 +
 +To install:
 +
 +```sh
 +rustup component add rustfmt --toolchain nightly
 +```
 +
 +To run on a cargo project in the current working directory:
 +
 +```sh
 +cargo +nightly fmt
 +```
 +
 +## Limitations
 +
 +Rustfmt tries to work on as much Rust code as possible. Sometimes, the code
++doesn't even need to compile! In general, we are looking to limit areas of
++instability; in particular, post-1.0, the formatting of most code should not
++change as Rustfmt improves. However, there are some things that Rustfmt can't
++do or can't do well (and thus where formatting might change significantly,
++even post-1.0). We would like to reduce the list of limitations over time.
 +
 +The following list enumerates areas where Rustfmt does not work or where the
 +stability guarantees do not apply (we don't make a distinction between the two
 +because in the future Rustfmt might work on code where it currently does not):
 +
 +* a program where any part of the program does not parse (parsing is an early
 +  stage of compilation and in Rust includes macro expansion).
 +* Macro declarations and uses (current status: some macro declarations and uses
 +  are formatted).
 +* Comments, including any AST node with a comment 'inside' (Rustfmt does not
 +  currently attempt to format comments, it does format code with comments inside, but that formatting may change in the future).
 +* Rust code in code blocks in comments.
 +* Any fragment of a program (i.e., stability guarantees only apply to whole
 +  programs, even where fragments of a program can be formatted today).
 +* Code containing non-ascii unicode characters (we believe Rustfmt mostly works
 +  here, but do not have the test coverage or experience to be 100% sure).
 +* Bugs in Rustfmt (like any software, Rustfmt has bugs, we do not consider bug
 +  fixes to break our stability guarantees).
 +
 +
 +## Installation
 +
 +```sh
 +rustup component add rustfmt
 +```
 +
 +## Installing from source
 +
 +To install from source (nightly required), first checkout to the tag or branch you want to install, then issue
 +
 +```sh
 +cargo install --path .
 +```
 +
 +This will install `rustfmt` in your `~/.cargo/bin`. Make sure to add `~/.cargo/bin` directory to
 +your PATH variable.
 +
 +
 +## Running
 +
 +You can run Rustfmt by just typing `rustfmt filename` if you used `cargo
 +install`. This runs rustfmt on the given file, if the file includes out of line
 +modules, then we reformat those too. So to run on a whole module or crate, you
 +just need to run on the root file (usually mod.rs or lib.rs). Rustfmt can also
 +read data from stdin. Alternatively, you can use `cargo fmt` to format all
 +binary and library targets of your crate.
 +
 +You can run `rustfmt --help` for information about available arguments.
 +The easiest way to run rustfmt against a project is with `cargo fmt`. `cargo fmt` works on both
 +single-crate projects and [cargo workspaces](https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html).
 +Please see `cargo fmt --help` for usage information.
 +
 +You can specify the path to your own `rustfmt` binary for cargo to use by setting the`RUSTFMT` 
 +environment variable. This was added in v1.4.22, so you must have this version or newer to leverage this feature (`cargo fmt --version`)
 +
 +### Running `rustfmt` directly
 +
 +To format individual files or arbitrary codes from stdin, the `rustfmt` binary should be used. Some
 +examples follow:
 +
 +- `rustfmt lib.rs main.rs` will format "lib.rs" and "main.rs" in place
 +- `rustfmt` will read a code from stdin and write formatting to stdout
 +  - `echo "fn     main() {}" | rustfmt` would emit "fn main() {}".
 +
 +For more information, including arguments and emit options, see `rustfmt --help`.
 +
 +### Verifying code is formatted
 +
 +When running with `--check`, Rustfmt will exit with `0` if Rustfmt would not
 +make any formatting changes to the input, and `1` if Rustfmt would make changes.
 +In other modes, Rustfmt will exit with `1` if there was some error during
 +formatting (for example a parsing or internal error) and `0` if formatting
 +completed without error (whether or not changes were made).
 +
 +
 +
 +## Running Rustfmt from your editor
 +
 +* [Vim](https://github.com/rust-lang/rust.vim#formatting-with-rustfmt)
 +* [Emacs](https://github.com/rust-lang/rust-mode)
 +* [Sublime Text 3](https://packagecontrol.io/packages/RustFmt)
 +* [Atom](atom.md)
 +* Visual Studio Code using [vscode-rust](https://github.com/editor-rs/vscode-rust), [vsc-rustfmt](https://github.com/Connorcpu/vsc-rustfmt) or [rls_vscode](https://github.com/jonathandturner/rls_vscode) through RLS.
 +* [IntelliJ or CLion](intellij.md)
 +
 +
 +## Checking style on a CI server
 +
 +To keep your code base consistently formatted, it can be helpful to fail the CI build
 +when a pull request contains unformatted code. Using `--check` instructs
 +rustfmt to exit with an error code if the input is not formatted correctly.
 +It will also print any found differences. (Older versions of Rustfmt don't
 +support `--check`, use `--write-mode diff`).
 +
 +A minimal Travis setup could look like this (requires Rust 1.31.0 or greater):
 +
 +```yaml
 +language: rust
 +before_script:
 +- rustup component add rustfmt
 +script:
 +- cargo build
 +- cargo test
 +- cargo fmt --all -- --check
 +```
 +
 +See [this blog post](https://medium.com/@ag_dubs/enforcing-style-in-ci-for-rust-projects-18f6b09ec69d)
 +for more info.
 +
 +## How to build and test
 +
 +`cargo build` to build.
 +
 +`cargo test` to run all tests.
 +
 +To run rustfmt after this, use `cargo run --bin rustfmt -- filename`. See the
 +notes above on running rustfmt.
 +
 +
 +## Configuring Rustfmt
 +
 +Rustfmt is designed to be very configurable. You can create a TOML file called
 +`rustfmt.toml` or `.rustfmt.toml`, place it in the project or any other parent
 +directory and it will apply the options in that file. See `rustfmt
 +--help=config` for the options which are available, or if you prefer to see
 +visual style previews, [GitHub page](https://rust-lang.github.io/rustfmt/).
 +
 +By default, Rustfmt uses a style which conforms to the [Rust style guide][style
 +guide] that has been formalized through the [style RFC
 +process][fmt rfcs].
 +
 +Configuration options are either stable or unstable. Stable options can always
 +be used, while unstable ones are only available on a nightly toolchain, and opt-in.
 +See [GitHub page](https://rust-lang.github.io/rustfmt/) for details.
 +
 +### Rust's Editions
 +
 +Rustfmt is able to pick up the edition used by reading the `Cargo.toml` file if
 +executed through the Cargo's formatting tool `cargo fmt`. Otherwise, the edition
 +needs to be specified in `rustfmt.toml`, e.g., with `edition = "2018"`.
 +
 +## Tips
 +
 +* For things you do not want rustfmt to mangle, use `#[rustfmt::skip]`
 +* To prevent rustfmt from formatting a macro or an attribute,
 +  use `#[rustfmt::skip::macros(target_macro_name)]` or
 +  `#[rustfmt::skip::attributes(target_attribute_name)]`
 +
 +  Example:
 +
 +    ```rust
 +    #![rustfmt::skip::attributes(custom_attribute)]
 +
 +    #[custom_attribute(formatting , here , should , be , Skipped)]
 +    #[rustfmt::skip::macros(html)]
 +    fn main() {
 +        let macro_result1 = html! { <div>
 +    Hello</div>
 +        }.to_string();
 +    ```
 +* When you run rustfmt, place a file named `rustfmt.toml` or `.rustfmt.toml` in
 +  target file directory or its parents to override the default settings of
 +  rustfmt. You can generate a file containing the default configuration with
 +  `rustfmt --print-config default rustfmt.toml` and customize as needed.
 +* After successful compilation, a `rustfmt` executable can be found in the
 +  target directory.
 +* If you're having issues compiling Rustfmt (or compile errors when trying to
 +  install), make sure you have the most recent version of Rust installed.
 +
 +* You can change the way rustfmt emits the changes with the --emit flag:
 +
 +  Example:
 +
 +  ```sh
 +  cargo fmt -- --emit files
 +  ```
 +
 +  Options:
 +
 +  | Flag |Description| Nightly Only |
 +  |:---:|:---:|:---:|
 +  | files | overwrites output to files | No |
 +  | stdout | writes output to stdout | No |
 +  | coverage | displays how much of the input file was processed | Yes |
 +  | checkstyle | emits in a checkstyle format | Yes |
 +  | json | emits diffs in a json format | Yes |
 +
 +## License
 +
 +Rustfmt is distributed under the terms of both the MIT license and the
 +Apache License (Version 2.0).
 +
 +See [LICENSE-APACHE](LICENSE-APACHE) and [LICENSE-MIT](LICENSE-MIT) for details.
 +
 +[rust]: https://github.com/rust-lang/rust
 +[fmt rfcs]: https://github.com/rust-dev-tools/fmt-rfcs
 +[style guide]: https://github.com/rust-dev-tools/fmt-rfcs/blob/master/guide/guide.md
index 78e7e098ed9e17cf1de271c9c637cf41fbb9158c,0000000000000000000000000000000000000000..513018213192df3625b11f5b87fc88673c00e5cf
mode 100644,000000..100644
--- /dev/null
@@@ -1,25 -1,0 +1,67 @@@
 +//! This crate provides a derive macro for `ConfigType`.
 +
 +#![recursion_limit = "256"]
 +
 +mod attrs;
 +mod config_type;
 +mod item_enum;
 +mod item_struct;
 +mod utils;
 +
++use std::str::FromStr;
++
 +use proc_macro::TokenStream;
 +use syn::parse_macro_input;
 +
 +#[proc_macro_attribute]
 +pub fn config_type(_args: TokenStream, input: TokenStream) -> TokenStream {
 +    let input = parse_macro_input!(input as syn::Item);
 +    let output = config_type::define_config_type(&input);
 +
 +    #[cfg(feature = "debug-with-rustfmt")]
 +    {
 +        utils::debug_with_rustfmt(&output);
 +    }
 +
 +    TokenStream::from(output)
 +}
++
++/// Used to conditionally output the TokenStream for tests that need to be run on nightly only.
++///
++/// ```rust
++/// #[nightly_only_test]
++/// #[test]
++/// fn test_needs_nightly_rustfmt() {
++///   assert!(true);
++/// }
++/// ```
++#[proc_macro_attribute]
++pub fn nightly_only_test(_args: TokenStream, input: TokenStream) -> TokenStream {
++    // if CFG_RELEASE_CHANNEL is not set we default to nightly, hence why the default is true
++    if option_env!("CFG_RELEASE_CHANNEL").map_or(true, |c| c == "nightly" || c == "dev") {
++        input
++    } else {
++        // output an empty token stream if CFG_RELEASE_CHANNEL is not set to "nightly" or "dev"
++        TokenStream::from_str("").unwrap()
++    }
++}
++
++/// Used to conditionally output the TokenStream for tests that need to be run on stable only.
++///
++/// ```rust
++/// #[stable_only_test]
++/// #[test]
++/// fn test_needs_stable_rustfmt() {
++///   assert!(true);
++/// }
++/// ```
++#[proc_macro_attribute]
++pub fn stable_only_test(_args: TokenStream, input: TokenStream) -> TokenStream {
++    // if CFG_RELEASE_CHANNEL is not set we default to nightly, hence why the default is false
++    if option_env!("CFG_RELEASE_CHANNEL").map_or(false, |c| c == "stable") {
++        input
++    } else {
++        // output an empty token stream if CFG_RELEASE_CHANNEL is not set or is not 'stable'
++        TokenStream::from_str("").unwrap()
++    }
++}
index 7b76c232937dc7cea63d7df07716777730cbeef2,0000000000000000000000000000000000000000..0f850b9b2f2fba2b84be6e51101161fbf5773a2e
mode 100644,000000..100644
--- /dev/null
@@@ -1,1913 -1,0 +1,1961 @@@
-         let (opener, closer, line_start) = if block_style {
-             CommentStyle::SingleBullet.to_str_tuplet()
 +// 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.
 +///
 +/// An item starts with either a star `*` or a dash `-`. Different level of indentation are
 +/// handled by shrinking the shape accordingly.
 +struct ItemizedBlock {
 +    /// the lines that are identified as part of an itemized block
 +    lines: Vec<String>,
 +    /// the number of whitespaces up to the item sigil
 +    indent: usize,
 +    /// the string that marks the start of an item
 +    opener: String,
 +    /// sequence of whitespaces to prefix new lines that are part of the item
 +    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();
 +        trimmed.starts_with("* ") || trimmed.starts_with("- ")
 +    }
 +
 +    /// Creates a new ItemizedBlock described with the given line.
 +    /// The `is_itemized_line` needs to be called first.
 +    fn new(line: &str) -> ItemizedBlock {
 +        let space_to_sigil = line.chars().take_while(|c| c.is_whitespace()).count();
 +        let indent = space_to_sigil + 2;
 +        ItemizedBlock {
 +            lines: vec![line[indent..].to_string()],
 +            indent,
 +            opener: line[..indent].to_string(),
 +            line_start: " ".repeat(indent),
 +        }
 +    }
 +
 +    /// 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")
 +    }
 +}
 +
 +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> {
-             comment_style(orig, config.normalize_comments()).to_str_tuplet()
++        let ((opener, closer, line_start), style) = if block_style {
++            (
++                CommentStyle::SingleBullet.to_str_tuplet(),
++                CommentStyle::SingleBullet,
++            )
 +        } else {
-             self.result.push_str(&self.comment_line_separator);
++            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);
-         let is_last = i == count_newlines(orig);
++
++            // 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,
 +    ) -> bool {
-             self.result.push_str(&self.comment_line_separator);
++        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);
-     s.contains("https://") || s.contains("http://") || s.contains("ftp://") || s.contains("file://")
++
++            // 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();
 +            }
 +        }
 +
 +        if self.fmt.config.wrap_comments()
 +            && unicode_str_width(line) > self.fmt.shape.width
 +            && !has_url(line)
 +        {
 +            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) {
 +            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 c5419d860c94312952f19dcaf26130e7cf1bfa35,0000000000000000000000000000000000000000..5dbe532ac388fee287d48ee200e7d2bed65e8206
mode 100644,000000..100644
--- /dev/null
@@@ -1,968 -1,0 +1,954 @@@
-         if !crate::is_nightly_channel!() {
-             return;
-         }
 +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";
 +    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, false, false, "Format generated files";
 +
 +    // Options that can change the source code beyond whitespace/blocks (somewhat linty things)
 +    merge_derives: bool, true, true, "Merge multiple `#[derive(...)]` into a single one";
 +    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 {
 +                    return Err(e);
 +                }
 +            }
 +            _ => {}
 +        }
 +    }
 +    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() {
-         if !crate::is_nightly_channel!() {
-             return;
-         }
 +        let toml = r#"license_template_path = "tests/license-template/lt.txt""#;
 +        let config = Config::from_toml(toml, Path::new("")).unwrap();
 +        assert!(config.license_template.is_some());
 +    }
 +
++    #[nightly_only_test]
 +    #[test]
 +    fn test_override_existing_license_with_no_license() {
-     // FIXME(#2183): these tests cannot be run in parallel because they use env vars.
-     // #[test]
-     // fn test_as_not_nightly_channel() {
-     //     let mut config = Config::default();
-     //     assert_eq!(config.was_set().unstable_features(), false);
-     //     config.set().unstable_features(true);
-     //     assert_eq!(config.was_set().unstable_features(), false);
-     // }
-     // #[test]
-     // fn test_as_nightly_channel() {
-     //     let v = ::std::env::var("CFG_RELEASE_CHANNEL").unwrap_or(String::from(""));
-     //     ::std::env::set_var("CFG_RELEASE_CHANNEL", "nightly");
-     //     let mut config = Config::default();
-     //     config.set().unstable_features(true);
-     //     assert_eq!(config.was_set().unstable_features(), false);
-     //     config.set().unstable_features(true);
-     //     assert_eq!(config.unstable_features(), true);
-     //     ::std::env::set_var("CFG_RELEASE_CHANNEL", v);
-     // }
-     // #[test]
-     // fn test_unstable_from_toml() {
-     //     let mut config = Config::from_toml("unstable_features = true").unwrap();
-     //     assert_eq!(config.was_set().unstable_features(), false);
-     //     let v = ::std::env::var("CFG_RELEASE_CHANNEL").unwrap_or(String::from(""));
-     //     ::std::env::set_var("CFG_RELEASE_CHANNEL", "nightly");
-     //     config = Config::from_toml("unstable_features = true").unwrap();
-     //     assert_eq!(config.was_set().unstable_features(), true);
-     //     assert_eq!(config.unstable_features(), true);
-     //     ::std::env::set_var("CFG_RELEASE_CHANNEL", v);
-     // }
 +        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
 +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 = false
 +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);
 +    }
 +
-             if !crate::is_nightly_channel!() {
-                 return;
-             }
++    #[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() {
-             if !crate::is_nightly_channel!() {
-                 return;
-             }
 +            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() {
-             if !crate::is_nightly_channel!() {
-                 return;
-             }
 +            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() {
-             if !crate::is_nightly_channel!() {
-                 return;
-             }
 +            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 58942e442de05ba39ef3472bbb6b51850711ebfc,0000000000000000000000000000000000000000..5fd86c1a4eadd2172394a0d871da875ff71c8e34
mode 100644,000000..100644
--- /dev/null
@@@ -1,2099 -1,0 +1,2140 @@@
-         ast::ExprKind::Try(..) | ast::ExprKind::Field(..) | ast::ExprKind::MethodCall(..) => {
-             rewrite_chain(expr, context, shape)
-         }
 +use std::borrow::Cow;
 +use std::cmp::min;
 +
 +use itertools::Itertools;
 +use rustc_ast::token::{DelimToken, LitKind};
 +use rustc_ast::{ast, ptr};
 +use rustc_span::{BytePos, Span};
 +
 +use crate::chains::rewrite_chain;
 +use crate::closures;
 +use crate::comment::{
 +    combine_strs_with_missing_comments, contains_comment, recover_comment_removed, rewrite_comment,
 +    rewrite_missing_comment, CharClasses, FindUncommented,
 +};
 +use crate::config::lists::*;
 +use crate::config::{Config, ControlBraceStyle, HexLiteralCase, IndentStyle, Version};
 +use crate::lists::{
 +    definitive_tactic, itemize_list, shape_for_tactic, struct_lit_formatting, struct_lit_shape,
 +    struct_lit_tactic, write_list, ListFormatting, Separator,
 +};
 +use crate::macros::{rewrite_macro, MacroPosition};
 +use crate::matches::rewrite_match;
 +use crate::overflow::{self, IntoOverflowableItem, OverflowableItem};
 +use crate::pairs::{rewrite_all_pairs, rewrite_pair, PairParts};
 +use crate::rewrite::{Rewrite, RewriteContext};
 +use crate::shape::{Indent, Shape};
 +use crate::source_map::{LineRangeUtils, SpanUtils};
 +use crate::spanned::Spanned;
 +use crate::string::{rewrite_string, StringFormat};
 +use crate::types::{rewrite_path, PathContext};
 +use crate::utils::{
 +    colon_spaces, contains_skip, count_newlines, first_line_ends_with, inner_attributes,
 +    last_line_extendable, last_line_width, mk_sp, outer_attributes, semicolon_for_expr,
 +    unicode_str_width, wrap_str,
 +};
 +use crate::vertical::rewrite_with_alignment;
 +use crate::visitor::FmtVisitor;
 +
 +impl Rewrite for ast::Expr {
 +    fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
 +        format_expr(self, ExprType::SubExpression, context, shape)
 +    }
 +}
 +
 +#[derive(Copy, Clone, PartialEq)]
 +pub(crate) enum ExprType {
 +    Statement,
 +    SubExpression,
 +}
 +
 +pub(crate) fn format_expr(
 +    expr: &ast::Expr,
 +    expr_type: ExprType,
 +    context: &RewriteContext<'_>,
 +    shape: Shape,
 +) -> Option<String> {
 +    skip_out_of_file_lines_range!(context, expr.span);
 +
 +    if contains_skip(&*expr.attrs) {
 +        return Some(context.snippet(expr.span()).to_owned());
 +    }
 +    let shape = if expr_type == ExprType::Statement && semicolon_for_expr(context, expr) {
 +        shape.sub_width(1)?
 +    } else {
 +        shape
 +    };
 +
 +    let expr_rw = match expr.kind {
 +        ast::ExprKind::Array(ref expr_vec) => rewrite_array(
 +            "",
 +            expr_vec.iter(),
 +            expr.span,
 +            context,
 +            shape,
 +            choose_separator_tactic(context, expr.span),
 +            None,
 +        ),
 +        ast::ExprKind::Lit(ref l) => {
 +            if let Some(expr_rw) = rewrite_literal(context, l, shape) {
 +                Some(expr_rw)
 +            } else {
 +                if let LitKind::StrRaw(_) = l.token.kind {
 +                    Some(context.snippet(l.span).trim().into())
 +                } else {
 +                    None
 +                }
 +            }
 +        }
 +        ast::ExprKind::Call(ref callee, ref args) => {
 +            let inner_span = mk_sp(callee.span.hi(), expr.span.hi());
 +            let callee_str = callee.rewrite(context, shape)?;
 +            rewrite_call(context, &callee_str, args, inner_span, shape)
 +        }
 +        ast::ExprKind::Paren(ref subexpr) => rewrite_paren(context, subexpr, shape, expr.span),
 +        ast::ExprKind::Binary(op, ref lhs, ref rhs) => {
 +            // FIXME: format comments between operands and operator
 +            rewrite_all_pairs(expr, shape, context).or_else(|| {
 +                rewrite_pair(
 +                    &**lhs,
 +                    &**rhs,
 +                    PairParts::infix(&format!(" {} ", context.snippet(op.span))),
 +                    context,
 +                    shape,
 +                    context.config.binop_separator(),
 +                )
 +            })
 +        }
 +        ast::ExprKind::Unary(op, ref subexpr) => rewrite_unary_op(context, op, subexpr, shape),
 +        ast::ExprKind::Struct(ref struct_expr) => {
 +            let ast::StructExpr {
 +                fields, path, rest, ..
 +            } = &**struct_expr;
 +            rewrite_struct_lit(context, path, fields, rest, &expr.attrs, expr.span, shape)
 +        }
 +        ast::ExprKind::Tup(ref items) => {
 +            rewrite_tuple(context, items.iter(), expr.span, shape, items.len() == 1)
 +        }
 +        ast::ExprKind::Let(..) => None,
 +        ast::ExprKind::If(..)
 +        | ast::ExprKind::ForLoop(..)
 +        | ast::ExprKind::Loop(..)
 +        | ast::ExprKind::While(..) => to_control_flow(expr, expr_type)
 +            .and_then(|control_flow| control_flow.rewrite(context, shape)),
 +        ast::ExprKind::ConstBlock(ref anon_const) => {
 +            Some(format!("const {}", anon_const.rewrite(context, shape)?))
 +        }
 +        ast::ExprKind::Block(ref block, opt_label) => {
 +            match expr_type {
 +                ExprType::Statement => {
 +                    if is_unsafe_block(block) {
 +                        rewrite_block(block, Some(&expr.attrs), opt_label, context, shape)
 +                    } else if let rw @ Some(_) =
 +                        rewrite_empty_block(context, block, Some(&expr.attrs), opt_label, "", shape)
 +                    {
 +                        // Rewrite block without trying to put it in a single line.
 +                        rw
 +                    } else {
 +                        let prefix = block_prefix(context, block, shape)?;
 +
 +                        rewrite_block_with_visitor(
 +                            context,
 +                            &prefix,
 +                            block,
 +                            Some(&expr.attrs),
 +                            opt_label,
 +                            shape,
 +                            true,
 +                        )
 +                    }
 +                }
 +                ExprType::SubExpression => {
 +                    rewrite_block(block, Some(&expr.attrs), opt_label, context, shape)
 +                }
 +            }
 +        }
 +        ast::ExprKind::Match(ref cond, ref arms) => {
 +            rewrite_match(context, cond, arms, shape, expr.span, &expr.attrs)
 +        }
 +        ast::ExprKind::Path(ref qself, ref path) => {
 +            rewrite_path(context, PathContext::Expr, qself.as_ref(), path, shape)
 +        }
 +        ast::ExprKind::Assign(ref lhs, ref rhs, _) => {
 +            rewrite_assignment(context, lhs, rhs, None, shape)
 +        }
 +        ast::ExprKind::AssignOp(ref op, ref lhs, ref rhs) => {
 +            rewrite_assignment(context, lhs, rhs, Some(op), shape)
 +        }
 +        ast::ExprKind::Continue(ref opt_label) => {
 +            let id_str = match *opt_label {
 +                Some(label) => format!(" {}", label.ident),
 +                None => String::new(),
 +            };
 +            Some(format!("continue{}", id_str))
 +        }
 +        ast::ExprKind::Break(ref opt_label, ref opt_expr) => {
 +            let id_str = match *opt_label {
 +                Some(label) => format!(" {}", label.ident),
 +                None => String::new(),
 +            };
 +
 +            if let Some(ref expr) = *opt_expr {
 +                rewrite_unary_prefix(context, &format!("break{} ", id_str), &**expr, shape)
 +            } else {
 +                Some(format!("break{}", id_str))
 +            }
 +        }
 +        ast::ExprKind::Yield(ref opt_expr) => {
 +            if let Some(ref expr) = *opt_expr {
 +                rewrite_unary_prefix(context, "yield ", &**expr, shape)
 +            } else {
 +                Some("yield".to_string())
 +            }
 +        }
 +        ast::ExprKind::Closure(capture, ref is_async, movability, ref fn_decl, ref body, _) => {
 +            closures::rewrite_closure(
 +                capture, is_async, movability, fn_decl, body, expr.span, context, shape,
 +            )
 +        }
-         ast::ExprKind::Await(_) => rewrite_chain(expr, context, shape),
++        ast::ExprKind::Try(..)
++        | ast::ExprKind::Field(..)
++        | ast::ExprKind::MethodCall(..)
++        | ast::ExprKind::Await(_) => rewrite_chain(expr, context, shape),
 +        ast::ExprKind::MacCall(ref mac) => {
 +            rewrite_macro(mac, None, context, shape, MacroPosition::Expression).or_else(|| {
 +                wrap_str(
 +                    context.snippet(expr.span).to_owned(),
 +                    context.config.max_width(),
 +                    shape,
 +                )
 +            })
 +        }
 +        ast::ExprKind::Ret(None) => Some("return".to_owned()),
 +        ast::ExprKind::Ret(Some(ref expr)) => {
 +            rewrite_unary_prefix(context, "return ", &**expr, shape)
 +        }
 +        ast::ExprKind::Box(ref expr) => rewrite_unary_prefix(context, "box ", &**expr, shape),
 +        ast::ExprKind::AddrOf(borrow_kind, mutability, ref expr) => {
 +            rewrite_expr_addrof(context, borrow_kind, mutability, expr, shape)
 +        }
 +        ast::ExprKind::Cast(ref expr, ref ty) => rewrite_pair(
 +            &**expr,
 +            &**ty,
 +            PairParts::infix(" as "),
 +            context,
 +            shape,
 +            SeparatorPlace::Front,
 +        ),
 +        ast::ExprKind::Type(ref expr, ref ty) => rewrite_pair(
 +            &**expr,
 +            &**ty,
 +            PairParts::infix(": "),
 +            context,
 +            shape,
 +            SeparatorPlace::Back,
 +        ),
 +        ast::ExprKind::Index(ref expr, ref index) => {
 +            rewrite_index(&**expr, &**index, context, shape)
 +        }
 +        ast::ExprKind::Repeat(ref expr, ref repeats) => rewrite_pair(
 +            &**expr,
 +            &*repeats.value,
 +            PairParts::new("[", "; ", "]"),
 +            context,
 +            shape,
 +            SeparatorPlace::Back,
 +        ),
 +        ast::ExprKind::Range(ref lhs, ref rhs, limits) => {
 +            let delim = match limits {
 +                ast::RangeLimits::HalfOpen => "..",
 +                ast::RangeLimits::Closed => "..=",
 +            };
 +
 +            fn needs_space_before_range(context: &RewriteContext<'_>, lhs: &ast::Expr) -> bool {
 +                match lhs.kind {
 +                    ast::ExprKind::Lit(ref lit) => match lit.kind {
 +                        ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) => {
 +                            context.snippet(lit.span).ends_with('.')
 +                        }
 +                        _ => false,
 +                    },
 +                    ast::ExprKind::Unary(_, ref expr) => needs_space_before_range(context, expr),
 +                    _ => false,
 +                }
 +            }
 +
 +            fn needs_space_after_range(rhs: &ast::Expr) -> bool {
 +                // Don't format `.. ..` into `....`, which is invalid.
 +                //
 +                // This check is unnecessary for `lhs`, because a range
 +                // starting from another range needs parentheses as `(x ..) ..`
 +                // (`x .. ..` is a range from `x` to `..`).
 +                matches!(rhs.kind, ast::ExprKind::Range(None, _, _))
 +            }
 +
 +            let default_sp_delim = |lhs: Option<&ast::Expr>, rhs: Option<&ast::Expr>| {
 +                let space_if = |b: bool| if b { " " } else { "" };
 +
 +                format!(
 +                    "{}{}{}",
 +                    lhs.map_or("", |lhs| space_if(needs_space_before_range(context, lhs))),
 +                    delim,
 +                    rhs.map_or("", |rhs| space_if(needs_space_after_range(rhs))),
 +                )
 +            };
 +
 +            match (lhs.as_ref().map(|x| &**x), rhs.as_ref().map(|x| &**x)) {
 +                (Some(lhs), Some(rhs)) => {
 +                    let sp_delim = if context.config.spaces_around_ranges() {
 +                        format!(" {} ", delim)
 +                    } else {
 +                        default_sp_delim(Some(lhs), Some(rhs))
 +                    };
 +                    rewrite_pair(
 +                        &*lhs,
 +                        &*rhs,
 +                        PairParts::infix(&sp_delim),
 +                        context,
 +                        shape,
 +                        context.config.binop_separator(),
 +                    )
 +                }
 +                (None, Some(rhs)) => {
 +                    let sp_delim = if context.config.spaces_around_ranges() {
 +                        format!("{} ", delim)
 +                    } else {
 +                        default_sp_delim(None, Some(rhs))
 +                    };
 +                    rewrite_unary_prefix(context, &sp_delim, &*rhs, shape)
 +                }
 +                (Some(lhs), None) => {
 +                    let sp_delim = if context.config.spaces_around_ranges() {
 +                        format!(" {}", delim)
 +                    } else {
 +                        default_sp_delim(Some(lhs), None)
 +                    };
 +                    rewrite_unary_suffix(context, &sp_delim, &*lhs, shape)
 +                }
 +                (None, None) => Some(delim.to_owned()),
 +            }
 +        }
 +        // We do not format these expressions yet, but they should still
 +        // satisfy our width restrictions.
 +        // Style Guide RFC for InlineAsm variant pending
 +        // https://github.com/rust-dev-tools/fmt-rfcs/issues/152
 +        ast::ExprKind::LlvmInlineAsm(..) | ast::ExprKind::InlineAsm(..) => {
 +            Some(context.snippet(expr.span).to_owned())
 +        }
 +        ast::ExprKind::TryBlock(ref block) => {
 +            if let rw @ Some(_) =
 +                rewrite_single_line_block(context, "try ", block, Some(&expr.attrs), None, shape)
 +            {
 +                rw
 +            } else {
 +                // 9 = `try `
 +                let budget = shape.width.saturating_sub(9);
 +                Some(format!(
 +                    "{}{}",
 +                    "try ",
 +                    rewrite_block(
 +                        block,
 +                        Some(&expr.attrs),
 +                        None,
 +                        context,
 +                        Shape::legacy(budget, shape.indent)
 +                    )?
 +                ))
 +            }
 +        }
 +        ast::ExprKind::Async(capture_by, _node_id, ref block) => {
 +            let mover = if capture_by == ast::CaptureBy::Value {
 +                "move "
 +            } else {
 +                ""
 +            };
 +            if let rw @ Some(_) = rewrite_single_line_block(
 +                context,
 +                format!("{}{}", "async ", mover).as_str(),
 +                block,
 +                Some(&expr.attrs),
 +                None,
 +                shape,
 +            ) {
 +                rw
 +            } else {
 +                // 6 = `async `
 +                let budget = shape.width.saturating_sub(6);
 +                Some(format!(
 +                    "{}{}{}",
 +                    "async ",
 +                    mover,
 +                    rewrite_block(
 +                        block,
 +                        Some(&expr.attrs),
 +                        None,
 +                        context,
 +                        Shape::legacy(budget, shape.indent)
 +                    )?
 +                ))
 +            }
 +        }
-     rewrite_assign_rhs(context, lhs_str, rhs, shape)
 +        ast::ExprKind::Underscore => Some("_".to_owned()),
 +        ast::ExprKind::Err => None,
 +    };
 +
 +    expr_rw
 +        .and_then(|expr_str| recover_comment_removed(expr_str, expr.span, context))
 +        .and_then(|expr_str| {
 +            let attrs = outer_attributes(&expr.attrs);
 +            let attrs_str = attrs.rewrite(context, shape)?;
 +            let span = mk_sp(
 +                attrs.last().map_or(expr.span.lo(), |attr| attr.span.hi()),
 +                expr.span.lo(),
 +            );
 +            combine_strs_with_missing_comments(context, &attrs_str, &expr_str, span, shape, false)
 +        })
 +}
 +
 +pub(crate) fn rewrite_array<'a, T: 'a + IntoOverflowableItem<'a>>(
 +    name: &'a str,
 +    exprs: impl Iterator<Item = &'a T>,
 +    span: Span,
 +    context: &'a RewriteContext<'_>,
 +    shape: Shape,
 +    force_separator_tactic: Option<SeparatorTactic>,
 +    delim_token: Option<DelimToken>,
 +) -> Option<String> {
 +    overflow::rewrite_with_square_brackets(
 +        context,
 +        name,
 +        exprs,
 +        shape,
 +        span,
 +        force_separator_tactic,
 +        delim_token,
 +    )
 +}
 +
 +fn rewrite_empty_block(
 +    context: &RewriteContext<'_>,
 +    block: &ast::Block,
 +    attrs: Option<&[ast::Attribute]>,
 +    label: Option<ast::Label>,
 +    prefix: &str,
 +    shape: Shape,
 +) -> Option<String> {
 +    if block_has_statements(block) {
 +        return None;
 +    }
 +
 +    let label_str = rewrite_label(label);
 +    if attrs.map_or(false, |a| !inner_attributes(a).is_empty()) {
 +        return None;
 +    }
 +
 +    if !block_contains_comment(context, block) && shape.width >= 2 {
 +        return Some(format!("{}{}{{}}", prefix, label_str));
 +    }
 +
 +    // If a block contains only a single-line comment, then leave it on one line.
 +    let user_str = context.snippet(block.span);
 +    let user_str = user_str.trim();
 +    if user_str.starts_with('{') && user_str.ends_with('}') {
 +        let comment_str = user_str[1..user_str.len() - 1].trim();
 +        if block.stmts.is_empty()
 +            && !comment_str.contains('\n')
 +            && !comment_str.starts_with("//")
 +            && comment_str.len() + 4 <= shape.width
 +        {
 +            return Some(format!("{}{}{{ {} }}", prefix, label_str, comment_str));
 +        }
 +    }
 +
 +    None
 +}
 +
 +fn block_prefix(context: &RewriteContext<'_>, block: &ast::Block, shape: Shape) -> Option<String> {
 +    Some(match block.rules {
 +        ast::BlockCheckMode::Unsafe(..) => {
 +            let snippet = context.snippet(block.span);
 +            let open_pos = snippet.find_uncommented("{")?;
 +            // Extract comment between unsafe and block start.
 +            let trimmed = &snippet[6..open_pos].trim();
 +
 +            if !trimmed.is_empty() {
 +                // 9 = "unsafe  {".len(), 7 = "unsafe ".len()
 +                let budget = shape.width.checked_sub(9)?;
 +                format!(
 +                    "unsafe {} ",
 +                    rewrite_comment(
 +                        trimmed,
 +                        true,
 +                        Shape::legacy(budget, shape.indent + 7),
 +                        context.config,
 +                    )?
 +                )
 +            } else {
 +                "unsafe ".to_owned()
 +            }
 +        }
 +        ast::BlockCheckMode::Default => String::new(),
 +    })
 +}
 +
 +fn rewrite_single_line_block(
 +    context: &RewriteContext<'_>,
 +    prefix: &str,
 +    block: &ast::Block,
 +    attrs: Option<&[ast::Attribute]>,
 +    label: Option<ast::Label>,
 +    shape: Shape,
 +) -> Option<String> {
 +    if is_simple_block(context, block, attrs) {
 +        let expr_shape = shape.offset_left(last_line_width(prefix))?;
 +        let expr_str = block.stmts[0].rewrite(context, expr_shape)?;
 +        let label_str = rewrite_label(label);
 +        let result = format!("{}{}{{ {} }}", prefix, label_str, expr_str);
 +        if result.len() <= shape.width && !result.contains('\n') {
 +            return Some(result);
 +        }
 +    }
 +    None
 +}
 +
 +pub(crate) fn rewrite_block_with_visitor(
 +    context: &RewriteContext<'_>,
 +    prefix: &str,
 +    block: &ast::Block,
 +    attrs: Option<&[ast::Attribute]>,
 +    label: Option<ast::Label>,
 +    shape: Shape,
 +    has_braces: bool,
 +) -> Option<String> {
 +    if let rw @ Some(_) = rewrite_empty_block(context, block, attrs, label, prefix, shape) {
 +        return rw;
 +    }
 +
 +    let mut visitor = FmtVisitor::from_context(context);
 +    visitor.block_indent = shape.indent;
 +    visitor.is_if_else_block = context.is_if_else_block();
 +    match (block.rules, label) {
 +        (ast::BlockCheckMode::Unsafe(..), _) | (ast::BlockCheckMode::Default, Some(_)) => {
 +            let snippet = context.snippet(block.span);
 +            let open_pos = snippet.find_uncommented("{")?;
 +            visitor.last_pos = block.span.lo() + BytePos(open_pos as u32)
 +        }
 +        (ast::BlockCheckMode::Default, None) => visitor.last_pos = block.span.lo(),
 +    }
 +
 +    let inner_attrs = attrs.map(inner_attributes);
 +    let label_str = rewrite_label(label);
 +    visitor.visit_block(block, inner_attrs.as_deref(), has_braces);
 +    let visitor_context = visitor.get_context();
 +    context
 +        .skipped_range
 +        .borrow_mut()
 +        .append(&mut visitor_context.skipped_range.borrow_mut());
 +    Some(format!("{}{}{}", prefix, label_str, visitor.buffer))
 +}
 +
 +impl Rewrite for ast::Block {
 +    fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
 +        rewrite_block(self, None, None, context, shape)
 +    }
 +}
 +
 +fn rewrite_block(
 +    block: &ast::Block,
 +    attrs: Option<&[ast::Attribute]>,
 +    label: Option<ast::Label>,
 +    context: &RewriteContext<'_>,
 +    shape: Shape,
 +) -> Option<String> {
 +    let prefix = block_prefix(context, block, shape)?;
 +
 +    // shape.width is used only for the single line case: either the empty block `{}`,
 +    // or an unsafe expression `unsafe { e }`.
 +    if let rw @ Some(_) = rewrite_empty_block(context, block, attrs, label, &prefix, shape) {
 +        return rw;
 +    }
 +
 +    let result = rewrite_block_with_visitor(context, &prefix, block, attrs, label, shape, true);
 +    if let Some(ref result_str) = result {
 +        if result_str.lines().count() <= 3 {
 +            if let rw @ Some(_) =
 +                rewrite_single_line_block(context, &prefix, block, attrs, label, shape)
 +            {
 +                return rw;
 +            }
 +        }
 +    }
 +
 +    result
 +}
 +
 +// Rewrite condition if the given expression has one.
 +pub(crate) fn rewrite_cond(
 +    context: &RewriteContext<'_>,
 +    expr: &ast::Expr,
 +    shape: Shape,
 +) -> Option<String> {
 +    match expr.kind {
 +        ast::ExprKind::Match(ref cond, _) => {
 +            // `match `cond` {`
 +            let cond_shape = match context.config.indent_style() {
 +                IndentStyle::Visual => shape.shrink_left(6).and_then(|s| s.sub_width(2))?,
 +                IndentStyle::Block => shape.offset_left(8)?,
 +            };
 +            cond.rewrite(context, cond_shape)
 +        }
 +        _ => to_control_flow(expr, ExprType::SubExpression).and_then(|control_flow| {
 +            let alt_block_sep =
 +                String::from("\n") + &shape.indent.block_only().to_string(context.config);
 +            control_flow
 +                .rewrite_cond(context, shape, &alt_block_sep)
 +                .map(|rw| rw.0)
 +        }),
 +    }
 +}
 +
 +// Abstraction over control flow expressions
 +#[derive(Debug)]
 +struct ControlFlow<'a> {
 +    cond: Option<&'a ast::Expr>,
 +    block: &'a ast::Block,
 +    else_block: Option<&'a ast::Expr>,
 +    label: Option<ast::Label>,
 +    pat: Option<&'a ast::Pat>,
 +    keyword: &'a str,
 +    matcher: &'a str,
 +    connector: &'a str,
 +    allow_single_line: bool,
 +    // HACK: `true` if this is an `if` expression in an `else if`.
 +    nested_if: bool,
 +    span: Span,
 +}
 +
 +fn extract_pats_and_cond(expr: &ast::Expr) -> (Option<&ast::Pat>, &ast::Expr) {
 +    match expr.kind {
 +        ast::ExprKind::Let(ref pat, ref cond, _) => (Some(pat), cond),
 +        _ => (None, expr),
 +    }
 +}
 +
 +// FIXME: Refactor this.
 +fn to_control_flow(expr: &ast::Expr, expr_type: ExprType) -> Option<ControlFlow<'_>> {
 +    match expr.kind {
 +        ast::ExprKind::If(ref cond, ref if_block, ref else_block) => {
 +            let (pat, cond) = extract_pats_and_cond(cond);
 +            Some(ControlFlow::new_if(
 +                cond,
 +                pat,
 +                if_block,
 +                else_block.as_ref().map(|e| &**e),
 +                expr_type == ExprType::SubExpression,
 +                false,
 +                expr.span,
 +            ))
 +        }
 +        ast::ExprKind::ForLoop(ref pat, ref cond, ref block, label) => {
 +            Some(ControlFlow::new_for(pat, cond, block, label, expr.span))
 +        }
 +        ast::ExprKind::Loop(ref block, label) => {
 +            Some(ControlFlow::new_loop(block, label, expr.span))
 +        }
 +        ast::ExprKind::While(ref cond, ref block, label) => {
 +            let (pat, cond) = extract_pats_and_cond(cond);
 +            Some(ControlFlow::new_while(pat, cond, block, label, expr.span))
 +        }
 +        _ => None,
 +    }
 +}
 +
 +fn choose_matcher(pat: Option<&ast::Pat>) -> &'static str {
 +    pat.map_or("", |_| "let")
 +}
 +
 +impl<'a> ControlFlow<'a> {
 +    fn new_if(
 +        cond: &'a ast::Expr,
 +        pat: Option<&'a ast::Pat>,
 +        block: &'a ast::Block,
 +        else_block: Option<&'a ast::Expr>,
 +        allow_single_line: bool,
 +        nested_if: bool,
 +        span: Span,
 +    ) -> ControlFlow<'a> {
 +        let matcher = choose_matcher(pat);
 +        ControlFlow {
 +            cond: Some(cond),
 +            block,
 +            else_block,
 +            label: None,
 +            pat,
 +            keyword: "if",
 +            matcher,
 +            connector: " =",
 +            allow_single_line,
 +            nested_if,
 +            span,
 +        }
 +    }
 +
 +    fn new_loop(block: &'a ast::Block, label: Option<ast::Label>, span: Span) -> ControlFlow<'a> {
 +        ControlFlow {
 +            cond: None,
 +            block,
 +            else_block: None,
 +            label,
 +            pat: None,
 +            keyword: "loop",
 +            matcher: "",
 +            connector: "",
 +            allow_single_line: false,
 +            nested_if: false,
 +            span,
 +        }
 +    }
 +
 +    fn new_while(
 +        pat: Option<&'a ast::Pat>,
 +        cond: &'a ast::Expr,
 +        block: &'a ast::Block,
 +        label: Option<ast::Label>,
 +        span: Span,
 +    ) -> ControlFlow<'a> {
 +        let matcher = choose_matcher(pat);
 +        ControlFlow {
 +            cond: Some(cond),
 +            block,
 +            else_block: None,
 +            label,
 +            pat,
 +            keyword: "while",
 +            matcher,
 +            connector: " =",
 +            allow_single_line: false,
 +            nested_if: false,
 +            span,
 +        }
 +    }
 +
 +    fn new_for(
 +        pat: &'a ast::Pat,
 +        cond: &'a ast::Expr,
 +        block: &'a ast::Block,
 +        label: Option<ast::Label>,
 +        span: Span,
 +    ) -> ControlFlow<'a> {
 +        ControlFlow {
 +            cond: Some(cond),
 +            block,
 +            else_block: None,
 +            label,
 +            pat: Some(pat),
 +            keyword: "for",
 +            matcher: "",
 +            connector: " in",
 +            allow_single_line: false,
 +            nested_if: false,
 +            span,
 +        }
 +    }
 +
 +    fn rewrite_single_line(
 +        &self,
 +        pat_expr_str: &str,
 +        context: &RewriteContext<'_>,
 +        width: usize,
 +    ) -> Option<String> {
 +        assert!(self.allow_single_line);
 +        let else_block = self.else_block?;
 +        let fixed_cost = self.keyword.len() + "  {  } else {  }".len();
 +
 +        if let ast::ExprKind::Block(ref else_node, _) = else_block.kind {
 +            if !is_simple_block(context, self.block, None)
 +                || !is_simple_block(context, else_node, None)
 +                || pat_expr_str.contains('\n')
 +            {
 +                return None;
 +            }
 +
 +            let new_width = width.checked_sub(pat_expr_str.len() + fixed_cost)?;
 +            let expr = &self.block.stmts[0];
 +            let if_str = expr.rewrite(context, Shape::legacy(new_width, Indent::empty()))?;
 +
 +            let new_width = new_width.checked_sub(if_str.len())?;
 +            let else_expr = &else_node.stmts[0];
 +            let else_str = else_expr.rewrite(context, Shape::legacy(new_width, Indent::empty()))?;
 +
 +            if if_str.contains('\n') || else_str.contains('\n') {
 +                return None;
 +            }
 +
 +            let result = format!(
 +                "{} {} {{ {} }} else {{ {} }}",
 +                self.keyword, pat_expr_str, if_str, else_str
 +            );
 +
 +            if result.len() <= width {
 +                return Some(result);
 +            }
 +        }
 +
 +        None
 +    }
 +}
 +
 +/// Returns `true` if the last line of pat_str has leading whitespace and it is wider than the
 +/// shape's indent.
 +fn last_line_offsetted(start_column: usize, pat_str: &str) -> bool {
 +    let mut leading_whitespaces = 0;
 +    for c in pat_str.chars().rev() {
 +        match c {
 +            '\n' => break,
 +            _ if c.is_whitespace() => leading_whitespaces += 1,
 +            _ => leading_whitespaces = 0,
 +        }
 +    }
 +    leading_whitespaces > start_column
 +}
 +
 +impl<'a> ControlFlow<'a> {
 +    fn rewrite_pat_expr(
 +        &self,
 +        context: &RewriteContext<'_>,
 +        expr: &ast::Expr,
 +        shape: Shape,
 +        offset: usize,
 +    ) -> Option<String> {
 +        debug!("rewrite_pat_expr {:?} {:?} {:?}", shape, self.pat, expr);
 +
 +        let cond_shape = shape.offset_left(offset)?;
 +        if let Some(pat) = self.pat {
 +            let matcher = if self.matcher.is_empty() {
 +                self.matcher.to_owned()
 +            } else {
 +                format!("{} ", self.matcher)
 +            };
 +            let pat_shape = cond_shape
 +                .offset_left(matcher.len())?
 +                .sub_width(self.connector.len())?;
 +            let pat_string = pat.rewrite(context, pat_shape)?;
 +            let comments_lo = context
 +                .snippet_provider
 +                .span_after(self.span.with_lo(pat.span.hi()), self.connector.trim());
 +            let comments_span = mk_sp(comments_lo, expr.span.lo());
 +            return rewrite_assign_rhs_with_comments(
 +                context,
 +                &format!("{}{}{}", matcher, pat_string, self.connector),
 +                expr,
 +                cond_shape,
++                &RhsAssignKind::Expr(&expr.kind, expr.span),
 +                RhsTactics::Default,
 +                comments_span,
 +                true,
 +            );
 +        }
 +
 +        let expr_rw = expr.rewrite(context, cond_shape);
 +        // The expression may (partially) fit on the current line.
 +        // We do not allow splitting between `if` and condition.
 +        if self.keyword == "if" || expr_rw.is_some() {
 +            return expr_rw;
 +        }
 +
 +        // The expression won't fit on the current line, jump to next.
 +        let nested_shape = shape
 +            .block_indent(context.config.tab_spaces())
 +            .with_max_width(context.config);
 +        let nested_indent_str = nested_shape.indent.to_string_with_newline(context.config);
 +        expr.rewrite(context, nested_shape)
 +            .map(|expr_rw| format!("{}{}", nested_indent_str, expr_rw))
 +    }
 +
 +    fn rewrite_cond(
 +        &self,
 +        context: &RewriteContext<'_>,
 +        shape: Shape,
 +        alt_block_sep: &str,
 +    ) -> Option<(String, usize)> {
 +        // Do not take the rhs overhead from the upper expressions into account
 +        // when rewriting pattern.
 +        let new_width = context.budget(shape.used_width());
 +        let fresh_shape = Shape {
 +            width: new_width,
 +            ..shape
 +        };
 +        let constr_shape = if self.nested_if {
 +            // We are part of an if-elseif-else chain. Our constraints are tightened.
 +            // 7 = "} else " .len()
 +            fresh_shape.offset_left(7)?
 +        } else {
 +            fresh_shape
 +        };
 +
 +        let label_string = rewrite_label(self.label);
 +        // 1 = space after keyword.
 +        let offset = self.keyword.len() + label_string.len() + 1;
 +
 +        let pat_expr_string = match self.cond {
 +            Some(cond) => self.rewrite_pat_expr(context, cond, constr_shape, offset)?,
 +            None => String::new(),
 +        };
 +
 +        let brace_overhead =
 +            if context.config.control_brace_style() != ControlBraceStyle::AlwaysNextLine {
 +                // 2 = ` {`
 +                2
 +            } else {
 +                0
 +            };
 +        let one_line_budget = context
 +            .config
 +            .max_width()
 +            .saturating_sub(constr_shape.used_width() + offset + brace_overhead);
 +        let force_newline_brace = (pat_expr_string.contains('\n')
 +            || pat_expr_string.len() > one_line_budget)
 +            && (!last_line_extendable(&pat_expr_string)
 +                || last_line_offsetted(shape.used_width(), &pat_expr_string));
 +
 +        // Try to format if-else on single line.
 +        if self.allow_single_line && context.config.single_line_if_else_max_width() > 0 {
 +            let trial = self.rewrite_single_line(&pat_expr_string, context, shape.width);
 +
 +            if let Some(cond_str) = trial {
 +                if cond_str.len() <= context.config.single_line_if_else_max_width() {
 +                    return Some((cond_str, 0));
 +                }
 +            }
 +        }
 +
 +        let cond_span = if let Some(cond) = self.cond {
 +            cond.span
 +        } else {
 +            mk_sp(self.block.span.lo(), self.block.span.lo())
 +        };
 +
 +        // `for event in event`
 +        // Do not include label in the span.
 +        let lo = self
 +            .label
 +            .map_or(self.span.lo(), |label| label.ident.span.hi());
 +        let between_kwd_cond = mk_sp(
 +            context
 +                .snippet_provider
 +                .span_after(mk_sp(lo, self.span.hi()), self.keyword.trim()),
 +            if self.pat.is_none() {
 +                cond_span.lo()
 +            } else if self.matcher.is_empty() {
 +                self.pat.unwrap().span.lo()
 +            } else {
 +                context
 +                    .snippet_provider
 +                    .span_before(self.span, self.matcher.trim())
 +            },
 +        );
 +
 +        let between_kwd_cond_comment = extract_comment(between_kwd_cond, context, shape);
 +
 +        let after_cond_comment =
 +            extract_comment(mk_sp(cond_span.hi(), self.block.span.lo()), context, shape);
 +
 +        let block_sep = if self.cond.is_none() && between_kwd_cond_comment.is_some() {
 +            ""
 +        } else if context.config.control_brace_style() == ControlBraceStyle::AlwaysNextLine
 +            || force_newline_brace
 +        {
 +            alt_block_sep
 +        } else {
 +            " "
 +        };
 +
 +        let used_width = if pat_expr_string.contains('\n') {
 +            last_line_width(&pat_expr_string)
 +        } else {
 +            // 2 = spaces after keyword and condition.
 +            label_string.len() + self.keyword.len() + pat_expr_string.len() + 2
 +        };
 +
 +        Some((
 +            format!(
 +                "{}{}{}{}{}",
 +                label_string,
 +                self.keyword,
 +                between_kwd_cond_comment.as_ref().map_or(
 +                    if pat_expr_string.is_empty() || pat_expr_string.starts_with('\n') {
 +                        ""
 +                    } else {
 +                        " "
 +                    },
 +                    |s| &**s,
 +                ),
 +                pat_expr_string,
 +                after_cond_comment.as_ref().map_or(block_sep, |s| &**s)
 +            ),
 +            used_width,
 +        ))
 +    }
 +}
 +
 +impl<'a> Rewrite for ControlFlow<'a> {
 +    fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
 +        debug!("ControlFlow::rewrite {:?} {:?}", self, shape);
 +
 +        let alt_block_sep = &shape.indent.to_string_with_newline(context.config);
 +        let (cond_str, used_width) = self.rewrite_cond(context, shape, alt_block_sep)?;
 +        // If `used_width` is 0, it indicates that whole control flow is written in a single line.
 +        if used_width == 0 {
 +            return Some(cond_str);
 +        }
 +
 +        let block_width = shape.width.saturating_sub(used_width);
 +        // This is used only for the empty block case: `{}`. So, we use 1 if we know
 +        // we should avoid the single line case.
 +        let block_width = if self.else_block.is_some() || self.nested_if {
 +            min(1, block_width)
 +        } else {
 +            block_width
 +        };
 +        let block_shape = Shape {
 +            width: block_width,
 +            ..shape
 +        };
 +        let block_str = {
 +            let old_val = context.is_if_else_block.replace(self.else_block.is_some());
 +            let result =
 +                rewrite_block_with_visitor(context, "", self.block, None, None, block_shape, true);
 +            context.is_if_else_block.replace(old_val);
 +            result?
 +        };
 +
 +        let mut result = format!("{}{}", cond_str, block_str);
 +
 +        if let Some(else_block) = self.else_block {
 +            let shape = Shape::indented(shape.indent, context.config);
 +            let mut last_in_chain = false;
 +            let rewrite = match else_block.kind {
 +                // If the else expression is another if-else expression, prevent it
 +                // from being formatted on a single line.
 +                // Note how we're passing the original shape, as the
 +                // cost of "else" should not cascade.
 +                ast::ExprKind::If(ref cond, ref if_block, ref next_else_block) => {
 +                    let (pats, cond) = extract_pats_and_cond(cond);
 +                    ControlFlow::new_if(
 +                        cond,
 +                        pats,
 +                        if_block,
 +                        next_else_block.as_ref().map(|e| &**e),
 +                        false,
 +                        true,
 +                        mk_sp(else_block.span.lo(), self.span.hi()),
 +                    )
 +                    .rewrite(context, shape)
 +                }
 +                _ => {
 +                    last_in_chain = true;
 +                    // When rewriting a block, the width is only used for single line
 +                    // blocks, passing 1 lets us avoid that.
 +                    let else_shape = Shape {
 +                        width: min(1, shape.width),
 +                        ..shape
 +                    };
 +                    format_expr(else_block, ExprType::Statement, context, else_shape)
 +                }
 +            };
 +
 +            let between_kwd_else_block = mk_sp(
 +                self.block.span.hi(),
 +                context
 +                    .snippet_provider
 +                    .span_before(mk_sp(self.block.span.hi(), else_block.span.lo()), "else"),
 +            );
 +            let between_kwd_else_block_comment =
 +                extract_comment(between_kwd_else_block, context, shape);
 +
 +            let after_else = mk_sp(
 +                context
 +                    .snippet_provider
 +                    .span_after(mk_sp(self.block.span.hi(), else_block.span.lo()), "else"),
 +                else_block.span.lo(),
 +            );
 +            let after_else_comment = extract_comment(after_else, context, shape);
 +
 +            let between_sep = match context.config.control_brace_style() {
 +                ControlBraceStyle::AlwaysNextLine | ControlBraceStyle::ClosingNextLine => {
 +                    &*alt_block_sep
 +                }
 +                ControlBraceStyle::AlwaysSameLine => " ",
 +            };
 +            let after_sep = match context.config.control_brace_style() {
 +                ControlBraceStyle::AlwaysNextLine if last_in_chain => &*alt_block_sep,
 +                _ => " ",
 +            };
 +
 +            result.push_str(&format!(
 +                "{}else{}",
 +                between_kwd_else_block_comment
 +                    .as_ref()
 +                    .map_or(between_sep, |s| &**s),
 +                after_else_comment.as_ref().map_or(after_sep, |s| &**s),
 +            ));
 +            result.push_str(&rewrite?);
 +        }
 +
 +        Some(result)
 +    }
 +}
 +
 +fn rewrite_label(opt_label: Option<ast::Label>) -> Cow<'static, str> {
 +    match opt_label {
 +        Some(label) => Cow::from(format!("{}: ", label.ident)),
 +        None => Cow::from(""),
 +    }
 +}
 +
 +fn extract_comment(span: Span, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
 +    match rewrite_missing_comment(span, shape, context) {
 +        Some(ref comment) if !comment.is_empty() => Some(format!(
 +            "{indent}{}{indent}",
 +            comment,
 +            indent = shape.indent.to_string_with_newline(context.config)
 +        )),
 +        _ => None,
 +    }
 +}
 +
 +pub(crate) fn block_contains_comment(context: &RewriteContext<'_>, block: &ast::Block) -> bool {
 +    contains_comment(context.snippet(block.span))
 +}
 +
 +// Checks that a block contains no statements, an expression and no comments or
 +// attributes.
 +// FIXME: incorrectly returns false when comment is contained completely within
 +// the expression.
 +pub(crate) fn is_simple_block(
 +    context: &RewriteContext<'_>,
 +    block: &ast::Block,
 +    attrs: Option<&[ast::Attribute]>,
 +) -> bool {
 +    block.stmts.len() == 1
 +        && stmt_is_expr(&block.stmts[0])
 +        && !block_contains_comment(context, block)
 +        && attrs.map_or(true, |a| a.is_empty())
 +}
 +
 +/// Checks whether a block contains at most one statement or expression, and no
 +/// comments or attributes.
 +pub(crate) fn is_simple_block_stmt(
 +    context: &RewriteContext<'_>,
 +    block: &ast::Block,
 +    attrs: Option<&[ast::Attribute]>,
 +) -> bool {
 +    block.stmts.len() <= 1
 +        && !block_contains_comment(context, block)
 +        && attrs.map_or(true, |a| a.is_empty())
 +}
 +
 +fn block_has_statements(block: &ast::Block) -> bool {
 +    block
 +        .stmts
 +        .iter()
 +        .any(|stmt| !matches!(stmt.kind, ast::StmtKind::Empty))
 +}
 +
 +/// Checks whether a block contains no statements, expressions, comments, or
 +/// inner attributes.
 +pub(crate) fn is_empty_block(
 +    context: &RewriteContext<'_>,
 +    block: &ast::Block,
 +    attrs: Option<&[ast::Attribute]>,
 +) -> bool {
 +    !block_has_statements(block)
 +        && !block_contains_comment(context, block)
 +        && attrs.map_or(true, |a| inner_attributes(a).is_empty())
 +}
 +
 +pub(crate) fn stmt_is_expr(stmt: &ast::Stmt) -> bool {
 +    matches!(stmt.kind, ast::StmtKind::Expr(..))
 +}
 +
 +pub(crate) fn is_unsafe_block(block: &ast::Block) -> bool {
 +    matches!(block.rules, ast::BlockCheckMode::Unsafe(..))
 +}
 +
 +pub(crate) fn rewrite_literal(
 +    context: &RewriteContext<'_>,
 +    l: &ast::Lit,
 +    shape: Shape,
 +) -> Option<String> {
 +    match l.kind {
 +        ast::LitKind::Str(_, ast::StrStyle::Cooked) => rewrite_string_lit(context, l.span, shape),
 +        ast::LitKind::Int(..) => rewrite_int_lit(context, l, shape),
 +        _ => wrap_str(
 +            context.snippet(l.span).to_owned(),
 +            context.config.max_width(),
 +            shape,
 +        ),
 +    }
 +}
 +
 +fn rewrite_string_lit(context: &RewriteContext<'_>, span: Span, shape: Shape) -> Option<String> {
 +    let string_lit = context.snippet(span);
 +
 +    if !context.config.format_strings() {
 +        if string_lit
 +            .lines()
 +            .dropping_back(1)
 +            .all(|line| line.ends_with('\\'))
 +            && context.config.version() == Version::Two
 +        {
 +            return Some(string_lit.to_owned());
 +        } else {
 +            return wrap_str(string_lit.to_owned(), context.config.max_width(), shape);
 +        }
 +    }
 +
 +    // Remove the quote characters.
 +    let str_lit = &string_lit[1..string_lit.len() - 1];
 +
 +    rewrite_string(
 +        str_lit,
 +        &StringFormat::new(shape.visual_indent(0), context.config),
 +        shape.width.saturating_sub(2),
 +    )
 +}
 +
 +fn rewrite_int_lit(context: &RewriteContext<'_>, lit: &ast::Lit, shape: Shape) -> Option<String> {
 +    let span = lit.span;
 +    let symbol = lit.token.symbol.as_str();
 +
 +    if let Some(symbol_stripped) = symbol.strip_prefix("0x") {
 +        let hex_lit = match context.config.hex_literal_case() {
 +            HexLiteralCase::Preserve => None,
 +            HexLiteralCase::Upper => Some(symbol_stripped.to_ascii_uppercase()),
 +            HexLiteralCase::Lower => Some(symbol_stripped.to_ascii_lowercase()),
 +        };
 +        if let Some(hex_lit) = hex_lit {
 +            return wrap_str(
 +                format!(
 +                    "0x{}{}",
 +                    hex_lit,
 +                    lit.token.suffix.map_or(String::new(), |s| s.to_string())
 +                ),
 +                context.config.max_width(),
 +                shape,
 +            );
 +        }
 +    }
 +
 +    wrap_str(
 +        context.snippet(span).to_owned(),
 +        context.config.max_width(),
 +        shape,
 +    )
 +}
 +
 +fn choose_separator_tactic(context: &RewriteContext<'_>, span: Span) -> Option<SeparatorTactic> {
 +    if context.inside_macro() {
 +        if span_ends_with_comma(context, span) {
 +            Some(SeparatorTactic::Always)
 +        } else {
 +            Some(SeparatorTactic::Never)
 +        }
 +    } else {
 +        None
 +    }
 +}
 +
 +pub(crate) fn rewrite_call(
 +    context: &RewriteContext<'_>,
 +    callee: &str,
 +    args: &[ptr::P<ast::Expr>],
 +    span: Span,
 +    shape: Shape,
 +) -> Option<String> {
 +    overflow::rewrite_with_parens(
 +        context,
 +        callee,
 +        args.iter(),
 +        shape,
 +        span,
 +        context.config.fn_call_width(),
 +        choose_separator_tactic(context, span),
 +    )
 +}
 +
 +pub(crate) fn is_simple_expr(expr: &ast::Expr) -> bool {
 +    match expr.kind {
 +        ast::ExprKind::Lit(..) => true,
 +        ast::ExprKind::Path(ref qself, ref path) => qself.is_none() && path.segments.len() <= 1,
 +        ast::ExprKind::AddrOf(_, _, ref expr)
 +        | ast::ExprKind::Box(ref expr)
 +        | ast::ExprKind::Cast(ref expr, _)
 +        | ast::ExprKind::Field(ref expr, _)
 +        | ast::ExprKind::Try(ref expr)
 +        | ast::ExprKind::Unary(_, ref expr) => is_simple_expr(expr),
 +        ast::ExprKind::Index(ref lhs, ref rhs) => is_simple_expr(lhs) && is_simple_expr(rhs),
 +        ast::ExprKind::Repeat(ref lhs, ref rhs) => {
 +            is_simple_expr(lhs) && is_simple_expr(&*rhs.value)
 +        }
 +        _ => false,
 +    }
 +}
 +
 +pub(crate) fn is_every_expr_simple(lists: &[OverflowableItem<'_>]) -> bool {
 +    lists.iter().all(OverflowableItem::is_simple)
 +}
 +
 +pub(crate) fn can_be_overflowed_expr(
 +    context: &RewriteContext<'_>,
 +    expr: &ast::Expr,
 +    args_len: usize,
 +) -> bool {
 +    match expr.kind {
 +        _ if !expr.attrs.is_empty() => false,
 +        ast::ExprKind::Match(..) => {
 +            (context.use_block_indent() && args_len == 1)
 +                || (context.config.indent_style() == IndentStyle::Visual && args_len > 1)
 +                || context.config.overflow_delimited_expr()
 +        }
 +        ast::ExprKind::If(..)
 +        | ast::ExprKind::ForLoop(..)
 +        | ast::ExprKind::Loop(..)
 +        | ast::ExprKind::While(..) => {
 +            context.config.combine_control_expr() && context.use_block_indent() && args_len == 1
 +        }
 +
 +        // Handle always block-like expressions
 +        ast::ExprKind::Async(..) | ast::ExprKind::Block(..) | ast::ExprKind::Closure(..) => true,
 +
 +        // Handle `[]` and `{}`-like expressions
 +        ast::ExprKind::Array(..) | ast::ExprKind::Struct(..) => {
 +            context.config.overflow_delimited_expr()
 +                || (context.use_block_indent() && args_len == 1)
 +        }
 +        ast::ExprKind::MacCall(ref mac) => {
 +            match (
 +                rustc_ast::ast::MacDelimiter::from_token(mac.args.delim()),
 +                context.config.overflow_delimited_expr(),
 +            ) {
 +                (Some(ast::MacDelimiter::Bracket), true)
 +                | (Some(ast::MacDelimiter::Brace), true) => true,
 +                _ => context.use_block_indent() && args_len == 1,
 +            }
 +        }
 +
 +        // Handle parenthetical expressions
 +        ast::ExprKind::Call(..) | ast::ExprKind::MethodCall(..) | ast::ExprKind::Tup(..) => {
 +            context.use_block_indent() && args_len == 1
 +        }
 +
 +        // Handle unary-like expressions
 +        ast::ExprKind::AddrOf(_, _, ref expr)
 +        | ast::ExprKind::Box(ref expr)
 +        | ast::ExprKind::Try(ref expr)
 +        | ast::ExprKind::Unary(_, ref expr)
 +        | ast::ExprKind::Cast(ref expr, _) => can_be_overflowed_expr(context, expr, args_len),
 +        _ => false,
 +    }
 +}
 +
 +pub(crate) fn is_nested_call(expr: &ast::Expr) -> bool {
 +    match expr.kind {
 +        ast::ExprKind::Call(..) | ast::ExprKind::MacCall(..) => true,
 +        ast::ExprKind::AddrOf(_, _, ref expr)
 +        | ast::ExprKind::Box(ref expr)
 +        | ast::ExprKind::Try(ref expr)
 +        | ast::ExprKind::Unary(_, ref expr)
 +        | ast::ExprKind::Cast(ref expr, _) => is_nested_call(expr),
 +        _ => false,
 +    }
 +}
 +
 +/// Returns `true` if a function call or a method call represented by the given span ends with a
 +/// trailing comma. This function is used when rewriting macro, as adding or removing a trailing
 +/// comma from macro can potentially break the code.
 +pub(crate) fn span_ends_with_comma(context: &RewriteContext<'_>, span: Span) -> bool {
 +    let mut result: bool = Default::default();
 +    let mut prev_char: char = Default::default();
 +    let closing_delimiters = &[')', '}', ']'];
 +
 +    for (kind, c) in CharClasses::new(context.snippet(span).chars()) {
 +        match c {
 +            _ if kind.is_comment() || c.is_whitespace() => continue,
 +            c if closing_delimiters.contains(&c) => {
 +                result &= !closing_delimiters.contains(&prev_char);
 +            }
 +            ',' => result = true,
 +            _ => result = false,
 +        }
 +        prev_char = c;
 +    }
 +
 +    result
 +}
 +
 +fn rewrite_paren(
 +    context: &RewriteContext<'_>,
 +    mut subexpr: &ast::Expr,
 +    shape: Shape,
 +    mut span: Span,
 +) -> Option<String> {
 +    debug!("rewrite_paren, shape: {:?}", shape);
 +
 +    // Extract comments within parens.
 +    let mut pre_span;
 +    let mut post_span;
 +    let mut pre_comment;
 +    let mut post_comment;
 +    let remove_nested_parens = context.config.remove_nested_parens();
 +    loop {
 +        // 1 = "(" or ")"
 +        pre_span = mk_sp(span.lo() + BytePos(1), subexpr.span.lo());
 +        post_span = mk_sp(subexpr.span.hi(), span.hi() - BytePos(1));
 +        pre_comment = rewrite_missing_comment(pre_span, shape, context)?;
 +        post_comment = rewrite_missing_comment(post_span, shape, context)?;
 +
 +        // Remove nested parens if there are no comments.
 +        if let ast::ExprKind::Paren(ref subsubexpr) = subexpr.kind {
 +            if remove_nested_parens && pre_comment.is_empty() && post_comment.is_empty() {
 +                span = subexpr.span;
 +                subexpr = subsubexpr;
 +                continue;
 +            }
 +        }
 +
 +        break;
 +    }
 +
 +    // 1 = `(` and `)`
 +    let sub_shape = shape.offset_left(1)?.sub_width(1)?;
 +    let subexpr_str = subexpr.rewrite(context, sub_shape)?;
 +    let fits_single_line = !pre_comment.contains("//") && !post_comment.contains("//");
 +    if fits_single_line {
 +        Some(format!("({}{}{})", pre_comment, subexpr_str, post_comment))
 +    } else {
 +        rewrite_paren_in_multi_line(context, subexpr, shape, pre_span, post_span)
 +    }
 +}
 +
 +fn rewrite_paren_in_multi_line(
 +    context: &RewriteContext<'_>,
 +    subexpr: &ast::Expr,
 +    shape: Shape,
 +    pre_span: Span,
 +    post_span: Span,
 +) -> Option<String> {
 +    let nested_indent = shape.indent.block_indent(context.config);
 +    let nested_shape = Shape::indented(nested_indent, context.config);
 +    let pre_comment = rewrite_missing_comment(pre_span, nested_shape, context)?;
 +    let post_comment = rewrite_missing_comment(post_span, nested_shape, context)?;
 +    let subexpr_str = subexpr.rewrite(context, nested_shape)?;
 +
 +    let mut result = String::with_capacity(subexpr_str.len() * 2);
 +    result.push('(');
 +    if !pre_comment.is_empty() {
 +        result.push_str(&nested_indent.to_string_with_newline(context.config));
 +        result.push_str(&pre_comment);
 +    }
 +    result.push_str(&nested_indent.to_string_with_newline(context.config));
 +    result.push_str(&subexpr_str);
 +    if !post_comment.is_empty() {
 +        result.push_str(&nested_indent.to_string_with_newline(context.config));
 +        result.push_str(&post_comment);
 +    }
 +    result.push_str(&shape.indent.to_string_with_newline(context.config));
 +    result.push(')');
 +
 +    Some(result)
 +}
 +
 +fn rewrite_index(
 +    expr: &ast::Expr,
 +    index: &ast::Expr,
 +    context: &RewriteContext<'_>,
 +    shape: Shape,
 +) -> Option<String> {
 +    let expr_str = expr.rewrite(context, shape)?;
 +
 +    let offset = last_line_width(&expr_str) + 1;
 +    let rhs_overhead = shape.rhs_overhead(context.config);
 +    let index_shape = if expr_str.contains('\n') {
 +        Shape::legacy(context.config.max_width(), shape.indent)
 +            .offset_left(offset)
 +            .and_then(|shape| shape.sub_width(1 + rhs_overhead))
 +    } else {
 +        match context.config.indent_style() {
 +            IndentStyle::Block => shape
 +                .offset_left(offset)
 +                .and_then(|shape| shape.sub_width(1)),
 +            IndentStyle::Visual => shape.visual_indent(offset).sub_width(offset + 1),
 +        }
 +    };
 +    let orig_index_rw = index_shape.and_then(|s| index.rewrite(context, s));
 +
 +    // Return if index fits in a single line.
 +    match orig_index_rw {
 +        Some(ref index_str) if !index_str.contains('\n') => {
 +            return Some(format!("{}[{}]", expr_str, index_str));
 +        }
 +        _ => (),
 +    }
 +
 +    // Try putting index on the next line and see if it fits in a single line.
 +    let indent = shape.indent.block_indent(context.config);
 +    let index_shape = Shape::indented(indent, context.config).offset_left(1)?;
 +    let index_shape = index_shape.sub_width(1 + rhs_overhead)?;
 +    let new_index_rw = index.rewrite(context, index_shape);
 +    match (orig_index_rw, new_index_rw) {
 +        (_, Some(ref new_index_str)) if !new_index_str.contains('\n') => Some(format!(
 +            "{}{}[{}]",
 +            expr_str,
 +            indent.to_string_with_newline(context.config),
 +            new_index_str,
 +        )),
 +        (None, Some(ref new_index_str)) => Some(format!(
 +            "{}{}[{}]",
 +            expr_str,
 +            indent.to_string_with_newline(context.config),
 +            new_index_str,
 +        )),
 +        (Some(ref index_str), _) => Some(format!("{}[{}]", expr_str, index_str)),
 +        _ => None,
 +    }
 +}
 +
 +fn struct_lit_can_be_aligned(fields: &[ast::ExprField], has_base: bool) -> bool {
 +    !has_base && fields.iter().all(|field| !field.is_shorthand)
 +}
 +
 +fn rewrite_struct_lit<'a>(
 +    context: &RewriteContext<'_>,
 +    path: &ast::Path,
 +    fields: &'a [ast::ExprField],
 +    struct_rest: &ast::StructRest,
 +    attrs: &[ast::Attribute],
 +    span: Span,
 +    shape: Shape,
 +) -> Option<String> {
 +    debug!("rewrite_struct_lit: shape {:?}", shape);
 +
 +    enum StructLitField<'a> {
 +        Regular(&'a ast::ExprField),
 +        Base(&'a ast::Expr),
 +        Rest(&'a Span),
 +    }
 +
 +    // 2 = " {".len()
 +    let path_shape = shape.sub_width(2)?;
 +    let path_str = rewrite_path(context, PathContext::Expr, None, path, path_shape)?;
 +
 +    let has_base_or_rest = match struct_rest {
 +        ast::StructRest::None if fields.is_empty() => return Some(format!("{} {{}}", path_str)),
 +        ast::StructRest::Rest(_) if fields.is_empty() => {
 +            return Some(format!("{} {{ .. }}", path_str));
 +        }
 +        ast::StructRest::Rest(_) | ast::StructRest::Base(_) => true,
 +        _ => false,
 +    };
 +
 +    // Foo { a: Foo } - indent is +3, width is -5.
 +    let (h_shape, v_shape) = struct_lit_shape(shape, context, path_str.len() + 3, 2)?;
 +
 +    let one_line_width = h_shape.map_or(0, |shape| shape.width);
 +    let body_lo = context.snippet_provider.span_after(span, "{");
 +    let fields_str = if struct_lit_can_be_aligned(fields, has_base_or_rest)
 +        && context.config.struct_field_align_threshold() > 0
 +    {
 +        rewrite_with_alignment(
 +            fields,
 +            context,
 +            v_shape,
 +            mk_sp(body_lo, span.hi()),
 +            one_line_width,
 +        )?
 +    } else {
 +        let field_iter = fields.iter().map(StructLitField::Regular).chain(
 +            match struct_rest {
 +                ast::StructRest::Base(expr) => Some(StructLitField::Base(&**expr)),
 +                ast::StructRest::Rest(span) => Some(StructLitField::Rest(span)),
 +                ast::StructRest::None => None,
 +            }
 +            .into_iter(),
 +        );
 +
 +        let span_lo = |item: &StructLitField<'_>| match *item {
 +            StructLitField::Regular(field) => field.span().lo(),
 +            StructLitField::Base(expr) => {
 +                let last_field_hi = fields.last().map_or(span.lo(), |field| field.span.hi());
 +                let snippet = context.snippet(mk_sp(last_field_hi, expr.span.lo()));
 +                let pos = snippet.find_uncommented("..").unwrap();
 +                last_field_hi + BytePos(pos as u32)
 +            }
 +            StructLitField::Rest(span) => span.lo(),
 +        };
 +        let span_hi = |item: &StructLitField<'_>| match *item {
 +            StructLitField::Regular(field) => field.span().hi(),
 +            StructLitField::Base(expr) => expr.span.hi(),
 +            StructLitField::Rest(span) => span.hi(),
 +        };
 +        let rewrite = |item: &StructLitField<'_>| match *item {
 +            StructLitField::Regular(field) => {
 +                // The 1 taken from the v_budget is for the comma.
 +                rewrite_field(context, field, v_shape.sub_width(1)?, 0)
 +            }
 +            StructLitField::Base(expr) => {
 +                // 2 = ..
 +                expr.rewrite(context, v_shape.offset_left(2)?)
 +                    .map(|s| format!("..{}", s))
 +            }
 +            StructLitField::Rest(_) => Some("..".to_owned()),
 +        };
 +
 +        let items = itemize_list(
 +            context.snippet_provider,
 +            field_iter,
 +            "}",
 +            ",",
 +            span_lo,
 +            span_hi,
 +            rewrite,
 +            body_lo,
 +            span.hi(),
 +            false,
 +        );
 +        let item_vec = items.collect::<Vec<_>>();
 +
 +        let tactic = struct_lit_tactic(h_shape, context, &item_vec);
 +        let nested_shape = shape_for_tactic(tactic, h_shape, v_shape);
 +
 +        let ends_with_comma = span_ends_with_comma(context, span);
 +        let force_no_trailing_comma = context.inside_macro() && !ends_with_comma;
 +
 +        let fmt = struct_lit_formatting(
 +            nested_shape,
 +            tactic,
 +            context,
 +            force_no_trailing_comma || has_base_or_rest || !context.use_block_indent(),
 +        );
 +
 +        write_list(&item_vec, &fmt)?
 +    };
 +
 +    let fields_str =
 +        wrap_struct_field(context, attrs, &fields_str, shape, v_shape, one_line_width)?;
 +    Some(format!("{} {{{}}}", path_str, fields_str))
 +
 +    // FIXME if context.config.indent_style() == Visual, but we run out
 +    // of space, we should fall back to BlockIndent.
 +}
 +
 +pub(crate) fn wrap_struct_field(
 +    context: &RewriteContext<'_>,
 +    attrs: &[ast::Attribute],
 +    fields_str: &str,
 +    shape: Shape,
 +    nested_shape: Shape,
 +    one_line_width: usize,
 +) -> Option<String> {
 +    let should_vertical = context.config.indent_style() == IndentStyle::Block
 +        && (fields_str.contains('\n')
 +            || !context.config.struct_lit_single_line()
 +            || fields_str.len() > one_line_width);
 +
 +    let inner_attrs = &inner_attributes(attrs);
 +    if inner_attrs.is_empty() {
 +        if should_vertical {
 +            Some(format!(
 +                "{}{}{}",
 +                nested_shape.indent.to_string_with_newline(context.config),
 +                fields_str,
 +                shape.indent.to_string_with_newline(context.config)
 +            ))
 +        } else {
 +            // One liner or visual indent.
 +            Some(format!(" {} ", fields_str))
 +        }
 +    } else {
 +        Some(format!(
 +            "{}{}{}{}{}",
 +            nested_shape.indent.to_string_with_newline(context.config),
 +            inner_attrs.rewrite(context, shape)?,
 +            nested_shape.indent.to_string_with_newline(context.config),
 +            fields_str,
 +            shape.indent.to_string_with_newline(context.config)
 +        ))
 +    }
 +}
 +
 +pub(crate) fn struct_lit_field_separator(config: &Config) -> &str {
 +    colon_spaces(config)
 +}
 +
 +pub(crate) fn rewrite_field(
 +    context: &RewriteContext<'_>,
 +    field: &ast::ExprField,
 +    shape: Shape,
 +    prefix_max_width: usize,
 +) -> Option<String> {
 +    if contains_skip(&field.attrs) {
 +        return Some(context.snippet(field.span()).to_owned());
 +    }
 +    let mut attrs_str = field.attrs.rewrite(context, shape)?;
 +    if !attrs_str.is_empty() {
 +        attrs_str.push_str(&shape.indent.to_string_with_newline(context.config));
 +    };
 +    let name = context.snippet(field.ident.span);
 +    if field.is_shorthand {
 +        Some(attrs_str + name)
 +    } else {
 +        let mut separator = String::from(struct_lit_field_separator(context.config));
 +        for _ in 0..prefix_max_width.saturating_sub(name.len()) {
 +            separator.push(' ');
 +        }
 +        let overhead = name.len() + separator.len();
 +        let expr_shape = shape.offset_left(overhead)?;
 +        let expr = field.expr.rewrite(context, expr_shape);
 +
 +        match expr {
 +            Some(ref e) if e.as_str() == name && context.config.use_field_init_shorthand() => {
 +                Some(attrs_str + name)
 +            }
 +            Some(e) => Some(format!("{}{}{}{}", attrs_str, name, separator, e)),
 +            None => {
 +                let expr_offset = shape.indent.block_indent(context.config);
 +                let expr = field
 +                    .expr
 +                    .rewrite(context, Shape::indented(expr_offset, context.config));
 +                expr.map(|s| {
 +                    format!(
 +                        "{}{}:\n{}{}",
 +                        attrs_str,
 +                        name,
 +                        expr_offset.to_string(context.config),
 +                        s
 +                    )
 +                })
 +            }
 +        }
 +    }
 +}
 +
 +fn rewrite_tuple_in_visual_indent_style<'a, T: 'a + IntoOverflowableItem<'a>>(
 +    context: &RewriteContext<'_>,
 +    mut items: impl Iterator<Item = &'a T>,
 +    span: Span,
 +    shape: Shape,
 +    is_singleton_tuple: bool,
 +) -> Option<String> {
 +    // In case of length 1, need a trailing comma
 +    debug!("rewrite_tuple_in_visual_indent_style {:?}", shape);
 +    if is_singleton_tuple {
 +        // 3 = "(" + ",)"
 +        let nested_shape = shape.sub_width(3)?.visual_indent(1);
 +        return items
 +            .next()
 +            .unwrap()
 +            .rewrite(context, nested_shape)
 +            .map(|s| format!("({},)", s));
 +    }
 +
 +    let list_lo = context.snippet_provider.span_after(span, "(");
 +    let nested_shape = shape.sub_width(2)?.visual_indent(1);
 +    let items = itemize_list(
 +        context.snippet_provider,
 +        items,
 +        ")",
 +        ",",
 +        |item| item.span().lo(),
 +        |item| item.span().hi(),
 +        |item| item.rewrite(context, nested_shape),
 +        list_lo,
 +        span.hi() - BytePos(1),
 +        false,
 +    );
 +    let item_vec: Vec<_> = items.collect();
 +    let tactic = definitive_tactic(
 +        &item_vec,
 +        ListTactic::HorizontalVertical,
 +        Separator::Comma,
 +        nested_shape.width,
 +    );
 +    let fmt = ListFormatting::new(nested_shape, context.config)
 +        .tactic(tactic)
 +        .ends_with_newline(false);
 +    let list_str = write_list(&item_vec, &fmt)?;
 +
 +    Some(format!("({})", list_str))
 +}
 +
 +pub(crate) fn rewrite_tuple<'a, T: 'a + IntoOverflowableItem<'a>>(
 +    context: &'a RewriteContext<'_>,
 +    items: impl Iterator<Item = &'a T>,
 +    span: Span,
 +    shape: Shape,
 +    is_singleton_tuple: bool,
 +) -> Option<String> {
 +    debug!("rewrite_tuple {:?}", shape);
 +    if context.use_block_indent() {
 +        // We use the same rule as function calls for rewriting tuples.
 +        let force_tactic = if context.inside_macro() {
 +            if span_ends_with_comma(context, span) {
 +                Some(SeparatorTactic::Always)
 +            } else {
 +                Some(SeparatorTactic::Never)
 +            }
 +        } else if is_singleton_tuple {
 +            Some(SeparatorTactic::Always)
 +        } else {
 +            None
 +        };
 +        overflow::rewrite_with_parens(
 +            context,
 +            "",
 +            items,
 +            shape,
 +            span,
 +            context.config.fn_call_width(),
 +            force_tactic,
 +        )
 +    } else {
 +        rewrite_tuple_in_visual_indent_style(context, items, span, shape, is_singleton_tuple)
 +    }
 +}
 +
 +pub(crate) fn rewrite_unary_prefix<R: Rewrite>(
 +    context: &RewriteContext<'_>,
 +    prefix: &str,
 +    rewrite: &R,
 +    shape: Shape,
 +) -> Option<String> {
 +    rewrite
 +        .rewrite(context, shape.offset_left(prefix.len())?)
 +        .map(|r| format!("{}{}", prefix, r))
 +}
 +
 +// FIXME: this is probably not correct for multi-line Rewrites. we should
 +// subtract suffix.len() from the last line budget, not the first!
 +pub(crate) fn rewrite_unary_suffix<R: Rewrite>(
 +    context: &RewriteContext<'_>,
 +    suffix: &str,
 +    rewrite: &R,
 +    shape: Shape,
 +) -> Option<String> {
 +    rewrite
 +        .rewrite(context, shape.sub_width(suffix.len())?)
 +        .map(|mut r| {
 +            r.push_str(suffix);
 +            r
 +        })
 +}
 +
 +fn rewrite_unary_op(
 +    context: &RewriteContext<'_>,
 +    op: ast::UnOp,
 +    expr: &ast::Expr,
 +    shape: Shape,
 +) -> Option<String> {
 +    // For some reason, an UnOp is not spanned like BinOp!
 +    rewrite_unary_prefix(context, ast::UnOp::to_string(op), expr, shape)
 +}
 +
++pub(crate) enum RhsAssignKind<'ast> {
++    Expr(&'ast ast::ExprKind, Span),
++    Bounds,
++    Ty,
++}
++
++impl<'ast> RhsAssignKind<'ast> {
++    // TODO(calebcartwright)
++    // Preemptive addition for handling RHS with chains, not yet utilized.
++    // It may make more sense to construct the chain first and then check
++    // whether there are actually chain elements.
++    #[allow(dead_code)]
++    fn is_chain(&self) -> bool {
++        match self {
++            RhsAssignKind::Expr(kind, _) => {
++                matches!(
++                    kind,
++                    ast::ExprKind::Try(..)
++                        | ast::ExprKind::Field(..)
++                        | ast::ExprKind::MethodCall(..)
++                        | ast::ExprKind::Await(_)
++                )
++            }
++            _ => false,
++        }
++    }
++}
++
 +fn rewrite_assignment(
 +    context: &RewriteContext<'_>,
 +    lhs: &ast::Expr,
 +    rhs: &ast::Expr,
 +    op: Option<&ast::BinOp>,
 +    shape: Shape,
 +) -> Option<String> {
 +    let operator_str = match op {
 +        Some(op) => context.snippet(op.span),
 +        None => "=",
 +    };
 +
 +    // 1 = space between lhs and operator.
 +    let lhs_shape = shape.sub_width(operator_str.len() + 1)?;
 +    let lhs_str = format!("{} {}", lhs.rewrite(context, lhs_shape)?, operator_str);
 +
-     rewrite_assign_rhs_with(context, lhs, ex, shape, RhsTactics::Default)
++    rewrite_assign_rhs(
++        context,
++        lhs_str,
++        rhs,
++        &RhsAssignKind::Expr(&rhs.kind, rhs.span),
++        shape,
++    )
 +}
 +
 +/// Controls where to put the rhs.
 +#[derive(Debug, Copy, Clone, PartialEq, Eq)]
 +pub(crate) enum RhsTactics {
 +    /// Use heuristics.
 +    Default,
 +    /// Put the rhs on the next line if it uses multiple line, without extra indentation.
 +    ForceNextLineWithoutIndent,
 +    /// Allow overflowing max width if neither `Default` nor `ForceNextLineWithoutIndent`
 +    /// did not work.
 +    AllowOverflow,
 +}
 +
 +// The left hand side must contain everything up to, and including, the
 +// assignment operator.
 +pub(crate) fn rewrite_assign_rhs<S: Into<String>, R: Rewrite>(
 +    context: &RewriteContext<'_>,
 +    lhs: S,
 +    ex: &R,
++    rhs_kind: &RhsAssignKind<'_>,
 +    shape: Shape,
 +) -> Option<String> {
-     let rhs = rewrite_assign_rhs_expr(context, &lhs, ex, shape, rhs_tactics)?;
++    rewrite_assign_rhs_with(context, lhs, ex, shape, rhs_kind, RhsTactics::Default)
 +}
 +
 +pub(crate) fn rewrite_assign_rhs_expr<R: Rewrite>(
 +    context: &RewriteContext<'_>,
 +    lhs: &str,
 +    ex: &R,
 +    shape: Shape,
++    rhs_kind: &RhsAssignKind<'_>,
 +    rhs_tactics: RhsTactics,
 +) -> Option<String> {
 +    let last_line_width = last_line_width(lhs).saturating_sub(if lhs.contains('\n') {
 +        shape.indent.width()
 +    } else {
 +        0
 +    });
 +    // 1 = space between operator and rhs.
 +    let orig_shape = shape.offset_left(last_line_width + 1).unwrap_or(Shape {
 +        width: 0,
 +        offset: shape.offset + last_line_width + 1,
 +        ..shape
 +    });
 +    let has_rhs_comment = if let Some(offset) = lhs.find_last_uncommented("=") {
 +        lhs.trim_end().len() > offset + 1
 +    } else {
 +        false
 +    };
 +
 +    choose_rhs(
 +        context,
 +        ex,
 +        orig_shape,
 +        ex.rewrite(context, orig_shape),
++        rhs_kind,
 +        rhs_tactics,
 +        has_rhs_comment,
 +    )
 +}
 +
 +pub(crate) fn rewrite_assign_rhs_with<S: Into<String>, R: Rewrite>(
 +    context: &RewriteContext<'_>,
 +    lhs: S,
 +    ex: &R,
 +    shape: Shape,
++    rhs_kind: &RhsAssignKind<'_>,
 +    rhs_tactics: RhsTactics,
 +) -> Option<String> {
 +    let lhs = lhs.into();
-     let rhs = rewrite_assign_rhs_expr(context, &lhs, ex, shape, rhs_tactics)?;
++    let rhs = rewrite_assign_rhs_expr(context, &lhs, ex, shape, rhs_kind, rhs_tactics)?;
 +    Some(lhs + &rhs)
 +}
 +
 +pub(crate) fn rewrite_assign_rhs_with_comments<S: Into<String>, R: Rewrite>(
 +    context: &RewriteContext<'_>,
 +    lhs: S,
 +    ex: &R,
 +    shape: Shape,
++    rhs_kind: &RhsAssignKind<'_>,
 +    rhs_tactics: RhsTactics,
 +    between_span: Span,
 +    allow_extend: bool,
 +) -> Option<String> {
 +    let lhs = lhs.into();
 +    let contains_comment = contains_comment(context.snippet(between_span));
 +    let shape = if contains_comment {
 +        shape.block_left(context.config.tab_spaces())?
 +    } else {
 +        shape
 +    };
++    let rhs = rewrite_assign_rhs_expr(context, &lhs, ex, shape, rhs_kind, rhs_tactics)?;
 +
 +    if contains_comment {
 +        let rhs = rhs.trim_start();
 +        combine_strs_with_missing_comments(context, &lhs, rhs, between_span, shape, allow_extend)
 +    } else {
 +        Some(lhs + &rhs)
 +    }
 +}
 +
 +fn choose_rhs<R: Rewrite>(
 +    context: &RewriteContext<'_>,
 +    expr: &R,
 +    shape: Shape,
 +    orig_rhs: Option<String>,
++    _rhs_kind: &RhsAssignKind<'_>,
 +    rhs_tactics: RhsTactics,
 +    has_rhs_comment: bool,
 +) -> Option<String> {
 +    match orig_rhs {
 +        Some(ref new_str) if new_str.is_empty() => {
 +            return Some(String::new());
 +        }
 +        Some(ref new_str)
 +            if !new_str.contains('\n') && unicode_str_width(new_str) <= shape.width =>
 +        {
 +            Some(format!(" {}", new_str))
 +        }
 +        _ => {
 +            // Expression did not fit on the same line as the identifier.
 +            // Try splitting the line and see if that works better.
 +            let new_shape = shape_from_rhs_tactic(context, shape, rhs_tactics)?;
 +            let new_rhs = expr.rewrite(context, new_shape);
 +            let new_indent_str = &shape
 +                .indent
 +                .block_indent(context.config)
 +                .to_string_with_newline(context.config);
 +            let before_space_str = if has_rhs_comment { "" } else { " " };
 +
 +            match (orig_rhs, new_rhs) {
 +                (Some(ref orig_rhs), Some(ref new_rhs))
 +                    if wrap_str(new_rhs.clone(), context.config.max_width(), new_shape)
 +                        .is_none() =>
 +                {
 +                    Some(format!("{}{}", before_space_str, orig_rhs))
 +                }
 +                (Some(ref orig_rhs), Some(ref new_rhs))
 +                    if prefer_next_line(orig_rhs, new_rhs, rhs_tactics) =>
 +                {
 +                    Some(format!("{}{}", new_indent_str, new_rhs))
 +                }
 +                (None, Some(ref new_rhs)) => Some(format!("{}{}", new_indent_str, new_rhs)),
 +                (None, None) if rhs_tactics == RhsTactics::AllowOverflow => {
 +                    let shape = shape.infinite_width();
 +                    expr.rewrite(context, shape)
 +                        .map(|s| format!("{}{}", before_space_str, s))
 +                }
 +                (None, None) => None,
 +                (Some(orig_rhs), _) => Some(format!("{}{}", before_space_str, orig_rhs)),
 +            }
 +        }
 +    }
 +}
 +
 +fn shape_from_rhs_tactic(
 +    context: &RewriteContext<'_>,
 +    shape: Shape,
 +    rhs_tactic: RhsTactics,
 +) -> Option<Shape> {
 +    match rhs_tactic {
 +        RhsTactics::ForceNextLineWithoutIndent => shape
 +            .with_max_width(context.config)
 +            .sub_width(shape.indent.width()),
 +        RhsTactics::Default | RhsTactics::AllowOverflow => {
 +            Shape::indented(shape.indent.block_indent(context.config), context.config)
 +                .sub_width(shape.rhs_overhead(context.config))
 +        }
 +    }
 +}
 +
 +/// Returns true if formatting next_line_rhs is better on a new line when compared to the
 +/// original's line formatting.
 +///
 +/// It is considered better if:
 +/// 1. the tactic is ForceNextLineWithoutIndent
 +/// 2. next_line_rhs doesn't have newlines
 +/// 3. the original line has more newlines than next_line_rhs
 +/// 4. the original formatting of the first line ends with `(`, `{`, or `[` and next_line_rhs
 +///    doesn't
 +pub(crate) fn prefer_next_line(
 +    orig_rhs: &str,
 +    next_line_rhs: &str,
 +    rhs_tactics: RhsTactics,
 +) -> bool {
 +    rhs_tactics == RhsTactics::ForceNextLineWithoutIndent
 +        || !next_line_rhs.contains('\n')
 +        || count_newlines(orig_rhs) > count_newlines(next_line_rhs) + 1
 +        || first_line_ends_with(orig_rhs, '(') && !first_line_ends_with(next_line_rhs, '(')
 +        || first_line_ends_with(orig_rhs, '{') && !first_line_ends_with(next_line_rhs, '{')
 +        || first_line_ends_with(orig_rhs, '[') && !first_line_ends_with(next_line_rhs, '[')
 +}
 +
 +fn rewrite_expr_addrof(
 +    context: &RewriteContext<'_>,
 +    borrow_kind: ast::BorrowKind,
 +    mutability: ast::Mutability,
 +    expr: &ast::Expr,
 +    shape: Shape,
 +) -> Option<String> {
 +    let operator_str = match (mutability, borrow_kind) {
 +        (ast::Mutability::Not, ast::BorrowKind::Ref) => "&",
 +        (ast::Mutability::Not, ast::BorrowKind::Raw) => "&raw const ",
 +        (ast::Mutability::Mut, ast::BorrowKind::Ref) => "&mut ",
 +        (ast::Mutability::Mut, ast::BorrowKind::Raw) => "&raw mut ",
 +    };
 +    rewrite_unary_prefix(context, operator_str, expr, shape)
 +}
 +
 +pub(crate) fn is_method_call(expr: &ast::Expr) -> bool {
 +    match expr.kind {
 +        ast::ExprKind::MethodCall(..) => true,
 +        ast::ExprKind::AddrOf(_, _, ref expr)
 +        | ast::ExprKind::Box(ref expr)
 +        | ast::ExprKind::Cast(ref expr, _)
 +        | ast::ExprKind::Try(ref expr)
 +        | ast::ExprKind::Unary(_, ref expr) => is_method_call(expr),
 +        _ => false,
 +    }
 +}
 +
 +#[cfg(test)]
 +mod test {
 +    use super::last_line_offsetted;
 +
 +    #[test]
 +    fn test_last_line_offsetted() {
 +        let lines = "one\n    two";
 +        assert_eq!(last_line_offsetted(2, lines), true);
 +        assert_eq!(last_line_offsetted(4, lines), false);
 +        assert_eq!(last_line_offsetted(6, lines), false);
 +
 +        let lines = "one    two";
 +        assert_eq!(last_line_offsetted(2, lines), false);
 +        assert_eq!(last_line_offsetted(0, lines), false);
 +
 +        let lines = "\ntwo";
 +        assert_eq!(last_line_offsetted(2, lines), false);
 +        assert_eq!(last_line_offsetted(0, lines), false);
 +
 +        let lines = "one\n    two      three";
 +        assert_eq!(last_line_offsetted(2, lines), true);
 +        let lines = "one\n two      three";
 +        assert_eq!(last_line_offsetted(2, lines), false);
 +    }
 +}
index d8974e12b8f5f4fe07742214ec1f28f2e41ddae2,0000000000000000000000000000000000000000..7738eee0a76049b769a2aa547ccdc908483c16fd
mode 100644,000000..100644
--- /dev/null
@@@ -1,57 -1,0 +1,53 @@@
-         match option_env!("CFG_RELEASE_CHANNEL") {
-             // this test requires nightly
-             None | Some("nightly") => {
-                 let config =
-                     Config::from_toml(r#"ignore = ["foo.rs", "bar_dir/*"]"#, Path::new(""))
-                         .unwrap();
-                 let ignore_path_set = IgnorePathSet::from_ignore_list(&config.ignore()).unwrap();
-                 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"))));
-             }
-             _ => (),
-         };
 +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 std::path::{Path, PathBuf};
 +
 +    use crate::config::{Config, FileName};
 +    use crate::ignore_path::IgnorePathSet;
 +
++    use rustfmt_config_proc_macro::nightly_only_test;
++
++    #[nightly_only_test]
 +    #[test]
 +    fn test_ignore_path_set() {
++        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 acc91f861e474df3f36879b9e71277246cc04495,0000000000000000000000000000000000000000..f36bdba26e98e91b8aecad2a4e4d433e7651596d
mode 100644,000000..100644
--- /dev/null
@@@ -1,3284 -1,0 +1,3274 @@@
-     rewrite_assign_rhs_with_comments, RhsTactics,
 +// 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,
-             result = rewrite_assign_rhs(context, result, init, nested_shape)?;
++    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)?;
 +
-                 &*expr.value,
++            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,
-             fn is_type(ty: &Option<rustc_ast::ptr::P<ast::Ty>>) -> bool {
-                 if let Some(lty) = ty {
-                     if let ast::TyKind::ImplTrait(..) = lty.kind {
-                         return false;
-                     }
-                 }
-                 true
-             }
-             fn is_opaque(ty: &Option<rustc_ast::ptr::P<ast::Ty>>) -> bool {
-                 !is_type(ty)
-             }
-             fn both_type(
-                 a: &Option<rustc_ast::ptr::P<ast::Ty>>,
-                 b: &Option<rustc_ast::ptr::P<ast::Ty>>,
-             ) -> bool {
-                 is_type(a) && is_type(b)
-             }
-             fn both_opaque(
-                 a: &Option<rustc_ast::ptr::P<ast::Ty>>,
-                 b: &Option<rustc_ast::ptr::P<ast::Ty>>,
-             ) -> bool {
-                 is_opaque(a) && is_opaque(b)
-             }
-             // In rustc-ap-v638 the `OpaqueTy` AssocItemKind variant was removed but
-             // we still need to differentiate to maintain sorting order.
-             // type -> opaque -> const -> macro -> method
-             use crate::ast::AssocItemKind::*;
-             fn need_empty_line(a: &ast::AssocItemKind, b: &ast::AssocItemKind) -> bool {
-                 match (a, b) {
-                     (TyAlias(lty), TyAlias(rty))
-                         if both_type(&lty.ty, &rty.ty) || both_opaque(&lty.ty, &rty.ty) =>
-                     {
-                         false
-                     }
-                     (Const(..), Const(..)) => false,
-                     _ => true,
-                 }
-             }
++                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();
 +            }
 +
-     if let ast::ItemKind::Impl(impl_kind) = &item.kind {
-         let ast::Impl {
-             ref generics,
-             ref self_ty,
-             ref items,
-             ..
-         } = **impl_kind;
-         let mut result = String::with_capacity(128);
-         let ref_and_type = format_impl_ref_and_type(context, item, offset)?;
-         let sep = offset.to_string_with_newline(context.config);
-         result.push_str(&ref_and_type);
 +            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 where_budget = if result.contains('\n') {
-             context.config.max_width()
-         } else {
-             context.budget(last_line_width(&result))
-         };
++    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 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 where_budget = if result.contains('\n') {
++        context.config.max_width()
++    } else {
++        context.budget(last_line_width(&result))
++    };
 +
-         let missing_span = mk_sp(self_ty.span.hi(), item.span.hi());
-         let where_span_end = context.snippet_provider.opt_span_before(missing_span, "{");
-         let where_clause_str = rewrite_where_clause(
-             context,
-             &generics.where_clause,
-             context.config.brace_style(),
-             Shape::legacy(where_budget, offset.block_only()),
-             false,
-             "{",
-             where_span_end,
-             self_ty.span.hi(),
-             option,
-         )?;
++    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();
++    }
 +
-         // 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);
-                     }
-                     _ => (),
++    let missing_span = mk_sp(self_ty.span.hi(), item.span.hi());
++    let where_span_end = context.snippet_provider.opt_span_before(missing_span, "{");
++    let where_clause_str = rewrite_where_clause(
++        context,
++        &generics.where_clause,
++        context.config.brace_style(),
++        Shape::legacy(where_budget, offset.block_only()),
++        false,
++        "{",
++        where_span_end,
++        self_ty.span.hi(),
++        option,
++    )?;
 +
-         if 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(" {}");
++    // 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);
 +                }
++                _ => (),
 +            }
 +        }
++    }
 +
-             return Some(result);
++    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(&where_clause_str);
++            result.push_str(&format!("{}{{{}}}", sep, sep));
++        } else {
++            result.push_str(" {}");
 +        }
++        return Some(result);
++    }
 +
-         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_str(&where_clause_str);
 +
-         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;
++    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(' ');
 +            }
 +        }
++    }
 +
-         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);
++    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;
 +
-             visitor.visit_attrs(&item.attrs, ast::AttrStyle::Inner);
-             visitor.visit_impl_items(items);
++    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.format_missing(item.span.hi() - BytePos(1));
++        visitor.visit_attrs(&item.attrs, ast::AttrStyle::Inner);
++        visitor.visit_impl_items(items);
 +
-             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);
++        visitor.format_missing(item.span.hi() - BytePos(1));
 +
-             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);
-         }
++        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('}');
++        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);
++    }
 +
-         Some(result)
-     } else {
-         unreachable!();
-     }
++    result.push('}');
 +
-     if let ast::ItemKind::Impl(impl_kind) = &item.kind {
-         let ast::Impl {
-             unsafety,
-             polarity,
-             defaultness,
-             constness,
-             ref generics,
-             of_trait: ref trait_ref,
-             ref self_ty,
-             ..
-         } = **impl_kind;
-         let mut result = String::with_capacity(128);
++    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> {
-         result.push_str(&format_visibility(context, &item.vis));
-         result.push_str(format_defaultness(defaultness));
-         result.push_str(format_unsafety(unsafety));
++    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);
 +
-         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));
++    result.push_str(&format_visibility(context, &item.vis));
++    result.push_str(format_defaultness(defaultness));
++    result.push_str(format_unsafety(unsafety));
 +
-         let polarity_str = match polarity {
-             ast::ImplPolarity::Negative(_) => "!",
-             ast::ImplPolarity::Positive => "",
-         };
++    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_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;
-         }
++    let polarity_str = match polarity {
++        ast::ImplPolarity::Negative(_) => "!",
++        ast::ImplPolarity::Positive => "",
++    };
 +
-         // 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);
++    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;
++    }
 +
-         // 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)
++    // 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);
 +        }
++    }
 +
-         unreachable!();
++    // 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 {
-     rewrite_assign_rhs(context, lhs, &trait_alias_bounds, shape.sub_width(1)?).map(|s| s + ";")
++        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,
 +                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,
 +            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,
 +    };
-     let ty_opt = ty.as_ref().map(|t| &**t);
++    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();
 +    let body_lo = 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);
 +    match rewrite_missing_comment(span, Shape::indented(offset, context.config), context) {
 +        Some(ref s) if s.is_empty() => (),
 +        Some(ref s) => {
 +            if !is_single_line(s) || first_line_contains_single_line_comment(s) {
 +                let nested_indent_str = offset
 +                    .block_indent(context.config)
 +                    .to_string_with_newline(context.config);
 +                result.push_str(&nested_indent_str);
 +            }
 +            result.push_str(s);
 +            if last_line_contains_single_line_comment(s) {
 +                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,
 +                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,
 +    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,
 +    } = *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, ident, span);
-     match (visitor_kind, ty_opt) {
-         (Item(_), None) => {
-             let op_ty = OpaqueType { bounds };
-             rewrite_ty(rw_info, Some(bounds), Some(&op_ty), vis)
++    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
-         (Item(_), Some(ty)) => rewrite_ty(rw_info, Some(bounds), Some(&*ty), vis),
++    match (visitor_kind, &op_ty) {
++        (Item(_) | AssocTraitItem(_) | ForeignItem(_), Some(ref op_bounds)) => {
++            let op = OpaqueType { bounds: op_bounds };
++            rewrite_ty(rw_info, Some(bounds), Some(&op), vis)
++        }
++        (Item(_) | AssocTraitItem(_) | ForeignItem(_), None) => {
++            rewrite_ty(rw_info, Some(bounds), ty_opt, vis)
 +        }
-             let result = if let Some(ast::Ty {
-                 kind: ast::TyKind::ImplTrait(_, ref bounds),
-                 ..
-             }) = ty_opt
-             {
-                 let op_ty = OpaqueType { bounds };
-                 rewrite_ty(rw_info, None, Some(&op_ty), &DEFAULT_VISIBILITY)
 +        (AssocImplItem(_), _) => {
-                 rewrite_ty(rw_info, None, ty.as_ref(), vis)
++            let result = if let Some(ref op_bounds) = op_ty {
++                let op = OpaqueType { bounds: op_bounds };
++                rewrite_ty(rw_info, Some(bounds), Some(&op), &DEFAULT_VISIBILITY)
 +            } else {
-         (AssocTraitItem(_), _) | (ForeignItem(_), _) => {
-             rewrite_ty(rw_info, Some(bounds), ty.as_ref(), vis)
-         }
++                rewrite_ty(rw_info, Some(bounds), ty_opt, vis)
 +            }?;
 +            match defaultness {
 +                ast::Defaultness::Default(..) => Some(format!("default {}", result)),
 +                _ => Some(result),
 +            }
 +        }
-         rewrite_assign_rhs(context, lhs, &*ty, shape).map(|s| s + ";")
 +    }
 +}
 +
 +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, ident, span) = *rw_info;
 +    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,
 +        &generics.where_clause,
 +        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 = !generics.where_clause.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(generics.where_clause.span.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)?;
-     let field_str = rewrite_assign_rhs(context, prefix, &*field.ty, shape)?;
++        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.
-                 rewrite_assign_rhs(context, prefix, &**ty, shape.sub_width(1)?).map(|s| s + ";")
++    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 {
 +                    result = combine_strs_with_missing_comments(
 +                        context,
 +                        &(param_attrs_result + &shape.to_string_with_newline(context.config)),
 +                        param_name,
 +                        span,
 +                        shape,
 +                        !has_multiple_attr_lines,
 +                    )?;
 +                    result.push_str(&before_comment);
 +                    result.push_str(colon_spaces(context.config));
 +                    result.push_str(&after_comment);
 +                    let overhead = last_line_width(&result);
 +                    let max_width = shape.width.checked_sub(overhead)?;
 +                    let ty_str = self
 +                        .ty
 +                        .rewrite(context, Shape::legacy(max_width, shape.indent))?;
 +                    result.push_str(&ty_str);
 +                }
 +            }
 +
 +            Some(result)
 +        } 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,
 +        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<'_>,
 +    where_clause: &ast::WhereClause,
 +    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,
 +        where_clause,
 +        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()
 +        && where_clause.predicates.len() == 1
 +        && !where_clause_option.veto_single_line;
 +
 +    let preds_str = rewrite_bounds_on_where_clause(
 +        context,
 +        where_clause,
 +        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<'_>,
 +    where_clause: &ast::WhereClause,
 +    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, where_clause);
 +    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<'_>,
 +    where_clause: &ast::WhereClause,
 +    shape: Shape,
 +    terminator: &str,
 +    span_end: Option<BytePos>,
 +    where_clause_option: WhereClauseOption,
 +    force_single_line: bool,
 +) -> Option<String> {
 +    let span_start = where_clause.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 = where_clause.predicates.len();
 +    let end_of_preds = where_clause.predicates[len - 1].span().hi();
 +    let span_end = span_end.unwrap_or(end_of_preds);
 +    let items = itemize_list(
 +        context.snippet_provider,
 +        where_clause.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<'_>,
 +    where_clause: &ast::WhereClause,
 +    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 where_clause.predicates.is_empty() {
 +        return Some(String::new());
 +    }
 +
 +    if context.config.indent_style() == IndentStyle::Block {
 +        return rewrite_where_clause_rfc_style(
 +            context,
 +            where_clause,
 +            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 = where_clause.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 = where_clause.predicates.len();
 +    let end_of_preds = where_clause.predicates[len - 1].span().hi();
 +    let span_end = span_end.unwrap_or(end_of_preds);
 +    let items = itemize_list(
 +        context.snippet_provider,
 +        where_clause.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,
 +    where_clause: &ast::WhereClause,
 +) -> (Span, Span) {
 +    let missing_span_before = mk_sp(before_item_span_end, where_clause.span.lo());
 +    // 5 = `where`
 +    let pos_after_where = where_clause.span.lo() + BytePos(5);
 +    let missing_span_after = mk_sp(pos_after_where, where_clause.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,
 +            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 d341ec8e6b0e7d15f428cc3e91527635129f9687,0000000000000000000000000000000000000000..3515dd172510c91e6bc109c7378ab8cccd82f0b2
mode 100644,000000..100644
--- /dev/null
@@@ -1,927 -1,0 +1,932 @@@
-                 // Use block-style only for the last item or multiline comments.
-                 let block_style = !formatting.ends_with_newline && last
-                     || comment.trim().contains('\n')
-                     || comment.trim().len() > width;
 +//! 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 if comment.trim().contains('\n') || comment.trim().len() > width {
++                    true
++                } else {
++                    false
++                };
 +
 +                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 preceeding 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,
 +) -> Option<String> {
 +    let white_space: &[_] = &[' ', '\t'];
 +
 +    // Cleanup post-comment: strip separators and whitespace.
 +    let post_snippet = post_snippet[..comment_end].trim();
 +    let post_snippet_trimmed = if post_snippet.starts_with(|c| c == ',' || c == ':') {
 +        post_snippet[1..].trim_matches(white_space)
 +    } else if let Some(stripped) = post_snippet.strip_prefix(separator) {
 +        stripped.trim_matches(white_space)
 +    }
 +    // not comment or over two lines
 +    else if post_snippet.ends_with(',')
 +        && (!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 comment_end = get_comment_end(
 +                post_snippet,
 +                self.separator,
 +                self.terminator,
 +                self.inner.peek().is_none(),
 +            );
 +            let new_lines = has_extra_newline(post_snippet, comment_end);
 +            let post_comment = extract_post_comment(post_snippet, comment_end, self.separator);
 +
 +            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 ef747638e33ec83459e4b9f9fe02d052e41c4812,0000000000000000000000000000000000000000..a52568be9eac44298a0f3cf40a1ef8b7e576081e
mode 100644,000000..100644
--- /dev/null
@@@ -1,1591 -1,0 +1,1592 @@@
- use crate::expr::rewrite_array;
 +// 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_parse::parser::{ForceCollect, Parser};
 +use rustc_parse::{stream_to_parser, MACRO_ARGUMENTS};
 +use rustc_span::{
 +    symbol::{self, kw},
 +    BytePos, Span, Symbol, DUMMY_SP,
 +};
 +
 +use crate::comment::{
 +    contains_comment, CharClasses, FindUncommented, FullCodeCharKind, LineClasses,
 +};
 +use crate::config::lists::*;
-         result.push_str(&crate::expr::rewrite_assign_rhs(
++use crate::expr::{rewrite_array, rewrite_assign_rhs, RhsAssignKind};
 +use crate::lists::{itemize_list, write_list, ListFormatting};
 +use crate::overflow;
 +use crate::rewrite::{Rewrite, RewriteContext};
 +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 {
 +    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()),
 +        }
 +    }
 +}
 +
 +fn build_parser<'a>(context: &RewriteContext<'a>, cursor: Cursor) -> Parser<'a> {
 +    stream_to_parser(
 +        context.parse_sess.inner(),
 +        cursor.collect(),
 +        MACRO_ARGUMENTS,
 +    )
 +}
 +
 +fn parse_macro_arg<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option<MacroArg> {
 +    macro_rules! parse_macro_arg {
 +        ($macro_arg:ident, $parser:expr, $f:expr) => {
 +            let mut cloned_parser = (*parser).clone();
 +            match $parser(&mut cloned_parser) {
 +                Ok(x) => {
 +                    if parser.sess.span_diagnostic.has_errors() {
 +                        parser.sess.span_diagnostic.reset_err_count();
 +                    } else {
 +                        // Parsing succeeded.
 +                        *parser = cloned_parser;
 +                        return Some(MacroArg::$macro_arg($f(x)?));
 +                    }
 +                }
 +                Err(mut e) => {
 +                    e.cancel();
 +                    parser.sess.span_diagnostic.reset_err_count();
 +                }
 +            }
 +        };
 +    }
 +
 +    parse_macro_arg!(
 +        Expr,
 +        |parser: &mut rustc_parse::parser::Parser<'b>| parser.parse_expr(),
 +        |x: ptr::P<ast::Expr>| Some(x)
 +    );
 +    parse_macro_arg!(
 +        Ty,
 +        |parser: &mut rustc_parse::parser::Parser<'b>| parser.parse_ty(),
 +        |x: ptr::P<ast::Ty>| Some(x)
 +    );
 +    parse_macro_arg!(
 +        Pat,
 +        |parser: &mut rustc_parse::parser::Parser<'b>| parser.parse_pat_no_top_alt(None),
 +        |x: ptr::P<ast::Pat>| Some(x)
 +    );
 +    // `parse_item` returns `Option<ptr::P<ast::Item>>`.
 +    parse_macro_arg!(
 +        Item,
 +        |parser: &mut rustc_parse::parser::Parser<'b>| parser.parse_item(ForceCollect::No),
 +        |x: Option<ptr::P<ast::Item>>| x
 +    );
 +
 +    None
 +}
 +
 +/// 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,
 +    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
 +    Some(context.snippet(span).to_owned())
 +}
 +
 +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
 +        .skip_macro(&context.snippet(mac.path.span).to_owned());
 +    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 check_keyword<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option<MacroArg> {
 +    for &keyword in RUST_KW.iter() {
 +        if parser.token.is_keyword(keyword)
 +            && parser.look_ahead(1, |t| {
 +                t.kind == TokenKind::Eof
 +                    || t.kind == TokenKind::Comma
 +                    || t.kind == TokenKind::CloseDelim(DelimToken::NoDelim)
 +            })
 +        {
 +            parser.bump();
 +            return Some(MacroArg::Keyword(
 +                symbol::Ident::with_dummy_span(keyword),
 +                parser.prev_token.span,
 +            ));
 +        }
 +    }
 +    None
 +}
 +
 +fn rewrite_macro_inner(
 +    mac: &ast::MacCall,
 +    extra_ident: Option<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 style = if FORCED_BRACKET_MACROS.contains(&&macro_name[..]) && !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) {
 +            return success;
 +        }
 +    }
 +
 +    let mut parser = build_parser(context, ts.trees());
 +    let mut arg_vec = Vec::new();
 +    let mut vec_with_semi = false;
 +    let mut trailing_comma = false;
 +
 +    if DelimToken::Brace != style {
 +        loop {
 +            if let Some(arg) = check_keyword(&mut parser) {
 +                arg_vec.push(arg);
 +            } else if let Some(arg) = parse_macro_arg(&mut parser) {
 +                arg_vec.push(arg);
 +            } else {
 +                return return_macro_parse_failure_fallback(context, shape.indent, mac.span());
 +            }
 +
 +            match parser.token.kind {
 +                TokenKind::Eof => break,
 +                TokenKind::Comma => (),
 +                TokenKind::Semi => {
 +                    // Try to parse `vec![expr; expr]`
 +                    if FORCED_BRACKET_MACROS.contains(&&macro_name[..]) {
 +                        parser.bump();
 +                        if parser.token.kind != TokenKind::Eof {
 +                            match parse_macro_arg(&mut parser) {
 +                                Some(arg) => {
 +                                    arg_vec.push(arg);
 +                                    parser.bump();
 +                                    if parser.token.kind == TokenKind::Eof && arg_vec.len() == 2 {
 +                                        vec_with_semi = true;
 +                                        break;
 +                                    }
 +                                }
 +                                None => {
 +                                    return return_macro_parse_failure_fallback(
 +                                        context,
 +                                        shape.indent,
 +                                        mac.span(),
 +                                    );
 +                                }
 +                            }
 +                        }
 +                    }
 +                    return return_macro_parse_failure_fallback(context, shape.indent, mac.span());
 +                }
 +                _ if arg_vec.last().map_or(false, MacroArg::is_item) => continue,
 +                _ => return return_macro_parse_failure_fallback(context, shape.indent, mac.span()),
 +            }
 +
 +            parser.bump();
 +
 +            if parser.token.kind == TokenKind::Eof {
 +                trailing_comma = true;
 +                break;
 +            }
 +        }
 +    }
 +
 +    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();
 +        let mut parser = build_parser(context, ts.trees());
 +
 +        Some(ast::Expr {
 +            id: ast::NodeId::root(), // dummy value
 +            kind: ast::ExprKind::Try(parser.parse_expr().ok()?),
 +            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 mut parser = build_parser(context, ts.trees());
 +    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));
 +
 +    macro_rules! parse_or {
 +        ($method:ident $(,)* $($arg:expr),* $(,)*) => {
 +            match parser.$method($($arg,)*) {
 +                Ok(val) => {
 +                    if parser.sess.span_diagnostic.has_errors() {
 +                        parser.sess.span_diagnostic.reset_err_count();
 +                        return None;
 +                    } else {
 +                        val
 +                    }
 +                }
 +                Err(mut err) => {
 +                    err.cancel();
 +                    parser.sess.span_diagnostic.reset_err_count();
 +                    return None;
 +                }
 +            }
 +        }
 +    }
 +
 +    while parser.token.kind != TokenKind::Eof {
 +        // Parse a `lazy_static!` item.
 +        let vis = crate::utils::format_visibility(
 +            context,
 +            &parse_or!(parse_visibility, rustc_parse::parser::FollowedByType::No),
 +        );
 +        parser.eat_keyword(kw::Static);
 +        parser.eat_keyword(kw::Ref);
 +        let id = parse_or!(parse_ident);
 +        parser.eat(&TokenKind::Colon);
 +        let ty = parse_or!(parse_ty);
 +        parser.eat(&TokenKind::Eq);
 +        let expr = parse_or!(parse_expr);
 +        parser.eat(&TokenKind::Semi);
 +
 +        // Rewrite as a static item.
 +        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 parser.token.kind != TokenKind::Eof {
 +            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)
 +}
 +
 +const RUST_KW: [Symbol; 59] = [
 +    kw::PathRoot,
 +    kw::DollarCrate,
 +    kw::Underscore,
 +    kw::As,
 +    kw::Box,
 +    kw::Break,
 +    kw::Const,
 +    kw::Continue,
 +    kw::Crate,
 +    kw::Else,
 +    kw::Enum,
 +    kw::Extern,
 +    kw::False,
 +    kw::Fn,
 +    kw::For,
 +    kw::If,
 +    kw::Impl,
 +    kw::In,
 +    kw::Let,
 +    kw::Loop,
 +    kw::Match,
 +    kw::Mod,
 +    kw::Move,
 +    kw::Mut,
 +    kw::Pub,
 +    kw::Ref,
 +    kw::Return,
 +    kw::SelfLower,
 +    kw::SelfUpper,
 +    kw::Static,
 +    kw::Struct,
 +    kw::Super,
 +    kw::Trait,
 +    kw::True,
 +    kw::Type,
 +    kw::Unsafe,
 +    kw::Use,
 +    kw::Where,
 +    kw::While,
 +    kw::Abstract,
 +    kw::Become,
 +    kw::Do,
 +    kw::Final,
 +    kw::Macro,
 +    kw::Override,
 +    kw::Priv,
 +    kw::Typeof,
 +    kw::Unsized,
 +    kw::Virtual,
 +    kw::Yield,
 +    kw::Dyn,
 +    kw::Async,
 +    kw::Try,
 +    kw::UnderscoreLifetime,
 +    kw::StaticLifetime,
 +    kw::Auto,
 +    kw::Catch,
 +    kw::Default,
 +    kw::Union,
 +];
index cdb4893d443b9b381cde8e4c1d284aee5154ddd4,0000000000000000000000000000000000000000..dd7c7352686e620eda97ad4411bc43da48aeac84
mode 100644,000000..100644
--- /dev/null
@@@ -1,470 -1,0 +1,465 @@@
-         use crate::is_nightly_channel;
 +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::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,
 +        })
 +    }
 +
 +    pub(crate) fn default_submod_path(
 +        &self,
 +        id: symbol::Ident,
 +        relative: Option<symbol::Ident>,
 +        dir_path: &Path,
 +    ) -> Result<rustc_expand::module::ModulePathSuccess, rustc_expand::module::ModError<'_>> {
 +        rustc_expand::module::default_submod_path(&self.parse_sess, id, relative, dir_path)
 +    }
 +
 +    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
 +    }
 +
 +    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 syntux module.
 +impl ParseSess {
 +    pub(super) fn emit_diagnostics(&self, diagnostics: Vec<Diagnostic>) {
 +        for diagnostic in diagnostics {
 +            self.parse_sess.span_diagnostic.emit_diagnostic(&diagnostic);
 +        }
 +    }
 +
 +    pub(crate) fn emit_or_cancel_diagnostic(&self, diagnostic: &mut Diagnostic) {
 +        self.parse_sess.span_diagnostic.emit_diagnostic(diagnostic);
 +        // The Handler will check whether the diagnostic should be emitted
 +        // based on the user's rustfmt configuration and the originating file
 +        // that caused the parser error. If the Handler determined it should skip
 +        // emission then we need to ensure the diagnostic is cancelled.
 +        if !diagnostic.cancelled() {
 +            diagnostic.cancel();
 +        }
 +    }
 +
 +    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()
 +    }
 +
 +    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;
-             if !is_nightly_channel!() {
-                 return;
-             }
 +        use crate::utils::mk_sp;
 +        use rustc_span::{FileName as SourceMapFileName, MultiSpan, RealFileName, DUMMY_SP};
 +        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 {
 +            Diagnostic {
 +                level,
 +                code: None,
 +                message: vec![],
 +                children: vec![],
 +                suggestions: vec![],
 +                span: span.unwrap_or_else(MultiSpan::new),
 +                sort_span: DUMMY_SP,
 +                is_lint: false,
 +            }
 +        }
 +
 +        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() {
-             if !is_nightly_channel!() {
-                 return;
-             }
 +            let num_emitted_errors = Lrc::new(AtomicU32::new(0));
 +            let can_reset_errors = Lrc::new(AtomicBool::new(false));
 +            let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
 +            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() {
-             if !is_nightly_channel!() {
-                 return;
-             }
 +            let num_emitted_errors = Lrc::new(AtomicU32::new(0));
 +            let can_reset_errors = Lrc::new(AtomicBool::new(false));
 +            let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
 +            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 e2620508c340bad91699e8ebd90b36f2a80f6049,0000000000000000000000000000000000000000..cceb28dfea6d7e82e06d3051983cc0a377fca9a2
mode 100644,000000..100644
--- /dev/null
@@@ -1,901 -1,0 +1,898 @@@
-         // these tests require nightly
-         if !is_nightly_channel!() {
-             return;
-         }
 +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");
 +    }
 +}
 +
 +// 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(), || {
-     // Issue-3443: these tests require nightly
-     if !is_nightly_channel!() {
-         return;
-     }
 +        // Get all files in the tests/target directory.
 +        let files = get_test_files(Path::new("tests/target"), true);
 +        let (_reports, count, fails) = check_files(files, &None);
 +
 +        // 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() {}\n".as_bytes());
 +    #[cfg(windows)]
 +    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());
 +}
 +
 +#[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 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");
 +}
index 9ea90c5e46dd87104522dd220558e7127ade7cdb,0000000000000000000000000000000000000000..88f5dc432451010436694b49a355e68fb9f27b98
mode 100644,000000..100644
--- /dev/null
@@@ -1,1065 -1,0 +1,1074 @@@
-                 rewrite_assign_rhs(context, lhs, bounds, shape)?
 +use std::iter::ExactSizeIterator;
 +use std::ops::Deref;
 +
 +use rustc_ast::ast::{self, FnRetTy, Mutability};
++use rustc_ast::ptr;
 +use rustc_span::{symbol::kw, BytePos, Pos, Span};
 +
 +use crate::comment::{combine_strs_with_missing_comments, contains_comment};
 +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::AssocTyConstraint),
 +}
 +
 +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::AssocTyConstraint {
 +    fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
 +        use ast::AssocTyConstraintKind::{Bound, Equality};
 +
 +        let mut result = String::with_capacity(128);
 +        result.push_str(rewrite_ident(context, self.ident));
 +
 +        if let Some(ref gen_args) = self.gen_args {
 +            let budget = shape.width.checked_sub(result.len())?;
 +            let shape = Shape::legacy(budget, shape.indent + result.len());
 +            let gen_str = rewrite_generic_args(gen_args, context, shape, gen_args.span())?;
 +            result.push_str(&gen_str);
 +        }
 +
 +        let infix = match (&self.kind, context.config.type_punctuation_density()) {
 +            (Bound { .. }, _) => ": ",
 +            (Equality { .. }, TypeDensity::Wide) => " = ",
 +            (Equality { .. }, TypeDensity::Compressed) => "=",
 +        };
 +        result.push_str(infix);
 +
 +        let budget = shape.width.checked_sub(result.len())?;
 +        let shape = Shape::legacy(budget, shape.indent + result.len());
 +        let rewrite = self.kind.rewrite(context, shape)?;
 +        result.push_str(&rewrite);
 +
 +        Some(result)
 +    }
 +}
 +
 +impl Rewrite for ast::AssocTyConstraintKind {
 +    fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
 +        match self {
 +            ast::AssocTyConstraintKind::Equality { ty } => ty.rewrite(context, shape),
 +            ast::AssocTyConstraintKind::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 requried so that we don't remove the separator inside macro calls.
 +                // This does not work in the presence of comment, hoping that people are
 +                // 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_ty_str, &**rhs_ty, shape)?
++                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(&format!("{} ", rw)),
 +            _ => (),
 +        }
 +
 +        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 527042d098a1c61f7d6252438ea4b97ba741c5d7,0000000000000000000000000000000000000000..e4a7be742abcbace1602fad9b5b45593b2dd71ad
mode 100644,000000..100644
--- /dev/null
@@@ -1,1001 -1,0 +1,1001 @@@
-                 ast::ItemKind::Impl { .. } => {
 +use std::cell::{Cell, RefCell};
 +use std::rc::Rc;
 +
 +use rustc_ast::{ast, token::DelimToken, visit, AstLike};
 +use rustc_data_structures::sync::Lrc;
 +use rustc_span::{symbol, BytePos, Pos, Span};
 +
 +use crate::attr::*;
 +use crate::comment::{contains_comment, rewrite_comment, CodeCharKind, CommentCodeSlices};
 +use crate::config::Version;
 +use crate::config::{BraceStyle, Config};
 +use crate::coverage::transform_missing_snippet;
 +use crate::items::{
 +    format_impl, format_trait, format_trait_alias, is_mod_decl, is_use_item, rewrite_extern_crate,
 +    rewrite_type_alias, FnBraceStyle, FnSig, ItemVisitorKind, StaticParts, StructParts,
 +};
 +use crate::macros::{macro_style, rewrite_macro, rewrite_macro_def, MacroPosition};
 +use crate::modules::Module;
 +use crate::rewrite::{Rewrite, RewriteContext};
 +use crate::shape::{Indent, Shape};
 +use crate::skip::{is_skip_attr, SkipContext};
 +use crate::source_map::{LineRangeUtils, SpanUtils};
 +use crate::spanned::Spanned;
 +use crate::stmt::Stmt;
 +use crate::syntux::session::ParseSess;
 +use crate::utils::{
 +    self, contains_skip, count_newlines, depr_skip_annotation, format_unsafety, inner_attributes,
 +    last_line_width, mk_sp, ptr_vec_to_ref_vec, rewrite_ident, starts_with_newline, stmt_expr,
 +};
 +use crate::{ErrorKind, FormatReport, FormattingError};
 +
 +/// Creates a string slice corresponding to the specified span.
 +pub(crate) struct SnippetProvider {
 +    /// A pointer to the content of the file we are formatting.
 +    big_snippet: Lrc<String>,
 +    /// A position of the start of `big_snippet`, used as an offset.
 +    start_pos: usize,
 +    /// An end position of the file that this snippet lives.
 +    end_pos: usize,
 +}
 +
 +impl SnippetProvider {
 +    pub(crate) fn span_to_snippet(&self, span: Span) -> Option<&str> {
 +        let start_index = span.lo().to_usize().checked_sub(self.start_pos)?;
 +        let end_index = span.hi().to_usize().checked_sub(self.start_pos)?;
 +        Some(&self.big_snippet[start_index..end_index])
 +    }
 +
 +    pub(crate) fn new(start_pos: BytePos, end_pos: BytePos, big_snippet: Lrc<String>) -> Self {
 +        let start_pos = start_pos.to_usize();
 +        let end_pos = end_pos.to_usize();
 +        SnippetProvider {
 +            big_snippet,
 +            start_pos,
 +            end_pos,
 +        }
 +    }
 +
 +    pub(crate) fn entire_snippet(&self) -> &str {
 +        self.big_snippet.as_str()
 +    }
 +
 +    pub(crate) fn start_pos(&self) -> BytePos {
 +        BytePos::from_usize(self.start_pos)
 +    }
 +
 +    pub(crate) fn end_pos(&self) -> BytePos {
 +        BytePos::from_usize(self.end_pos)
 +    }
 +}
 +
 +pub(crate) struct FmtVisitor<'a> {
 +    parent_context: Option<&'a RewriteContext<'a>>,
 +    pub(crate) parse_sess: &'a ParseSess,
 +    pub(crate) buffer: String,
 +    pub(crate) last_pos: BytePos,
 +    // FIXME: use an RAII util or closure for indenting
 +    pub(crate) block_indent: Indent,
 +    pub(crate) config: &'a Config,
 +    pub(crate) is_if_else_block: bool,
 +    pub(crate) snippet_provider: &'a SnippetProvider,
 +    pub(crate) line_number: usize,
 +    /// List of 1-based line ranges which were annotated with skip
 +    /// Both bounds are inclusifs.
 +    pub(crate) skipped_range: Rc<RefCell<Vec<(usize, usize)>>>,
 +    pub(crate) macro_rewrite_failure: bool,
 +    pub(crate) report: FormatReport,
 +    pub(crate) skip_context: SkipContext,
 +    pub(crate) is_macro_def: bool,
 +}
 +
 +impl<'a> Drop for FmtVisitor<'a> {
 +    fn drop(&mut self) {
 +        if let Some(ctx) = self.parent_context {
 +            if self.macro_rewrite_failure {
 +                ctx.macro_rewrite_failure.replace(true);
 +            }
 +        }
 +    }
 +}
 +
 +impl<'b, 'a: 'b> FmtVisitor<'a> {
 +    fn set_parent_context(&mut self, context: &'a RewriteContext<'_>) {
 +        self.parent_context = Some(context);
 +    }
 +
 +    pub(crate) fn shape(&self) -> Shape {
 +        Shape::indented(self.block_indent, self.config)
 +    }
 +
 +    fn next_span(&self, hi: BytePos) -> Span {
 +        mk_sp(self.last_pos, hi)
 +    }
 +
 +    fn visit_stmt(&mut self, stmt: &Stmt<'_>, include_empty_semi: bool) {
 +        debug!(
 +            "visit_stmt: {}",
 +            self.parse_sess.span_to_debug_info(stmt.span())
 +        );
 +
 +        if stmt.is_empty() {
 +            // If the statement is empty, just skip over it. Before that, make sure any comment
 +            // snippet preceding the semicolon is picked up.
 +            let snippet = self.snippet(mk_sp(self.last_pos, stmt.span().lo()));
 +            let original_starts_with_newline = snippet
 +                .find(|c| c != ' ')
 +                .map_or(false, |i| starts_with_newline(&snippet[i..]));
 +            let snippet = snippet.trim();
 +            if !snippet.is_empty() {
 +                // FIXME(calebcartwright 2021-01-03) - This exists strictly to maintain legacy
 +                // formatting where rustfmt would preserve redundant semicolons on Items in a
 +                // statement position.
 +                // See comment within `walk_stmts` for more info
 +                if include_empty_semi {
 +                    self.format_missing(stmt.span().hi());
 +                } else {
 +                    if original_starts_with_newline {
 +                        self.push_str("\n");
 +                    }
 +
 +                    self.push_str(&self.block_indent.to_string(self.config));
 +                    self.push_str(snippet);
 +                }
 +            } else if include_empty_semi {
 +                self.push_str(";");
 +            }
 +            self.last_pos = stmt.span().hi();
 +            return;
 +        }
 +
 +        match stmt.as_ast_node().kind {
 +            ast::StmtKind::Item(ref item) => {
 +                self.visit_item(item);
 +                self.last_pos = stmt.span().hi();
 +            }
 +            ast::StmtKind::Local(..) | ast::StmtKind::Expr(..) | ast::StmtKind::Semi(..) => {
 +                let attrs = get_attrs_from_stmt(stmt.as_ast_node());
 +                if contains_skip(attrs) {
 +                    self.push_skipped_with_span(
 +                        attrs,
 +                        stmt.span(),
 +                        get_span_without_attrs(stmt.as_ast_node()),
 +                    );
 +                } else {
 +                    let shape = self.shape();
 +                    let rewrite = self.with_context(|ctx| stmt.rewrite(ctx, shape));
 +                    self.push_rewrite(stmt.span(), rewrite)
 +                }
 +            }
 +            ast::StmtKind::MacCall(ref mac_stmt) => {
 +                if self.visit_attrs(&mac_stmt.attrs, ast::AttrStyle::Outer) {
 +                    self.push_skipped_with_span(
 +                        &mac_stmt.attrs,
 +                        stmt.span(),
 +                        get_span_without_attrs(stmt.as_ast_node()),
 +                    );
 +                } else {
 +                    self.visit_mac(&mac_stmt.mac, None, MacroPosition::Statement);
 +                }
 +                self.format_missing(stmt.span().hi());
 +            }
 +            ast::StmtKind::Empty => (),
 +        }
 +    }
 +
 +    /// Remove spaces between the opening brace and the first statement or the inner attribute
 +    /// of the block.
 +    fn trim_spaces_after_opening_brace(
 +        &mut self,
 +        b: &ast::Block,
 +        inner_attrs: Option<&[ast::Attribute]>,
 +    ) {
 +        if let Some(first_stmt) = b.stmts.first() {
 +            let hi = inner_attrs
 +                .and_then(|attrs| inner_attributes(attrs).first().map(|attr| attr.span.lo()))
 +                .unwrap_or_else(|| first_stmt.span().lo());
 +            let missing_span = self.next_span(hi);
 +            let snippet = self.snippet(missing_span);
 +            let len = CommentCodeSlices::new(snippet)
 +                .next()
 +                .and_then(|(kind, _, s)| {
 +                    if kind == CodeCharKind::Normal {
 +                        s.rfind('\n')
 +                    } else {
 +                        None
 +                    }
 +                });
 +            if let Some(len) = len {
 +                self.last_pos = self.last_pos + BytePos::from_usize(len);
 +            }
 +        }
 +    }
 +
 +    pub(crate) fn visit_block(
 +        &mut self,
 +        b: &ast::Block,
 +        inner_attrs: Option<&[ast::Attribute]>,
 +        has_braces: bool,
 +    ) {
 +        debug!(
 +            "visit_block: {}",
 +            self.parse_sess.span_to_debug_info(b.span),
 +        );
 +
 +        // Check if this block has braces.
 +        let brace_compensation = BytePos(if has_braces { 1 } else { 0 });
 +
 +        self.last_pos = self.last_pos + brace_compensation;
 +        self.block_indent = self.block_indent.block_indent(self.config);
 +        self.push_str("{");
 +        self.trim_spaces_after_opening_brace(b, inner_attrs);
 +
 +        // Format inner attributes if available.
 +        if let Some(attrs) = inner_attrs {
 +            self.visit_attrs(attrs, ast::AttrStyle::Inner);
 +        }
 +
 +        self.walk_block_stmts(b);
 +
 +        if !b.stmts.is_empty() {
 +            if let Some(expr) = stmt_expr(&b.stmts[b.stmts.len() - 1]) {
 +                if utils::semicolon_for_expr(&self.get_context(), expr) {
 +                    self.push_str(";");
 +                }
 +            }
 +        }
 +
 +        let rest_span = self.next_span(b.span.hi());
 +        if out_of_file_lines_range!(self, rest_span) {
 +            self.push_str(self.snippet(rest_span));
 +            self.block_indent = self.block_indent.block_unindent(self.config);
 +        } else {
 +            // Ignore the closing brace.
 +            let missing_span = self.next_span(b.span.hi() - brace_compensation);
 +            self.close_block(missing_span, self.unindent_comment_on_closing_brace(b));
 +        }
 +        self.last_pos = source!(self, b.span).hi();
 +    }
 +
 +    fn close_block(&mut self, span: Span, unindent_comment: bool) {
 +        let config = self.config;
 +
 +        let mut last_hi = span.lo();
 +        let mut unindented = false;
 +        let mut prev_ends_with_newline = false;
 +        let mut extra_newline = false;
 +
 +        let skip_normal = |s: &str| {
 +            let trimmed = s.trim();
 +            trimmed.is_empty() || trimmed.chars().all(|c| c == ';')
 +        };
 +
 +        let comment_snippet = self.snippet(span);
 +
 +        let align_to_right = if unindent_comment && contains_comment(comment_snippet) {
 +            let first_lines = comment_snippet.splitn(2, '/').next().unwrap_or("");
 +            last_line_width(first_lines) > last_line_width(comment_snippet)
 +        } else {
 +            false
 +        };
 +
 +        for (kind, offset, sub_slice) in CommentCodeSlices::new(comment_snippet) {
 +            let sub_slice = transform_missing_snippet(config, sub_slice);
 +
 +            debug!("close_block: {:?} {:?} {:?}", kind, offset, sub_slice);
 +
 +            match kind {
 +                CodeCharKind::Comment => {
 +                    if !unindented && unindent_comment && !align_to_right {
 +                        unindented = true;
 +                        self.block_indent = self.block_indent.block_unindent(config);
 +                    }
 +                    let span_in_between = mk_sp(last_hi, span.lo() + BytePos::from_usize(offset));
 +                    let snippet_in_between = self.snippet(span_in_between);
 +                    let mut comment_on_same_line = !snippet_in_between.contains('\n');
 +
 +                    let mut comment_shape =
 +                        Shape::indented(self.block_indent, config).comment(config);
 +                    if self.config.version() == Version::Two && comment_on_same_line {
 +                        self.push_str(" ");
 +                        // put the first line of the comment on the same line as the
 +                        // block's last line
 +                        match sub_slice.find('\n') {
 +                            None => {
 +                                self.push_str(&sub_slice);
 +                            }
 +                            Some(offset) if offset + 1 == sub_slice.len() => {
 +                                self.push_str(&sub_slice[..offset]);
 +                            }
 +                            Some(offset) => {
 +                                let first_line = &sub_slice[..offset];
 +                                self.push_str(first_line);
 +                                self.push_str(&self.block_indent.to_string_with_newline(config));
 +
 +                                // put the other lines below it, shaping it as needed
 +                                let other_lines = &sub_slice[offset + 1..];
 +                                let comment_str =
 +                                    rewrite_comment(other_lines, false, comment_shape, config);
 +                                match comment_str {
 +                                    Some(ref s) => self.push_str(s),
 +                                    None => self.push_str(other_lines),
 +                                }
 +                            }
 +                        }
 +                    } else {
 +                        if comment_on_same_line {
 +                            // 1 = a space before `//`
 +                            let offset_len = 1 + last_line_width(&self.buffer)
 +                                .saturating_sub(self.block_indent.width());
 +                            match comment_shape
 +                                .visual_indent(offset_len)
 +                                .sub_width(offset_len)
 +                            {
 +                                Some(shp) => comment_shape = shp,
 +                                None => comment_on_same_line = false,
 +                            }
 +                        };
 +
 +                        if comment_on_same_line {
 +                            self.push_str(" ");
 +                        } else {
 +                            if count_newlines(snippet_in_between) >= 2 || extra_newline {
 +                                self.push_str("\n");
 +                            }
 +                            self.push_str(&self.block_indent.to_string_with_newline(config));
 +                        }
 +
 +                        let comment_str = rewrite_comment(&sub_slice, false, comment_shape, config);
 +                        match comment_str {
 +                            Some(ref s) => self.push_str(s),
 +                            None => self.push_str(&sub_slice),
 +                        }
 +                    }
 +                }
 +                CodeCharKind::Normal if skip_normal(&sub_slice) => {
 +                    extra_newline = prev_ends_with_newline && sub_slice.contains('\n');
 +                    continue;
 +                }
 +                CodeCharKind::Normal => {
 +                    self.push_str(&self.block_indent.to_string_with_newline(config));
 +                    self.push_str(sub_slice.trim());
 +                }
 +            }
 +            prev_ends_with_newline = sub_slice.ends_with('\n');
 +            extra_newline = false;
 +            last_hi = span.lo() + BytePos::from_usize(offset + sub_slice.len());
 +        }
 +        if unindented {
 +            self.block_indent = self.block_indent.block_indent(self.config);
 +        }
 +        self.block_indent = self.block_indent.block_unindent(self.config);
 +        self.push_str(&self.block_indent.to_string_with_newline(config));
 +        self.push_str("}");
 +    }
 +
 +    fn unindent_comment_on_closing_brace(&self, b: &ast::Block) -> bool {
 +        self.is_if_else_block && !b.stmts.is_empty()
 +    }
 +
 +    // Note that this only gets called for function definitions. Required methods
 +    // on traits do not get handled here.
 +    pub(crate) fn visit_fn(
 +        &mut self,
 +        fk: visit::FnKind<'_>,
 +        generics: &ast::Generics,
 +        fd: &ast::FnDecl,
 +        s: Span,
 +        defaultness: ast::Defaultness,
 +        inner_attrs: Option<&[ast::Attribute]>,
 +    ) {
 +        let indent = self.block_indent;
 +        let block;
 +        let rewrite = match fk {
 +            visit::FnKind::Fn(_, ident, _, _, Some(ref b)) => {
 +                block = b;
 +                self.rewrite_fn_before_block(
 +                    indent,
 +                    ident,
 +                    &FnSig::from_fn_kind(&fk, generics, fd, defaultness),
 +                    mk_sp(s.lo(), b.span.lo()),
 +                )
 +            }
 +            _ => unreachable!(),
 +        };
 +
 +        if let Some((fn_str, fn_brace_style)) = rewrite {
 +            self.format_missing_with_indent(source!(self, s).lo());
 +
 +            if let Some(rw) = self.single_line_fn(&fn_str, block, inner_attrs) {
 +                self.push_str(&rw);
 +                self.last_pos = s.hi();
 +                return;
 +            }
 +
 +            self.push_str(&fn_str);
 +            match fn_brace_style {
 +                FnBraceStyle::SameLine => self.push_str(" "),
 +                FnBraceStyle::NextLine => {
 +                    self.push_str(&self.block_indent.to_string_with_newline(self.config))
 +                }
 +                _ => unreachable!(),
 +            }
 +            self.last_pos = source!(self, block.span).lo();
 +        } else {
 +            self.format_missing(source!(self, block.span).lo());
 +        }
 +
 +        self.visit_block(block, inner_attrs, true)
 +    }
 +
 +    pub(crate) fn visit_item(&mut self, item: &ast::Item) {
 +        skip_out_of_file_lines_range_visitor!(self, item.span);
 +
 +        // This is where we bail out if there is a skip attribute. This is only
 +        // complex in the module case. It is complex because the module could be
 +        // in a separate file and there might be attributes in both files, but
 +        // the AST lumps them all together.
 +        let filtered_attrs;
 +        let mut attrs = &item.attrs;
 +        let skip_context_saved = self.skip_context.clone();
 +        self.skip_context.update_with_attrs(attrs);
 +
 +        let should_visit_node_again = match item.kind {
 +            // For use/extern crate items, skip rewriting attributes but check for a skip attribute.
 +            ast::ItemKind::Use(..) | ast::ItemKind::ExternCrate(_) => {
 +                if contains_skip(attrs) {
 +                    self.push_skipped_with_span(attrs.as_slice(), item.span(), item.span());
 +                    false
 +                } else {
 +                    true
 +                }
 +            }
 +            // Module is inline, in this case we treat it like any other item.
 +            _ if !is_mod_decl(item) => {
 +                if self.visit_attrs(&item.attrs, ast::AttrStyle::Outer) {
 +                    self.push_skipped_with_span(item.attrs.as_slice(), item.span(), item.span());
 +                    false
 +                } else {
 +                    true
 +                }
 +            }
 +            // Module is not inline, but should be skipped.
 +            ast::ItemKind::Mod(..) if contains_skip(&item.attrs) => false,
 +            // Module is not inline and should not be skipped. We want
 +            // to process only the attributes in the current file.
 +            ast::ItemKind::Mod(..) => {
 +                filtered_attrs = filter_inline_attrs(&item.attrs, item.span());
 +                // Assert because if we should skip it should be caught by
 +                // the above case.
 +                assert!(!self.visit_attrs(&filtered_attrs, ast::AttrStyle::Outer));
 +                attrs = &filtered_attrs;
 +                true
 +            }
 +            _ => {
 +                if self.visit_attrs(&item.attrs, ast::AttrStyle::Outer) {
 +                    self.push_skipped_with_span(item.attrs.as_slice(), item.span(), item.span());
 +                    false
 +                } else {
 +                    true
 +                }
 +            }
 +        };
 +
 +        // TODO(calebcartwright): consider enabling box_patterns feature gate
 +        if should_visit_node_again {
 +            match item.kind {
 +                ast::ItemKind::Use(ref tree) => self.format_import(item, tree),
-                     let rw = self.with_context(|ctx| format_impl(ctx, item, block_indent));
++                ast::ItemKind::Impl(ref iimpl) => {
 +                    let block_indent = self.block_indent;
++                    let rw = self.with_context(|ctx| format_impl(ctx, item, iimpl, block_indent));
 +                    self.push_rewrite(item.span, rw);
 +                }
 +                ast::ItemKind::Trait(..) => {
 +                    let block_indent = self.block_indent;
 +                    let rw = self.with_context(|ctx| format_trait(ctx, item, block_indent));
 +                    self.push_rewrite(item.span, rw);
 +                }
 +                ast::ItemKind::TraitAlias(ref generics, ref generic_bounds) => {
 +                    let shape = Shape::indented(self.block_indent, self.config);
 +                    let rw = format_trait_alias(
 +                        &self.get_context(),
 +                        item.ident,
 +                        &item.vis,
 +                        generics,
 +                        generic_bounds,
 +                        shape,
 +                    );
 +                    self.push_rewrite(item.span, rw);
 +                }
 +                ast::ItemKind::ExternCrate(_) => {
 +                    let rw = rewrite_extern_crate(&self.get_context(), item, self.shape());
 +                    let span = if attrs.is_empty() {
 +                        item.span
 +                    } else {
 +                        mk_sp(attrs[0].span.lo(), item.span.hi())
 +                    };
 +                    self.push_rewrite(span, rw);
 +                }
 +                ast::ItemKind::Struct(..) | ast::ItemKind::Union(..) => {
 +                    self.visit_struct(&StructParts::from_item(item));
 +                }
 +                ast::ItemKind::Enum(ref def, ref generics) => {
 +                    self.format_missing_with_indent(source!(self, item.span).lo());
 +                    self.visit_enum(item.ident, &item.vis, def, generics, item.span);
 +                    self.last_pos = source!(self, item.span).hi();
 +                }
 +                ast::ItemKind::Mod(unsafety, ref mod_kind) => {
 +                    self.format_missing_with_indent(source!(self, item.span).lo());
 +                    self.format_mod(mod_kind, unsafety, &item.vis, item.span, item.ident, attrs);
 +                }
 +                ast::ItemKind::MacCall(ref mac) => {
 +                    self.visit_mac(mac, Some(item.ident), MacroPosition::Item);
 +                }
 +                ast::ItemKind::ForeignMod(ref foreign_mod) => {
 +                    self.format_missing_with_indent(source!(self, item.span).lo());
 +                    self.format_foreign_mod(foreign_mod, item.span);
 +                }
 +                ast::ItemKind::Static(..) | ast::ItemKind::Const(..) => {
 +                    self.visit_static(&StaticParts::from_item(item));
 +                }
 +                ast::ItemKind::Fn(ref fn_kind) => {
 +                    let ast::Fn {
 +                        defaultness,
 +                        ref sig,
 +                        ref generics,
 +                        ref body,
 +                    } = **fn_kind;
 +                    if let Some(ref body) = body {
 +                        let inner_attrs = inner_attributes(&item.attrs);
 +                        let fn_ctxt = match sig.header.ext {
 +                            ast::Extern::None => visit::FnCtxt::Free,
 +                            _ => visit::FnCtxt::Foreign,
 +                        };
 +                        self.visit_fn(
 +                            visit::FnKind::Fn(fn_ctxt, item.ident, &sig, &item.vis, Some(body)),
 +                            generics,
 +                            &sig.decl,
 +                            item.span,
 +                            defaultness,
 +                            Some(&inner_attrs),
 +                        )
 +                    } else {
 +                        let indent = self.block_indent;
 +                        let rewrite = self.rewrite_required_fn(
 +                            indent, item.ident, &sig, &item.vis, generics, item.span,
 +                        );
 +                        self.push_rewrite(item.span, rewrite);
 +                    }
 +                }
 +                ast::ItemKind::TyAlias(ref ty_alias) => {
 +                    use ItemVisitorKind::Item;
 +                    self.visit_ty_alias_kind(ty_alias, &Item(&item), item.span);
 +                }
 +                ast::ItemKind::GlobalAsm(..) => {
 +                    let snippet = Some(self.snippet(item.span).to_owned());
 +                    self.push_rewrite(item.span, snippet);
 +                }
 +                ast::ItemKind::MacroDef(ref def) => {
 +                    let rewrite = rewrite_macro_def(
 +                        &self.get_context(),
 +                        self.shape(),
 +                        self.block_indent,
 +                        def,
 +                        item.ident,
 +                        &item.vis,
 +                        item.span,
 +                    );
 +                    self.push_rewrite(item.span, rewrite);
 +                }
 +            };
 +        }
 +        self.skip_context = skip_context_saved;
 +    }
 +
 +    fn visit_ty_alias_kind(
 +        &mut self,
 +        ty_kind: &ast::TyAlias,
 +        visitor_kind: &ItemVisitorKind<'_>,
 +        span: Span,
 +    ) {
 +        let rewrite = rewrite_type_alias(
 +            ty_kind,
 +            &self.get_context(),
 +            self.block_indent,
 +            visitor_kind,
 +            span,
 +        );
 +        self.push_rewrite(span, rewrite);
 +    }
 +
 +    fn visit_assoc_item(&mut self, visitor_kind: &ItemVisitorKind<'_>) {
 +        use ItemVisitorKind::*;
 +        // TODO(calebcartwright): Not sure the skip spans are correct
 +        let (ai, skip_span, assoc_ctxt) = match visitor_kind {
 +            AssocTraitItem(ai) => (*ai, ai.span(), visit::AssocCtxt::Trait),
 +            AssocImplItem(ai) => (*ai, ai.span, visit::AssocCtxt::Impl),
 +            _ => unreachable!(),
 +        };
 +        skip_out_of_file_lines_range_visitor!(self, ai.span);
 +
 +        if self.visit_attrs(&ai.attrs, ast::AttrStyle::Outer) {
 +            self.push_skipped_with_span(&ai.attrs.as_slice(), skip_span, skip_span);
 +            return;
 +        }
 +
 +        // TODO(calebcartwright): consider enabling box_patterns feature gate
 +        match (&ai.kind, visitor_kind) {
 +            (ast::AssocItemKind::Const(..), AssocTraitItem(_)) => {
 +                self.visit_static(&StaticParts::from_trait_item(&ai))
 +            }
 +            (ast::AssocItemKind::Const(..), AssocImplItem(_)) => {
 +                self.visit_static(&StaticParts::from_impl_item(&ai))
 +            }
 +            (ast::AssocItemKind::Fn(ref fn_kind), _) => {
 +                let ast::Fn {
 +                    defaultness,
 +                    ref sig,
 +                    ref generics,
 +                    ref body,
 +                } = **fn_kind;
 +                if let Some(ref body) = body {
 +                    let inner_attrs = inner_attributes(&ai.attrs);
 +                    let fn_ctxt = visit::FnCtxt::Assoc(assoc_ctxt);
 +                    self.visit_fn(
 +                        visit::FnKind::Fn(fn_ctxt, ai.ident, sig, &ai.vis, Some(body)),
 +                        generics,
 +                        &sig.decl,
 +                        ai.span,
 +                        defaultness,
 +                        Some(&inner_attrs),
 +                    );
 +                } else {
 +                    let indent = self.block_indent;
 +                    let rewrite =
 +                        self.rewrite_required_fn(indent, ai.ident, sig, &ai.vis, generics, ai.span);
 +                    self.push_rewrite(ai.span, rewrite);
 +                }
 +            }
 +            (ast::AssocItemKind::TyAlias(ref ty_alias), _) => {
 +                self.visit_ty_alias_kind(ty_alias, visitor_kind, ai.span);
 +            }
 +            (ast::AssocItemKind::MacCall(ref mac), _) => {
 +                self.visit_mac(mac, Some(ai.ident), MacroPosition::Item);
 +            }
 +            _ => unreachable!(),
 +        }
 +    }
 +
 +    pub(crate) fn visit_trait_item(&mut self, ti: &ast::AssocItem) {
 +        self.visit_assoc_item(&ItemVisitorKind::AssocTraitItem(ti));
 +    }
 +
 +    pub(crate) fn visit_impl_item(&mut self, ii: &ast::AssocItem) {
 +        self.visit_assoc_item(&ItemVisitorKind::AssocImplItem(ii));
 +    }
 +
 +    fn visit_mac(&mut self, mac: &ast::MacCall, ident: Option<symbol::Ident>, pos: MacroPosition) {
 +        skip_out_of_file_lines_range_visitor!(self, mac.span());
 +
 +        // 1 = ;
 +        let shape = self.shape().saturating_sub_width(1);
 +        let rewrite = self.with_context(|ctx| rewrite_macro(mac, ident, ctx, shape, pos));
 +        // As of v638 of the rustc-ap-* crates, the associated span no longer includes
 +        // the trailing semicolon. This determines the correct span to ensure scenarios
 +        // with whitespace between the delimiters and trailing semi (i.e. `foo!(abc)     ;`)
 +        // are formatted correctly.
 +        let (span, rewrite) = match macro_style(mac, &self.get_context()) {
 +            DelimToken::Bracket | DelimToken::Paren if MacroPosition::Item == pos => {
 +                let search_span = mk_sp(mac.span().hi(), self.snippet_provider.end_pos());
 +                let hi = self.snippet_provider.span_before(search_span, ";");
 +                let target_span = mk_sp(mac.span().lo(), hi + BytePos(1));
 +                let rewrite = rewrite.map(|rw| {
 +                    if !rw.ends_with(';') {
 +                        format!("{};", rw)
 +                    } else {
 +                        rw
 +                    }
 +                });
 +                (target_span, rewrite)
 +            }
 +            _ => (mac.span(), rewrite),
 +        };
 +
 +        self.push_rewrite(span, rewrite);
 +    }
 +
 +    pub(crate) fn push_str(&mut self, s: &str) {
 +        self.line_number += count_newlines(s);
 +        self.buffer.push_str(s);
 +    }
 +
 +    #[allow(clippy::needless_pass_by_value)]
 +    fn push_rewrite_inner(&mut self, span: Span, rewrite: Option<String>) {
 +        if let Some(ref s) = rewrite {
 +            self.push_str(s);
 +        } else {
 +            let snippet = self.snippet(span);
 +            self.push_str(snippet.trim());
 +        }
 +        self.last_pos = source!(self, span).hi();
 +    }
 +
 +    pub(crate) fn push_rewrite(&mut self, span: Span, rewrite: Option<String>) {
 +        self.format_missing_with_indent(source!(self, span).lo());
 +        self.push_rewrite_inner(span, rewrite);
 +    }
 +
 +    pub(crate) fn push_skipped_with_span(
 +        &mut self,
 +        attrs: &[ast::Attribute],
 +        item_span: Span,
 +        main_span: Span,
 +    ) {
 +        self.format_missing_with_indent(source!(self, item_span).lo());
 +        // do not take into account the lines with attributes as part of the skipped range
 +        let attrs_end = attrs
 +            .iter()
 +            .map(|attr| self.parse_sess.line_of_byte_pos(attr.span.hi()))
 +            .max()
 +            .unwrap_or(1);
 +        let first_line = self.parse_sess.line_of_byte_pos(main_span.lo());
 +        // Statement can start after some newlines and/or spaces
 +        // or it can be on the same line as the last attribute.
 +        // So here we need to take a minimum between the two.
 +        let lo = std::cmp::min(attrs_end + 1, first_line);
 +        self.push_rewrite_inner(item_span, None);
 +        let hi = self.line_number + 1;
 +        self.skipped_range.borrow_mut().push((lo, hi));
 +    }
 +
 +    pub(crate) fn from_context(ctx: &'a RewriteContext<'_>) -> FmtVisitor<'a> {
 +        let mut visitor = FmtVisitor::from_parse_sess(
 +            ctx.parse_sess,
 +            ctx.config,
 +            ctx.snippet_provider,
 +            ctx.report.clone(),
 +        );
 +        visitor.skip_context.update(ctx.skip_context.clone());
 +        visitor.set_parent_context(ctx);
 +        visitor
 +    }
 +
 +    pub(crate) fn from_parse_sess(
 +        parse_session: &'a ParseSess,
 +        config: &'a Config,
 +        snippet_provider: &'a SnippetProvider,
 +        report: FormatReport,
 +    ) -> FmtVisitor<'a> {
 +        FmtVisitor {
 +            parent_context: None,
 +            parse_sess: parse_session,
 +            buffer: String::with_capacity(snippet_provider.big_snippet.len() * 2),
 +            last_pos: BytePos(0),
 +            block_indent: Indent::empty(),
 +            config,
 +            is_if_else_block: false,
 +            snippet_provider,
 +            line_number: 0,
 +            skipped_range: Rc::new(RefCell::new(vec![])),
 +            is_macro_def: false,
 +            macro_rewrite_failure: false,
 +            report,
 +            skip_context: Default::default(),
 +        }
 +    }
 +
 +    pub(crate) fn opt_snippet(&'b self, span: Span) -> Option<&'a str> {
 +        self.snippet_provider.span_to_snippet(span)
 +    }
 +
 +    pub(crate) fn snippet(&'b self, span: Span) -> &'a str {
 +        self.opt_snippet(span).unwrap()
 +    }
 +
 +    // Returns true if we should skip the following item.
 +    pub(crate) fn visit_attrs(&mut self, attrs: &[ast::Attribute], style: ast::AttrStyle) -> bool {
 +        for attr in attrs {
 +            if attr.has_name(depr_skip_annotation()) {
 +                let file_name = self.parse_sess.span_to_filename(attr.span);
 +                self.report.append(
 +                    file_name,
 +                    vec![FormattingError::from_span(
 +                        attr.span,
 +                        self.parse_sess,
 +                        ErrorKind::DeprecatedAttr,
 +                    )],
 +                );
 +            } else {
 +                match &attr.kind {
 +                    ast::AttrKind::Normal(ref attribute_item, _)
 +                        if self.is_unknown_rustfmt_attr(&attribute_item.path.segments) =>
 +                    {
 +                        let file_name = self.parse_sess.span_to_filename(attr.span);
 +                        self.report.append(
 +                            file_name,
 +                            vec![FormattingError::from_span(
 +                                attr.span,
 +                                self.parse_sess,
 +                                ErrorKind::BadAttr,
 +                            )],
 +                        );
 +                    }
 +                    _ => (),
 +                }
 +            }
 +        }
 +        if contains_skip(attrs) {
 +            return true;
 +        }
 +
 +        let attrs: Vec<_> = attrs.iter().filter(|a| a.style == style).cloned().collect();
 +        if attrs.is_empty() {
 +            return false;
 +        }
 +
 +        let rewrite = attrs.rewrite(&self.get_context(), self.shape());
 +        let span = mk_sp(attrs[0].span.lo(), attrs[attrs.len() - 1].span.hi());
 +        self.push_rewrite(span, rewrite);
 +
 +        false
 +    }
 +
 +    fn is_unknown_rustfmt_attr(&self, segments: &[ast::PathSegment]) -> bool {
 +        if segments[0].ident.to_string() != "rustfmt" {
 +            return false;
 +        }
 +        !is_skip_attr(segments)
 +    }
 +
 +    fn walk_mod_items(&mut self, items: &[rustc_ast::ptr::P<ast::Item>]) {
 +        self.visit_items_with_reordering(&ptr_vec_to_ref_vec(items));
 +    }
 +
 +    fn walk_stmts(&mut self, stmts: &[Stmt<'_>], include_current_empty_semi: bool) {
 +        if stmts.is_empty() {
 +            return;
 +        }
 +
 +        // Extract leading `use ...;`.
 +        let items: Vec<_> = stmts
 +            .iter()
 +            .take_while(|stmt| stmt.to_item().map_or(false, is_use_item))
 +            .filter_map(|stmt| stmt.to_item())
 +            .collect();
 +
 +        if items.is_empty() {
 +            self.visit_stmt(&stmts[0], include_current_empty_semi);
 +
 +            // FIXME(calebcartwright 2021-01-03) - This exists strictly to maintain legacy
 +            // formatting where rustfmt would preserve redundant semicolons on Items in a
 +            // statement position.
 +            //
 +            // Starting in rustc-ap-* v692 (~2020-12-01) the rustc parser now parses this as
 +            // two separate statements (Item and Empty kinds), whereas before it was parsed as
 +            // a single statement with the statement's span including the redundant semicolon.
 +            //
 +            // rustfmt typically tosses unnecessary/redundant semicolons, and eventually we
 +            // should toss these as well, but doing so at this time would
 +            // break the Stability Guarantee
 +            // N.B. This could be updated to utilize the version gates.
 +            let include_next_empty = if stmts.len() > 1 {
 +                matches!(
 +                    (&stmts[0].as_ast_node().kind, &stmts[1].as_ast_node().kind),
 +                    (ast::StmtKind::Item(_), ast::StmtKind::Empty)
 +                )
 +            } else {
 +                false
 +            };
 +
 +            self.walk_stmts(&stmts[1..], include_next_empty);
 +        } else {
 +            self.visit_items_with_reordering(&items);
 +            self.walk_stmts(&stmts[items.len()..], false);
 +        }
 +    }
 +
 +    fn walk_block_stmts(&mut self, b: &ast::Block) {
 +        self.walk_stmts(&Stmt::from_ast_nodes(b.stmts.iter()), false)
 +    }
 +
 +    fn format_mod(
 +        &mut self,
 +        mod_kind: &ast::ModKind,
 +        unsafety: ast::Unsafe,
 +        vis: &ast::Visibility,
 +        s: Span,
 +        ident: symbol::Ident,
 +        attrs: &[ast::Attribute],
 +    ) {
 +        let vis_str = utils::format_visibility(&self.get_context(), vis);
 +        self.push_str(&*vis_str);
 +        self.push_str(format_unsafety(unsafety));
 +        self.push_str("mod ");
 +        // Calling `to_owned()` to work around borrow checker.
 +        let ident_str = rewrite_ident(&self.get_context(), ident).to_owned();
 +        self.push_str(&ident_str);
 +
 +        if let ast::ModKind::Loaded(ref items, ast::Inline::Yes, inner_span) = mod_kind {
 +            match self.config.brace_style() {
 +                BraceStyle::AlwaysNextLine => {
 +                    let indent_str = self.block_indent.to_string_with_newline(self.config);
 +                    self.push_str(&indent_str);
 +                    self.push_str("{");
 +                }
 +                _ => self.push_str(" {"),
 +            }
 +            // Hackery to account for the closing }.
 +            let mod_lo = self.snippet_provider.span_after(source!(self, s), "{");
 +            let body_snippet =
 +                self.snippet(mk_sp(mod_lo, source!(self, inner_span).hi() - BytePos(1)));
 +            let body_snippet = body_snippet.trim();
 +            if body_snippet.is_empty() {
 +                self.push_str("}");
 +            } else {
 +                self.last_pos = mod_lo;
 +                self.block_indent = self.block_indent.block_indent(self.config);
 +                self.visit_attrs(attrs, ast::AttrStyle::Inner);
 +                self.walk_mod_items(items);
 +                let missing_span = self.next_span(inner_span.hi() - BytePos(1));
 +                self.close_block(missing_span, false);
 +            }
 +            self.last_pos = source!(self, inner_span).hi();
 +        } else {
 +            self.push_str(";");
 +            self.last_pos = source!(self, s).hi();
 +        }
 +    }
 +
 +    pub(crate) fn format_separate_mod(&mut self, m: &Module<'_>, end_pos: BytePos) {
 +        self.block_indent = Indent::empty();
 +        if self.visit_attrs(m.attrs(), ast::AttrStyle::Inner) {
 +            self.push_skipped_with_span(m.attrs(), m.span, m.span);
 +        } else {
 +            self.walk_mod_items(&m.items);
 +            self.format_missing_with_indent(end_pos);
 +        }
 +    }
 +
 +    pub(crate) fn skip_empty_lines(&mut self, end_pos: BytePos) {
 +        while let Some(pos) = self
 +            .snippet_provider
 +            .opt_span_after(self.next_span(end_pos), "\n")
 +        {
 +            if let Some(snippet) = self.opt_snippet(self.next_span(pos)) {
 +                if snippet.trim().is_empty() {
 +                    self.last_pos = pos;
 +                } else {
 +                    return;
 +                }
 +            }
 +        }
 +    }
 +
 +    pub(crate) fn with_context<F>(&mut self, f: F) -> Option<String>
 +    where
 +        F: Fn(&RewriteContext<'_>) -> Option<String>,
 +    {
 +        let context = self.get_context();
 +        let result = f(&context);
 +
 +        self.macro_rewrite_failure |= context.macro_rewrite_failure.get();
 +        result
 +    }
 +
 +    pub(crate) fn get_context(&self) -> RewriteContext<'_> {
 +        RewriteContext {
 +            parse_sess: self.parse_sess,
 +            config: self.config,
 +            inside_macro: Rc::new(Cell::new(false)),
 +            use_block: Cell::new(false),
 +            is_if_else_block: Cell::new(false),
 +            force_one_line_chain: Cell::new(false),
 +            snippet_provider: self.snippet_provider,
 +            macro_rewrite_failure: Cell::new(false),
 +            is_macro_def: self.is_macro_def,
 +            report: self.report.clone(),
 +            skip_context: self.skip_context.clone(),
 +            skipped_range: self.skipped_range.clone(),
 +        }
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b96c02802d69c23ee699fbc58c1fcc9ba8151b0a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,129 @@@
++// rustfmt-wrap_comments: true
++
++// https://github.com/rust-lang/rustfmt/issues/4909
++pub enum E {
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++    Variant1,
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++    Variant2,
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++}
++
++pub enum E2 {
++    // This can be changed once https://github.com/rust-lang/rustfmt/issues/4854 is fixed
++// Expand as needed, numbers should be ascending according to the stage
++// through the inclusion pipeline, or according to the descriptions
++}
++
++pub enum E3 {
++    // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions
++    Variant1,
++    // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions
++    Variant2,
++    // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions
++
++}
++
++pub struct S {
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++    some_field: usize,
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++    last_field: usize,
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++}
++
++pub struct S2 {
++    // This can be changed once https://github.com/rust-lang/rustfmt/issues/4854 is fixed
++// Expand as needed, numbers should be ascending according to the stage
++// through the inclusion pipeline, or according to the descriptions
++}
++
++pub struct S3 {
++    // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions
++    some_field: usize,
++    // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions
++    last_field: usize,
++    // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions
++}
++
++fn foo(
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++    a: usize,
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++    b: usize,
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++) -> usize {
++    5
++}
++
++fn foo2(// Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++) -> usize {
++    5
++}
++
++fn foo3(
++    // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions
++    a: usize,
++    // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions
++    b: usize,
++    // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions
++
++) -> usize {
++    5
++}
++
++fn main() {
++    let v = vec![
++        // Expand as needed, numbers should be ascending according to the stage
++        // through the inclusion pipeline, or according to the descriptions
++        1,
++        // Expand as needed, numbers should be ascending according to the stage
++        // through the inclusion pipeline, or according to the descriptions
++        2,
++        // Expand as needed, numbers should be ascending according to the stage
++        // through the inclusion pipeline, or according to the descriptions
++    ];
++
++    let v2: Vec<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..360b838520eda3030167d60618818505c5f25dc9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,130 @@@
++// rustfmt-normalize_comments: true
++// rustfmt-wrap_comments: true
++
++// https://github.com/rust-lang/rustfmt/issues/4909
++pub enum E {
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++    Variant1,
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++    Variant2,
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++}
++
++pub enum E2 {
++    // This can be changed once https://github.com/rust-lang/rustfmt/issues/4854 is fixed
++// Expand as needed, numbers should be ascending according to the stage
++// through the inclusion pipeline, or according to the descriptions
++}
++
++pub enum E3 {
++    // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions
++    Variant1,
++    // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions
++    Variant2,
++    // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions
++
++}
++
++pub struct S {
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++    some_field: usize,
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++    last_field: usize,
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++}
++
++pub struct S2 {
++    // This can be changed once https://github.com/rust-lang/rustfmt/issues/4854 is fixed
++// Expand as needed, numbers should be ascending according to the stage
++// through the inclusion pipeline, or according to the descriptions
++}
++
++pub struct S3 {
++    // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions
++    some_field: usize,
++    // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions
++    last_field: usize,
++    // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions
++}
++
++fn foo(
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++    a: usize,
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++    b: usize,
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++) -> usize {
++    5
++}
++
++fn foo2(// Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++) -> usize {
++    5
++}
++
++fn foo3(
++    // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions
++    a: usize,
++    // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions
++    b: usize,
++    // Expand as needed, numbers should be ascending according to the stage through the inclusion pipeline, or according to the descriptions
++
++) -> usize {
++    5
++}
++
++fn main() {
++    let v = vec![
++        // Expand as needed, numbers should be ascending according to the stage
++        // through the inclusion pipeline, or according to the descriptions
++        1,
++        // Expand as needed, numbers should be ascending according to the stage
++        // through the inclusion pipeline, or according to the descriptions
++        2,
++        // Expand as needed, numbers should be ascending according to the stage
++        // through the inclusion pipeline, or according to the descriptions
++    ];
++
++    let v2: Vec<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..09f68cae424063e9523d8b66e8c07e0c110b5ac0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,33 @@@
++// rustfmt-wrap_comments: true
++
++fn main() {
++    {
++        {
++            {
++                {
++                    {
++                        {
++                            {
++                                {
++                                    {
++                                        {
++                                            {
++                                                // - aaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa bbbbbbbbbb bbbbbbbbb bbbbbbbbb ccc cccccccccc ccccccc cccccccc
++
++                                                // * aaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa bbbbbbbbbb bbbbbbbbb bbbbbbbbb ccc cccccccccc ccccccc cccccccc
++
++                                                /* - aaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa bbbbbbbbbb bbbbbbbbb bbbbbbbbb ccc cccccccccc ccccccc cccccccc */
++
++                                                /* * aaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa bbbbbbbbbb bbbbbbbbb bbbbbbbbb ccc cccccccccc ccccccc cccccccc */
++                                            };
++                                        };
++                                    };
++                                };
++                            };
++                        };
++                    };
++                };
++            };
++        };
++    };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..75f748000f9ba3ef60b320775ed63b9aed7d5054
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++// rustfmt-wrap_comments: true
++
++//
++// - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
++// - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
++
++//
++// * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
++// * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
++
++/*
++ * - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/
++/*
++ * - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/
++
++/*
++ * * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/
++/*
++ * * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..00437f00216babc50ec4a9fb5e07270f51f5460a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,13 @@@
++// rustfmt-wrap_comments: true
++
++// - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
++// - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
++
++// * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
++// * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
++
++/* - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/
++/* - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/
++
++/* * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/
++/* * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a008dd3d83817cd4ca944381901d33da193d62e9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,5 @@@
++macro_rules! m {
++() => {
++type Type;
++};
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..67beeb23b7114de8d0a9497be139824eacfa8cf9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,7 @@@
++// rustfmt-version: Two
++
++pub type Iter<'a, D> =                 impl       DoubleEndedIterator<Item = (SomethingSomethingSomethingLongType<D>)>+ ExactSizeIterator+ 'a;
++
++trait FOo {pub type Iter<'a, D> = impl        DoubleEndedIterator<Item = (SomethingSomethingSomethingLongType<D>)>+ ExactSizeIterator+ 'a;}
++
++impl Bar {pub type Iter<'a, D> = impl             DoubleEndedIterator<Item = (SomethingSomethingSomethingLongType<D>)>+ ExactSizeIterator+ 'a;}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1644c9d2ccbbb5bfff3e0002f8da7c823cc5a5c0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,2 @@@
++#[cfg(any())]
++    type   Type :   Bound ;
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..be31bf0a33198511f63e93dae7bec3cc09bd0fa1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,96 @@@
++// rustfmt-format_code_in_doc_comments: true
++
++// https://github.com/rust-lang/rustfmt/issues/4420
++enum Minimal {
++    Example,
++    //[thisisremoved thatsleft
++    // canbeanything
++}
++
++struct Minimal2 {
++    Example: usize,
++    //[thisisremoved thatsleft
++    // canbeanything
++}
++
++pub enum E {
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++    Variant1,
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++    Variant2,
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++}
++
++pub enum E2 {
++    // This can be changed once https://github.com/rust-lang/rustfmt/issues/4854 is fixed
++// Expand as needed, numbers should be ascending according to the stage
++// through the inclusion pipeline, or according to the descriptions
++}
++
++pub struct S {
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++    some_field: usize,
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++    last_field: usize,
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++}
++
++pub struct S2 {
++    // This can be changed once https://github.com/rust-lang/rustfmt/issues/4854 is fixed
++// Expand as needed, numbers should be ascending according to the stage
++// through the inclusion pipeline, or according to the descriptions
++}
++
++fn foo(
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++    a: usize,
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++    b: usize,
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++) -> usize {
++    5
++}
++
++fn foo2(// Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++) -> usize {
++    5
++}
++
++fn main() {
++    let v = vec![
++        // Expand as needed, numbers should be ascending according to the stage
++        // through the inclusion pipeline, or according to the descriptions
++        1,
++        // Expand as needed, numbers should be ascending according to the stage
++        // through the inclusion pipeline, or according to the descriptions
++        2,
++        // Expand as needed, numbers should be ascending according to the stage
++        // through the inclusion pipeline, or according to the descriptions
++    ];
++
++    let v2: Vec<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 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..80aea59d1b520982bdec702488733c7dceb9aa1f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,85 @@@
++// 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
++}
++
++pub struct S {
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++    some_field: usize,
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++    last_field: usize,
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++}
++
++pub struct S2 {
++    // This can be changed once https://github.com/rust-lang/rustfmt/issues/4854 is fixed
++// Expand as needed, numbers should be ascending according to the stage
++// through the inclusion pipeline, or according to the descriptions
++}
++
++fn foo(
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++    a: usize,
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++    b: usize,
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++) -> usize {
++    5
++}
++
++fn foo2(// Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++) -> usize {
++    5
++}
++
++fn main() {
++    let v = vec![
++        // Expand as needed, numbers should be ascending according to the stage
++        // through the inclusion pipeline, or according to the descriptions
++        1,
++        // Expand as needed, numbers should be ascending according to the stage
++        // through the inclusion pipeline, or according to the descriptions
++        2,
++        // Expand as needed, numbers should be ascending according to the stage
++        // through the inclusion pipeline, or according to the descriptions
++    ];
++
++    let v2: Vec<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 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..52315f470e4b9d01a53ba86f767aecbf7270290a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,142 @@@
++// rustfmt-wrap_comments: true
++
++// https://github.com/rust-lang/rustfmt/issues/4909
++pub enum E {
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++    Variant1,
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++    Variant2,
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++}
++
++pub enum E2 {
++    // This can be changed once https://github.com/rust-lang/rustfmt/issues/4854 is fixed
++// Expand as needed, numbers should be ascending according to the stage
++// through the inclusion pipeline, or according to the descriptions
++}
++
++pub enum E3 {
++    // Expand as needed, numbers should be ascending according to the stage through the inclusion
++    // pipeline, or according to the descriptions
++    Variant1,
++    // Expand as needed, numbers should be ascending according to the stage through the inclusion
++    // pipeline, or according to the descriptions
++    Variant2,
++    // Expand as needed, numbers should be ascending according to the stage through the inclusion
++    // pipeline, or according to the descriptions
++}
++
++pub struct S {
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++    some_field: usize,
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++    last_field: usize,
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++}
++
++pub struct S2 {
++    // This can be changed once https://github.com/rust-lang/rustfmt/issues/4854 is fixed
++// Expand as needed, numbers should be ascending according to the stage
++// through the inclusion pipeline, or according to the descriptions
++}
++
++pub struct S3 {
++    // Expand as needed, numbers should be ascending according to the stage through the inclusion
++    // pipeline, or according to the descriptions
++    some_field: usize,
++    // Expand as needed, numbers should be ascending according to the stage through the inclusion
++    // pipeline, or according to the descriptions
++    last_field: usize,
++    // Expand as needed, numbers should be ascending according to the stage through the inclusion
++    // pipeline, or according to the descriptions
++}
++
++fn foo(
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++    a: usize,
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++    b: usize,
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++) -> usize {
++    5
++}
++
++fn foo2(// Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++) -> usize {
++    5
++}
++
++fn foo3(
++    // Expand as needed, numbers should be ascending according to the stage through the inclusion
++    // pipeline, or according to the descriptions
++    a: usize,
++    // Expand as needed, numbers should be ascending according to the stage through the inclusion
++    // pipeline, or according to the descriptions
++    b: usize,
++    // Expand as needed, numbers should be ascending according to the stage through the inclusion
++    // pipeline, or according to the descriptions
++) -> usize {
++    5
++}
++
++fn main() {
++    let v = vec![
++        // Expand as needed, numbers should be ascending according to the stage
++        // through the inclusion pipeline, or according to the descriptions
++        1,
++        // Expand as needed, numbers should be ascending according to the stage
++        // through the inclusion pipeline, or according to the descriptions
++        2,
++        // Expand as needed, numbers should be ascending according to the stage
++        // through the inclusion pipeline, or according to the descriptions
++    ];
++
++    let v2: Vec<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..e0bfcf0b5007da31ba944a6cf5ddc517e65a1aad
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,143 @@@
++// rustfmt-normalize_comments: true
++// rustfmt-wrap_comments: true
++
++// https://github.com/rust-lang/rustfmt/issues/4909
++pub enum E {
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++    Variant1,
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++    Variant2,
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++}
++
++pub enum E2 {
++    // This can be changed once https://github.com/rust-lang/rustfmt/issues/4854 is fixed
++// Expand as needed, numbers should be ascending according to the stage
++// through the inclusion pipeline, or according to the descriptions
++}
++
++pub enum E3 {
++    // Expand as needed, numbers should be ascending according to the stage through the inclusion
++    // pipeline, or according to the descriptions
++    Variant1,
++    // Expand as needed, numbers should be ascending according to the stage through the inclusion
++    // pipeline, or according to the descriptions
++    Variant2,
++    // Expand as needed, numbers should be ascending according to the stage through the inclusion
++    // pipeline, or according to the descriptions
++}
++
++pub struct S {
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++    some_field: usize,
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++    last_field: usize,
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++}
++
++pub struct S2 {
++    // This can be changed once https://github.com/rust-lang/rustfmt/issues/4854 is fixed
++// Expand as needed, numbers should be ascending according to the stage
++// through the inclusion pipeline, or according to the descriptions
++}
++
++pub struct S3 {
++    // Expand as needed, numbers should be ascending according to the stage through the inclusion
++    // pipeline, or according to the descriptions
++    some_field: usize,
++    // Expand as needed, numbers should be ascending according to the stage through the inclusion
++    // pipeline, or according to the descriptions
++    last_field: usize,
++    // Expand as needed, numbers should be ascending according to the stage through the inclusion
++    // pipeline, or according to the descriptions
++}
++
++fn foo(
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++    a: usize,
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++    b: usize,
++    // Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++) -> usize {
++    5
++}
++
++fn foo2(// Expand as needed, numbers should be ascending according to the stage
++    // through the inclusion pipeline, or according to the descriptions
++) -> usize {
++    5
++}
++
++fn foo3(
++    // Expand as needed, numbers should be ascending according to the stage through the inclusion
++    // pipeline, or according to the descriptions
++    a: usize,
++    // Expand as needed, numbers should be ascending according to the stage through the inclusion
++    // pipeline, or according to the descriptions
++    b: usize,
++    // Expand as needed, numbers should be ascending according to the stage through the inclusion
++    // pipeline, or according to the descriptions
++) -> usize {
++    5
++}
++
++fn main() {
++    let v = vec![
++        // Expand as needed, numbers should be ascending according to the stage
++        // through the inclusion pipeline, or according to the descriptions
++        1,
++        // Expand as needed, numbers should be ascending according to the stage
++        // through the inclusion pipeline, or according to the descriptions
++        2,
++        // Expand as needed, numbers should be ascending according to the stage
++        // through the inclusion pipeline, or according to the descriptions
++    ];
++
++    let v2: Vec<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..f4801de0184813300efdc48d6d338acb4d36c6a1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,33 @@@
++// rustfmt-wrap_comments: false
++
++fn main() {
++    {
++        {
++            {
++                {
++                    {
++                        {
++                            {
++                                {
++                                    {
++                                        {
++                                            {
++                                                // - aaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa bbbbbbbbbb bbbbbbbbb bbbbbbbbb ccc cccccccccc ccccccc cccccccc
++
++                                                // * aaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa bbbbbbbbbb bbbbbbbbb bbbbbbbbb ccc cccccccccc ccccccc cccccccc
++
++                                                /* - aaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa bbbbbbbbbb bbbbbbbbb bbbbbbbbb ccc cccccccccc ccccccc cccccccc */
++
++                                                /* * aaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa bbbbbbbbbb bbbbbbbbb bbbbbbbbb ccc cccccccccc ccccccc cccccccc */
++                                            };
++                                        };
++                                    };
++                                };
++                            };
++                        };
++                    };
++                };
++            };
++        };
++    };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b289c9f859e0e2f93f32e35d33922e21abadc5eb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,49 @@@
++// rustfmt-wrap_comments: true
++
++fn main() {
++    {
++        {
++            {
++                {
++                    {
++                        {
++                            {
++                                {
++                                    {
++                                        {
++                                            {
++                                                // - aaaa aaaaaaaaa aaaaaaaaa
++                                                //   aaaaaaaaa aaaaaaaaa
++                                                //   bbbbbbbbbb bbbbbbbbb
++                                                //   bbbbbbbbb ccc cccccccccc
++                                                //   ccccccc cccccccc
++
++                                                // * aaaa aaaaaaaaa aaaaaaaaa
++                                                //   aaaaaaaaa aaaaaaaaa
++                                                //   bbbbbbbbbb bbbbbbbbb
++                                                //   bbbbbbbbb ccc cccccccccc
++                                                //   ccccccc cccccccc
++
++                                                /* - aaaa aaaaaaaaa aaaaaaaaa
++                                                 *   aaaaaaaaa aaaaaaaaa
++                                                 *   bbbbbbbbbb bbbbbbbbb
++                                                 *   bbbbbbbbb ccc cccccccccc
++                                                 *   ccccccc cccccccc */
++
++                                                /* * aaaa aaaaaaaaa aaaaaaaaa
++                                                 *   aaaaaaaaa aaaaaaaaa
++                                                 *   bbbbbbbbbb bbbbbbbbb
++                                                 *   bbbbbbbbb ccc cccccccccc
++                                                 *   ccccccc cccccccc */
++                                            };
++                                        };
++                                    };
++                                };
++                            };
++                        };
++                    };
++                };
++            };
++        };
++    };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..60beed1b04828b730ea5da76a06818810ec512d6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++// rustfmt-wrap_comments: false
++
++// - some itemized block 1
++// - some itemized block 2
++
++// * some itemized block 3
++// * some itemized block 4
++
++/*
++ * - some itemized block 5
++ * - some itemized block 6
++ */
++
++/*
++ * * some itemized block 7
++ * * some itemized block 8
++ */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..84fba4b7c198629c56741ecb62055ff6e42ed3cf
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++// rustfmt-wrap_comments: true
++
++// - some itemized block 1
++// - some itemized block 2
++
++// * some itemized block 3
++// * some itemized block 4
++
++/*
++ * - some itemized block 5
++ * - some itemized block 6
++ */
++
++/*
++ * * some itemized block 7
++ * * some itemized block 8
++ */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d1bf44f6c7413aaf31620513da469b146430dbd4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,37 @@@
++// rustfmt-wrap_comments: false
++
++// Some text
++// - some itemized block 1
++// - some itemized block 2
++// Some more text
++// - some itemized block 3
++// - some itemized block 4
++// Even more text
++
++// Some text
++// * some itemized block 5
++// * some itemized block 6
++// Some more text
++// * some itemized block 7
++// * some itemized block 8
++// Even more text
++
++/*
++ * Some text
++ * - some itemized block 9
++ * - some itemized block 10
++ * Some more text
++ * - some itemized block 11
++ * - some itemized block 12
++ * Even more text
++ */
++
++/*
++ * Some text
++ * * some itemized block 13
++ * * some itemized block 14
++ * Some more text
++ * * some itemized block 15
++ * * some itemized block 16
++ * Even more text
++ */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f767491f902d41d2db619690c1f5dfd3296d6427
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,37 @@@
++// rustfmt-wrap_comments: true
++
++// Some text
++// - some itemized block 1
++// - some itemized block 2
++// Some more text
++// - some itemized block 3
++// - some itemized block 4
++// Even more text
++
++// Some text
++// * some itemized block 5
++// * some itemized block 6
++// Some more text
++// * some itemized block 7
++// * some itemized block 8
++// Even more text
++
++/*
++ * Some text
++ * - some itemized block 9
++ * - some itemized block 10
++ * Some more text
++ * - some itemized block 11
++ * - some itemized block 12
++ * Even more text
++ */
++
++/*
++ * Some text
++ * * some itemized block 13
++ * * some itemized block 14
++ * Some more text
++ * * some itemized block 15
++ * * some itemized block 16
++ * Even more text
++ */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2cd85c787f974d34f1c2cfae8f673d348e5f6258
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++// rustfmt-wrap_comments: false
++
++// - some itemized block 1
++
++// * some itemized block 2
++
++/* - some itemized block 3 */
++
++/* * some itemized block 4 */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e9f343d75d5ebec61c7c39cb8a6152f187e1be3c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++// rustfmt-wrap_comments: true
++
++// - some itemized block 1
++
++// * some itemized block 2
++
++/* - some itemized block 3 */
++
++/* * some itemized block 4 */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..97bb7733d189f1674954fb8019551d728f8fb1f8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++// rustfmt-wrap_comments: false
++
++//
++// - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
++// - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
++
++//
++// * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
++// * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
++
++/*
++ * - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/
++/*
++ * - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/
++
++/*
++ * * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/
++/*
++ * * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c8af8383e058a117b7000a20b40bbaef2c1db746
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++// rustfmt-wrap_comments: true
++
++//
++// - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
++//   tempor incididunt ut labore et dolore magna aliqua.
++// - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
++//   tempor incididunt ut labore et dolore magna aliqua.
++
++//
++// * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
++//   tempor incididunt ut labore et dolore magna aliqua.
++// * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
++//   tempor incididunt ut labore et dolore magna aliqua.
++
++/*
++ * - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
++ *   tempor incididunt ut labore et dolore magna aliqua. */
++/*
++ * - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
++ *   tempor incididunt ut labore et dolore magna aliqua. */
++
++/*
++ * * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
++ *   tempor incididunt ut labore et dolore magna aliqua. */
++/*
++ * * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
++ *   tempor incididunt ut labore et dolore magna aliqua. */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..75cc42c0e66bb119dd1a248aa25efec85126560c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++// rustfmt-wrap_comments: false
++
++//
++// - some itemized block 1
++// - some itemized block 2
++
++//
++// * some itemized block 3
++// * some itemized block 4
++
++/*
++ * - some itemized block 5
++ * - some itemized block 6 */
++
++/*
++ * * some itemized block 7
++ * * some itemized block 8 */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ef2c8f90cd309743d4d9b680c9781f60f426c3da
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++// rustfmt-wrap_comments: true
++
++//
++// - some itemized block 1
++// - some itemized block 2
++
++//
++// * some itemized block 3
++// * some itemized block 4
++
++/*
++ * - some itemized block 5
++ * - some itemized block 6 */
++
++/*
++ * * some itemized block 7
++ * * some itemized block 8 */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c826cc5d4da6e4f58575ec1ea89b9330d65de9c3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,13 @@@
++// rustfmt-wrap_comments: false
++
++// - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
++// - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
++
++// * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
++// * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
++
++/* - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/
++/* - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/
++
++/* * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/
++/* * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.*/
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7f764dbd8a22b6665cdf1bb46bc1ac67d0f70847
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++// rustfmt-wrap_comments: true
++
++// - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
++//   tempor incididunt ut labore et dolore magna aliqua.
++// - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
++//   tempor incididunt ut labore et dolore magna aliqua.
++
++// * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
++//   tempor incididunt ut labore et dolore magna aliqua.
++// * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
++//   tempor incididunt ut labore et dolore magna aliqua.
++
++/* - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
++ *   tempor incididunt ut labore et dolore magna aliqua. */
++/* - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
++ *   tempor incididunt ut labore et dolore magna aliqua. */
++
++/* * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
++ *   tempor incididunt ut labore et dolore magna aliqua. */
++/* * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
++ *   tempor incididunt ut labore et dolore magna aliqua. */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6981a65808c900ab1ca33ba66c518cf6047f09f2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++// rustfmt-wrap_comments: true
++
++pub mod a_long_name {
++    pub mod b_long_name {
++        pub mod c_long_name {
++            pub mod d_long_name {
++                pub mod e_long_name {
++                    pub struct Bananas;
++                    impl Bananas {
++                        pub fn fantastic() {}
++                    }
++
++                    pub mod f_long_name {
++                        pub struct Apples;
++                    }
++                }
++            }
++        }
++    }
++}
++
++/// Check out [my other struct] ([`Bananas`]) and [the method it has].
++///
++/// [my other struct]: a_long_name::b_long_name::c_long_name::d_long_name::e_long_name::f_long_name::Apples
++/// [`Bananas`]: a_long_name::b_long_name::c_long_name::d_long_name::e_long_name::Bananas::fantastic()
++/// [the method it has]: a_long_name::b_long_name::c_long_name::d_long_name::e_long_name::Bananas::fantastic()
++pub struct A;
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..de17467c0efad51e613126a491a110b92da35537
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,5 @@@
++macro_rules! m {
++    () => {
++        type Type;
++    };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..26d771720b6c52963262acaee3343cb15db04109
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++// rustfmt-version: Two
++
++pub type Iter<'a, D> = impl DoubleEndedIterator<Item = (SomethingSomethingSomethingLongType<D>)>
++    + ExactSizeIterator
++    + 'a;
++
++trait FOo {
++    pub type Iter<'a, D> = impl DoubleEndedIterator<Item = (SomethingSomethingSomethingLongType<D>)>
++        + ExactSizeIterator
++        + 'a;
++}
++
++impl Bar {
++    type Iter<'a, D> = impl DoubleEndedIterator<Item = (SomethingSomethingSomethingLongType<D>)>
++        + ExactSizeIterator
++        + 'a;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7a0be06f7917a6c7d8ca93ff41ad071266b194f7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,2 @@@
++#[cfg(any())]
++type Type: Bound;