Merge commit 'b23b69900eab1260be510b2bd8922f4b6de6cf1e' into sync-from-rustfmt

This commit is contained in:
Yacin Tmimi 2024-09-19 21:46:44 -04:00
commit eeda9dd070
197 changed files with 4709 additions and 1557 deletions

View File

@ -4630,7 +4630,7 @@ dependencies = [
[[package]] [[package]]
name = "rustfmt-nightly" name = "rustfmt-nightly"
version = "1.7.1" version = "1.8.0"
dependencies = [ dependencies = [
"annotate-snippets 0.9.2", "annotate-snippets 0.9.2",
"anyhow", "anyhow",

View File

@ -21,7 +21,7 @@ jobs:
steps: steps:
- name: checkout - name: checkout
uses: actions/checkout@v3 uses: actions/checkout@v4
- name: install rustup - name: install rustup
run: | run: |

View File

@ -64,7 +64,7 @@ jobs:
steps: steps:
- name: checkout - name: checkout
uses: actions/checkout@v3 uses: actions/checkout@v4
# Run build # Run build
- name: install rustup - name: install rustup

View File

@ -26,7 +26,7 @@ jobs:
steps: steps:
- name: checkout - name: checkout
uses: actions/checkout@v3 uses: actions/checkout@v4
# Run build # Run build
- name: install rustup - name: install rustup

View File

@ -8,7 +8,6 @@ on:
jobs: jobs:
test: test:
# https://help.github.com/en/actions/automating-your-workflow-with-github-actions/virtual-environments-for-github-hosted-runners#supported-runners-and-hardware-resources # 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 runs-on: macos-latest
name: (${{ matrix.target }}, ${{ matrix.cfg_release_channel }}) name: (${{ matrix.target }}, ${{ matrix.cfg_release_channel }})
env: env:
@ -23,7 +22,7 @@ jobs:
steps: steps:
- name: checkout - name: checkout
uses: actions/checkout@v3 uses: actions/checkout@v4
# Run build # Run build
- name: install rustup - name: install rustup

View File

@ -11,7 +11,7 @@ jobs:
name: rustdoc check name: rustdoc check
steps: steps:
- name: checkout - name: checkout
uses: actions/checkout@v3 uses: actions/checkout@v4
- name: install rustup - name: install rustup
run: | run: |

View File

@ -31,7 +31,7 @@ jobs:
target: x86_64-pc-windows-msvc target: x86_64-pc-windows-msvc
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
# Run build # Run build
- name: install rustup - name: install rustup

View File

@ -33,7 +33,7 @@ jobs:
- name: disable git eol translation - name: disable git eol translation
run: git config --global core.autocrlf false run: git config --global core.autocrlf false
- name: checkout - name: checkout
uses: actions/checkout@v3 uses: actions/checkout@v4
# Run build # Run build
- name: Install Rustup using win.rustup.rs - name: Install Rustup using win.rustup.rs

View File

@ -1,6 +1,66 @@
# Changelog # Changelog
## [Unreleased] ## [1.8.0] 2024-09-20
### Fixed
- Fix issue where rustfmt would crash on Windows when using the `ignore` option [#6178](https://github.com/rust-lang/rustfmt/issues/6178)
### Changed
- `rustfmt --version` now prints a commit hash that is 10 characters long [#6258](https://github.com/rust-lang/rustfmt/pull/6258)
- `rustfmt --version` will no longer print empty git information when git information isn't available at build time.
For example, git information is not available when building rustfmt from a source tarball [#6266](https://github.com/rust-lang/rustfmt/pull/6266)
- `version` has been soft deprecated and replaced by `style_edition`.
`style_edition=2024` is equivalent to `version=Two` and `style_edition={2015|2018|2021}`
are equivalent to `version=One` [#6247](https://github.com/rust-lang/rustfmt/pull/6247)
- When `style_edition=2024` is configured `overflow_delimited_expr` will default to `true` [#6260](https://github.com/rust-lang/rustfmt/pull/6260).
```rust
// with style_edition=2015
do_thing(
x,
Bar {
x: value,
y: value2,
},
);
// with style_edition=2024
do_thing(x, Bar {
x: value,
y: value2,
});
```
- When `style_edition=2024` is configured rustfmt will apply the [style guide's version sorting algorithm]
when sorting imports [#6284](https://github.com/rust-lang/rustfmt/pull/6284)
```rust
// with style_edition=2015
use std::num::{NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8};
// with style_edition=2024
use std::num::{NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64};
```
[style guide's version sorting algorithm]: https://doc.rust-lang.org/nightly/style-guide/#sorting
- When parsing rustfmt configurations fails, rustfmt will now include the path to the toml file in the erorr message [#6302](https://github.com/rust-lang/rustfmt/issues/6302)
### Added
- rustfmt now formats trailing where clauses in type aliases [#5887](https://github.com/rust-lang/rustfmt/pull/5887)
```rust
type Foo
= Bar
where
A: B,
C: D;
```
- Users can now configure which `style_edition` rustfmt uses when formatting their code as specified
in [RFC 3338](https://rust-lang.github.io/rfcs/3338-style-evolution.html). Users are encouraged to configure `style_edition`
in their `rustfmt.toml` files, but the value can also be specified via the cli with `--unstable-features --style-edition={style_edition}`.
When `style_edition` is not explicitly configured it will be inferred from the `edition` configuration.
When neither `style_edition` nor `edition` are configured `style_edition` defaults to `2015` [#6247](https://github.com/rust-lang/rustfmt/pull/6247)
### Misc
- Removed `tracing-attributes` dependency [#6208](https://github.com/rust-lang/rustfmt/pull/6208)
- Reduced syn's features in the internal `config_proc_macro` crate [#6237](https://github.com/rust-lang/rustfmt/pull/6237)
## [1.7.1] 2024-06-24
### Fixed ### Fixed
@ -238,7 +298,7 @@
### Added ### Added
- New configuration option (`skip_macro_invocations`)[https://rust-lang.github.io/rustfmt/?version=master&search=#skip_macro_invocations] [#5347](https://github.com/rust-lang/rustfmt/pull/5347) that can be used to globally define a single enumerated list of macro calls that rustfmt should skip formatting. rustfmt [currently also supports this via a custom tool attribute](https://github.com/rust-lang/rustfmt#tips), however, these cannot be used in all contexts because [custom inner attributes are unstable](https://github.com/rust-lang/rust/issues/54726) - New configuration option [`skip_macro_invocations`](https://rust-lang.github.io/rustfmt/?version=master&search=#skip_macro_invocations) [#5347](https://github.com/rust-lang/rustfmt/pull/5347) that can be used to globally define a single enumerated list of macro calls that rustfmt should skip formatting. rustfmt [currently also supports this via a custom tool attribute](https://github.com/rust-lang/rustfmt#tips), however, these cannot be used in all contexts because [custom inner attributes are unstable](https://github.com/rust-lang/rust/issues/54726)
### Misc ### Misc

View File

@ -499,7 +499,7 @@ dependencies = [
[[package]] [[package]]
name = "rustfmt-nightly" name = "rustfmt-nightly"
version = "1.7.1" version = "1.8.0"
dependencies = [ dependencies = [
"annotate-snippets", "annotate-snippets",
"anyhow", "anyhow",
@ -710,21 +710,9 @@ checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"pin-project-lite", "pin-project-lite",
"tracing-attributes",
"tracing-core", "tracing-core",
] ]
[[package]]
name = "tracing-attributes"
version = "0.1.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "tracing-core" name = "tracing-core"
version = "0.1.31" version = "0.1.31"

View File

@ -1,7 +1,7 @@
[package] [package]
name = "rustfmt-nightly" name = "rustfmt-nightly"
version = "1.7.1" version = "1.8.0"
description = "Tool to find and fix Rust formatting issues" description = "Tool to find and fix Rust formatting issues"
repository = "https://github.com/rust-lang/rustfmt" repository = "https://github.com/rust-lang/rustfmt"
readme = "README.md" readme = "README.md"
@ -50,7 +50,7 @@ serde_json = "1.0"
term = "0.7" term = "0.7"
thiserror = "1.0.40" thiserror = "1.0.40"
toml = "0.7.4" toml = "0.7.4"
tracing = "0.1.37" tracing = { version = "0.1.37", default-features = false, features = ["std"] }
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
unicode-segmentation = "1.9" unicode-segmentation = "1.9"
unicode-width = "0.1" unicode-width = "0.1"

View File

@ -534,7 +534,7 @@ Note that this option may be soft-deprecated in the future once the [ignore](#ig
Specifies which edition is used by the parser. Specifies which edition is used by the parser.
- **Default value**: `"2015"` - **Default value**: `"2015"`
- **Possible values**: `"2015"`, `"2018"`, `"2021"` - **Possible values**: `"2015"`, `"2018"`, `"2021"`, `"2024"`
- **Stable**: Yes - **Stable**: Yes
Rustfmt is able to pick up the edition used by reading the `Cargo.toml` file if executed Rustfmt is able to pick up the edition used by reading the `Cargo.toml` file if executed
@ -2692,6 +2692,17 @@ By default this option is set as a percentage of [`max_width`](#max_width) provi
See also [`max_width`](#max_width) and [`use_small_heuristics`](#use_small_heuristics) See also [`max_width`](#max_width) and [`use_small_heuristics`](#use_small_heuristics)
## `style_edition`
Controls the edition of the [Rust Style Guide] to use for formatting ([RFC 3338])
- **Default value**: `"2015"`
- **Possible values**: `"2015"`, `"2018"`, `"2021"`, `"2024"` (unstable variant)
- **Stable**: No
[Rust Style Guide]: https://doc.rust-lang.org/nightly/style-guide/
[RFC 3338]: https://rust-lang.github.io/rfcs/3338-style-evolution.html
## `tab_spaces` ## `tab_spaces`
Number of spaces per tab Number of spaces per tab
@ -3051,9 +3062,7 @@ fn main() {
## `version` ## `version`
Which version of the formatting rules to use. `Version::One` is backwards-compatible This option is deprecated and has been replaced by [`style_edition`](#style_edition)
with Rustfmt 1.0. Other versions are only backwards compatible within a major
version number.
- **Default value**: `One` - **Default value**: `One`
- **Possible values**: `One`, `Two` - **Possible values**: `One`, `Two`

View File

@ -109,17 +109,17 @@ If you want to test modified `cargo-fmt`, or run `rustfmt` on the whole project
RUSTFMT="./target/debug/rustfmt" cargo run --bin cargo-fmt -- --manifest-path path/to/project/you/want2test/Cargo.toml RUSTFMT="./target/debug/rustfmt" cargo run --bin cargo-fmt -- --manifest-path path/to/project/you/want2test/Cargo.toml
``` ```
### Version-gate formatting changes ### Gate formatting changes
A change that introduces a different code-formatting should be gated on the A change that introduces a different code-formatting must be gated on the
`version` configuration. This is to ensure the formatting of the current major `style_edition` configuration. This is to ensure rustfmt upholds its formatting
release is preserved, while allowing fixes to be implemented for the next stability guarantees and adheres to the Style Edition process set in [RFC 3338]
release.
This is done by conditionally guarding the change like so: This can be done by conditionally guarding the formatting change, e.g.:
```rust ```rust
if config.version() == Version::One { // if the current major release is 1.x // if the current stable Style Edition is Edition 2024
if config.style_edition() <= StyleEdition::Edition2024 {
// current formatting // current formatting
} else { } else {
// new formatting // new formatting
@ -129,13 +129,14 @@ if config.version() == Version::One { // if the current major release is 1.x
This allows the user to apply the next formatting explicitly via the This allows the user to apply the next formatting explicitly via the
configuration, while being stable by default. configuration, while being stable by default.
When the next major release is done, the code block of the previous formatting This can then be enhanced as needed if and when there are
can be deleted, e.g., the first block in the example above when going from `1.x` new Style Editions with differing formatting prescriptions.
to `2.x`.
| Note: Only formatting changes with default options need to be gated. | | Note: Only formatting changes with default options need to be gated. |
| --- | | --- |
[RFC 3338]: https://rust-lang.github.io/rfcs/3338-style-evolution.html
### A quick tour of Rustfmt ### A quick tour of Rustfmt
Rustfmt is basically a pretty printer - that is, its mode of operation is to Rustfmt is basically a pretty printer - that is, its mode of operation is to

View File

@ -25,7 +25,7 @@ fn main() {
// (git not installed or if this is not a git repository) just return an empty string. // (git not installed or if this is not a git repository) just return an empty string.
fn commit_info() -> String { fn commit_info() -> String {
match (channel(), commit_hash(), commit_date()) { match (channel(), commit_hash(), commit_date()) {
(channel, Some(hash), Some(date)) => format!("{} ({} {})", channel, hash.trim_end(), date), (channel, Some(hash), Some(date)) => format!("{} ({} {})", channel, hash, date),
_ => String::new(), _ => String::new(),
} }
} }
@ -39,17 +39,20 @@ fn channel() -> String {
} }
fn commit_hash() -> Option<String> { fn commit_hash() -> Option<String> {
Command::new("git") let output = Command::new("git")
.args(["rev-parse", "--short", "HEAD"]) .args(["rev-parse", "HEAD"])
.output() .output()
.ok() .ok()?;
.and_then(|r| String::from_utf8(r.stdout).ok()) let mut stdout = output.status.success().then_some(output.stdout)?;
stdout.truncate(10);
String::from_utf8(stdout).ok()
} }
fn commit_date() -> Option<String> { fn commit_date() -> Option<String> {
Command::new("git") let output = Command::new("git")
.args(["log", "-1", "--date=short", "--pretty=format:%cd"]) .args(["log", "-1", "--date=short", "--pretty=format:%cd"])
.output() .output()
.ok() .ok()?;
.and_then(|r| String::from_utf8(r.stdout).ok()) let stdout = output.status.success().then_some(output.stdout)?;
String::from_utf8(stdout).ok()
} }

View File

@ -2,6 +2,15 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 3
[[package]]
name = "aho-corasick"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "anstream" name = "anstream"
version = "0.6.14" version = "0.6.14"
@ -51,11 +60,26 @@ dependencies = [
"windows-sys", "windows-sys",
] ]
[[package]]
name = "bitflags"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "check_diff" name = "check_diff"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"clap", "clap",
"tempfile",
"tracing",
"tracing-subscriber",
] ]
[[package]] [[package]]
@ -104,6 +128,22 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
[[package]]
name = "errno"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
dependencies = [
"libc",
"windows-sys",
]
[[package]]
name = "fastrand"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
[[package]] [[package]]
name = "heck" name = "heck"
version = "0.5.0" version = "0.5.0"
@ -116,6 +156,73 @@ version = "1.70.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.155"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
[[package]]
name = "linux-raw-sys"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
[[package]]
name = "log"
version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
[[package]]
name = "matchers"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
dependencies = [
"regex-automata 0.1.10",
]
[[package]]
name = "memchr"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "nu-ansi-term"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
dependencies = [
"overload",
"winapi",
]
[[package]]
name = "once_cell"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "overload"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
name = "pin-project-lite"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.83" version = "1.0.83"
@ -134,6 +241,78 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "regex"
version = "1.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata 0.4.7",
"regex-syntax 0.8.4",
]
[[package]]
name = "regex-automata"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
dependencies = [
"regex-syntax 0.6.29",
]
[[package]]
name = "regex-automata"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax 0.8.4",
]
[[package]]
name = "regex-syntax"
version = "0.6.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
[[package]]
name = "regex-syntax"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
[[package]]
name = "rustix"
version = "0.38.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
dependencies = [
"bitflags",
"errno",
"libc",
"linux-raw-sys",
"windows-sys",
]
[[package]]
name = "sharded-slab"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
dependencies = [
"lazy_static",
]
[[package]]
name = "smallvec"
version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]] [[package]]
name = "strsim" name = "strsim"
version = "0.11.1" version = "0.11.1"
@ -151,6 +330,89 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "tempfile"
version = "3.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
dependencies = [
"cfg-if",
"fastrand",
"rustix",
"windows-sys",
]
[[package]]
name = "thread_local"
version = "1.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
dependencies = [
"cfg-if",
"once_cell",
]
[[package]]
name = "tracing"
version = "0.1.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
dependencies = [
"pin-project-lite",
"tracing-attributes",
"tracing-core",
]
[[package]]
name = "tracing-attributes"
version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tracing-core"
version = "0.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
dependencies = [
"once_cell",
"valuable",
]
[[package]]
name = "tracing-log"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
dependencies = [
"log",
"once_cell",
"tracing-core",
]
[[package]]
name = "tracing-subscriber"
version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
dependencies = [
"matchers",
"nu-ansi-term",
"once_cell",
"regex",
"sharded-slab",
"smallvec",
"thread_local",
"tracing",
"tracing-core",
"tracing-log",
]
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.12" version = "1.0.12"
@ -163,6 +425,34 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "valuable"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.52.0" version = "0.52.0"

View File

@ -7,3 +7,7 @@ edition = "2021"
[dependencies] [dependencies]
clap = { version = "4.4.2", features = ["derive"] } clap = { version = "4.4.2", features = ["derive"] }
tracing = "0.1.37"
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
[dev-dependencies]
tempfile = "3"

View File

@ -0,0 +1,58 @@
use std::env;
use std::io;
use std::path::Path;
use std::process::Command;
use tracing::info;
pub enum GitError {
FailedClone { stdout: Vec<u8>, stderr: Vec<u8> },
IO(std::io::Error),
}
impl From<io::Error> for GitError {
fn from(error: io::Error) -> Self {
GitError::IO(error)
}
}
/// Clone a git repository
///
/// Parameters:
/// url: git clone url
/// dest: directory where the repo should be cloned
pub fn clone_git_repo(url: &str, dest: &Path) -> Result<(), GitError> {
let git_cmd = Command::new("git")
.env("GIT_TERMINAL_PROMPT", "0")
.args([
"clone",
"--quiet",
url,
"--depth",
"1",
dest.to_str().unwrap(),
])
.output()?;
// if the git command does not return successfully,
// any command on the repo will fail. So fail fast.
if !git_cmd.status.success() {
let error = GitError::FailedClone {
stdout: git_cmd.stdout,
stderr: git_cmd.stderr,
};
return Err(error);
}
info!("Successfully clone repository.");
return Ok(());
}
pub fn change_directory_to_path(dest: &Path) -> io::Result<()> {
let dest_path = Path::new(&dest);
env::set_current_dir(&dest_path)?;
info!(
"Current directory: {}",
env::current_dir().unwrap().display()
);
return Ok(());
}

View File

@ -1,4 +1,5 @@
use clap::Parser; use clap::Parser;
/// Inputs for the check_diff script /// Inputs for the check_diff script
#[derive(Parser)] #[derive(Parser)]
struct CliInputs { struct CliInputs {
@ -16,10 +17,5 @@ struct CliInputs {
} }
fn main() { fn main() {
let args = CliInputs::parse(); let _args = CliInputs::parse();
println!(
"remote_repo_url: {:?}, feature_branch: {:?},
optional_commit_hash: {:?}, optional_rustfmt_config: {:?}",
args.remote_repo_url, args.feature_branch, args.commit_hash, args.rustfmt_config
);
} }

View File

@ -0,0 +1,12 @@
use check_diff::change_directory_to_path;
use std::env;
use tempfile::Builder;
#[test]
fn cd_test() {
// Creates an empty directory in the current working directory
let dir = Builder::new().tempdir_in("").unwrap();
let dest_path = dir.path();
change_directory_to_path(dest_path).unwrap();
assert_eq!(env::current_dir().unwrap(), dest_path);
}

View File

@ -0,0 +1,16 @@
use check_diff::clone_git_repo;
use tempfile::Builder;
#[test]
fn clone_repo_test() {
// Creates an empty directory in the current working directory
let dir = Builder::new().tempdir_in("").unwrap();
let sample_repo = "https://github.com/rust-lang/rustfmt.git";
let dest_path = dir.path();
let result = clone_git_repo(sample_repo, dest_path);
assert!(result.is_ok());
// check whether a .git folder exists after cloning the repo
let git_repo = dest_path.join(".git");
assert!(git_repo.exists());
}

View File

@ -13,7 +13,13 @@ if "%CFG_RELEASE_CHANNEL%"=="nightly" (
) )
cargo test || exit /b 1 cargo test || exit /b 1
:: Build and test other crates :: Build and test config_proc_macro
cd config_proc_macro || exit /b 1 cd config_proc_macro || exit /b 1
cargo build --locked || exit /b 1 cargo build --locked || exit /b 1
cargo test || exit /b 1 cargo test || exit /b 1
:: Build and test check_diff
cd ..
cd check_diff || exit /b 1
cargo build --locked || exit /b 1
cargo test || exit /b 1

View File

@ -17,7 +17,13 @@ else
fi fi
cargo test cargo test
# Build and test other crates # Build and test config_proc_macro
cd config_proc_macro cd config_proc_macro
cargo build --locked cargo build --locked
cargo test cargo test
# Build and test check_diff
cd ..
cd check_diff
cargo build --locked
cargo test

View File

@ -1,7 +1,7 @@
[package] [package]
name = "rustfmt-config_proc_macro" name = "rustfmt-config_proc_macro"
version = "0.3.0" version = "0.3.0"
edition = "2018" edition = "2021"
description = "A collection of procedural macros for rustfmt" description = "A collection of procedural macros for rustfmt"
license = "Apache-2.0 OR MIT" license = "Apache-2.0 OR MIT"
categories = ["development-tools::procedural-macro-helpers"] categories = ["development-tools::procedural-macro-helpers"]
@ -13,7 +13,7 @@ proc-macro = true
[dependencies] [dependencies]
proc-macro2 = "1.0" proc-macro2 = "1.0"
quote = "1.0" quote = "1.0"
syn = { version = "2.0", features = ["full", "visit"] } syn = { version = "2.0", default-features = false, features = ["full", "parsing", "proc-macro", "printing"] }
[dev-dependencies] [dev-dependencies]
serde = { version = "1.0.160", features = ["derive"] } serde = { version = "1.0.160", features = ["derive"] }

View File

@ -1,5 +1,5 @@
use proc_macro2::TokenStream; use proc_macro2::TokenStream;
use quote::{quote, ToTokens}; use quote::{ToTokens, quote};
pub fn fold_quote<F, I, T>(input: impl Iterator<Item = I>, f: F) -> TokenStream pub fn fold_quote<F, I, T>(input: impl Iterator<Item = I>, f: F) -> TokenStream
where where

View File

@ -1,3 +1,3 @@
[toolchain] [toolchain]
channel = "nightly-2024-06-13" channel = "nightly-2024-09-10"
components = ["llvm-tools", "rustc-dev"] components = ["llvm-tools", "rustc-dev"]

View File

@ -1,3 +1,4 @@
error_on_line_overflow = true error_on_line_overflow = true
error_on_unformatted = true error_on_unformatted = true
version = "Two" style_edition = "2024"
overflow_delimited_expr = false

View File

@ -1,21 +1,21 @@
//! Format attributes and meta items. //! Format attributes and meta items.
use rustc_ast::ast;
use rustc_ast::HasAttrs; use rustc_ast::HasAttrs;
use rustc_span::{symbol::sym, Span}; use rustc_ast::ast;
use rustc_span::{Span, symbol::sym};
use tracing::debug; use tracing::debug;
use self::doc_comment::DocCommentFormatter; use self::doc_comment::DocCommentFormatter;
use crate::comment::{contains_comment, rewrite_doc_comment, CommentStyle}; use crate::comment::{CommentStyle, contains_comment, rewrite_doc_comment};
use crate::config::lists::*;
use crate::config::IndentStyle; use crate::config::IndentStyle;
use crate::config::lists::*;
use crate::expr::rewrite_literal; use crate::expr::rewrite_literal;
use crate::lists::{definitive_tactic, itemize_list, write_list, ListFormatting, Separator}; use crate::lists::{ListFormatting, Separator, definitive_tactic, itemize_list, write_list};
use crate::overflow; use crate::overflow;
use crate::rewrite::{Rewrite, RewriteContext}; use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult};
use crate::shape::Shape; use crate::shape::Shape;
use crate::source_map::SpanUtils; use crate::source_map::SpanUtils;
use crate::types::{rewrite_path, PathContext}; use crate::types::{PathContext, rewrite_path};
use crate::utils::{count_newlines, mk_sp}; use crate::utils::{count_newlines, mk_sp};
mod doc_comment; mod doc_comment;
@ -100,7 +100,7 @@ fn format_derive(
",", ",",
|span| span.lo(), |span| span.lo(),
|span| span.hi(), |span| span.hi(),
|span| Some(context.snippet(*span).to_owned()), |span| Ok(context.snippet(*span).to_owned()),
// We update derive attribute spans to start after the opening '(' // We update derive attribute spans to start after the opening '('
// This helps us focus parsing to just what's inside #[derive(...)] // This helps us focus parsing to just what's inside #[derive(...)]
context.snippet_provider.span_after(attr.span, "("), context.snippet_provider.span_after(attr.span, "("),
@ -148,7 +148,7 @@ fn format_derive(
.tactic(tactic) .tactic(tactic)
.trailing_separator(trailing_separator) .trailing_separator(trailing_separator)
.ends_with_newline(false); .ends_with_newline(false);
let item_str = write_list(&all_items, &fmt)?; let item_str = write_list(&all_items, &fmt).ok()?;
debug!("item_str: '{}'", item_str); debug!("item_str: '{}'", item_str);
@ -218,9 +218,9 @@ fn rewrite_initial_doc_comments(
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
attrs: &[ast::Attribute], attrs: &[ast::Attribute],
shape: Shape, shape: Shape,
) -> Option<(usize, Option<String>)> { ) -> Result<(usize, Option<String>), RewriteError> {
if attrs.is_empty() { if attrs.is_empty() {
return Some((0, None)); return Ok((0, None));
} }
// Rewrite doc comments // Rewrite doc comments
let sugared_docs = take_while_with_pred(context, attrs, |a| a.is_doc_comment()); let sugared_docs = take_while_with_pred(context, attrs, |a| a.is_doc_comment());
@ -230,7 +230,7 @@ fn rewrite_initial_doc_comments(
.map(|a| context.snippet(a.span)) .map(|a| context.snippet(a.span))
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join("\n"); .join("\n");
return Some(( return Ok((
sugared_docs.len(), sugared_docs.len(),
Some(rewrite_doc_comment( Some(rewrite_doc_comment(
&snippet, &snippet,
@ -240,13 +240,19 @@ fn rewrite_initial_doc_comments(
)); ));
} }
Some((0, None)) Ok((0, None))
} }
impl Rewrite for ast::NestedMetaItem { impl Rewrite for ast::NestedMetaItem {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
match self { match self {
ast::NestedMetaItem::MetaItem(ref meta_item) => meta_item.rewrite(context, shape), ast::NestedMetaItem::MetaItem(ref meta_item) => {
meta_item.rewrite_result(context, shape)
}
ast::NestedMetaItem::Lit(ref l) => { ast::NestedMetaItem::Lit(ref l) => {
rewrite_literal(context, l.as_token_lit(), l.span, shape) rewrite_literal(context, l.as_token_lit(), l.span, shape)
} }
@ -275,7 +281,11 @@ fn has_newlines_before_after_comment(comment: &str) -> (&str, &str) {
impl Rewrite for ast::MetaItem { impl Rewrite for ast::MetaItem {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
Some(match self.kind { self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
Ok(match self.kind {
ast::MetaItemKind::Word => { ast::MetaItemKind::Word => {
rewrite_path(context, PathContext::Type, &None, &self.path, shape)? rewrite_path(context, PathContext::Type, &None, &self.path, shape)?
} }
@ -287,7 +297,7 @@ impl Rewrite for ast::MetaItem {
&path, &path,
list.iter(), list.iter(),
// 1 = "]" // 1 = "]"
shape.sub_width(1)?, shape.sub_width(1).max_width_error(shape.width, self.span)?,
self.span, self.span,
context.config.attr_fn_like_width(), context.config.attr_fn_like_width(),
Some(if has_trailing_comma { Some(if has_trailing_comma {
@ -300,7 +310,9 @@ impl Rewrite for ast::MetaItem {
ast::MetaItemKind::NameValue(ref lit) => { ast::MetaItemKind::NameValue(ref lit) => {
let path = rewrite_path(context, PathContext::Type, &None, &self.path, shape)?; let path = rewrite_path(context, PathContext::Type, &None, &self.path, shape)?;
// 3 = ` = ` // 3 = ` = `
let lit_shape = shape.shrink_left(path.len() + 3)?; let lit_shape = shape
.shrink_left(path.len() + 3)
.max_width_error(shape.width, self.span)?;
// `rewrite_literal` returns `None` when `lit` exceeds max // `rewrite_literal` returns `None` when `lit` exceeds max
// width. Since a literal is basically unformattable unless it // width. Since a literal is basically unformattable unless it
// is a string literal (and only if `format_strings` is set), // is a string literal (and only if `format_strings` is set),
@ -308,7 +320,7 @@ impl Rewrite for ast::MetaItem {
// is longer than the max width and continue on formatting. // is longer than the max width and continue on formatting.
// See #2479 for example. // See #2479 for example.
let value = rewrite_literal(context, lit.as_token_lit(), lit.span, lit_shape) let value = rewrite_literal(context, lit.as_token_lit(), lit.span, lit_shape)
.unwrap_or_else(|| context.snippet(lit.span).to_owned()); .unwrap_or_else(|_| context.snippet(lit.span).to_owned());
format!("{path} = {value}") format!("{path} = {value}")
} }
}) })
@ -317,6 +329,10 @@ impl Rewrite for ast::MetaItem {
impl Rewrite for ast::Attribute { impl Rewrite for ast::Attribute {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
let snippet = context.snippet(self.span); let snippet = context.snippet(self.span);
if self.is_doc_comment() { if self.is_doc_comment() {
rewrite_doc_comment(snippet, shape.comment(context.config), context.config) rewrite_doc_comment(snippet, shape.comment(context.config), context.config)
@ -328,7 +344,7 @@ impl Rewrite for ast::Attribute {
let prefix = attr_prefix(self); let prefix = attr_prefix(self);
if should_skip || contains_comment(snippet) { if should_skip || contains_comment(snippet) {
return Some(snippet.to_owned()); return Ok(snippet.to_owned());
} }
if let Some(ref meta) = self.meta() { if let Some(ref meta) = self.meta() {
@ -353,9 +369,11 @@ impl Rewrite for ast::Attribute {
} }
// 1 = `[` // 1 = `[`
let shape = shape.offset_left(prefix.len() + 1)?; let shape = shape
Some(meta.rewrite(context, shape).map_or_else( .offset_left(prefix.len() + 1)
|| snippet.to_owned(), .max_width_error(shape.width, self.span)?;
Ok(meta.rewrite_result(context, shape).map_or_else(
|_| snippet.to_owned(),
|rw| match &self.kind { |rw| match &self.kind {
ast::AttrKind::Normal(normal_attr) => match normal_attr.item.unsafety { ast::AttrKind::Normal(normal_attr) => match normal_attr.item.unsafety {
// For #![feature(unsafe_attributes)] // For #![feature(unsafe_attributes)]
@ -367,7 +385,7 @@ impl Rewrite for ast::Attribute {
}, },
)) ))
} else { } else {
Some(snippet.to_owned()) Ok(snippet.to_owned())
} }
} }
} }
@ -375,8 +393,12 @@ impl Rewrite for ast::Attribute {
impl Rewrite for [ast::Attribute] { impl Rewrite for [ast::Attribute] {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
if self.is_empty() { if self.is_empty() {
return Some(String::new()); return Ok(String::new());
} }
// The current remaining attributes. // The current remaining attributes.
@ -392,7 +414,7 @@ impl Rewrite for [ast::Attribute] {
// merging derives into a single attribute. // merging derives into a single attribute.
loop { loop {
if attrs.is_empty() { if attrs.is_empty() {
return Some(result); return Ok(result);
} }
// Handle doc comments. // Handle doc comments.
@ -431,7 +453,7 @@ impl Rewrite for [ast::Attribute] {
// Handle derives if we will merge them. // Handle derives if we will merge them.
if !skip_derives && context.config.merge_derives() && is_derive(&attrs[0]) { if !skip_derives && context.config.merge_derives() && is_derive(&attrs[0]) {
let derives = take_while_with_pred(context, attrs, is_derive); let derives = take_while_with_pred(context, attrs, is_derive);
let derive_str = format_derive(derives, shape, context)?; let derive_str = format_derive(derives, shape, context).unknown_error()?;
result.push_str(&derive_str); result.push_str(&derive_str);
let missing_span = attrs let missing_span = attrs
@ -464,7 +486,7 @@ impl Rewrite for [ast::Attribute] {
// If we get here, then we have a regular attribute, just handle one // If we get here, then we have a regular attribute, just handle one
// at a time. // at a time.
let formatted_attr = attrs[0].rewrite(context, shape)?; let formatted_attr = attrs[0].rewrite_result(context, shape)?;
result.push_str(&formatted_attr); result.push_str(&formatted_attr);
let missing_span = attrs let missing_span = attrs

View File

@ -1,6 +1,6 @@
#![feature(rustc_private)] #![feature(rustc_private)]
use anyhow::{format_err, Result}; use anyhow::{Result, format_err};
use io::Error as IoError; use io::Error as IoError;
use thiserror::Error; use thiserror::Error;
@ -11,15 +11,15 @@ use tracing_subscriber::EnvFilter;
use std::collections::HashMap; use std::collections::HashMap;
use std::env; use std::env;
use std::fs::File; use std::fs::File;
use std::io::{self, stdout, Read, Write}; use std::io::{self, Read, Write, stdout};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::str::FromStr; use std::str::FromStr;
use getopts::{Matches, Options}; use getopts::{Matches, Options};
use crate::rustfmt::{ use crate::rustfmt::{
load_config, CliOptions, Color, Config, Edition, EmitMode, FileLines, FileName, CliOptions, Color, Config, Edition, EmitMode, FileLines, FileName,
FormatReportFormatterBuilder, Input, Session, Verbosity, FormatReportFormatterBuilder, Input, Session, StyleEdition, Verbosity, Version, load_config,
}; };
const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rustfmt/issues/new?labels=bug"; const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rustfmt/issues/new?labels=bug";
@ -129,7 +129,12 @@ fn make_opts() -> Options {
found reverts to the input file path", found reverts to the input file path",
"[Path for the configuration file]", "[Path for the configuration file]",
); );
opts.optopt("", "edition", "Rust edition to use", "[2015|2018|2021]"); opts.optopt(
"",
"edition",
"Rust edition to use",
"[2015|2018|2021|2024]",
);
opts.optopt( opts.optopt(
"", "",
"color", "color",
@ -181,6 +186,12 @@ fn make_opts() -> Options {
"skip-children", "skip-children",
"Don't reformat child modules (unstable).", "Don't reformat child modules (unstable).",
); );
opts.optopt(
"",
"style-edition",
"The edition of the Style Guide (unstable).",
"[2015|2018|2021|2024]",
);
} }
opts.optflag("v", "verbose", "Print verbose output"); opts.optflag("v", "verbose", "Print verbose output");
@ -263,24 +274,35 @@ fn format_string(input: String, options: GetOptsOptions) -> Result<i32> {
let (mut config, _) = load_config(Some(Path::new(".")), Some(options.clone()))?; let (mut config, _) = load_config(Some(Path::new(".")), Some(options.clone()))?;
if options.check { if options.check {
config.set().emit_mode(EmitMode::Diff); config.set_cli().emit_mode(EmitMode::Diff);
} else { } else {
match options.emit_mode { match options.emit_mode {
// Emit modes which work with standard input // Emit modes which work with standard input
// None means default, which is Stdout. // None means default, which is Stdout.
None | Some(EmitMode::Stdout) | Some(EmitMode::Checkstyle) | Some(EmitMode::Json) => {} None => {
config
.set()
.emit_mode(options.emit_mode.unwrap_or(EmitMode::Stdout));
}
Some(EmitMode::Stdout) | Some(EmitMode::Checkstyle) | Some(EmitMode::Json) => {
config
.set_cli()
.emit_mode(options.emit_mode.unwrap_or(EmitMode::Stdout));
}
Some(emit_mode) => { Some(emit_mode) => {
return Err(OperationError::StdinBadEmit(emit_mode).into()); return Err(OperationError::StdinBadEmit(emit_mode).into());
} }
} }
config
.set()
.emit_mode(options.emit_mode.unwrap_or(EmitMode::Stdout));
} }
config.set().verbose(Verbosity::Quiet); config.set().verbose(Verbosity::Quiet);
// parse file_lines // parse file_lines
config.set().file_lines(options.file_lines); if options.file_lines.is_all() {
config.set().file_lines(options.file_lines);
} else {
config.set_cli().file_lines(options.file_lines);
}
for f in config.file_lines().files() { for f in config.file_lines().files() {
match *f { match *f {
FileName::Stdin => {} FileName::Stdin => {}
@ -319,10 +341,10 @@ fn format(
for file in files { for file in files {
if !file.exists() { if !file.exists() {
eprintln!("Error: file `{}` does not exist", file.to_str().unwrap()); eprintln!("Error: file `{}` does not exist", file.display());
session.add_operational_error(); session.add_operational_error();
} else if file.is_dir() { } else if file.is_dir() {
eprintln!("Error: `{}` is a directory", file.to_str().unwrap()); eprintln!("Error: `{}` is a directory", file.display());
session.add_operational_error(); session.add_operational_error();
} else { } else {
// Check the file directory if the config-path could not be read or not provided // Check the file directory if the config-path could not be read or not provided
@ -428,27 +450,27 @@ are included as out of line modules from `src/lib.rs`."
} }
fn print_version() { fn print_version() {
let version_info = format!( let version_number = option_env!("CARGO_PKG_VERSION").unwrap_or("unknown");
"{}-{}", let commit_info = include_str!(concat!(env!("OUT_DIR"), "/commit-info.txt"));
option_env!("CARGO_PKG_VERSION").unwrap_or("unknown"),
include_str!(concat!(env!("OUT_DIR"), "/commit-info.txt"))
);
println!("rustfmt {version_info}"); if commit_info.is_empty() {
println!("rustfmt {version_number}");
} else {
println!("rustfmt {version_number}-{commit_info}");
}
} }
fn determine_operation(matches: &Matches) -> Result<Operation, OperationError> { fn determine_operation(matches: &Matches) -> Result<Operation, OperationError> {
if matches.opt_present("h") { if matches.opt_present("h") {
let topic = matches.opt_str("h"); let Some(topic) = matches.opt_str("h") else {
if topic.is_none() {
return Ok(Operation::Help(HelpOp::None)); return Ok(Operation::Help(HelpOp::None));
} else if topic == Some("config".to_owned()) { };
return Ok(Operation::Help(HelpOp::Config));
} else if topic == Some("file-lines".to_owned()) && is_nightly() { return match topic.as_str() {
return Ok(Operation::Help(HelpOp::FileLines)); "config" => Ok(Operation::Help(HelpOp::Config)),
} else { "file-lines" if is_nightly() => Ok(Operation::Help(HelpOp::FileLines)),
return Err(OperationError::UnknownHelpTopic(topic.unwrap())); _ => Err(OperationError::UnknownHelpTopic(topic)),
} };
} }
let mut free_matches = matches.free.iter(); let mut free_matches = matches.free.iter();
@ -514,6 +536,7 @@ struct GetOptsOptions {
backup: bool, backup: bool,
check: bool, check: bool,
edition: Option<Edition>, edition: Option<Edition>,
style_edition: Option<StyleEdition>,
color: Option<Color>, color: Option<Color>,
file_lines: FileLines, // Default is all lines in all files. file_lines: FileLines, // Default is all lines in all files.
unstable_features: bool, unstable_features: bool,
@ -545,6 +568,10 @@ impl GetOptsOptions {
if let Some(ref file_lines) = matches.opt_str("file-lines") { if let Some(ref file_lines) = matches.opt_str("file-lines") {
options.file_lines = file_lines.parse()?; options.file_lines = file_lines.parse()?;
} }
if let Some(ref edition_str) = matches.opt_str("style-edition") {
options.style_edition =
Some(style_edition_from_style_edition_str(edition_str)?);
}
} else { } else {
let mut unstable_options = vec![]; let mut unstable_options = vec![];
if matches.opt_present("skip-children") { if matches.opt_present("skip-children") {
@ -556,6 +583,9 @@ impl GetOptsOptions {
if matches.opt_present("file-lines") { if matches.opt_present("file-lines") {
unstable_options.push("`--file-lines`"); unstable_options.push("`--file-lines`");
} }
if matches.opt_present("style-edition") {
unstable_options.push("`--style-edition`");
}
if !unstable_options.is_empty() { if !unstable_options.is_empty() {
let s = if unstable_options.len() == 1 { "" } else { "s" }; let s = if unstable_options.len() == 1 { "" } else { "s" };
return Err(format_err!( return Err(format_err!(
@ -650,36 +680,49 @@ impl GetOptsOptions {
impl CliOptions for GetOptsOptions { impl CliOptions for GetOptsOptions {
fn apply_to(self, config: &mut Config) { fn apply_to(self, config: &mut Config) {
if self.verbose { if self.verbose {
config.set().verbose(Verbosity::Verbose); config.set_cli().verbose(Verbosity::Verbose);
} else if self.quiet { } else if self.quiet {
config.set().verbose(Verbosity::Quiet); config.set_cli().verbose(Verbosity::Quiet);
} else { } else {
config.set().verbose(Verbosity::Normal); config.set().verbose(Verbosity::Normal);
} }
config.set().file_lines(self.file_lines);
config.set().unstable_features(self.unstable_features); if self.file_lines.is_all() {
config.set().file_lines(self.file_lines);
} else {
config.set_cli().file_lines(self.file_lines);
}
if self.unstable_features {
config.set_cli().unstable_features(self.unstable_features);
} else {
config.set().unstable_features(self.unstable_features);
}
if let Some(skip_children) = self.skip_children { if let Some(skip_children) = self.skip_children {
config.set().skip_children(skip_children); config.set_cli().skip_children(skip_children);
} }
if let Some(error_on_unformatted) = self.error_on_unformatted { if let Some(error_on_unformatted) = self.error_on_unformatted {
config.set().error_on_unformatted(error_on_unformatted); config.set_cli().error_on_unformatted(error_on_unformatted);
} }
if let Some(edition) = self.edition { if let Some(edition) = self.edition {
config.set().edition(edition); config.set_cli().edition(edition);
}
if let Some(edition) = self.style_edition {
config.set_cli().style_edition(edition);
} }
if self.check { if self.check {
config.set().emit_mode(EmitMode::Diff); config.set_cli().emit_mode(EmitMode::Diff);
} else if let Some(emit_mode) = self.emit_mode { } else if let Some(emit_mode) = self.emit_mode {
config.set().emit_mode(emit_mode); config.set_cli().emit_mode(emit_mode);
} }
if self.backup { if self.backup {
config.set().make_backup(true); config.set_cli().make_backup(true);
} }
if let Some(color) = self.color { if let Some(color) = self.color {
config.set().color(color); config.set_cli().color(color);
} }
if self.print_misformatted_file_names { if self.print_misformatted_file_names {
config.set().print_misformatted_file_names(true); config.set_cli().print_misformatted_file_names(true);
} }
for (key, val) in self.inline_config { for (key, val) in self.inline_config {
@ -690,6 +733,25 @@ impl CliOptions for GetOptsOptions {
fn config_path(&self) -> Option<&Path> { fn config_path(&self) -> Option<&Path> {
self.config_path.as_deref() self.config_path.as_deref()
} }
fn edition(&self) -> Option<Edition> {
self.inline_config
.get("edition")
.map_or(self.edition, |e| Edition::from_str(e).ok())
}
fn style_edition(&self) -> Option<StyleEdition> {
self.inline_config
.get("style_edition")
.map_or(self.style_edition, |se| StyleEdition::from_str(se).ok())
}
fn version(&self) -> Option<Version> {
self.inline_config
.get("version")
.map(|version| Version::from_str(version).ok())
.flatten()
}
} }
fn edition_from_edition_str(edition_str: &str) -> Result<Edition> { fn edition_from_edition_str(edition_str: &str) -> Result<Edition> {
@ -702,6 +764,16 @@ fn edition_from_edition_str(edition_str: &str) -> Result<Edition> {
} }
} }
fn style_edition_from_style_edition_str(edition_str: &str) -> Result<StyleEdition> {
match edition_str {
"2015" => Ok(StyleEdition::Edition2015),
"2018" => Ok(StyleEdition::Edition2018),
"2021" => Ok(StyleEdition::Edition2021),
"2024" => Ok(StyleEdition::Edition2024),
_ => Err(format_err!("Invalid value for `--style-edition`")),
}
}
fn emit_mode_from_emit_str(emit_str: &str) -> Result<EmitMode> { fn emit_mode_from_emit_str(emit_str: &str) -> Result<EmitMode> {
match emit_str { match emit_str {
"files" => Ok(EmitMode::Files), "files" => Ok(EmitMode::Files),
@ -712,3 +784,185 @@ fn emit_mode_from_emit_str(emit_str: &str) -> Result<EmitMode> {
_ => Err(format_err!("Invalid value for `--emit`")), _ => Err(format_err!("Invalid value for `--emit`")),
} }
} }
#[cfg(test)]
#[allow(dead_code)]
mod test {
use super::*;
use rustfmt_config_proc_macro::nightly_only_test;
fn get_config<O: CliOptions>(path: Option<&Path>, options: Option<O>) -> Config {
load_config(path, options).unwrap().0
}
#[nightly_only_test]
#[test]
fn flag_sets_style_edition_override_correctly() {
let mut options = GetOptsOptions::default();
options.style_edition = Some(StyleEdition::Edition2024);
let config = get_config(None, Some(options));
assert_eq!(config.style_edition(), StyleEdition::Edition2024);
}
#[nightly_only_test]
#[test]
fn edition_sets_style_edition_override_correctly() {
let mut options = GetOptsOptions::default();
options.edition = Some(Edition::Edition2024);
let config = get_config(None, Some(options));
assert_eq!(config.style_edition(), StyleEdition::Edition2024);
}
#[nightly_only_test]
#[test]
fn version_sets_style_edition_override_correctly() {
let mut options = GetOptsOptions::default();
options.inline_config = HashMap::from([("version".to_owned(), "Two".to_owned())]);
let config = get_config(None, Some(options));
assert_eq!(config.style_edition(), StyleEdition::Edition2024);
assert_eq!(config.overflow_delimited_expr(), true);
}
#[nightly_only_test]
#[test]
fn version_config_file_sets_style_edition_override_correctly() {
let options = GetOptsOptions::default();
let config_file = Some(Path::new("tests/config/style-edition/just-version"));
let config = get_config(config_file, Some(options));
assert_eq!(config.style_edition(), StyleEdition::Edition2024);
assert_eq!(config.overflow_delimited_expr(), true);
}
#[nightly_only_test]
#[test]
fn style_edition_flag_has_correct_precedence_over_edition() {
let mut options = GetOptsOptions::default();
options.style_edition = Some(StyleEdition::Edition2021);
options.edition = Some(Edition::Edition2024);
let config = get_config(None, Some(options));
assert_eq!(config.style_edition(), StyleEdition::Edition2021);
}
#[nightly_only_test]
#[test]
fn style_edition_flag_has_correct_precedence_over_version() {
let mut options = GetOptsOptions::default();
options.style_edition = Some(StyleEdition::Edition2018);
options.inline_config = HashMap::from([("version".to_owned(), "Two".to_owned())]);
let config = get_config(None, Some(options));
assert_eq!(config.style_edition(), StyleEdition::Edition2018);
}
#[nightly_only_test]
#[test]
fn style_edition_flag_has_correct_precedence_over_edition_version() {
let mut options = GetOptsOptions::default();
options.style_edition = Some(StyleEdition::Edition2021);
options.edition = Some(Edition::Edition2018);
options.inline_config = HashMap::from([("version".to_owned(), "Two".to_owned())]);
let config = get_config(None, Some(options));
assert_eq!(config.style_edition(), StyleEdition::Edition2021);
}
#[nightly_only_test]
#[test]
fn style_edition_inline_has_correct_precedence_over_edition_version() {
let mut options = GetOptsOptions::default();
options.edition = Some(Edition::Edition2018);
options.inline_config = HashMap::from([
("version".to_owned(), "One".to_owned()),
("style_edition".to_owned(), "2024".to_owned()),
]);
let config = get_config(None, Some(options));
assert_eq!(config.style_edition(), StyleEdition::Edition2024);
assert_eq!(config.overflow_delimited_expr(), true);
}
#[nightly_only_test]
#[test]
fn style_edition_config_file_trumps_edition_flag_version_inline() {
let mut options = GetOptsOptions::default();
let config_file = Some(Path::new("tests/config/style-edition/just-style-edition"));
options.edition = Some(Edition::Edition2018);
options.inline_config = HashMap::from([("version".to_owned(), "One".to_owned())]);
let config = get_config(config_file, Some(options));
assert_eq!(config.style_edition(), StyleEdition::Edition2024);
}
#[nightly_only_test]
#[test]
fn style_edition_config_file_trumps_edition_config_and_version_inline() {
let mut options = GetOptsOptions::default();
let config_file = Some(Path::new(
"tests/config/style-edition/style-edition-and-edition",
));
options.inline_config = HashMap::from([("version".to_owned(), "Two".to_owned())]);
let config = get_config(config_file, Some(options));
assert_eq!(config.style_edition(), StyleEdition::Edition2021);
assert_eq!(config.edition(), Edition::Edition2024);
}
#[nightly_only_test]
#[test]
fn version_config_trumps_edition_config_and_flag() {
let mut options = GetOptsOptions::default();
let config_file = Some(Path::new("tests/config/style-edition/version-edition"));
options.edition = Some(Edition::Edition2018);
let config = get_config(config_file, Some(options));
assert_eq!(config.style_edition(), StyleEdition::Edition2024);
}
#[nightly_only_test]
#[test]
fn style_edition_config_file_trumps_version_config() {
let options = GetOptsOptions::default();
let config_file = Some(Path::new(
"tests/config/style-edition/version-style-edition",
));
let config = get_config(config_file, Some(options));
assert_eq!(config.style_edition(), StyleEdition::Edition2021);
}
#[nightly_only_test]
#[test]
fn style_edition_config_file_trumps_edition_version_config() {
let options = GetOptsOptions::default();
let config_file = Some(Path::new(
"tests/config/style-edition/version-style-edition-and-edition",
));
let config = get_config(config_file, Some(options));
assert_eq!(config.style_edition(), StyleEdition::Edition2021);
}
#[nightly_only_test]
#[test]
fn correct_defaults_for_style_edition_loaded() {
let mut options = GetOptsOptions::default();
options.style_edition = Some(StyleEdition::Edition2024);
let config = get_config(None, Some(options));
assert_eq!(config.style_edition(), StyleEdition::Edition2024);
assert_eq!(config.overflow_delimited_expr(), true);
}
#[nightly_only_test]
#[test]
fn style_edition_defaults_overridden_from_config() {
let options = GetOptsOptions::default();
let config_file = Some(Path::new("tests/config/style-edition/overrides"));
let config = get_config(config_file, Some(options));
assert_eq!(config.style_edition(), StyleEdition::Edition2024);
assert_eq!(config.overflow_delimited_expr(), false);
}
#[nightly_only_test]
#[test]
fn style_edition_defaults_overridden_from_cli() {
let mut options = GetOptsOptions::default();
let config_file = Some(Path::new("tests/config/style-edition/just-style-edition"));
options.inline_config =
HashMap::from([("overflow_delimited_expr".to_owned(), "false".to_owned())]);
let config = get_config(config_file, Some(options));
assert_eq!(config.style_edition(), StyleEdition::Edition2024);
assert_eq!(config.overflow_delimited_expr(), false);
}
}

View File

@ -59,15 +59,15 @@ use std::borrow::Cow;
use std::cmp::min; use std::cmp::min;
use rustc_ast::{ast, ptr}; use rustc_ast::{ast, ptr};
use rustc_span::{symbol, BytePos, Span}; use rustc_span::{BytePos, Span, symbol};
use tracing::debug; use tracing::debug;
use crate::comment::{rewrite_comment, CharClasses, FullCodeCharKind, RichChar}; use crate::comment::{CharClasses, FullCodeCharKind, RichChar, rewrite_comment};
use crate::config::{IndentStyle, Version}; use crate::config::{IndentStyle, StyleEdition};
use crate::expr::rewrite_call; use crate::expr::rewrite_call;
use crate::lists::extract_pre_comment; use crate::lists::extract_pre_comment;
use crate::macros::convert_try_mac; use crate::macros::convert_try_mac;
use crate::rewrite::{Rewrite, RewriteContext}; use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult};
use crate::shape::Shape; use crate::shape::Shape;
use crate::source_map::SpanUtils; use crate::source_map::SpanUtils;
use crate::utils::{ use crate::utils::{
@ -80,6 +80,9 @@ use thin_vec::ThinVec;
/// Provides the original input contents from the span /// Provides the original input contents from the span
/// of a chain element with trailing spaces trimmed. /// of a chain element with trailing spaces trimmed.
fn format_overflow_style(span: Span, context: &RewriteContext<'_>) -> Option<String> { fn format_overflow_style(span: Span, context: &RewriteContext<'_>) -> Option<String> {
// TODO(ding-young): Currently returning None when the given span is out of the range
// covered by the snippet provider. If this is a common cause for internal
// rewrite failure, add a new enum variant and return RewriteError instead of None
context.snippet_provider.span_to_snippet(span).map(|s| { context.snippet_provider.span_to_snippet(span).map(|s| {
s.lines() s.lines()
.map(|l| l.trim_end()) .map(|l| l.trim_end())
@ -93,12 +96,16 @@ fn format_chain_item(
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
rewrite_shape: Shape, rewrite_shape: Shape,
allow_overflow: bool, allow_overflow: bool,
) -> Option<String> { ) -> RewriteResult {
if allow_overflow { if allow_overflow {
item.rewrite(context, rewrite_shape) // TODO(ding-young): Consider calling format_overflow_style()
.or_else(|| format_overflow_style(item.span, context)) // only when item.rewrite_result() returns RewriteError::ExceedsMaxWidth.
// It may be inappropriate to call format_overflow_style on other RewriteError
// since the current approach retries formatting if allow_overflow is true
item.rewrite_result(context, rewrite_shape)
.or_else(|_| format_overflow_style(item.span, context).unknown_error())
} else { } else {
item.rewrite(context, rewrite_shape) item.rewrite_result(context, rewrite_shape)
} }
} }
@ -135,17 +142,17 @@ pub(crate) fn rewrite_chain(
expr: &ast::Expr, expr: &ast::Expr,
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
shape: Shape, shape: Shape,
) -> Option<String> { ) -> RewriteResult {
let chain = Chain::from_ast(expr, context); let chain = Chain::from_ast(expr, context);
debug!("rewrite_chain {:?} {:?}", chain, shape); debug!("rewrite_chain {:?} {:?}", chain, shape);
// If this is just an expression with some `?`s, then format it trivially and // If this is just an expression with some `?`s, then format it trivially and
// return early. // return early.
if chain.children.is_empty() { if chain.children.is_empty() {
return chain.parent.rewrite(context, shape); return chain.parent.rewrite_result(context, shape);
} }
chain.rewrite(context, shape) chain.rewrite_result(context, shape)
} }
#[derive(Debug)] #[derive(Debug)]
@ -203,7 +210,7 @@ impl ChainItemKind {
fn is_tup_field_access(expr: &ast::Expr) -> bool { fn is_tup_field_access(expr: &ast::Expr) -> bool {
match expr.kind { match expr.kind {
ast::ExprKind::Field(_, ref field) => { ast::ExprKind::Field(_, ref field) => {
field.name.to_string().chars().all(|c| c.is_digit(10)) field.name.as_str().chars().all(|c| c.is_digit(10))
} }
_ => false, _ => false,
} }
@ -269,7 +276,13 @@ impl ChainItemKind {
impl Rewrite for ChainItem { impl Rewrite for ChainItem {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
let shape = shape.sub_width(self.tries)?; self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
let shape = shape
.sub_width(self.tries)
.max_width_error(shape.width, self.span)?;
let rewrite = match self.kind { let rewrite = match self.kind {
ChainItemKind::Parent { ChainItemKind::Parent {
ref expr, ref expr,
@ -278,14 +291,14 @@ impl Rewrite for ChainItem {
ChainItemKind::Parent { ChainItemKind::Parent {
ref expr, ref expr,
parens: false, parens: false,
} => expr.rewrite(context, shape)?, } => expr.rewrite_result(context, shape)?,
ChainItemKind::MethodCall(ref segment, ref types, ref exprs) => { ChainItemKind::MethodCall(ref segment, ref types, ref exprs) => {
Self::rewrite_method_call(segment.ident, types, exprs, self.span, context, shape)? Self::rewrite_method_call(segment.ident, types, exprs, self.span, context, shape)?
} }
ChainItemKind::StructField(ident) => format!(".{}", rewrite_ident(context, ident)), ChainItemKind::StructField(ident) => format!(".{}", rewrite_ident(context, ident)),
ChainItemKind::TupleField(ident, nested) => format!( ChainItemKind::TupleField(ident, nested) => format!(
"{}.{}", "{}.{}",
if nested && context.config.version() == Version::One { if nested && context.config.style_edition() <= StyleEdition::Edition2021 {
" " " "
} else { } else {
"" ""
@ -297,7 +310,7 @@ impl Rewrite for ChainItem {
rewrite_comment(comment, false, shape, context.config)? rewrite_comment(comment, false, shape, context.config)?
} }
}; };
Some(format!("{rewrite}{}", "?".repeat(self.tries))) Ok(format!("{rewrite}{}", "?".repeat(self.tries)))
} }
} }
@ -327,14 +340,14 @@ impl ChainItem {
span: Span, span: Span,
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
shape: Shape, shape: Shape,
) -> Option<String> { ) -> RewriteResult {
let type_str = if types.is_empty() { let type_str = if types.is_empty() {
String::new() String::new()
} else { } else {
let type_list = types let type_list = types
.iter() .iter()
.map(|ty| ty.rewrite(context, shape)) .map(|ty| ty.rewrite_result(context, shape))
.collect::<Option<Vec<_>>>()?; .collect::<Result<Vec<_>, RewriteError>>()?;
format!("::<{}>", type_list.join(", ")) format!("::<{}>", type_list.join(", "))
}; };
@ -519,6 +532,10 @@ impl Chain {
impl Rewrite for Chain { impl Rewrite for Chain {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
debug!("rewrite chain {:?} {:?}", self, shape); debug!("rewrite chain {:?} {:?}", self, shape);
let mut formatter = match context.config.indent_style() { let mut formatter = match context.config.indent_style() {
@ -532,17 +549,25 @@ impl Rewrite for Chain {
formatter.format_root(&self.parent, context, shape)?; formatter.format_root(&self.parent, context, shape)?;
if let Some(result) = formatter.pure_root() { if let Some(result) = formatter.pure_root() {
return wrap_str(result, context.config.max_width(), shape); return wrap_str(result, context.config.max_width(), shape)
.max_width_error(shape.width, self.parent.span);
} }
let first = self.children.first().unwrap_or(&self.parent);
let last = self.children.last().unwrap_or(&self.parent);
let children_span = mk_sp(first.span.lo(), last.span.hi());
let full_span = self.parent.span.with_hi(children_span.hi());
// Decide how to layout the rest of the chain. // Decide how to layout the rest of the chain.
let child_shape = formatter.child_shape(context, shape)?; let child_shape = formatter
.child_shape(context, shape)
.max_width_error(shape.width, children_span)?;
formatter.format_children(context, child_shape)?; formatter.format_children(context, child_shape)?;
formatter.format_last_child(context, shape, child_shape)?; formatter.format_last_child(context, shape, child_shape)?;
let result = formatter.join_rewrites(context, child_shape)?; let result = formatter.join_rewrites(context, child_shape)?;
wrap_str(result, context.config.max_width(), shape) wrap_str(result, context.config.max_width(), shape).max_width_error(shape.width, full_span)
} }
} }
@ -564,16 +589,20 @@ trait ChainFormatter {
parent: &ChainItem, parent: &ChainItem,
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
shape: Shape, shape: Shape,
) -> Option<()>; ) -> Result<(), RewriteError>;
fn child_shape(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<Shape>; fn child_shape(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<Shape>;
fn format_children(&mut self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<()>; fn format_children(
&mut self,
context: &RewriteContext<'_>,
child_shape: Shape,
) -> Result<(), RewriteError>;
fn format_last_child( fn format_last_child(
&mut self, &mut self,
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
shape: Shape, shape: Shape,
child_shape: Shape, child_shape: Shape,
) -> Option<()>; ) -> Result<(), RewriteError>;
fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<String>; fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> RewriteResult;
// Returns `Some` if the chain is only a root, None otherwise. // Returns `Some` if the chain is only a root, None otherwise.
fn pure_root(&mut self) -> Option<String>; fn pure_root(&mut self) -> Option<String>;
} }
@ -616,12 +645,16 @@ impl<'a> ChainFormatterShared<'a> {
} }
} }
fn format_children(&mut self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<()> { fn format_children(
&mut self,
context: &RewriteContext<'_>,
child_shape: Shape,
) -> Result<(), RewriteError> {
for item in &self.children[..self.children.len() - 1] { for item in &self.children[..self.children.len() - 1] {
let rewrite = format_chain_item(item, context, child_shape, self.allow_overflow)?; let rewrite = format_chain_item(item, context, child_shape, self.allow_overflow)?;
self.rewrites.push(rewrite); self.rewrites.push(rewrite);
} }
Some(()) Ok(())
} }
// Rewrite the last child. The last child of a chain requires special treatment. We need to // Rewrite the last child. The last child of a chain requires special treatment. We need to
@ -662,8 +695,8 @@ impl<'a> ChainFormatterShared<'a> {
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
shape: Shape, shape: Shape,
child_shape: Shape, child_shape: Shape,
) -> Option<()> { ) -> Result<(), RewriteError> {
let last = self.children.last()?; let last = self.children.last().unknown_error()?;
let extendable = may_extend && last_line_extendable(&self.rewrites[0]); let extendable = may_extend && last_line_extendable(&self.rewrites[0]);
let prev_last_line_width = last_line_width(&self.rewrites[0]); let prev_last_line_width = last_line_width(&self.rewrites[0]);
@ -687,11 +720,17 @@ impl<'a> ChainFormatterShared<'a> {
&& self.rewrites.iter().all(|s| !s.contains('\n')) && self.rewrites.iter().all(|s| !s.contains('\n'))
&& one_line_budget > 0; && one_line_budget > 0;
let last_shape = if all_in_one_line { let last_shape = if all_in_one_line {
shape.sub_width(last.tries)? shape
.sub_width(last.tries)
.max_width_error(shape.width, last.span)?
} else if extendable { } else if extendable {
child_shape.sub_width(last.tries)? child_shape
.sub_width(last.tries)
.max_width_error(child_shape.width, last.span)?
} else { } else {
child_shape.sub_width(shape.rhs_overhead(context.config) + last.tries)? child_shape
.sub_width(shape.rhs_overhead(context.config) + last.tries)
.max_width_error(child_shape.width, last.span)?
}; };
let mut last_subexpr_str = None; let mut last_subexpr_str = None;
@ -707,7 +746,7 @@ impl<'a> ChainFormatterShared<'a> {
}; };
if let Some(one_line_shape) = one_line_shape { if let Some(one_line_shape) = one_line_shape {
if let Some(rw) = last.rewrite(context, one_line_shape) { if let Ok(rw) = last.rewrite_result(context, one_line_shape) {
// We allow overflowing here only if both of the following conditions match: // We allow overflowing here only if both of the following conditions match:
// 1. The entire chain fits in a single line except the last child. // 1. The entire chain fits in a single line except the last child.
// 2. `last_child_str.lines().count() >= 5`. // 2. `last_child_str.lines().count() >= 5`.
@ -722,17 +761,18 @@ impl<'a> ChainFormatterShared<'a> {
// last child on its own line, and compare two rewrites to choose which is // last child on its own line, and compare two rewrites to choose which is
// better. // better.
let last_shape = child_shape let last_shape = child_shape
.sub_width(shape.rhs_overhead(context.config) + last.tries)?; .sub_width(shape.rhs_overhead(context.config) + last.tries)
match last.rewrite(context, last_shape) { .max_width_error(child_shape.width, last.span)?;
Some(ref new_rw) if !could_fit_single_line => { match last.rewrite_result(context, last_shape) {
Ok(ref new_rw) if !could_fit_single_line => {
last_subexpr_str = Some(new_rw.clone()); last_subexpr_str = Some(new_rw.clone());
} }
Some(ref new_rw) if new_rw.lines().count() >= line_count => { Ok(ref new_rw) if new_rw.lines().count() >= line_count => {
last_subexpr_str = Some(rw); last_subexpr_str = Some(rw);
self.fits_single_line = could_fit_single_line && all_in_one_line; self.fits_single_line = could_fit_single_line && all_in_one_line;
} }
new_rw @ Some(..) => { Ok(new_rw) => {
last_subexpr_str = new_rw; last_subexpr_str = Some(new_rw);
} }
_ => { _ => {
last_subexpr_str = Some(rw); last_subexpr_str = Some(rw);
@ -747,22 +787,28 @@ impl<'a> ChainFormatterShared<'a> {
let last_shape = if context.use_block_indent() { let last_shape = if context.use_block_indent() {
last_shape last_shape
} else { } else {
child_shape.sub_width(shape.rhs_overhead(context.config) + last.tries)? child_shape
.sub_width(shape.rhs_overhead(context.config) + last.tries)
.max_width_error(child_shape.width, last.span)?
}; };
last_subexpr_str = last_subexpr_str.or_else(|| last.rewrite(context, last_shape)); let last_subexpr_str =
self.rewrites.push(last_subexpr_str?); last_subexpr_str.unwrap_or(last.rewrite_result(context, last_shape)?);
Some(()) self.rewrites.push(last_subexpr_str);
Ok(())
} }
fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<String> { fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> RewriteResult {
let connector = if self.fits_single_line { let connector = if self.fits_single_line {
// Yay, we can put everything on one line. // Yay, we can put everything on one line.
Cow::from("") Cow::from("")
} else { } else {
// Use new lines. // Use new lines.
if context.force_one_line_chain.get() { if context.force_one_line_chain.get() {
return None; return Err(RewriteError::ExceedsMaxWidth {
configured_width: child_shape.width,
span: self.children.last().unknown_error()?.span,
});
} }
child_shape.to_string_with_newline(context.config) child_shape.to_string_with_newline(context.config)
}; };
@ -781,7 +827,7 @@ impl<'a> ChainFormatterShared<'a> {
result.push_str(rewrite); result.push_str(rewrite);
} }
Some(result) Ok(result)
} }
} }
@ -806,8 +852,8 @@ impl<'a> ChainFormatter for ChainFormatterBlock<'a> {
parent: &ChainItem, parent: &ChainItem,
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
shape: Shape, shape: Shape,
) -> Option<()> { ) -> Result<(), RewriteError> {
let mut root_rewrite: String = parent.rewrite(context, shape)?; let mut root_rewrite: String = parent.rewrite_result(context, shape)?;
let mut root_ends_with_block = parent.kind.is_block_like(context, &root_rewrite); let mut root_ends_with_block = parent.kind.is_block_like(context, &root_rewrite);
let tab_width = context.config.tab_spaces().saturating_sub(shape.offset); let tab_width = context.config.tab_spaces().saturating_sub(shape.offset);
@ -817,10 +863,12 @@ impl<'a> ChainFormatter for ChainFormatterBlock<'a> {
if let ChainItemKind::Comment(..) = item.kind { if let ChainItemKind::Comment(..) = item.kind {
break; break;
} }
let shape = shape.offset_left(root_rewrite.len())?; let shape = shape
match &item.rewrite(context, shape) { .offset_left(root_rewrite.len())
Some(rewrite) => root_rewrite.push_str(rewrite), .max_width_error(shape.width, item.span)?;
None => break, match &item.rewrite_result(context, shape) {
Ok(rewrite) => root_rewrite.push_str(rewrite),
Err(_) => break,
} }
root_ends_with_block = last_line_extendable(&root_rewrite); root_ends_with_block = last_line_extendable(&root_rewrite);
@ -832,7 +880,7 @@ impl<'a> ChainFormatter for ChainFormatterBlock<'a> {
} }
self.shared.rewrites.push(root_rewrite); self.shared.rewrites.push(root_rewrite);
self.root_ends_with_block = root_ends_with_block; self.root_ends_with_block = root_ends_with_block;
Some(()) Ok(())
} }
fn child_shape(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<Shape> { fn child_shape(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<Shape> {
@ -840,7 +888,11 @@ impl<'a> ChainFormatter for ChainFormatterBlock<'a> {
Some(get_block_child_shape(block_end, context, shape)) Some(get_block_child_shape(block_end, context, shape))
} }
fn format_children(&mut self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<()> { fn format_children(
&mut self,
context: &RewriteContext<'_>,
child_shape: Shape,
) -> Result<(), RewriteError> {
self.shared.format_children(context, child_shape) self.shared.format_children(context, child_shape)
} }
@ -849,12 +901,12 @@ impl<'a> ChainFormatter for ChainFormatterBlock<'a> {
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
shape: Shape, shape: Shape,
child_shape: Shape, child_shape: Shape,
) -> Option<()> { ) -> Result<(), RewriteError> {
self.shared self.shared
.format_last_child(true, context, shape, child_shape) .format_last_child(true, context, shape, child_shape)
} }
fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<String> { fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> RewriteResult {
self.shared.join_rewrites(context, child_shape) self.shared.join_rewrites(context, child_shape)
} }
@ -885,9 +937,9 @@ impl<'a> ChainFormatter for ChainFormatterVisual<'a> {
parent: &ChainItem, parent: &ChainItem,
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
shape: Shape, shape: Shape,
) -> Option<()> { ) -> Result<(), RewriteError> {
let parent_shape = shape.visual_indent(0); let parent_shape = shape.visual_indent(0);
let mut root_rewrite = parent.rewrite(context, parent_shape)?; let mut root_rewrite = parent.rewrite_result(context, parent_shape)?;
let multiline = root_rewrite.contains('\n'); let multiline = root_rewrite.contains('\n');
self.offset = if multiline { self.offset = if multiline {
last_line_width(&root_rewrite).saturating_sub(shape.used_width()) last_line_width(&root_rewrite).saturating_sub(shape.used_width())
@ -899,18 +951,19 @@ impl<'a> ChainFormatter for ChainFormatterVisual<'a> {
let item = &self.shared.children[0]; let item = &self.shared.children[0];
if let ChainItemKind::Comment(..) = item.kind { if let ChainItemKind::Comment(..) = item.kind {
self.shared.rewrites.push(root_rewrite); self.shared.rewrites.push(root_rewrite);
return Some(()); return Ok(());
} }
let child_shape = parent_shape let child_shape = parent_shape
.visual_indent(self.offset) .visual_indent(self.offset)
.sub_width(self.offset)?; .sub_width(self.offset)
let rewrite = item.rewrite(context, child_shape)?; .max_width_error(parent_shape.width, item.span)?;
let rewrite = item.rewrite_result(context, child_shape)?;
if filtered_str_fits(&rewrite, context.config.max_width(), shape) { if filtered_str_fits(&rewrite, context.config.max_width(), shape) {
root_rewrite.push_str(&rewrite); root_rewrite.push_str(&rewrite);
} else { } else {
// We couldn't fit in at the visual indent, try the last // We couldn't fit in at the visual indent, try the last
// indent. // indent.
let rewrite = item.rewrite(context, parent_shape)?; let rewrite = item.rewrite_result(context, parent_shape)?;
root_rewrite.push_str(&rewrite); root_rewrite.push_str(&rewrite);
self.offset = 0; self.offset = 0;
} }
@ -919,7 +972,7 @@ impl<'a> ChainFormatter for ChainFormatterVisual<'a> {
} }
self.shared.rewrites.push(root_rewrite); self.shared.rewrites.push(root_rewrite);
Some(()) Ok(())
} }
fn child_shape(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<Shape> { fn child_shape(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<Shape> {
@ -932,7 +985,11 @@ impl<'a> ChainFormatter for ChainFormatterVisual<'a> {
) )
} }
fn format_children(&mut self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<()> { fn format_children(
&mut self,
context: &RewriteContext<'_>,
child_shape: Shape,
) -> Result<(), RewriteError> {
self.shared.format_children(context, child_shape) self.shared.format_children(context, child_shape)
} }
@ -941,12 +998,12 @@ impl<'a> ChainFormatter for ChainFormatterVisual<'a> {
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
shape: Shape, shape: Shape,
child_shape: Shape, child_shape: Shape,
) -> Option<()> { ) -> Result<(), RewriteError> {
self.shared self.shared
.format_last_child(false, context, shape, child_shape) .format_last_child(false, context, shape, child_shape)
} }
fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<String> { fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> RewriteResult {
self.shared.join_rewrites(context, child_shape) self.shared.join_rewrites(context, child_shape)
} }

View File

@ -4,17 +4,17 @@ use thin_vec::thin_vec;
use tracing::debug; use tracing::debug;
use crate::attr::get_attrs_from_stmt; use crate::attr::get_attrs_from_stmt;
use crate::config::StyleEdition;
use crate::config::lists::*; use crate::config::lists::*;
use crate::config::Version;
use crate::expr::{block_contains_comment, is_simple_block, is_unsafe_block, rewrite_cond}; use crate::expr::{block_contains_comment, is_simple_block, is_unsafe_block, rewrite_cond};
use crate::items::{span_hi_for_param, span_lo_for_param}; use crate::items::{span_hi_for_param, span_lo_for_param};
use crate::lists::{definitive_tactic, itemize_list, write_list, ListFormatting, Separator}; use crate::lists::{ListFormatting, Separator, definitive_tactic, itemize_list, write_list};
use crate::overflow::OverflowableItem; use crate::overflow::OverflowableItem;
use crate::rewrite::{Rewrite, RewriteContext}; use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult};
use crate::shape::Shape; use crate::shape::Shape;
use crate::source_map::SpanUtils; use crate::source_map::SpanUtils;
use crate::types::rewrite_bound_params; use crate::types::rewrite_bound_params;
use crate::utils::{last_line_width, left_most_sub_expr, stmt_expr, NodeIdExt}; use crate::utils::{NodeIdExt, last_line_width, left_most_sub_expr, stmt_expr};
// This module is pretty messy because of the rules around closures and blocks: // This module is pretty messy because of the rules around closures and blocks:
// FIXME - the below is probably no longer true in full. // FIXME - the below is probably no longer true in full.
@ -37,7 +37,7 @@ pub(crate) fn rewrite_closure(
span: Span, span: Span,
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
shape: Shape, shape: Shape,
) -> Option<String> { ) -> RewriteResult {
debug!("rewrite_closure {:?}", body); debug!("rewrite_closure {:?}", body);
let (prefix, extra_offset) = rewrite_closure_fn_decl( let (prefix, extra_offset) = rewrite_closure_fn_decl(
@ -53,13 +53,15 @@ pub(crate) fn rewrite_closure(
shape, shape,
)?; )?;
// 1 = space between `|...|` and body. // 1 = space between `|...|` and body.
let body_shape = shape.offset_left(extra_offset)?; let body_shape = shape
.offset_left(extra_offset)
.max_width_error(shape.width, span)?;
if let ast::ExprKind::Block(ref block, _) = body.kind { if let ast::ExprKind::Block(ref block, _) = body.kind {
// The body of the closure is an empty block. // The body of the closure is an empty block.
if block.stmts.is_empty() && !block_contains_comment(context, block) { if block.stmts.is_empty() && !block_contains_comment(context, block) {
return body return body
.rewrite(context, shape) .rewrite_result(context, shape)
.map(|s| format!("{} {}", prefix, s)); .map(|s| format!("{} {}", prefix, s));
} }
@ -67,15 +69,15 @@ pub(crate) fn rewrite_closure(
ast::FnRetTy::Default(_) if !context.inside_macro() => { ast::FnRetTy::Default(_) if !context.inside_macro() => {
try_rewrite_without_block(body, &prefix, context, shape, body_shape) try_rewrite_without_block(body, &prefix, context, shape, body_shape)
} }
_ => None, _ => Err(RewriteError::Unknown),
}; };
result.or_else(|| { result.or_else(|_| {
// Either we require a block, or tried without and failed. // Either we require a block, or tried without and failed.
rewrite_closure_block(block, &prefix, context, body_shape) rewrite_closure_block(block, &prefix, context, body_shape)
}) })
} else { } else {
rewrite_closure_expr(body, &prefix, context, body_shape).or_else(|| { rewrite_closure_expr(body, &prefix, context, body_shape).or_else(|_| {
// The closure originally had a non-block expression, but we can't fit on // The closure originally had a non-block expression, but we can't fit on
// one line, so we'll insert a block. // one line, so we'll insert a block.
rewrite_closure_with_block(body, &prefix, context, body_shape) rewrite_closure_with_block(body, &prefix, context, body_shape)
@ -89,7 +91,7 @@ fn try_rewrite_without_block(
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
shape: Shape, shape: Shape,
body_shape: Shape, body_shape: Shape,
) -> Option<String> { ) -> RewriteResult {
let expr = get_inner_expr(expr, prefix, context); let expr = get_inner_expr(expr, prefix, context);
if is_block_closure_forced(context, expr) { if is_block_closure_forced(context, expr) {
@ -153,11 +155,11 @@ fn rewrite_closure_with_block(
prefix: &str, prefix: &str,
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
shape: Shape, shape: Shape,
) -> Option<String> { ) -> RewriteResult {
let left_most = left_most_sub_expr(body); let left_most = left_most_sub_expr(body);
let veto_block = veto_block(body) && !expr_requires_semi_to_be_stmt(left_most); let veto_block = veto_block(body) && !expr_requires_semi_to_be_stmt(left_most);
if veto_block { if veto_block {
return None; return Err(RewriteError::Unknown);
} }
let block = ast::Block { let block = ast::Block {
@ -185,7 +187,7 @@ fn rewrite_closure_with_block(
shape, shape,
false, false,
)?; )?;
Some(format!("{prefix} {block}")) Ok(format!("{prefix} {block}"))
} }
// Rewrite closure with a single expression without wrapping its body with block. // Rewrite closure with a single expression without wrapping its body with block.
@ -194,7 +196,7 @@ fn rewrite_closure_expr(
prefix: &str, prefix: &str,
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
shape: Shape, shape: Shape,
) -> Option<String> { ) -> RewriteResult {
fn allow_multi_line(expr: &ast::Expr) -> bool { fn allow_multi_line(expr: &ast::Expr) -> bool {
match expr.kind { match expr.kind {
ast::ExprKind::Match(..) ast::ExprKind::Match(..)
@ -217,12 +219,12 @@ fn rewrite_closure_expr(
// unless it is a block-like expression or we are inside macro call. // unless it is a block-like expression or we are inside macro call.
let veto_multiline = (!allow_multi_line(expr) && !context.inside_macro()) let veto_multiline = (!allow_multi_line(expr) && !context.inside_macro())
|| context.config.force_multiline_blocks(); || context.config.force_multiline_blocks();
expr.rewrite(context, shape) expr.rewrite_result(context, shape)
.and_then(|rw| { .and_then(|rw| {
if veto_multiline && rw.contains('\n') { if veto_multiline && rw.contains('\n') {
None Err(RewriteError::Unknown)
} else { } else {
Some(rw) Ok(rw)
} }
}) })
.map(|rw| format!("{} {}", prefix, rw)) .map(|rw| format!("{} {}", prefix, rw))
@ -234,8 +236,12 @@ fn rewrite_closure_block(
prefix: &str, prefix: &str,
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
shape: Shape, shape: Shape,
) -> Option<String> { ) -> RewriteResult {
Some(format!("{} {}", prefix, block.rewrite(context, shape)?)) Ok(format!(
"{} {}",
prefix,
block.rewrite_result(context, shape)?
))
} }
// Return type is (prefix, extra_offset) // Return type is (prefix, extra_offset)
@ -250,13 +256,14 @@ fn rewrite_closure_fn_decl(
span: Span, span: Span,
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
shape: Shape, shape: Shape,
) -> Option<(String, usize)> { ) -> Result<(String, usize), RewriteError> {
let binder = match binder { let binder = match binder {
ast::ClosureBinder::For { generic_params, .. } if generic_params.is_empty() => { ast::ClosureBinder::For { generic_params, .. } if generic_params.is_empty() => {
"for<> ".to_owned() "for<> ".to_owned()
} }
ast::ClosureBinder::For { generic_params, .. } => { ast::ClosureBinder::For { generic_params, .. } => {
let lifetime_str = rewrite_bound_params(context, shape, generic_params)?; let lifetime_str =
rewrite_bound_params(context, shape, generic_params).unknown_error()?;
format!("for<{lifetime_str}> ") format!("for<{lifetime_str}> ")
} }
ast::ClosureBinder::NotPresent => "".to_owned(), ast::ClosureBinder::NotPresent => "".to_owned(),
@ -287,13 +294,17 @@ fn rewrite_closure_fn_decl(
// 4 = "|| {".len(), which is overconservative when the closure consists of // 4 = "|| {".len(), which is overconservative when the closure consists of
// a single expression. // a single expression.
let nested_shape = shape let nested_shape = shape
.shrink_left(binder.len() + const_.len() + immovable.len() + coro.len() + mover.len())? .shrink_left(binder.len() + const_.len() + immovable.len() + coro.len() + mover.len())
.sub_width(4)?; .and_then(|shape| shape.sub_width(4))
.max_width_error(shape.width, span)?;
// 1 = | // 1 = |
let param_offset = nested_shape.indent + 1; let param_offset = nested_shape.indent + 1;
let param_shape = nested_shape.offset_left(1)?.visual_indent(0); let param_shape = nested_shape
let ret_str = fn_decl.output.rewrite(context, param_shape)?; .offset_left(1)
.max_width_error(nested_shape.width, span)?
.visual_indent(0);
let ret_str = fn_decl.output.rewrite_result(context, param_shape)?;
let param_items = itemize_list( let param_items = itemize_list(
context.snippet_provider, context.snippet_provider,
@ -302,7 +313,7 @@ fn rewrite_closure_fn_decl(
",", ",",
|param| span_lo_for_param(param), |param| span_lo_for_param(param),
|param| span_hi_for_param(context, param), |param| span_hi_for_param(context, param),
|param| param.rewrite(context, param_shape), |param| param.rewrite_result(context, param_shape),
context.snippet_provider.span_after(span, "|"), context.snippet_provider.span_after(span, "|"),
body.span.lo(), body.span.lo(),
false, false,
@ -317,7 +328,9 @@ fn rewrite_closure_fn_decl(
horizontal_budget, horizontal_budget,
); );
let param_shape = match tactic { let param_shape = match tactic {
DefinitiveListTactic::Horizontal => param_shape.sub_width(ret_str.len() + 1)?, DefinitiveListTactic::Horizontal => param_shape
.sub_width(ret_str.len() + 1)
.max_width_error(param_shape.width, span)?,
_ => param_shape, _ => param_shape,
}; };
@ -339,7 +352,7 @@ fn rewrite_closure_fn_decl(
// 1 = space between `|...|` and body. // 1 = space between `|...|` and body.
let extra_offset = last_line_width(&prefix) + 1; let extra_offset = last_line_width(&prefix) + 1;
Some((prefix, extra_offset)) Ok((prefix, extra_offset))
} }
// Rewriting closure which is placed at the end of the function call's arg. // Rewriting closure which is placed at the end of the function call's arg.
@ -348,7 +361,7 @@ pub(crate) fn rewrite_last_closure(
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
expr: &ast::Expr, expr: &ast::Expr,
shape: Shape, shape: Shape,
) -> Option<String> { ) -> RewriteResult {
if let ast::ExprKind::Closure(ref closure) = expr.kind { if let ast::ExprKind::Closure(ref closure) = expr.kind {
let ast::Closure { let ast::Closure {
ref binder, ref binder,
@ -385,10 +398,12 @@ pub(crate) fn rewrite_last_closure(
)?; )?;
// If the closure goes multi line before its body, do not overflow the closure. // If the closure goes multi line before its body, do not overflow the closure.
if prefix.contains('\n') { if prefix.contains('\n') {
return None; return Err(RewriteError::Unknown);
} }
let body_shape = shape.offset_left(extra_offset)?; let body_shape = shape
.offset_left(extra_offset)
.max_width_error(shape.width, expr.span)?;
// We force to use block for the body of the closure for certain kinds of expressions. // We force to use block for the body of the closure for certain kinds of expressions.
if is_block_closure_forced(context, body) { if is_block_closure_forced(context, body) {
@ -400,7 +415,7 @@ pub(crate) fn rewrite_last_closure(
// closure. However, if the closure has a return type, then we must // closure. However, if the closure has a return type, then we must
// keep the blocks. // keep the blocks.
match rewrite_closure_expr(body, &prefix, context, shape) { match rewrite_closure_expr(body, &prefix, context, shape) {
Some(single_line_body_str) Ok(single_line_body_str)
if !single_line_body_str.contains('\n') => if !single_line_body_str.contains('\n') =>
{ {
single_line_body_str single_line_body_str
@ -424,9 +439,9 @@ pub(crate) fn rewrite_last_closure(
} }
// Seems fine, just format the closure in usual manner. // Seems fine, just format the closure in usual manner.
return expr.rewrite(context, shape); return expr.rewrite_result(context, shape);
} }
None Err(RewriteError::Unknown)
} }
/// Returns `true` if the given vector of arguments has more than one `ast::ExprKind::Closure`. /// Returns `true` if the given vector of arguments has more than one `ast::ExprKind::Closure`.
@ -443,18 +458,18 @@ fn is_block_closure_forced(context: &RewriteContext<'_>, expr: &ast::Expr) -> bo
if context.inside_macro() { if context.inside_macro() {
false false
} else { } else {
is_block_closure_forced_inner(expr, context.config.version()) is_block_closure_forced_inner(expr, context.config.style_edition())
} }
} }
fn is_block_closure_forced_inner(expr: &ast::Expr, version: Version) -> bool { fn is_block_closure_forced_inner(expr: &ast::Expr, style_edition: StyleEdition) -> bool {
match expr.kind { match expr.kind {
ast::ExprKind::If(..) | ast::ExprKind::While(..) | ast::ExprKind::ForLoop { .. } => true, ast::ExprKind::If(..) | ast::ExprKind::While(..) | ast::ExprKind::ForLoop { .. } => true,
ast::ExprKind::Loop(..) if version == Version::Two => true, ast::ExprKind::Loop(..) if style_edition >= StyleEdition::Edition2024 => true,
ast::ExprKind::AddrOf(_, _, ref expr) ast::ExprKind::AddrOf(_, _, ref expr)
| ast::ExprKind::Try(ref expr) | ast::ExprKind::Try(ref expr)
| ast::ExprKind::Unary(_, ref expr) | ast::ExprKind::Unary(_, ref expr)
| ast::ExprKind::Cast(ref expr, _) => is_block_closure_forced_inner(expr, version), | ast::ExprKind::Cast(ref expr, _) => is_block_closure_forced_inner(expr, style_edition),
_ => false, _ => false,
} }
} }

View File

@ -2,14 +2,14 @@
use std::{borrow::Cow, iter}; use std::{borrow::Cow, iter};
use itertools::{multipeek, MultiPeek}; use itertools::{Itertools as _, MultiPeek, multipeek};
use rustc_span::Span; use rustc_span::Span;
use tracing::{debug, trace}; use tracing::{debug, trace};
use crate::config::Config; use crate::config::Config;
use crate::rewrite::RewriteContext; use crate::rewrite::{RewriteContext, RewriteErrorExt, RewriteResult};
use crate::shape::{Indent, Shape}; use crate::shape::{Indent, Shape};
use crate::string::{rewrite_string, StringFormat}; use crate::string::{StringFormat, rewrite_string};
use crate::utils::{ use crate::utils::{
count_newlines, first_line_width, last_line_width, trim_left_preserve_layout, count_newlines, first_line_width, last_line_width, trim_left_preserve_layout,
trimmed_last_line_width, unicode_str_width, trimmed_last_line_width, unicode_str_width,
@ -158,7 +158,7 @@ pub(crate) fn combine_strs_with_missing_comments(
span: Span, span: Span,
shape: Shape, shape: Shape,
allow_extend: bool, allow_extend: bool,
) -> Option<String> { ) -> RewriteResult {
trace!( trace!(
"combine_strs_with_missing_comments `{}` `{}` {:?} {:?}", "combine_strs_with_missing_comments `{}` `{}` {:?} {:?}",
prev_str, next_str, span, shape prev_str, next_str, span, shape
@ -188,7 +188,7 @@ pub(crate) fn combine_strs_with_missing_comments(
result.push_str(&indent.to_string_with_newline(config)) result.push_str(&indent.to_string_with_newline(config))
} }
result.push_str(next_str); result.push_str(next_str);
return Some(result); return Ok(result);
} }
// We have a missing comment between the first expression and the second expression. // We have a missing comment between the first expression and the second expression.
@ -233,10 +233,10 @@ pub(crate) fn combine_strs_with_missing_comments(
result.push_str(&second_sep); result.push_str(&second_sep);
result.push_str(next_str); result.push_str(next_str);
Some(result) Ok(result)
} }
pub(crate) fn rewrite_doc_comment(orig: &str, shape: Shape, config: &Config) -> Option<String> { pub(crate) fn rewrite_doc_comment(orig: &str, shape: Shape, config: &Config) -> RewriteResult {
identify_comment(orig, false, shape, config, true) identify_comment(orig, false, shape, config, true)
} }
@ -245,7 +245,7 @@ pub(crate) fn rewrite_comment(
block_style: bool, block_style: bool,
shape: Shape, shape: Shape,
config: &Config, config: &Config,
) -> Option<String> { ) -> RewriteResult {
identify_comment(orig, block_style, shape, config, false) identify_comment(orig, block_style, shape, config, false)
} }
@ -255,7 +255,7 @@ fn identify_comment(
shape: Shape, shape: Shape,
config: &Config, config: &Config,
is_doc_comment: bool, is_doc_comment: bool,
) -> Option<String> { ) -> RewriteResult {
let style = comment_style(orig, false); let style = comment_style(orig, false);
// Computes the byte length of line taking into account a newline if the line is part of a // Computes the byte length of line taking into account a newline if the line is part of a
@ -347,7 +347,7 @@ fn identify_comment(
let (first_group, rest) = orig.split_at(first_group_ending); let (first_group, rest) = orig.split_at(first_group_ending);
let rewritten_first_group = let rewritten_first_group =
if !config.normalize_comments() && has_bare_lines && style.is_block_comment() { if !config.normalize_comments() && has_bare_lines && style.is_block_comment() {
trim_left_preserve_layout(first_group, shape.indent, config)? trim_left_preserve_layout(first_group, shape.indent, config).unknown_error()?
} else if !config.normalize_comments() } else if !config.normalize_comments()
&& !config.wrap_comments() && !config.wrap_comments()
&& !( && !(
@ -368,7 +368,7 @@ fn identify_comment(
)? )?
}; };
if rest.is_empty() { if rest.is_empty() {
Some(rewritten_first_group) Ok(rewritten_first_group)
} else { } else {
identify_comment( identify_comment(
rest.trim_start(), rest.trim_start(),
@ -534,10 +534,11 @@ impl ItemizedBlock {
/// Returns the block as a string, with each line trimmed at the start. /// Returns the block as a string, with each line trimmed at the start.
fn trimmed_block_as_string(&self) -> String { fn trimmed_block_as_string(&self) -> String {
self.lines self.lines.iter().fold(String::new(), |mut acc, line| {
.iter() acc.push_str(line.trim_start());
.map(|line| format!("{} ", line.trim_start())) acc.push(' ');
.collect::<String>() acc
})
} }
/// Returns the block as a string under its original form. /// Returns the block as a string under its original form.
@ -900,7 +901,7 @@ fn rewrite_comment_inner(
shape: Shape, shape: Shape,
config: &Config, config: &Config,
is_doc_comment: bool, is_doc_comment: bool,
) -> Option<String> { ) -> RewriteResult {
let mut rewriter = CommentRewrite::new(orig, block_style, shape, config); let mut rewriter = CommentRewrite::new(orig, block_style, shape, config);
let line_breaks = count_newlines(orig.trim_end()); let line_breaks = count_newlines(orig.trim_end());
@ -934,7 +935,7 @@ fn rewrite_comment_inner(
} }
} }
Some(rewriter.finish()) Ok(rewriter.finish())
} }
const RUSTFMT_CUSTOM_COMMENT_PREFIX: &str = "//#### "; const RUSTFMT_CUSTOM_COMMENT_PREFIX: &str = "//#### ";
@ -999,7 +1000,7 @@ pub(crate) fn rewrite_missing_comment(
span: Span, span: Span,
shape: Shape, shape: Shape,
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
) -> Option<String> { ) -> RewriteResult {
let missing_snippet = context.snippet(span); let missing_snippet = context.snippet(span);
let trimmed_snippet = missing_snippet.trim(); let trimmed_snippet = missing_snippet.trim();
// check the span starts with a comment // check the span starts with a comment
@ -1007,7 +1008,7 @@ pub(crate) fn rewrite_missing_comment(
if !trimmed_snippet.is_empty() && pos.is_some() { if !trimmed_snippet.is_empty() && pos.is_some() {
rewrite_comment(trimmed_snippet, false, shape, context.config) rewrite_comment(trimmed_snippet, false, shape, context.config)
} else { } else {
Some(String::new()) Ok(String::new())
} }
} }
@ -1019,13 +1020,13 @@ pub(crate) fn recover_missing_comment_in_span(
shape: Shape, shape: Shape,
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
used_width: usize, used_width: usize,
) -> Option<String> { ) -> RewriteResult {
let missing_comment = rewrite_missing_comment(span, shape, context)?; let missing_comment = rewrite_missing_comment(span, shape, context)?;
if missing_comment.is_empty() { if missing_comment.is_empty() {
Some(String::new()) Ok(String::new())
} else { } else {
let missing_snippet = context.snippet(span); let missing_snippet = context.snippet(span);
let pos = missing_snippet.find('/')?; let pos = missing_snippet.find('/').unknown_error()?;
// 1 = ` ` // 1 = ` `
let total_width = missing_comment.len() + used_width + 1; let total_width = missing_comment.len() + used_width + 1;
let force_new_line_before_comment = let force_new_line_before_comment =
@ -1035,7 +1036,7 @@ pub(crate) fn recover_missing_comment_in_span(
} else { } else {
Cow::from(" ") Cow::from(" ")
}; };
Some(format!("{sep}{missing_comment}")) Ok(format!("{sep}{missing_comment}"))
} }
} }
@ -1055,8 +1056,7 @@ fn light_rewrite_comment(
config: &Config, config: &Config,
is_doc_comment: bool, is_doc_comment: bool,
) -> String { ) -> String {
let lines: Vec<&str> = orig orig.lines()
.lines()
.map(|l| { .map(|l| {
// This is basically just l.trim(), but in the case that a line starts // 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 // with `*` we want to leave one space before it, so it aligns with the
@ -1074,8 +1074,7 @@ fn light_rewrite_comment(
// Preserve markdown's double-space line break syntax in doc comment. // Preserve markdown's double-space line break syntax in doc comment.
trim_end_unless_two_whitespaces(left_trimmed, is_doc_comment) trim_end_unless_two_whitespaces(left_trimmed, is_doc_comment)
}) })
.collect(); .join(&format!("\n{}", offset.to_string(config)))
lines.join(&format!("\n{}", offset.to_string(config)))
} }
/// Trims comment characters and possibly a single space from the left of a string. /// Trims comment characters and possibly a single space from the left of a string.
@ -1704,12 +1703,11 @@ impl<'a> Iterator for CommentCodeSlices<'a> {
} }
/// Checks is `new` didn't miss any comment from `span`, if it removed any, return previous text /// 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( pub(crate) fn recover_comment_removed(
new: String, new: String,
span: Span, span: Span,
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
) -> Option<String> { ) -> String {
let snippet = context.snippet(span); let snippet = context.snippet(span);
if snippet != new && changed_comment_content(snippet, &new) { if snippet != new && changed_comment_content(snippet, &new) {
// We missed some comments. Warn and keep the original text. // We missed some comments. Warn and keep the original text.
@ -1723,9 +1721,9 @@ pub(crate) fn recover_comment_removed(
)], )],
); );
} }
Some(snippet.to_owned()) snippet.to_owned()
} else { } else {
Some(new) new
} }
} }

View File

@ -70,15 +70,15 @@ macro_rules! create_config {
// //
// - $i: the ident name of the option // - $i: the ident name of the option
// - $ty: the type of the option value // - $ty: the type of the option value
// - $def: the default value of the option
// - $stb: true if the option is stable // - $stb: true if the option is stable
// - $dstring: description of the option // - $dstring: description of the option
($($i:ident: $ty:ty, $def:expr, $stb:expr, $( $dstring:expr ),+ );+ $(;)*) => ( ($($i:ident: $ty:ty, $stb:expr, $( $dstring:expr ),+ );+ $(;)*) => (
#[cfg(test)] #[cfg(test)]
use std::collections::HashSet; use std::collections::HashSet;
use std::io::Write; use std::io::Write;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use $crate::config::style_edition::StyleEditionDefault;
#[derive(Clone)] #[derive(Clone)]
#[allow(unreachable_pub)] #[allow(unreachable_pub)]
@ -89,7 +89,10 @@ macro_rules! create_config {
// - 1: true if the option was manually initialized // - 1: true if the option was manually initialized
// - 2: the option value // - 2: the option value
// - 3: true if the option is unstable // - 3: true if the option is unstable
$($i: (Cell<bool>, bool, $ty, bool)),+ // - 4: true if the option was set manually from a CLI flag
// FIXME: 4 is probably unnecessary and duplicative
// https://github.com/rust-lang/rustfmt/issues/6252
$($i: (Cell<bool>, bool, <$ty as StyleEditionDefault>::ConfigType, bool, bool)),+
} }
// Just like the Config struct but with each property wrapped // Just like the Config struct but with each property wrapped
@ -100,7 +103,7 @@ macro_rules! create_config {
#[derive(Deserialize, Serialize, Clone)] #[derive(Deserialize, Serialize, Clone)]
#[allow(unreachable_pub)] #[allow(unreachable_pub)]
pub struct PartialConfig { pub struct PartialConfig {
$(pub $i: Option<$ty>),+ $(pub $i: Option<<$ty as StyleEditionDefault>::ConfigType>),+
} }
// Macro hygiene won't allow us to make `set_$i()` methods on Config // Macro hygiene won't allow us to make `set_$i()` methods on Config
@ -114,7 +117,7 @@ macro_rules! create_config {
impl<'a> ConfigSetter<'a> { impl<'a> ConfigSetter<'a> {
$( $(
#[allow(unreachable_pub)] #[allow(unreachable_pub)]
pub fn $i(&mut self, value: $ty) { pub fn $i(&mut self, value: <$ty as StyleEditionDefault>::ConfigType) {
(self.0).$i.2 = value; (self.0).$i.2 = value;
match stringify!($i) { match stringify!($i) {
"max_width" "max_width"
@ -130,6 +133,37 @@ macro_rules! create_config {
"merge_imports" => self.0.set_merge_imports(), "merge_imports" => self.0.set_merge_imports(),
"fn_args_layout" => self.0.set_fn_args_layout(), "fn_args_layout" => self.0.set_fn_args_layout(),
"hide_parse_errors" => self.0.set_hide_parse_errors(), "hide_parse_errors" => self.0.set_hide_parse_errors(),
"version" => self.0.set_version(),
&_ => (),
}
}
)+
}
#[allow(unreachable_pub)]
pub struct CliConfigSetter<'a>(&'a mut Config);
impl<'a> CliConfigSetter<'a> {
$(
#[allow(unreachable_pub)]
pub fn $i(&mut self, value: <$ty as StyleEditionDefault>::ConfigType) {
(self.0).$i.2 = value;
(self.0).$i.4 = true;
match stringify!($i) {
"max_width"
| "use_small_heuristics"
| "fn_call_width"
| "single_line_if_else_max_width"
| "single_line_let_else_max_width"
| "attr_fn_like_width"
| "struct_lit_width"
| "struct_variant_width"
| "array_width"
| "chain_width" => self.0.set_heuristics(),
"merge_imports" => self.0.set_merge_imports(),
"fn_args_layout" => self.0.set_fn_args_layout(),
"hide_parse_errors" => self.0.set_hide_parse_errors(),
"version" => self.0.set_version(),
&_ => (), &_ => (),
} }
} }
@ -150,25 +184,66 @@ macro_rules! create_config {
)+ )+
} }
// Query each option, returns true if the user set the option via a CLI flag,
// false if a default was used.
#[allow(unreachable_pub)]
pub struct CliConfigWasSet<'a>(&'a Config);
impl<'a> CliConfigWasSet<'a> {
$(
#[allow(unreachable_pub)]
pub fn $i(&self) -> bool {
(self.0).$i.4
}
)+
}
impl Config { impl Config {
$( $(
#[allow(unreachable_pub)] #[allow(unreachable_pub)]
pub fn $i(&self) -> $ty { pub fn $i(&self) -> <$ty as StyleEditionDefault>::ConfigType {
self.$i.0.set(true); self.$i.0.set(true);
self.$i.2.clone() self.$i.2.clone()
} }
)+ )+
#[allow(unreachable_pub)]
pub(super) fn default_with_style_edition(style_edition: StyleEdition) -> Config {
Config {
$(
$i: (
Cell::new(false),
false,
<$ty as StyleEditionDefault>::style_edition_default(
style_edition
),
$stb,
false,
),
)+
}
}
#[allow(unreachable_pub)] #[allow(unreachable_pub)]
pub fn set(&mut self) -> ConfigSetter<'_> { pub fn set(&mut self) -> ConfigSetter<'_> {
ConfigSetter(self) ConfigSetter(self)
} }
#[allow(unreachable_pub)]
pub fn set_cli(&mut self) -> CliConfigSetter<'_> {
CliConfigSetter(self)
}
#[allow(unreachable_pub)] #[allow(unreachable_pub)]
pub fn was_set(&self) -> ConfigWasSet<'_> { pub fn was_set(&self) -> ConfigWasSet<'_> {
ConfigWasSet(self) ConfigWasSet(self)
} }
#[allow(unreachable_pub)]
pub fn was_set_cli(&self) -> CliConfigWasSet<'_> {
CliConfigWasSet(self)
}
fn fill_from_parsed_config(mut self, parsed: PartialConfig, dir: &Path) -> Config { fn fill_from_parsed_config(mut self, parsed: PartialConfig, dir: &Path) -> Config {
$( $(
if let Some(option_value) = parsed.$i { if let Some(option_value) = parsed.$i {
@ -186,6 +261,7 @@ macro_rules! create_config {
self.set_merge_imports(); self.set_merge_imports();
self.set_fn_args_layout(); self.set_fn_args_layout();
self.set_hide_parse_errors(); self.set_hide_parse_errors();
self.set_version();
self self
} }
@ -212,7 +288,9 @@ macro_rules! create_config {
pub fn is_valid_key_val(key: &str, val: &str) -> bool { pub fn is_valid_key_val(key: &str, val: &str) -> bool {
match key { match key {
$( $(
stringify!($i) => val.parse::<$ty>().is_ok(), stringify!($i) => {
val.parse::<<$ty as StyleEditionDefault>::ConfigType>().is_ok()
}
)+ )+
_ => false, _ => false,
} }
@ -246,11 +324,15 @@ macro_rules! create_config {
match key { match key {
$( $(
stringify!($i) => { stringify!($i) => {
let option_value = val.parse::<$ty>() let value = val.parse::<<$ty as StyleEditionDefault>::ConfigType>()
.expect(&format!("Failed to parse override for {} (\"{}\") as a {}", .expect(
stringify!($i), &format!(
val, "Failed to parse override for {} (\"{}\") as a {}",
stringify!($ty))); stringify!($i),
val,
stringify!(<$ty as StyleEditionDefault>::ConfigType)
)
);
// Users are currently allowed to set unstable // Users are currently allowed to set unstable
// options/variants via the `--config` options override. // options/variants via the `--config` options override.
@ -261,7 +343,7 @@ macro_rules! create_config {
// For now, do not validate whether the option or value is stable, // For now, do not validate whether the option or value is stable,
// just always set it. // just always set it.
self.$i.1 = true; self.$i.1 = true;
self.$i.2 = option_value; self.$i.2 = value;
} }
)+ )+
_ => panic!("Unknown config key in override: {}", key) _ => panic!("Unknown config key in override: {}", key)
@ -281,6 +363,7 @@ macro_rules! create_config {
"merge_imports" => self.set_merge_imports(), "merge_imports" => self.set_merge_imports(),
"fn_args_layout" => self.set_fn_args_layout(), "fn_args_layout" => self.set_fn_args_layout(),
"hide_parse_errors" => self.set_hide_parse_errors(), "hide_parse_errors" => self.set_hide_parse_errors(),
"version" => self.set_version(),
&_ => (), &_ => (),
} }
} }
@ -301,6 +384,7 @@ macro_rules! create_config {
#[allow(unreachable_pub)] #[allow(unreachable_pub)]
pub fn print_docs(out: &mut dyn Write, include_unstable: bool) { pub fn print_docs(out: &mut dyn Write, include_unstable: bool) {
let style_edition = StyleEdition::Edition2015;
use std::cmp; use std::cmp;
let max = 0; let max = 0;
$( let max = cmp::max(max, stringify!($i).len()+1); )+ $( let max = cmp::max(max, stringify!($i).len()+1); )+
@ -317,14 +401,17 @@ macro_rules! create_config {
} }
name_out.push_str(name_raw); name_out.push_str(name_raw);
name_out.push(' '); name_out.push(' ');
let mut default_str = format!("{}", $def); let default_value = <$ty as StyleEditionDefault>::style_edition_default(
style_edition
);
let mut default_str = format!("{}", default_value);
if default_str.is_empty() { if default_str.is_empty() {
default_str = String::from("\"\""); default_str = String::from("\"\"");
} }
writeln!(out, writeln!(out,
"{}{} Default: {}{}", "{}{} Default: {}{}",
name_out, name_out,
<$ty>::doc_hint(), <<$ty as StyleEditionDefault>::ConfigType>::doc_hint(),
default_str, default_str,
if !$stb { " (unstable)" } else { "" }).unwrap(); if !$stb { " (unstable)" } else { "" }).unwrap();
$( $(
@ -477,12 +564,36 @@ macro_rules! create_config {
} }
} }
fn set_version(&mut self) {
if !self.was_set().version() {
return;
}
eprintln!(
"Warning: the `version` option is deprecated. \
Use `style_edition` instead."
);
if self.was_set().style_edition() || self.was_set_cli().style_edition() {
eprintln!(
"Warning: the deprecated `version` option was \
used in conjunction with the `style_edition` \
option which takes precedence. \
The value of the `version` option will be ignored."
);
}
}
#[allow(unreachable_pub)] #[allow(unreachable_pub)]
/// Returns `true` if the config key was explicitly set and is the default value. /// Returns `true` if the config key was explicitly set and is the default value.
pub fn is_default(&self, key: &str) -> bool { pub fn is_default(&self, key: &str) -> bool {
let style_edition = StyleEdition::Edition2015;
$( $(
let default_value = <$ty as StyleEditionDefault>::style_edition_default(
style_edition
);
if let stringify!($i) = key { if let stringify!($i) = key {
return self.$i.1 && self.$i.2 == $def; return self.$i.1 && self.$i.2 == default_value;
} }
)+ )+
false false
@ -492,11 +603,7 @@ macro_rules! create_config {
// Template for the default configuration // Template for the default configuration
impl Default for Config { impl Default for Config {
fn default() -> Config { fn default() -> Config {
Config { Config::default_with_style_edition(StyleEdition::Edition2015)
$(
$i: (Cell::new(false), false, $def, $stb),
)+
}
} }
} }
) )

View File

@ -7,7 +7,7 @@ use std::{cmp, fmt, iter, str};
use rustc_data_structures::sync::Lrc; use rustc_data_structures::sync::Lrc;
use rustc_span::SourceFile; use rustc_span::SourceFile;
use serde::{ser, Deserialize, Deserializer, Serialize, Serializer}; use serde::{Deserialize, Deserializer, Serialize, Serializer, ser};
use serde_json as json; use serde_json as json;
use thiserror::Error; use thiserror::Error;
@ -38,7 +38,7 @@ impl From<rustc_span::FileName> for FileName {
impl fmt::Display for FileName { impl fmt::Display for FileName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
FileName::Real(p) => write!(f, "{}", p.to_str().unwrap()), FileName::Real(p) => write!(f, "{}", p.display()),
FileName::Stdin => write!(f, "<stdin>"), FileName::Stdin => write!(f, "<stdin>"),
} }
} }
@ -201,7 +201,7 @@ impl FileLines {
} }
/// Returns `true` if this `FileLines` contains all lines in all files. /// Returns `true` if this `FileLines` contains all lines in all files.
pub(crate) fn is_all(&self) -> bool { pub fn is_all(&self) -> bool {
self.0.is_none() self.0.is_none()
} }

View File

@ -10,9 +10,7 @@ use crate::config::config_type::ConfigType;
#[allow(unreachable_pub)] #[allow(unreachable_pub)]
pub use crate::config::file_lines::{FileLines, FileName, Range}; pub use crate::config::file_lines::{FileLines, FileName, Range};
#[allow(unreachable_pub)] #[allow(unreachable_pub)]
pub use crate::config::lists::*; pub use crate::config::macro_names::MacroSelector;
#[allow(unreachable_pub)]
pub use crate::config::macro_names::{MacroSelector, MacroSelectors};
#[allow(unreachable_pub)] #[allow(unreachable_pub)]
pub use crate::config::options::*; pub use crate::config::options::*;
@ -34,161 +32,169 @@ pub(crate) mod style_edition;
// `name: value type, default value, is stable, description;` // `name: value type, default value, is stable, description;`
create_config! { create_config! {
// Fundamental stuff // Fundamental stuff
max_width: usize, 100, true, "Maximum width of each line"; max_width: MaxWidth, true, "Maximum width of each line";
hard_tabs: bool, false, true, "Use tab characters for indentation, spaces for alignment"; hard_tabs: HardTabs, true, "Use tab characters for indentation, spaces for alignment";
tab_spaces: usize, 4, true, "Number of spaces per tab"; tab_spaces: TabSpaces, true, "Number of spaces per tab";
newline_style: NewlineStyle, NewlineStyle::Auto, true, "Unix or Windows line endings"; newline_style: NewlineStyleConfig, true, "Unix or Windows line endings";
indent_style: IndentStyle, IndentStyle::Block, false, "How do we indent expressions or items"; indent_style: IndentStyleConfig, false, "How do we indent expressions or items";
// Width Heuristics // Width Heuristics
use_small_heuristics: Heuristics, Heuristics::Default, true, "Whether to use different \ use_small_heuristics: UseSmallHeuristics, true, "Whether to use different \
formatting for items and expressions if they satisfy a heuristic notion of 'small'"; formatting for items and expressions if they satisfy a heuristic notion of 'small'";
width_heuristics: WidthHeuristics, WidthHeuristics::scaled(100), false, width_heuristics: WidthHeuristicsConfig, false, "'small' heuristic values";
"'small' heuristic values"; fn_call_width: FnCallWidth, true, "Maximum width of the args of a function call before \
fn_call_width: usize, 60, true, "Maximum width of the args of a function call before \
falling back to vertical formatting."; falling back to vertical formatting.";
attr_fn_like_width: usize, 70, true, "Maximum width of the args of a function-like \ attr_fn_like_width: AttrFnLikeWidth, true, "Maximum width of the args of a function-like \
attributes before falling back to vertical formatting."; attributes before falling back to vertical formatting.";
struct_lit_width: usize, 18, true, "Maximum width in the body of a struct lit before \ struct_lit_width: StructLitWidth, true, "Maximum width in the body of a struct lit before \
falling back to vertical formatting."; falling back to vertical formatting.";
struct_variant_width: usize, 35, true, "Maximum width in the body of a struct variant before \ struct_variant_width: StructVariantWidth, true, "Maximum width in the body of a struct variant \
falling back to vertical formatting."; before falling back to vertical formatting.";
array_width: usize, 60, true, "Maximum width of an array literal before falling \ array_width: ArrayWidth, true, "Maximum width of an array literal before falling \
back to vertical formatting."; back to vertical formatting.";
chain_width: usize, 60, true, "Maximum length of a chain to fit on a single line."; chain_width: ChainWidth, 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 \ single_line_if_else_max_width: SingleLineIfElseMaxWidth, true, "Maximum line length for single \
expressions. A value of zero means always break if-else expressions."; line if-else expressions. A value of zero means always break if-else expressions.";
single_line_let_else_max_width: usize, 50, true, "Maximum line length for single line \ single_line_let_else_max_width: SingleLineLetElseMaxWidth, true, "Maximum line length for \
let-else statements. A value of zero means always format the divergent `else` block \ single line let-else statements. A value of zero means always format the divergent `else` \
over multiple lines."; block over multiple lines.";
// Comments. macros, and strings // Comments. macros, and strings
wrap_comments: bool, false, false, "Break comments to fit on the line"; wrap_comments: WrapComments, false, "Break comments to fit on the line";
format_code_in_doc_comments: bool, false, false, "Format the code snippet in doc comments."; format_code_in_doc_comments: FormatCodeInDocComments, false, "Format the code snippet in \
doc_comment_code_block_width: usize, 100, false, "Maximum width for code snippets in doc \ doc comments.";
comments. No effect unless format_code_in_doc_comments = true"; doc_comment_code_block_width: DocCommentCodeBlockWidth, false, "Maximum width for code \
comment_width: usize, 80, false, snippets in doc comments. No effect unless format_code_in_doc_comments = true";
comment_width: CommentWidth, false,
"Maximum length of comments. No effect unless wrap_comments = true"; "Maximum length of comments. No effect unless wrap_comments = true";
normalize_comments: bool, false, false, "Convert /* */ comments to // comments where possible"; normalize_comments: NormalizeComments, false, "Convert /* */ comments to // comments where \
normalize_doc_attributes: bool, false, false, "Normalize doc attributes as doc comments"; possible";
format_strings: bool, false, false, "Format string literals where necessary"; normalize_doc_attributes: NormalizeDocAttributes, false, "Normalize doc attributes as doc \
format_macro_matchers: bool, false, false, comments";
format_strings: FormatStrings, false, "Format string literals where necessary";
format_macro_matchers: FormatMacroMatchers, false,
"Format the metavariable matching patterns in macros"; "Format the metavariable matching patterns in macros";
format_macro_bodies: bool, true, false, "Format the bodies of declarative macro definitions"; format_macro_bodies: FormatMacroBodies, false,
skip_macro_invocations: MacroSelectors, MacroSelectors::default(), false, "Format the bodies of declarative macro definitions";
skip_macro_invocations: SkipMacroInvocations, false,
"Skip formatting the bodies of macros invoked with the following names."; "Skip formatting the bodies of macros invoked with the following names.";
hex_literal_case: HexLiteralCase, HexLiteralCase::Preserve, false, hex_literal_case: HexLiteralCaseConfig, false, "Format hexadecimal integer literals";
"Format hexadecimal integer literals";
// Single line expressions and items // Single line expressions and items
empty_item_single_line: bool, true, false, empty_item_single_line: EmptyItemSingleLine, false,
"Put empty-body functions and impls on a single line"; "Put empty-body functions and impls on a single line";
struct_lit_single_line: bool, true, false, struct_lit_single_line: StructLitSingleLine, false,
"Put small struct literals on a single line"; "Put small struct literals on a single line";
fn_single_line: bool, false, false, "Put single-expression functions on a single line"; fn_single_line: FnSingleLine, false, "Put single-expression functions on a single line";
where_single_line: bool, false, false, "Force where-clauses to be on a single line"; where_single_line: WhereSingleLine, false, "Force where-clauses to be on a single line";
// Imports // Imports
imports_indent: IndentStyle, IndentStyle::Block, false, "Indent of imports"; imports_indent: ImportsIndent, false, "Indent of imports";
imports_layout: ListTactic, ListTactic::Mixed, false, "Item layout inside a import block"; imports_layout: ImportsLayout, false, "Item layout inside a import block";
imports_granularity: ImportGranularity, ImportGranularity::Preserve, false, imports_granularity: ImportsGranularityConfig, false,
"Merge or split imports to the provided granularity"; "Merge or split imports to the provided granularity";
group_imports: GroupImportsTactic, GroupImportsTactic::Preserve, false, group_imports: GroupImportsTacticConfig, false,
"Controls the strategy for how imports are grouped together"; "Controls the strategy for how imports are grouped together";
merge_imports: bool, false, false, "(deprecated: use imports_granularity instead)"; merge_imports: MergeImports, false, "(deprecated: use imports_granularity instead)";
// Ordering // Ordering
reorder_imports: bool, true, true, "Reorder import and extern crate statements alphabetically"; reorder_imports: ReorderImports, true, "Reorder import and extern crate statements \
reorder_modules: bool, true, true, "Reorder module statements alphabetically in group"; alphabetically";
reorder_impl_items: bool, false, false, "Reorder impl items"; reorder_modules: ReorderModules, true, "Reorder module statements alphabetically in group";
reorder_impl_items: ReorderImplItems, false, "Reorder impl items";
// Spaces around punctuation // Spaces around punctuation
type_punctuation_density: TypeDensity, TypeDensity::Wide, false, type_punctuation_density: TypePunctuationDensity, false,
"Determines if '+' or '=' are wrapped in spaces in the punctuation of types"; "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_before_colon: SpaceBeforeColon, false, "Leave a space before the colon";
space_after_colon: bool, true, false, "Leave a space after the colon"; space_after_colon: SpaceAfterColon, false, "Leave a space after the colon";
spaces_around_ranges: bool, false, false, "Put spaces around the .. and ..= range operators"; spaces_around_ranges: SpacesAroundRanges, false, "Put spaces around the .. and ..= range \
binop_separator: SeparatorPlace, SeparatorPlace::Front, false, operators";
binop_separator: BinopSeparator, false,
"Where to put a binary operator when a binary expression goes multiline"; "Where to put a binary operator when a binary expression goes multiline";
// Misc. // Misc.
remove_nested_parens: bool, true, true, "Remove nested parens"; remove_nested_parens: RemoveNestedParens, true, "Remove nested parens";
combine_control_expr: bool, true, false, "Combine control expressions with function calls"; combine_control_expr: CombineControlExpr, false, "Combine control expressions with function \
short_array_element_width_threshold: usize, 10, true, calls";
short_array_element_width_threshold: ShortArrayElementWidthThreshold, true,
"Width threshold for an array element to be considered short"; "Width threshold for an array element to be considered short";
overflow_delimited_expr: bool, false, false, overflow_delimited_expr: OverflowDelimitedExpr, false,
"Allow trailing bracket/brace delimited expressions to overflow"; "Allow trailing bracket/brace delimited expressions to overflow";
struct_field_align_threshold: usize, 0, false, struct_field_align_threshold: StructFieldAlignThreshold, false,
"Align struct fields if their diffs fits within threshold"; "Align struct fields if their diffs fits within threshold";
enum_discrim_align_threshold: usize, 0, false, enum_discrim_align_threshold: EnumDiscrimAlignThreshold, false,
"Align enum variants discrims, if their diffs fit within threshold"; "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 \ match_arm_blocks: MatchArmBlocks, false, "Wrap the body of arms in blocks when it does not fit \
the same line with the pattern of arms"; on the same line with the pattern of arms";
match_arm_leading_pipes: MatchArmLeadingPipe, MatchArmLeadingPipe::Never, true, match_arm_leading_pipes: MatchArmLeadingPipeConfig, true,
"Determines whether leading pipes are emitted on match arms"; "Determines whether leading pipes are emitted on match arms";
force_multiline_blocks: bool, false, false, force_multiline_blocks: ForceMultilineBlocks, false,
"Force multiline closure bodies and match arms to be wrapped in a block"; "Force multiline closure bodies and match arms to be wrapped in a block";
fn_args_layout: Density, Density::Tall, true, fn_args_layout: FnArgsLayout, true,
"(deprecated: use fn_params_layout instead)"; "(deprecated: use fn_params_layout instead)";
fn_params_layout: Density, Density::Tall, true, fn_params_layout: FnParamsLayout, true,
"Control the layout of parameters in function signatures."; "Control the layout of parameters in function signatures.";
brace_style: BraceStyle, BraceStyle::SameLineWhere, false, "Brace style for items"; brace_style: BraceStyleConfig, false, "Brace style for items";
control_brace_style: ControlBraceStyle, ControlBraceStyle::AlwaysSameLine, false, control_brace_style: ControlBraceStyleConfig, false,
"Brace style for control flow constructs"; "Brace style for control flow constructs";
trailing_semicolon: bool, true, false, trailing_semicolon: TrailingSemicolon, false,
"Add trailing semicolon after break, continue and return"; "Add trailing semicolon after break, continue and return";
trailing_comma: SeparatorTactic, SeparatorTactic::Vertical, false, trailing_comma: TrailingComma, false,
"How to handle trailing commas for lists"; "How to handle trailing commas for lists";
match_block_trailing_comma: bool, false, true, match_block_trailing_comma: MatchBlockTrailingComma, true,
"Put a trailing comma after a block based match arm (non-block arms are not affected)"; "Put a trailing comma after a block based match arm (non-block arms are not affected)";
blank_lines_upper_bound: usize, 1, false, blank_lines_upper_bound: BlankLinesUpperBound, false,
"Maximum number of blank lines which can be put between items"; "Maximum number of blank lines which can be put between items";
blank_lines_lower_bound: usize, 0, false, blank_lines_lower_bound: BlankLinesLowerBound, false,
"Minimum number of blank lines which must be put between items"; "Minimum number of blank lines which must be put between items";
edition: Edition, Edition::Edition2015, true, "The edition of the parser (RFC 2052)"; edition: EditionConfig, true, "The edition of the parser (RFC 2052)";
version: Version, Version::One, false, "Version of formatting rules"; style_edition: StyleEditionConfig, false, "The edition of the Style Guide (RFC 3338)";
inline_attribute_width: usize, 0, false, version: VersionConfig, false, "Version of formatting rules";
inline_attribute_width: InlineAttributeWidth, false,
"Write an item and its attribute on the same line \ "Write an item and its attribute on the same line \
if their combined width is below a threshold"; if their combined width is below a threshold";
format_generated_files: bool, true, false, "Format generated files"; format_generated_files: FormatGeneratedFiles, false, "Format generated files";
generated_marker_line_search_limit: usize, 5, false, "Number of lines to check for a \ generated_marker_line_search_limit: GeneratedMarkerLineSearchLimit, false, "Number of lines to \
`@generated` marker when `format_generated_files` is enabled"; check for a `@generated` marker when `format_generated_files` is enabled";
// Options that can change the source code beyond whitespace/blocks (somewhat linty things) // 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"; merge_derives: MergeDerives, true, "Merge multiple `#[derive(...)]` into a single one";
use_try_shorthand: bool, false, true, "Replace uses of the try! macro by the ? shorthand"; use_try_shorthand: UseTryShorthand, true, "Replace uses of the try! macro by the ? shorthand";
use_field_init_shorthand: bool, false, true, "Use field initialization shorthand if possible"; use_field_init_shorthand: UseFieldInitShorthand, true, "Use field initialization shorthand if \
force_explicit_abi: bool, true, true, "Always print the abi for extern items"; possible";
condense_wildcard_suffixes: bool, false, false, "Replace strings of _ wildcards by a single .. \ force_explicit_abi: ForceExplicitAbi, true, "Always print the abi for extern items";
in tuple patterns"; condense_wildcard_suffixes: CondenseWildcardSuffixes, false, "Replace strings of _ wildcards \
by a single .. in tuple patterns";
// Control options (changes the operation of rustfmt, rather than the formatting) // Control options (changes the operation of rustfmt, rather than the formatting)
color: Color, Color::Auto, false, color: ColorConfig, false,
"What Color option to use when none is supplied: Always, Never, Auto"; "What Color option to use when none is supplied: Always, Never, Auto";
required_version: String, env!("CARGO_PKG_VERSION").to_owned(), false, required_version: RequiredVersion, false,
"Require a specific version of rustfmt"; "Require a specific version of rustfmt";
unstable_features: bool, false, false, unstable_features: UnstableFeatures, false,
"Enables unstable features. Only available on nightly channel"; "Enables unstable features. Only available on nightly channel";
disable_all_formatting: bool, false, true, "Don't reformat anything"; disable_all_formatting: DisableAllFormatting, true, "Don't reformat anything";
skip_children: bool, false, false, "Don't reformat out of line modules"; skip_children: SkipChildren, false, "Don't reformat out of line modules";
hide_parse_errors: bool, false, false, "(deprecated: use show_parse_errors instead)"; hide_parse_errors: HideParseErrors, false, "Hide errors from the parser";
show_parse_errors: bool, true, false, "Show errors from the parser (unstable)"; show_parse_errors: ShowParseErrors, false, "Show errors from the parser (unstable)";
error_on_line_overflow: bool, false, false, "Error if unable to get all lines within max_width"; error_on_line_overflow: ErrorOnLineOverflow, false, "Error if unable to get all lines within \
error_on_unformatted: bool, false, false, max_width";
error_on_unformatted: ErrorOnUnformatted, false,
"Error if unable to get comments or string literals within max_width, \ "Error if unable to get comments or string literals within max_width, \
or they are left with trailing whitespaces"; or they are left with trailing whitespaces";
ignore: IgnoreList, IgnoreList::default(), false, ignore: Ignore, false,
"Skip formatting the specified files and directories"; "Skip formatting the specified files and directories";
// Not user-facing // Not user-facing
verbose: Verbosity, Verbosity::Normal, false, "How much to information to emit to the user"; verbose: Verbose, false, "How much to information to emit to the user";
file_lines: FileLines, FileLines::all(), false, file_lines: FileLinesConfig, false,
"Lines to format; this is not supported in rustfmt.toml, and can only be specified \ "Lines to format; this is not supported in rustfmt.toml, and can only be specified \
via the --file-lines option"; via the --file-lines option";
emit_mode: EmitMode, EmitMode::Files, false, emit_mode: EmitModeConfig, false,
"What emit Mode to use when none is supplied"; "What emit Mode to use when none is supplied";
make_backup: bool, false, false, "Backup changed files"; make_backup: MakeBackup, false, "Backup changed files";
print_misformatted_file_names: bool, false, true, print_misformatted_file_names: PrintMisformattedFileNames, true,
"Prints the names of mismatched files that were formatted. Prints the names of \ "Prints the names of mismatched files that were formatted. Prints the names of \
files that would be formatted when used with `--check` mode. "; files that would be formatted when used with `--check` mode. ";
} }
@ -211,9 +217,48 @@ impl PartialConfig {
::toml::to_string(&cloned).map_err(ToTomlError) ::toml::to_string(&cloned).map_err(ToTomlError)
} }
pub(super) fn to_parsed_config(
self,
style_edition_override: Option<StyleEdition>,
edition_override: Option<Edition>,
version_override: Option<Version>,
dir: &Path,
) -> Config {
Config::default_for_possible_style_edition(
style_edition_override.or(self.style_edition),
edition_override.or(self.edition),
version_override.or(self.version),
)
.fill_from_parsed_config(self, dir)
}
} }
impl Config { impl Config {
pub fn default_for_possible_style_edition(
style_edition: Option<StyleEdition>,
edition: Option<Edition>,
version: Option<Version>,
) -> Config {
// Ensures the configuration defaults associated with Style Editions
// follow the precedence set in
// https://rust-lang.github.io/rfcs/3338-style-evolution.html
// 'version' is a legacy alias for 'style_edition' that we'll support
// for some period of time
// FIXME(calebcartwright) - remove 'version' at some point
match (style_edition, version, edition) {
(Some(se), _, _) => Self::default_with_style_edition(se),
(None, Some(Version::Two), _) => {
Self::default_with_style_edition(StyleEdition::Edition2024)
}
(None, Some(Version::One), _) => {
Self::default_with_style_edition(StyleEdition::Edition2015)
}
(None, None, Some(e)) => Self::default_with_style_edition(e.into()),
(None, None, None) => Config::default(),
}
}
pub(crate) fn version_meets_requirement(&self) -> bool { pub(crate) fn version_meets_requirement(&self) -> bool {
if self.was_set().required_version() { if self.was_set().required_version() {
let version = env!("CARGO_PKG_VERSION"); let version = env!("CARGO_PKG_VERSION");
@ -237,11 +282,16 @@ impl Config {
/// ///
/// Returns a `Config` if the config could be read and parsed from /// Returns a `Config` if the config could be read and parsed from
/// the file, otherwise errors. /// the file, otherwise errors.
pub(super) fn from_toml_path(file_path: &Path) -> Result<Config, Error> { pub(super) fn from_toml_path(
file_path: &Path,
edition: Option<Edition>,
style_edition: Option<StyleEdition>,
version: Option<Version>,
) -> Result<Config, Error> {
let mut file = File::open(&file_path)?; let mut file = File::open(&file_path)?;
let mut toml = String::new(); let mut toml = String::new();
file.read_to_string(&mut toml)?; file.read_to_string(&mut toml)?;
Config::from_toml(&toml, file_path.parent().unwrap()) Config::from_toml_for_style_edition(&toml, file_path, edition, style_edition, version)
.map_err(|err| Error::new(ErrorKind::InvalidData, err)) .map_err(|err| Error::new(ErrorKind::InvalidData, err))
} }
@ -254,9 +304,14 @@ impl Config {
/// ///
/// Returns the `Config` to use, and the path of the project file if there was /// Returns the `Config` to use, and the path of the project file if there was
/// one. /// one.
pub(super) fn from_resolved_toml_path(dir: &Path) -> Result<(Config, Option<PathBuf>), Error> { pub(super) fn from_resolved_toml_path(
dir: &Path,
edition: Option<Edition>,
style_edition: Option<StyleEdition>,
version: Option<Version>,
) -> Result<(Config, Option<PathBuf>), Error> {
/// Try to find a project file in the given directory and its parents. /// 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, /// Returns the path of the nearest project file if one exists,
/// or `None` if no project file was found. /// or `None` if no project file was found.
fn resolve_project_file(dir: &Path) -> Result<Option<PathBuf>, Error> { fn resolve_project_file(dir: &Path) -> Result<Option<PathBuf>, Error> {
let mut current = if dir.is_relative() { let mut current = if dir.is_relative() {
@ -299,12 +354,27 @@ impl Config {
} }
match resolve_project_file(dir)? { match resolve_project_file(dir)? {
None => Ok((Config::default(), None)), None => Ok((
Some(path) => Config::from_toml_path(&path).map(|config| (config, Some(path))), Config::default_for_possible_style_edition(style_edition, edition, version),
None,
)),
Some(path) => Config::from_toml_path(&path, edition, style_edition, version)
.map(|config| (config, Some(path))),
} }
} }
pub(crate) fn from_toml(toml: &str, dir: &Path) -> Result<Config, String> { #[allow(dead_code)]
pub(super) fn from_toml(toml: &str, file_path: &Path) -> Result<Config, String> {
Self::from_toml_for_style_edition(toml, file_path, None, None, None)
}
pub(crate) fn from_toml_for_style_edition(
toml: &str,
file_path: &Path,
edition: Option<Edition>,
style_edition: Option<StyleEdition>,
version: Option<Version>,
) -> Result<Config, String> {
let parsed: ::toml::Value = toml let parsed: ::toml::Value = toml
.parse() .parse()
.map_err(|e| format!("Could not parse TOML: {}", e))?; .map_err(|e| format!("Could not parse TOML: {}", e))?;
@ -318,18 +388,25 @@ impl Config {
err.push_str(msg) err.push_str(msg)
} }
} }
match parsed.try_into() {
match parsed.try_into::<PartialConfig>() {
Ok(parsed_config) => { Ok(parsed_config) => {
if !err.is_empty() { if !err.is_empty() {
eprint!("{err}"); eprint!("{err}");
} }
Ok(Config::default().fill_from_parsed_config(parsed_config, dir)) let dir = file_path.parent().ok_or_else(|| {
format!("failed to get parent directory for {}", file_path.display())
})?;
Ok(parsed_config.to_parsed_config(style_edition, edition, version, dir))
} }
Err(e) => { Err(e) => {
err.push_str("Error: Decoding config file failed:\n"); let err_msg = format!(
err.push_str(format!("{e}\n").as_str()); "The file `{}` failed to parse.\nError details: {e}",
err.push_str("Please check your config file."); file_path.display()
Err(err) );
err.push_str(&err_msg);
Err(err_msg)
} }
} }
} }
@ -341,17 +418,26 @@ pub fn load_config<O: CliOptions>(
file_path: Option<&Path>, file_path: Option<&Path>,
options: Option<O>, options: Option<O>,
) -> Result<(Config, Option<PathBuf>), Error> { ) -> Result<(Config, Option<PathBuf>), Error> {
let over_ride = match options { let (over_ride, edition, style_edition, version) = match options {
Some(ref opts) => config_path(opts)?, Some(ref opts) => (
None => None, config_path(opts)?,
opts.edition(),
opts.style_edition(),
opts.version(),
),
None => (None, None, None, None),
}; };
let result = if let Some(over_ride) = over_ride { 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()))) Config::from_toml_path(over_ride.as_ref(), edition, style_edition, version)
.map(|p| (p, Some(over_ride.to_owned())))
} else if let Some(file_path) = file_path { } else if let Some(file_path) = file_path {
Config::from_resolved_toml_path(file_path) Config::from_resolved_toml_path(file_path, edition, style_edition, version)
} else { } else {
Ok((Config::default(), None)) Ok((
Config::default_for_possible_style_edition(style_edition, edition, version),
None,
))
}; };
result.map(|(mut c, p)| { result.map(|(mut c, p)| {
@ -362,7 +448,7 @@ pub fn load_config<O: CliOptions>(
}) })
} }
// Check for the presence of known config file names (`rustfmt.toml, `.rustfmt.toml`) in `dir` // 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 // 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> { fn get_toml_path(dir: &Path) -> Result<Option<PathBuf>, Error> {
@ -372,7 +458,7 @@ fn get_toml_path(dir: &Path) -> Result<Option<PathBuf>, Error> {
match fs::metadata(&config_file) { match fs::metadata(&config_file) {
// Only return if it's a file to handle the unlikely situation of a directory named // Only return if it's a file to handle the unlikely situation of a directory named
// `rustfmt.toml`. // `rustfmt.toml`.
Ok(ref md) if md.is_file() => return Ok(Some(config_file)), Ok(ref md) if md.is_file() => return Ok(Some(config_file.canonicalize()?)),
// Return the error if it's something other than `NotFound`; otherwise we didn't // Return the error if it's something other than `NotFound`; otherwise we didn't
// find the project file yet, and continue searching. // find the project file yet, and continue searching.
Err(e) => { Err(e) => {
@ -411,7 +497,11 @@ fn config_path(options: &dyn CliOptions) -> Result<Option<PathBuf>, Error> {
config_path_not_found(path.to_str().unwrap()) config_path_not_found(path.to_str().unwrap())
} }
} }
path => Ok(path.map(ToOwned::to_owned)), Some(path) => Ok(Some(
// Canonicalize only after checking above that the `path.exists()`.
path.canonicalize()?,
)),
None => Ok(None),
} }
} }
@ -420,12 +510,13 @@ mod test {
use super::*; use super::*;
use std::str; use std::str;
use crate::config::macro_names::MacroName; use crate::config::macro_names::{MacroName, MacroSelectors};
use rustfmt_config_proc_macro::{nightly_only_test, stable_only_test}; use rustfmt_config_proc_macro::{nightly_only_test, stable_only_test};
#[allow(dead_code)] #[allow(dead_code)]
mod mock { mod mock {
use super::super::*; use super::super::*;
use crate::config_option_with_style_edition_default;
use rustfmt_config_proc_macro::config_type; use rustfmt_config_proc_macro::config_type;
#[config_type] #[config_type]
@ -436,66 +527,69 @@ mod test {
V3, V3,
} }
config_option_with_style_edition_default!(
StableOption, bool, _ => false;
UnstableOption, bool, _ => false;
PartiallyUnstable, PartiallyUnstableOption, _ => PartiallyUnstableOption::V1;
);
create_config! { create_config! {
// Options that are used by the generated functions // Options that are used by the generated functions
max_width: usize, 100, true, "Maximum width of each line"; max_width: MaxWidth, true, "Maximum width of each line";
required_version: String, env!("CARGO_PKG_VERSION").to_owned(), false, required_version: RequiredVersion, false, "Require a specific version of rustfmt.";
"Require a specific version of rustfmt."; ignore: Ignore, false, "Skip formatting the specified files and directories.";
ignore: IgnoreList, IgnoreList::default(), false, verbose: Verbose, false, "How much to information to emit to the user";
"Skip formatting the specified files and directories."; file_lines: FileLinesConfig, false,
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 \ "Lines to format; this is not supported in rustfmt.toml, and can only be specified \
via the --file-lines option"; via the --file-lines option";
// merge_imports deprecation // merge_imports deprecation
imports_granularity: ImportGranularity, ImportGranularity::Preserve, false, imports_granularity: ImportsGranularityConfig, false, "Merge imports";
"Merge imports"; merge_imports: MergeImports, false, "(deprecated: use imports_granularity instead)";
merge_imports: bool, false, false, "(deprecated: use imports_granularity instead)";
// fn_args_layout renamed to fn_params_layout // fn_args_layout renamed to fn_params_layout
fn_args_layout: Density, Density::Tall, true, fn_args_layout: FnArgsLayout, true, "(deprecated: use fn_params_layout instead)";
"(deprecated: use fn_params_layout instead)"; fn_params_layout: FnParamsLayout, true,
fn_params_layout: Density, Density::Tall, true,
"Control the layout of parameters in a function signatures."; "Control the layout of parameters in a function signatures.";
// hide_parse_errors renamed to show_parse_errors // hide_parse_errors renamed to show_parse_errors
hide_parse_errors: bool, false, false, hide_parse_errors: HideParseErrors, false,
"(deprecated: use show_parse_errors instead)"; "(deprecated: use show_parse_errors instead)";
show_parse_errors: bool, true, false, show_parse_errors: ShowParseErrors, false,
"Show errors from the parser (unstable)"; "Show errors from the parser (unstable)";
// Width Heuristics // Width Heuristics
use_small_heuristics: Heuristics, Heuristics::Default, true, use_small_heuristics: UseSmallHeuristics, true,
"Whether to use different formatting for items and \ "Whether to use different formatting for items and \
expressions if they satisfy a heuristic notion of 'small'."; expressions if they satisfy a heuristic notion of 'small'.";
width_heuristics: WidthHeuristics, WidthHeuristics::scaled(100), false, width_heuristics: WidthHeuristicsConfig, false, "'small' heuristic values";
"'small' heuristic values";
fn_call_width: usize, 60, true, "Maximum width of the args of a function call before \ fn_call_width: FnCallWidth, true, "Maximum width of the args of a function call before \
falling back to vertical formatting."; falling back to vertical formatting.";
attr_fn_like_width: usize, 70, true, "Maximum width of the args of a function-like \ attr_fn_like_width: AttrFnLikeWidth, true, "Maximum width of the args of a \
attributes before falling back to vertical formatting."; 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 \ struct_lit_width: StructLitWidth, true, "Maximum width in the body of a struct lit \
falling back to vertical formatting."; before falling back to vertical formatting.";
struct_variant_width: usize, 35, true, "Maximum width in the body of a struct \ struct_variant_width: StructVariantWidth, true, "Maximum width in the body of a struct \
variant before falling back to vertical formatting."; variant before falling back to vertical formatting.";
array_width: usize, 60, true, "Maximum width of an array literal before falling \ array_width: ArrayWidth, true, "Maximum width of an array literal before falling \
back to vertical formatting."; back to vertical formatting.";
chain_width: usize, 60, true, "Maximum length of a chain to fit on a single line."; chain_width: ChainWidth, 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 \ single_line_if_else_max_width: SingleLineIfElseMaxWidth, true, "Maximum line length \
line if-else expressions. A value of zero means always break if-else expressions."; for single line if-else expressions. A value of zero means always break if-else \
single_line_let_else_max_width: usize, 50, false, "Maximum line length for single \ expressions.";
line let-else statements. A value of zero means always format the divergent \ single_line_let_else_max_width: SingleLineLetElseMaxWidth, false, "Maximum line length \
`else` block over multiple lines."; for single line let-else statements. A value of zero means always format the \
divergent `else` block over multiple lines.";
// Options that are used by the tests // Options that are used by the tests
stable_option: bool, false, true, "A stable option"; stable_option: StableOption, true, "A stable option";
unstable_option: bool, false, false, "An unstable option"; unstable_option: UnstableOption, false, "An unstable option";
partially_unstable_option: PartiallyUnstableOption, PartiallyUnstableOption::V1, true, partially_unstable_option: PartiallyUnstable, true, "A partially unstable option";
"A partially unstable option"; edition: EditionConfig, true, "blah";
style_edition: StyleEditionConfig, true, "blah";
version: VersionConfig, false, "blah blah"
} }
#[cfg(test)] #[cfg(test)]
@ -580,7 +674,7 @@ mod test {
#[test] #[test]
fn test_was_set() { fn test_was_set() {
let config = Config::from_toml("hard_tabs = true", Path::new("")).unwrap(); let config = Config::from_toml("hard_tabs = true", Path::new("./rustfmt.toml")).unwrap();
assert_eq!(config.was_set().hard_tabs(), true); assert_eq!(config.was_set().hard_tabs(), true);
assert_eq!(config.was_set().verbose(), false); assert_eq!(config.was_set().verbose(), false);
@ -679,6 +773,7 @@ match_block_trailing_comma = false
blank_lines_upper_bound = 1 blank_lines_upper_bound = 1
blank_lines_lower_bound = 0 blank_lines_lower_bound = 0
edition = "2015" edition = "2015"
style_edition = "2015"
version = "One" version = "One"
inline_attribute_width = 0 inline_attribute_width = 0
format_generated_files = true format_generated_files = true
@ -706,6 +801,114 @@ make_backup = false
assert_eq!(&toml, &default_config); assert_eq!(&toml, &default_config);
} }
#[test]
fn test_dump_style_edition_2024_config() {
let edition_2024_config = format!(
r#"max_width = 100
hard_tabs = false
tab_spaces = 4
newline_style = "Auto"
indent_style = "Block"
use_small_heuristics = "Default"
fn_call_width = 60
attr_fn_like_width = 70
struct_lit_width = 18
struct_variant_width = 35
array_width = 60
chain_width = 60
single_line_if_else_max_width = 50
single_line_let_else_max_width = 50
wrap_comments = false
format_code_in_doc_comments = false
doc_comment_code_block_width = 100
comment_width = 80
normalize_comments = false
normalize_doc_attributes = false
format_strings = false
format_macro_matchers = false
format_macro_bodies = true
skip_macro_invocations = []
hex_literal_case = "Preserve"
empty_item_single_line = true
struct_lit_single_line = true
fn_single_line = false
where_single_line = false
imports_indent = "Block"
imports_layout = "Mixed"
imports_granularity = "Preserve"
group_imports = "Preserve"
reorder_imports = true
reorder_modules = true
reorder_impl_items = false
type_punctuation_density = "Wide"
space_before_colon = false
space_after_colon = true
spaces_around_ranges = false
binop_separator = "Front"
remove_nested_parens = true
combine_control_expr = true
short_array_element_width_threshold = 10
overflow_delimited_expr = true
struct_field_align_threshold = 0
enum_discrim_align_threshold = 0
match_arm_blocks = true
match_arm_leading_pipes = "Never"
force_multiline_blocks = false
fn_params_layout = "Tall"
brace_style = "SameLineWhere"
control_brace_style = "AlwaysSameLine"
trailing_semicolon = true
trailing_comma = "Vertical"
match_block_trailing_comma = false
blank_lines_upper_bound = 1
blank_lines_lower_bound = 0
edition = "2015"
style_edition = "2024"
version = "Two"
inline_attribute_width = 0
format_generated_files = true
generated_marker_line_search_limit = 5
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
show_parse_errors = true
error_on_line_overflow = false
error_on_unformatted = false
ignore = []
emit_mode = "Files"
make_backup = false
"#,
env!("CARGO_PKG_VERSION")
);
let toml = Config::default_with_style_edition(StyleEdition::Edition2024)
.all_options()
.to_toml()
.unwrap();
assert_eq!(&toml, &edition_2024_config);
}
#[test]
fn test_editions_2015_2018_2021_identical() {
let get_edition_toml = |style_edition: StyleEdition| {
Config::default_with_style_edition(style_edition)
.all_options()
.to_toml()
.unwrap()
};
let edition2015 = get_edition_toml(StyleEdition::Edition2015);
let edition2018 = get_edition_toml(StyleEdition::Edition2018);
let edition2021 = get_edition_toml(StyleEdition::Edition2021);
assert_eq!(edition2015, edition2018);
assert_eq!(edition2018, edition2021);
}
#[stable_only_test] #[stable_only_test]
#[test] #[test]
fn test_as_not_nightly_channel() { fn test_as_not_nightly_channel() {
@ -730,11 +933,26 @@ make_backup = false
#[nightly_only_test] #[nightly_only_test]
#[test] #[test]
fn test_unstable_from_toml() { fn test_unstable_from_toml() {
let config = Config::from_toml("unstable_features = true", Path::new("")).unwrap(); let config =
Config::from_toml("unstable_features = true", Path::new("./rustfmt.toml")).unwrap();
assert_eq!(config.was_set().unstable_features(), true); assert_eq!(config.was_set().unstable_features(), true);
assert_eq!(config.unstable_features(), true); assert_eq!(config.unstable_features(), true);
} }
#[test]
fn test_set_cli() {
let mut config = Config::default();
assert_eq!(config.was_set().edition(), false);
assert_eq!(config.was_set_cli().edition(), false);
config.set().edition(Edition::Edition2021);
assert_eq!(config.was_set().edition(), false);
assert_eq!(config.was_set_cli().edition(), false);
config.set_cli().edition(Edition::Edition2021);
assert_eq!(config.was_set().edition(), false);
assert_eq!(config.was_set_cli().edition(), true);
assert_eq!(config.was_set_cli().emit_mode(), false);
}
#[cfg(test)] #[cfg(test)]
mod deprecated_option_merge_imports { mod deprecated_option_merge_imports {
use super::*; use super::*;
@ -746,7 +964,7 @@ make_backup = false
unstable_features = true unstable_features = true
merge_imports = true merge_imports = true
"#; "#;
let config = Config::from_toml(toml, Path::new("")).unwrap(); let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
assert_eq!(config.imports_granularity(), ImportGranularity::Crate); assert_eq!(config.imports_granularity(), ImportGranularity::Crate);
} }
@ -758,7 +976,7 @@ make_backup = false
merge_imports = true merge_imports = true
imports_granularity = "Preserve" imports_granularity = "Preserve"
"#; "#;
let config = Config::from_toml(toml, Path::new("")).unwrap(); let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
assert_eq!(config.imports_granularity(), ImportGranularity::Preserve); assert_eq!(config.imports_granularity(), ImportGranularity::Preserve);
} }
@ -769,7 +987,7 @@ make_backup = false
unstable_features = true unstable_features = true
merge_imports = true merge_imports = true
"#; "#;
let mut config = Config::from_toml(toml, Path::new("")).unwrap(); let mut config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
config.override_value("imports_granularity", "Preserve"); config.override_value("imports_granularity", "Preserve");
assert_eq!(config.imports_granularity(), ImportGranularity::Preserve); assert_eq!(config.imports_granularity(), ImportGranularity::Preserve);
} }
@ -781,7 +999,7 @@ make_backup = false
unstable_features = true unstable_features = true
imports_granularity = "Module" imports_granularity = "Module"
"#; "#;
let mut config = Config::from_toml(toml, Path::new("")).unwrap(); let mut config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
config.override_value("merge_imports", "true"); config.override_value("merge_imports", "true");
// no effect: the new option always takes precedence // no effect: the new option always takes precedence
assert_eq!(config.imports_granularity(), ImportGranularity::Module); assert_eq!(config.imports_granularity(), ImportGranularity::Module);
@ -798,7 +1016,7 @@ make_backup = false
use_small_heuristics = "Default" use_small_heuristics = "Default"
max_width = 200 max_width = 200
"#; "#;
let config = Config::from_toml(toml, Path::new("")).unwrap(); let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
assert_eq!(config.array_width(), 120); assert_eq!(config.array_width(), 120);
assert_eq!(config.attr_fn_like_width(), 140); assert_eq!(config.attr_fn_like_width(), 140);
assert_eq!(config.chain_width(), 120); assert_eq!(config.chain_width(), 120);
@ -814,7 +1032,7 @@ make_backup = false
use_small_heuristics = "Max" use_small_heuristics = "Max"
max_width = 120 max_width = 120
"#; "#;
let config = Config::from_toml(toml, Path::new("")).unwrap(); let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
assert_eq!(config.array_width(), 120); assert_eq!(config.array_width(), 120);
assert_eq!(config.attr_fn_like_width(), 120); assert_eq!(config.attr_fn_like_width(), 120);
assert_eq!(config.chain_width(), 120); assert_eq!(config.chain_width(), 120);
@ -830,11 +1048,11 @@ make_backup = false
use_small_heuristics = "Off" use_small_heuristics = "Off"
max_width = 100 max_width = 100
"#; "#;
let config = Config::from_toml(toml, Path::new("")).unwrap(); let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
assert_eq!(config.array_width(), usize::max_value()); assert_eq!(config.array_width(), usize::MAX);
assert_eq!(config.attr_fn_like_width(), usize::max_value()); assert_eq!(config.attr_fn_like_width(), usize::MAX);
assert_eq!(config.chain_width(), usize::max_value()); assert_eq!(config.chain_width(), usize::MAX);
assert_eq!(config.fn_call_width(), usize::max_value()); assert_eq!(config.fn_call_width(), usize::MAX);
assert_eq!(config.single_line_if_else_max_width(), 0); assert_eq!(config.single_line_if_else_max_width(), 0);
assert_eq!(config.struct_lit_width(), 0); assert_eq!(config.struct_lit_width(), 0);
assert_eq!(config.struct_variant_width(), 0); assert_eq!(config.struct_variant_width(), 0);
@ -852,7 +1070,7 @@ make_backup = false
struct_lit_width = 30 struct_lit_width = 30
struct_variant_width = 34 struct_variant_width = 34
"#; "#;
let config = Config::from_toml(toml, Path::new("")).unwrap(); let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
assert_eq!(config.array_width(), 20); assert_eq!(config.array_width(), 20);
assert_eq!(config.attr_fn_like_width(), 40); assert_eq!(config.attr_fn_like_width(), 40);
assert_eq!(config.chain_width(), 20); assert_eq!(config.chain_width(), 20);
@ -874,7 +1092,7 @@ make_backup = false
struct_lit_width = 30 struct_lit_width = 30
struct_variant_width = 34 struct_variant_width = 34
"#; "#;
let config = Config::from_toml(toml, Path::new("")).unwrap(); let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
assert_eq!(config.array_width(), 20); assert_eq!(config.array_width(), 20);
assert_eq!(config.attr_fn_like_width(), 40); assert_eq!(config.attr_fn_like_width(), 40);
assert_eq!(config.chain_width(), 20); assert_eq!(config.chain_width(), 20);
@ -896,7 +1114,7 @@ make_backup = false
struct_lit_width = 30 struct_lit_width = 30
struct_variant_width = 34 struct_variant_width = 34
"#; "#;
let config = Config::from_toml(toml, Path::new("")).unwrap(); let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
assert_eq!(config.array_width(), 20); assert_eq!(config.array_width(), 20);
assert_eq!(config.attr_fn_like_width(), 40); assert_eq!(config.attr_fn_like_width(), 40);
assert_eq!(config.chain_width(), 20); assert_eq!(config.chain_width(), 20);
@ -912,7 +1130,7 @@ make_backup = false
max_width = 90 max_width = 90
fn_call_width = 95 fn_call_width = 95
"#; "#;
let config = Config::from_toml(toml, Path::new("")).unwrap(); let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
assert_eq!(config.fn_call_width(), 90); assert_eq!(config.fn_call_width(), 90);
} }
@ -922,7 +1140,7 @@ make_backup = false
max_width = 80 max_width = 80
attr_fn_like_width = 90 attr_fn_like_width = 90
"#; "#;
let config = Config::from_toml(toml, Path::new("")).unwrap(); let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
assert_eq!(config.attr_fn_like_width(), 80); assert_eq!(config.attr_fn_like_width(), 80);
} }
@ -932,7 +1150,7 @@ make_backup = false
max_width = 78 max_width = 78
struct_lit_width = 90 struct_lit_width = 90
"#; "#;
let config = Config::from_toml(toml, Path::new("")).unwrap(); let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
assert_eq!(config.struct_lit_width(), 78); assert_eq!(config.struct_lit_width(), 78);
} }
@ -942,7 +1160,7 @@ make_backup = false
max_width = 80 max_width = 80
struct_variant_width = 90 struct_variant_width = 90
"#; "#;
let config = Config::from_toml(toml, Path::new("")).unwrap(); let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
assert_eq!(config.struct_variant_width(), 80); assert_eq!(config.struct_variant_width(), 80);
} }
@ -952,7 +1170,7 @@ make_backup = false
max_width = 60 max_width = 60
array_width = 80 array_width = 80
"#; "#;
let config = Config::from_toml(toml, Path::new("")).unwrap(); let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
assert_eq!(config.array_width(), 60); assert_eq!(config.array_width(), 60);
} }
@ -962,7 +1180,7 @@ make_backup = false
max_width = 80 max_width = 80
chain_width = 90 chain_width = 90
"#; "#;
let config = Config::from_toml(toml, Path::new("")).unwrap(); let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
assert_eq!(config.chain_width(), 80); assert_eq!(config.chain_width(), 80);
} }
@ -972,7 +1190,7 @@ make_backup = false
max_width = 70 max_width = 70
single_line_if_else_max_width = 90 single_line_if_else_max_width = 90
"#; "#;
let config = Config::from_toml(toml, Path::new("")).unwrap(); let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
assert_eq!(config.single_line_if_else_max_width(), 70); assert_eq!(config.single_line_if_else_max_width(), 70);
} }

View File

@ -1,6 +1,6 @@
#![allow(unused_imports)] #![allow(unused_imports)]
use std::collections::{hash_set, HashSet}; use std::collections::{HashSet, hash_set};
use std::fmt; use std::fmt;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::str::FromStr; use std::str::FromStr;
@ -11,8 +11,10 @@ use serde::de::{SeqAccess, Visitor};
use serde::ser::SerializeSeq; use serde::ser::SerializeSeq;
use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::config::lists::*;
use crate::config::Config; use crate::config::Config;
use crate::config::file_lines::FileLines;
use crate::config::lists::*;
use crate::config::macro_names::MacroSelectors;
#[config_type] #[config_type]
pub enum NewlineStyle { pub enum NewlineStyle {
@ -253,12 +255,12 @@ impl WidthHeuristics {
// Using this WidthHeuristics means we ignore heuristics. // Using this WidthHeuristics means we ignore heuristics.
pub fn null() -> WidthHeuristics { pub fn null() -> WidthHeuristics {
WidthHeuristics { WidthHeuristics {
fn_call_width: usize::max_value(), fn_call_width: usize::MAX,
attr_fn_like_width: usize::max_value(), attr_fn_like_width: usize::MAX,
struct_lit_width: 0, struct_lit_width: 0,
struct_variant_width: 0, struct_variant_width: 0,
array_width: usize::max_value(), array_width: usize::MAX,
chain_width: usize::max_value(), chain_width: usize::MAX,
single_line_if_else_max_width: 0, single_line_if_else_max_width: 0,
single_line_let_else_max_width: 0, single_line_let_else_max_width: 0,
} }
@ -413,7 +415,13 @@ impl FromStr for IgnoreList {
/// values in a config with values from the command line. /// values in a config with values from the command line.
pub trait CliOptions { pub trait CliOptions {
fn apply_to(self, config: &mut Config); fn apply_to(self, config: &mut Config);
/// It is ok if the returned path doesn't exist or is not canonicalized
/// (i.e. the callers are expected to handle such cases).
fn config_path(&self) -> Option<&Path>; fn config_path(&self) -> Option<&Path>;
fn edition(&self) -> Option<Edition>;
fn style_edition(&self) -> Option<StyleEdition>;
fn version(&self) -> Option<Version>;
} }
/// The edition of the syntax and semantics of code (RFC 2052). /// The edition of the syntax and semantics of code (RFC 2052).
@ -454,6 +462,17 @@ impl From<Edition> for rustc_span::edition::Edition {
} }
} }
impl From<Edition> for StyleEdition {
fn from(edition: Edition) -> Self {
match edition {
Edition::Edition2015 => StyleEdition::Edition2015,
Edition::Edition2018 => StyleEdition::Edition2018,
Edition::Edition2021 => StyleEdition::Edition2021,
Edition::Edition2024 => StyleEdition::Edition2024,
}
}
}
impl PartialOrd for Edition { impl PartialOrd for Edition {
fn partial_cmp(&self, other: &Edition) -> Option<std::cmp::Ordering> { fn partial_cmp(&self, other: &Edition) -> Option<std::cmp::Ordering> {
rustc_span::edition::Edition::partial_cmp(&(*self).into(), &(*other).into()) rustc_span::edition::Edition::partial_cmp(&(*self).into(), &(*other).into())
@ -471,10 +490,11 @@ pub enum MatchArmLeadingPipe {
Preserve, Preserve,
} }
/// Defines the default values for each config according to [the style guide]. /// Defines the default values for each config according to the edition of the
/// rustfmt output may differ between style editions. /// [Style Guide] as per [RFC 3338]. Rustfmt output may differ between Style editions.
/// ///
/// [the style guide]: https://doc.rust-lang.org/nightly/style-guide/ /// [Style Guide]: https://doc.rust-lang.org/nightly/style-guide/
/// [RFC 3338]: https://rust-lang.github.io/rfcs/3338-style-evolution.html
#[config_type] #[config_type]
pub enum StyleEdition { pub enum StyleEdition {
#[value = "2015"] #[value = "2015"]
@ -491,6 +511,169 @@ pub enum StyleEdition {
Edition2021, Edition2021,
#[value = "2024"] #[value = "2024"]
#[doc_hint = "2024"] #[doc_hint = "2024"]
#[unstable_variant]
/// [Edition 2024](). /// [Edition 2024]().
Edition2024, Edition2024,
} }
impl From<StyleEdition> for rustc_span::edition::Edition {
fn from(edition: StyleEdition) -> Self {
match edition {
StyleEdition::Edition2015 => Self::Edition2015,
StyleEdition::Edition2018 => Self::Edition2018,
StyleEdition::Edition2021 => Self::Edition2021,
StyleEdition::Edition2024 => Self::Edition2024,
}
}
}
impl PartialOrd for StyleEdition {
fn partial_cmp(&self, other: &StyleEdition) -> Option<std::cmp::Ordering> {
rustc_span::edition::Edition::partial_cmp(&(*self).into(), &(*other).into())
}
}
/// Defines unit structs to implement `StyleEditionDefault` for.
#[macro_export]
macro_rules! config_option_with_style_edition_default {
($name:ident, $config_ty:ty, _ => $default:expr) => {
#[allow(unreachable_pub)]
pub struct $name;
$crate::style_edition_default!($name, $config_ty, _ => $default);
};
($name:ident, $config_ty:ty, Edition2024 => $default_2024:expr, _ => $default_2015:expr) => {
pub struct $name;
$crate::style_edition_default!(
$name,
$config_ty,
Edition2024 => $default_2024,
_ => $default_2015
);
};
(
$($name:ident, $config_ty:ty, $(Edition2024 => $default_2024:expr,)? _ => $default:expr);*
$(;)*
) => {
$(
config_option_with_style_edition_default!(
$name, $config_ty, $(Edition2024 => $default_2024,)? _ => $default
);
)*
};
}
// TODO(ytmimi) Some of the configuration values have a `Config` suffix, while others don't.
// I chose to add a `Config` suffix in cases where a type for the config option was already
// defined. For example, `NewlineStyle` and `NewlineStyleConfig`. There was some discussion
// about using the `Config` suffix more consistently.
config_option_with_style_edition_default!(
// Fundamental stuff
MaxWidth, usize, _ => 100;
HardTabs, bool, _ => false;
TabSpaces, usize, _ => 4;
NewlineStyleConfig, NewlineStyle, _ => NewlineStyle::Auto;
IndentStyleConfig, IndentStyle, _ => IndentStyle::Block;
// Width Heuristics
UseSmallHeuristics, Heuristics, _ => Heuristics::Default;
WidthHeuristicsConfig, WidthHeuristics, _ => WidthHeuristics::scaled(100);
FnCallWidth, usize, _ => 60;
AttrFnLikeWidth, usize, _ => 70;
StructLitWidth, usize, _ => 18;
StructVariantWidth, usize, _ => 35;
ArrayWidth, usize, _ => 60;
ChainWidth, usize, _ => 60;
SingleLineIfElseMaxWidth, usize, _ => 50;
SingleLineLetElseMaxWidth, usize, _ => 50;
// Comments. macros, and strings
WrapComments, bool, _ => false;
FormatCodeInDocComments, bool, _ => false;
DocCommentCodeBlockWidth, usize, _ => 100;
CommentWidth, usize, _ => 80;
NormalizeComments, bool, _ => false;
NormalizeDocAttributes, bool, _ => false;
FormatStrings, bool, _ => false;
FormatMacroMatchers, bool, _ => false;
FormatMacroBodies, bool, _ => true;
SkipMacroInvocations, MacroSelectors, _ => MacroSelectors::default();
HexLiteralCaseConfig, HexLiteralCase, _ => HexLiteralCase::Preserve;
// Single line expressions and items
EmptyItemSingleLine, bool, _ => true;
StructLitSingleLine, bool, _ => true;
FnSingleLine, bool, _ => false;
WhereSingleLine, bool, _ => false;
// Imports
ImportsIndent, IndentStyle, _ => IndentStyle::Block;
ImportsLayout, ListTactic, _ => ListTactic::Mixed;
ImportsGranularityConfig, ImportGranularity, _ => ImportGranularity::Preserve;
GroupImportsTacticConfig, GroupImportsTactic, _ => GroupImportsTactic::Preserve;
MergeImports, bool, _ => false;
// Ordering
ReorderImports, bool, _ => true;
ReorderModules, bool, _ => true;
ReorderImplItems, bool, _ => false;
// Spaces around punctuation
TypePunctuationDensity, TypeDensity, _ => TypeDensity::Wide;
SpaceBeforeColon, bool, _ => false;
SpaceAfterColon, bool, _ => true;
SpacesAroundRanges, bool, _ => false;
BinopSeparator, SeparatorPlace, _ => SeparatorPlace::Front;
// Misc.
RemoveNestedParens, bool, _ => true;
CombineControlExpr, bool, _ => true;
ShortArrayElementWidthThreshold, usize, _ => 10;
OverflowDelimitedExpr, bool, Edition2024 => true, _ => false;
StructFieldAlignThreshold, usize, _ => 0;
EnumDiscrimAlignThreshold, usize, _ => 0;
MatchArmBlocks, bool, _ => true;
MatchArmLeadingPipeConfig, MatchArmLeadingPipe, _ => MatchArmLeadingPipe::Never;
ForceMultilineBlocks, bool, _ => false;
FnArgsLayout, Density, _ => Density::Tall;
FnParamsLayout, Density, _ => Density::Tall;
BraceStyleConfig, BraceStyle, _ => BraceStyle::SameLineWhere;
ControlBraceStyleConfig, ControlBraceStyle, _ => ControlBraceStyle::AlwaysSameLine;
TrailingSemicolon, bool, _ => true;
TrailingComma, SeparatorTactic, _ => SeparatorTactic::Vertical;
MatchBlockTrailingComma, bool, _ => false;
BlankLinesUpperBound, usize, _ => 1;
BlankLinesLowerBound, usize, _ => 0;
EditionConfig, Edition, _ => Edition::Edition2015;
StyleEditionConfig, StyleEdition,
Edition2024 => StyleEdition::Edition2024, _ => StyleEdition::Edition2015;
VersionConfig, Version, Edition2024 => Version::Two, _ => Version::One;
InlineAttributeWidth, usize, _ => 0;
FormatGeneratedFiles, bool, _ => true;
GeneratedMarkerLineSearchLimit, usize, _ => 5;
// Options that can change the source code beyond whitespace/blocks (somewhat linty things)
MergeDerives, bool, _ => true;
UseTryShorthand, bool, _ => false;
UseFieldInitShorthand, bool, _ => false;
ForceExplicitAbi, bool, _ => true;
CondenseWildcardSuffixes, bool, _ => false;
// Control options (changes the operation of rustfmt, rather than the formatting)
ColorConfig, Color, _ => Color::Auto;
RequiredVersion, String, _ => env!("CARGO_PKG_VERSION").to_owned();
UnstableFeatures, bool, _ => false;
DisableAllFormatting, bool, _ => false;
SkipChildren, bool, _ => false;
HideParseErrors, bool, _ => false;
ShowParseErrors, bool, _ => true;
ErrorOnLineOverflow, bool, _ => false;
ErrorOnUnformatted, bool, _ => false;
Ignore, IgnoreList, _ => IgnoreList::default();
// Not user-facing
Verbose, Verbosity, _ => Verbosity::Normal;
FileLinesConfig, FileLines, _ => FileLines::all();
EmitModeConfig, EmitMode, _ => EmitMode::Files;
MakeBackup, bool, _ => false;
PrintMisformattedFileNames, bool, _ => false;
);

View File

@ -2,7 +2,7 @@ use crate::config::StyleEdition;
/// Defines the default value for the given style edition /// Defines the default value for the given style edition
#[allow(dead_code)] #[allow(dead_code)]
pub(crate) trait StyleEditionDefault { pub trait StyleEditionDefault {
type ConfigType; type ConfigType;
fn style_edition_default(style_edition: StyleEdition) -> Self::ConfigType; fn style_edition_default(style_edition: StyleEdition) -> Self::ConfigType;
} }

View File

@ -1,6 +1,6 @@
use self::xml::XmlEscaped; use self::xml::XmlEscaped;
use super::*; use super::*;
use crate::rustfmt_diff::{make_diff, DiffLine, Mismatch}; use crate::rustfmt_diff::{DiffLine, Mismatch, make_diff};
mod xml; mod xml;

View File

@ -1,5 +1,5 @@
use super::*; use super::*;
use crate::rustfmt_diff::{make_diff, DiffLine, Mismatch}; use crate::rustfmt_diff::{DiffLine, Mismatch, make_diff};
use serde::Serialize; use serde::Serialize;
use serde_json::to_string as to_json_string; use serde_json::to_string as to_json_string;

View File

@ -1,5 +1,5 @@
use super::*; use super::*;
use crate::rustfmt_diff::{make_diff, ModifiedLines}; use crate::rustfmt_diff::{ModifiedLines, make_diff};
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub(crate) struct ModifiedLinesEmitter; pub(crate) struct ModifiedLinesEmitter;

File diff suppressed because it is too large Load Diff

View File

@ -17,7 +17,7 @@ use crate::parse::parser::{DirectoryOwnership, Parser, ParserError};
use crate::parse::session::ParseSess; use crate::parse::session::ParseSess;
use crate::utils::{contains_skip, count_newlines}; use crate::utils::{contains_skip, count_newlines};
use crate::visitor::FmtVisitor; use crate::visitor::FmtVisitor;
use crate::{modules, source_file, ErrorKind, FormatReport, Input, Session}; use crate::{ErrorKind, FormatReport, Input, Session, modules, source_file};
mod generated; mod generated;
mod newline_style; mod newline_style;

View File

@ -13,7 +13,9 @@ use rustfmt_nightly as rustfmt;
use tracing::debug; use tracing::debug;
use tracing_subscriber::EnvFilter; use tracing_subscriber::EnvFilter;
use crate::rustfmt::{load_config, CliOptions, FormatReportFormatterBuilder, Input, Session}; use crate::rustfmt::{
CliOptions, FormatReportFormatterBuilder, Input, Session, Version, load_config,
};
fn prune_files(files: Vec<&str>) -> Vec<&str> { fn prune_files(files: Vec<&str>) -> Vec<&str> {
let prefixes: Vec<_> = files let prefixes: Vec<_> = files
@ -87,6 +89,15 @@ impl CliOptions for NullOptions {
fn config_path(&self) -> Option<&Path> { fn config_path(&self) -> Option<&Path> {
unreachable!(); unreachable!();
} }
fn edition(&self) -> Option<rustfmt_nightly::Edition> {
unreachable!();
}
fn style_edition(&self) -> Option<rustfmt_nightly::StyleEdition> {
unreachable!();
}
fn version(&self) -> Option<Version> {
unreachable!();
}
} }
fn uncommitted_files() -> Vec<String> { fn uncommitted_files() -> Vec<String> {

View File

@ -41,8 +41,11 @@ mod test {
use crate::ignore_path::IgnorePathSet; use crate::ignore_path::IgnorePathSet;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
let config = let config = Config::from_toml(
Config::from_toml(r#"ignore = ["foo.rs", "bar_dir/*"]"#, Path::new("")).unwrap(); r#"ignore = ["foo.rs", "bar_dir/*"]"#,
Path::new("./rustfmt.toml"),
)
.unwrap();
let ignore_path_set = IgnorePathSet::from_ignore_list(&config.ignore()).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("src/foo.rs"))));
@ -59,7 +62,7 @@ mod test {
let config = Config::from_toml( let config = Config::from_toml(
r#"ignore = ["foo.rs", "bar_dir/*", "!bar_dir/*/what.rs"]"#, r#"ignore = ["foo.rs", "bar_dir/*", "!bar_dir/*/what.rs"]"#,
Path::new(""), Path::new("./rustfmt.toml"),
) )
.unwrap(); .unwrap();
let ignore_path_set = IgnorePathSet::from_ignore_list(&config.ignore()).unwrap(); let ignore_path_set = IgnorePathSet::from_ignore_list(&config.ignore()).unwrap();

View File

@ -8,19 +8,20 @@ use itertools::Itertools;
use rustc_ast::ast::{self, UseTreeKind}; use rustc_ast::ast::{self, UseTreeKind};
use rustc_span::{ use rustc_span::{
BytePos, DUMMY_SP, Span,
symbol::{self, sym}, symbol::{self, sym},
BytePos, Span, DUMMY_SP,
}; };
use crate::comment::combine_strs_with_missing_comments; use crate::comment::combine_strs_with_missing_comments;
use crate::config::lists::*;
use crate::config::ImportGranularity; use crate::config::ImportGranularity;
use crate::config::{Edition, IndentStyle, Version}; use crate::config::lists::*;
use crate::config::{Edition, IndentStyle, StyleEdition};
use crate::lists::{ use crate::lists::{
definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator, ListFormatting, ListItem, Separator, definitive_tactic, itemize_list, write_list,
}; };
use crate::rewrite::{Rewrite, RewriteContext}; use crate::rewrite::{Rewrite, RewriteContext, RewriteErrorExt, RewriteResult};
use crate::shape::Shape; use crate::shape::Shape;
use crate::sort::version_sort;
use crate::source_map::SpanUtils; use crate::source_map::SpanUtils;
use crate::spanned::Spanned; use crate::spanned::Spanned;
use crate::utils::{is_same_visibility, mk_sp, rewrite_ident}; use crate::utils::{is_same_visibility, mk_sp, rewrite_ident};
@ -44,7 +45,8 @@ impl<'a> FmtVisitor<'a> {
Some(item.span.lo()), Some(item.span.lo()),
Some(item.attrs.clone()), Some(item.attrs.clone()),
) )
.rewrite_top_level(&self.get_context(), shape); .rewrite_top_level(&self.get_context(), shape)
.ok();
match rw { match rw {
Some(ref s) if s.is_empty() => { Some(ref s) if s.is_empty() => {
// Format up to last newline // Format up to last newline
@ -104,7 +106,7 @@ pub(crate) enum UseSegmentKind {
#[derive(Clone, Eq, PartialEq)] #[derive(Clone, Eq, PartialEq)]
pub(crate) struct UseSegment { pub(crate) struct UseSegment {
pub(crate) kind: UseSegmentKind, pub(crate) kind: UseSegmentKind,
pub(crate) version: Version, pub(crate) style_edition: StyleEdition,
} }
#[derive(Clone)] #[derive(Clone)]
@ -149,7 +151,7 @@ impl UseSegment {
}; };
UseSegment { UseSegment {
kind, kind,
version: self.version, style_edition: self.style_edition,
} }
} }
@ -197,7 +199,7 @@ impl UseSegment {
Some(UseSegment { Some(UseSegment {
kind, kind,
version: context.config.version(), style_edition: context.config.style_edition(),
}) })
} }
@ -331,12 +333,17 @@ impl UseTree {
&self, &self,
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
shape: Shape, shape: Shape,
) -> Option<String> { ) -> RewriteResult {
let vis = self.visibility.as_ref().map_or(Cow::from(""), |vis| { let vis = self.visibility.as_ref().map_or(Cow::from(""), |vis| {
crate::utils::format_visibility(context, vis) crate::utils::format_visibility(context, vis)
}); });
let use_str = self let use_str = self
.rewrite(context, shape.offset_left(vis.len())?) .rewrite_result(
context,
shape
.offset_left(vis.len())
.max_width_error(shape.width, self.span())?,
)
.map(|s| { .map(|s| {
if s.is_empty() { if s.is_empty() {
s s
@ -346,8 +353,8 @@ impl UseTree {
})?; })?;
match self.attrs { match self.attrs {
Some(ref attrs) if !attrs.is_empty() => { Some(ref attrs) if !attrs.is_empty() => {
let attr_str = attrs.rewrite(context, shape)?; let attr_str = attrs.rewrite_result(context, shape)?;
let lo = attrs.last().as_ref()?.span.hi(); let lo = attrs.last().unknown_error()?.span.hi();
let hi = self.span.lo(); let hi = self.span.lo();
let span = mk_sp(lo, hi); let span = mk_sp(lo, hi);
@ -368,7 +375,7 @@ impl UseTree {
allow_extend, allow_extend,
) )
} }
_ => Some(use_str), _ => Ok(use_str),
} }
} }
@ -444,18 +451,21 @@ impl UseTree {
} }
} }
let version = context.config.version(); let style_edition = context.config.style_edition();
match a.kind { match a.kind {
UseTreeKind::Glob => { UseTreeKind::Glob => {
// in case of a global path and the glob starts at the root, e.g., "::*" // in case of a global path and the glob starts at the root, e.g., "::*"
if a.prefix.segments.len() == 1 && leading_modsep { if a.prefix.segments.len() == 1 && leading_modsep {
let kind = UseSegmentKind::Ident("".to_owned(), None); let kind = UseSegmentKind::Ident("".to_owned(), None);
result.path.push(UseSegment { kind, version }); result.path.push(UseSegment {
kind,
style_edition,
});
} }
result.path.push(UseSegment { result.path.push(UseSegment {
kind: UseSegmentKind::Glob, kind: UseSegmentKind::Glob,
version, style_edition,
}); });
} }
UseTreeKind::Nested { UseTreeKind::Nested {
@ -470,7 +480,7 @@ impl UseTree {
",", ",",
|tree| tree.span.lo(), |tree| tree.span.lo(),
|tree| tree.span.hi(), |tree| tree.span.hi(),
|_| Some("".to_owned()), // We only need comments for now. |_| Ok("".to_owned()), // We only need comments for now.
context.snippet_provider.span_after(a.span, "{"), context.snippet_provider.span_after(a.span, "{"),
a.span.hi(), a.span.hi(),
false, false,
@ -480,7 +490,10 @@ impl UseTree {
// e.g., "::{foo, bar}" // e.g., "::{foo, bar}"
if a.prefix.segments.len() == 1 && leading_modsep { if a.prefix.segments.len() == 1 && leading_modsep {
let kind = UseSegmentKind::Ident("".to_owned(), None); let kind = UseSegmentKind::Ident("".to_owned(), None);
result.path.push(UseSegment { kind, version }); result.path.push(UseSegment {
kind,
style_edition,
});
} }
let kind = UseSegmentKind::List( let kind = UseSegmentKind::List(
list.iter() list.iter()
@ -490,7 +503,10 @@ impl UseTree {
}) })
.collect(), .collect(),
); );
result.path.push(UseSegment { kind, version }); result.path.push(UseSegment {
kind,
style_edition,
});
} }
UseTreeKind::Simple(ref rename) => { UseTreeKind::Simple(ref rename) => {
// If the path has leading double colons and is composed of only 2 segments, then we // If the path has leading double colons and is composed of only 2 segments, then we
@ -519,7 +535,10 @@ impl UseTree {
_ => UseSegmentKind::Ident(name, alias), _ => UseSegmentKind::Ident(name, alias),
}; };
let segment = UseSegment { kind, version }; let segment = UseSegment {
kind,
style_edition,
};
// `name` is already in result. // `name` is already in result.
result.path.pop(); result.path.pop();
@ -614,7 +633,7 @@ impl UseTree {
list.sort(); list.sort();
last = UseSegment { last = UseSegment {
kind: UseSegmentKind::List(list), kind: UseSegmentKind::List(list),
version: last.version, style_edition: last.style_edition,
}; };
} }
@ -732,9 +751,12 @@ impl UseTree {
}) = self.path.last() }) = self.path.last()
{ {
let self_segment = self.path.pop().unwrap(); let self_segment = self.path.pop().unwrap();
let version = self_segment.version; let style_edition = self_segment.style_edition;
let kind = UseSegmentKind::List(vec![UseTree::from_path(vec![self_segment], DUMMY_SP)]); let kind = UseSegmentKind::List(vec![UseTree::from_path(vec![self_segment], DUMMY_SP)]);
self.path.push(UseSegment { kind, version }); self.path.push(UseSegment {
kind,
style_edition,
});
} }
self self
} }
@ -750,7 +772,7 @@ fn merge_rest(
return None; return None;
} }
if a.len() != len && b.len() != len { if a.len() != len && b.len() != len {
let version = a[len].version; let style_edition = a[len].style_edition;
if let UseSegmentKind::List(ref list) = a[len].kind { if let UseSegmentKind::List(ref list) = a[len].kind {
let mut list = list.clone(); let mut list = list.clone();
merge_use_trees_inner( merge_use_trees_inner(
@ -760,7 +782,10 @@ fn merge_rest(
); );
let mut new_path = b[..len].to_vec(); let mut new_path = b[..len].to_vec();
let kind = UseSegmentKind::List(list); let kind = UseSegmentKind::List(list);
new_path.push(UseSegment { kind, version }); new_path.push(UseSegment {
kind,
style_edition,
});
return Some(new_path); return Some(new_path);
} }
} else if len == 1 { } else if len == 1 {
@ -770,9 +795,12 @@ fn merge_rest(
(&b[0], &a[1..]) (&b[0], &a[1..])
}; };
let kind = UseSegmentKind::Slf(common.get_alias().map(ToString::to_string)); let kind = UseSegmentKind::Slf(common.get_alias().map(ToString::to_string));
let version = a[0].version; let style_edition = a[0].style_edition;
let mut list = vec![UseTree::from_path( let mut list = vec![UseTree::from_path(
vec![UseSegment { kind, version }], vec![UseSegment {
kind,
style_edition,
}],
DUMMY_SP, DUMMY_SP,
)]; )];
match rest { match rest {
@ -788,7 +816,7 @@ fn merge_rest(
b[0].clone(), b[0].clone(),
UseSegment { UseSegment {
kind: UseSegmentKind::List(list), kind: UseSegmentKind::List(list),
version, style_edition,
}, },
]); ]);
} else { } else {
@ -801,8 +829,11 @@ fn merge_rest(
list.sort(); list.sort();
let mut new_path = b[..len].to_vec(); let mut new_path = b[..len].to_vec();
let kind = UseSegmentKind::List(list); let kind = UseSegmentKind::List(list);
let version = a[0].version; let style_edition = a[0].style_edition;
new_path.push(UseSegment { kind, version }); new_path.push(UseSegment {
kind,
style_edition,
});
Some(new_path) Some(new_path)
} }
@ -892,8 +923,8 @@ impl Ord for UseSegment {
| (Super(ref a), Super(ref b)) | (Super(ref a), Super(ref b))
| (Crate(ref a), Crate(ref b)) => match (a, b) { | (Crate(ref a), Crate(ref b)) => match (a, b) {
(Some(sa), Some(sb)) => { (Some(sa), Some(sb)) => {
if self.version == Version::Two { if self.style_edition >= StyleEdition::Edition2024 {
sa.trim_start_matches("r#").cmp(sb.trim_start_matches("r#")) version_sort(sa.trim_start_matches("r#"), sb.trim_start_matches("r#"))
} else { } else {
a.cmp(b) a.cmp(b)
} }
@ -902,25 +933,31 @@ impl Ord for UseSegment {
}, },
(Glob, Glob) => Ordering::Equal, (Glob, Glob) => Ordering::Equal,
(Ident(ref pia, ref aa), Ident(ref pib, ref ab)) => { (Ident(ref pia, ref aa), Ident(ref pib, ref ab)) => {
let (ia, ib) = if self.version == Version::Two { let (ia, ib) = if self.style_edition >= StyleEdition::Edition2024 {
(pia.trim_start_matches("r#"), pib.trim_start_matches("r#")) (pia.trim_start_matches("r#"), pib.trim_start_matches("r#"))
} else { } else {
(pia.as_str(), pib.as_str()) (pia.as_str(), pib.as_str())
}; };
// snake_case < CamelCase < UPPER_SNAKE_CASE
if ia.starts_with(char::is_uppercase) && ib.starts_with(char::is_lowercase) { let ident_ord = if self.style_edition >= StyleEdition::Edition2024 {
return Ordering::Greater; version_sort(ia, ib)
} } else {
if ia.starts_with(char::is_lowercase) && ib.starts_with(char::is_uppercase) { // snake_case < CamelCase < UPPER_SNAKE_CASE
return Ordering::Less; if ia.starts_with(char::is_uppercase) && ib.starts_with(char::is_lowercase) {
} return Ordering::Greater;
if is_upper_snake_case(ia) && !is_upper_snake_case(ib) { }
return Ordering::Greater; if ia.starts_with(char::is_lowercase) && ib.starts_with(char::is_uppercase) {
} return Ordering::Less;
if !is_upper_snake_case(ia) && is_upper_snake_case(ib) { }
return Ordering::Less; if is_upper_snake_case(ia) && !is_upper_snake_case(ib) {
} return Ordering::Greater;
let ident_ord = ia.cmp(ib); }
if !is_upper_snake_case(ia) && is_upper_snake_case(ib) {
return Ordering::Less;
}
ia.cmp(ib)
};
if ident_ord != Ordering::Equal { if ident_ord != Ordering::Equal {
return ident_ord; return ident_ord;
} }
@ -928,9 +965,8 @@ impl Ord for UseSegment {
(None, Some(_)) => Ordering::Less, (None, Some(_)) => Ordering::Less,
(Some(_), None) => Ordering::Greater, (Some(_), None) => Ordering::Greater,
(Some(aas), Some(abs)) => { (Some(aas), Some(abs)) => {
if self.version == Version::Two { if self.style_edition >= StyleEdition::Edition2024 {
aas.trim_start_matches("r#") version_sort(aas.trim_start_matches("r#"), abs.trim_start_matches("r#"))
.cmp(abs.trim_start_matches("r#"))
} else { } else {
aas.cmp(abs) aas.cmp(abs)
} }
@ -982,21 +1018,24 @@ fn rewrite_nested_use_tree(
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
use_tree_list: &[UseTree], use_tree_list: &[UseTree],
shape: Shape, shape: Shape,
) -> Option<String> { ) -> RewriteResult {
let mut list_items = Vec::with_capacity(use_tree_list.len()); let mut list_items = Vec::with_capacity(use_tree_list.len());
let nested_shape = match context.config.imports_indent() { let nested_shape = match context.config.imports_indent() {
IndentStyle::Block => shape IndentStyle::Block => shape
.block_indent(context.config.tab_spaces()) .block_indent(context.config.tab_spaces())
.with_max_width(context.config) .with_max_width(context.config)
.sub_width(1)?, .sub_width(1)
.unknown_error()?,
IndentStyle::Visual => shape.visual_indent(0), IndentStyle::Visual => shape.visual_indent(0),
}; };
for use_tree in use_tree_list { for use_tree in use_tree_list {
if let Some(mut list_item) = use_tree.list_item.clone() { if let Some(mut list_item) = use_tree.list_item.clone() {
list_item.item = use_tree.rewrite(context, nested_shape); list_item.item = use_tree.rewrite_result(context, nested_shape);
list_items.push(list_item); list_items.push(list_item);
} else { } else {
list_items.push(ListItem::from_str(use_tree.rewrite(context, nested_shape)?)); list_items.push(ListItem::from_str(
use_tree.rewrite_result(context, nested_shape)?,
));
} }
} }
let has_nested_list = use_tree_list.iter().any(|use_segment| { let has_nested_list = use_tree_list.iter().any(|use_segment| {
@ -1049,12 +1088,16 @@ fn rewrite_nested_use_tree(
format!("{{{list_str}}}") format!("{{{list_str}}}")
}; };
Some(result) Ok(result)
} }
impl Rewrite for UseSegment { impl Rewrite for UseSegment {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
Some(match self.kind { self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
Ok(match self.kind {
UseSegmentKind::Ident(ref ident, Some(ref rename)) => { UseSegmentKind::Ident(ref ident, Some(ref rename)) => {
format!("{ident} as {rename}") format!("{ident} as {rename}")
} }
@ -1066,31 +1109,42 @@ impl Rewrite for UseSegment {
UseSegmentKind::Crate(Some(ref rename)) => format!("crate as {rename}"), UseSegmentKind::Crate(Some(ref rename)) => format!("crate as {rename}"),
UseSegmentKind::Crate(None) => "crate".to_owned(), UseSegmentKind::Crate(None) => "crate".to_owned(),
UseSegmentKind::Glob => "*".to_owned(), UseSegmentKind::Glob => "*".to_owned(),
UseSegmentKind::List(ref use_tree_list) => rewrite_nested_use_tree( UseSegmentKind::List(ref use_tree_list) => {
context, rewrite_nested_use_tree(
use_tree_list, context,
// 1 = "{" and "}" use_tree_list,
shape.offset_left(1)?.sub_width(1)?, // 1 = "{" and "}"
)?, shape
.offset_left(1)
.and_then(|s| s.sub_width(1))
.unknown_error()?,
)?
}
}) })
} }
} }
impl Rewrite for UseTree { impl Rewrite for UseTree {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
// This does NOT format attributes and visibility or add a trailing `;`. // This does NOT format attributes and visibility or add a trailing `;`.
fn rewrite(&self, context: &RewriteContext<'_>, mut shape: Shape) -> Option<String> { fn rewrite_result(&self, context: &RewriteContext<'_>, mut shape: Shape) -> RewriteResult {
let mut result = String::with_capacity(256); let mut result = String::with_capacity(256);
let mut iter = self.path.iter().peekable(); let mut iter = self.path.iter().peekable();
while let Some(segment) = iter.next() { while let Some(segment) = iter.next() {
let segment_str = segment.rewrite(context, shape)?; let segment_str = segment.rewrite_result(context, shape)?;
result.push_str(&segment_str); result.push_str(&segment_str);
if iter.peek().is_some() { if iter.peek().is_some() {
result.push_str("::"); result.push_str("::");
// 2 = "::" // 2 = "::"
shape = shape.offset_left(2 + segment_str.len())?; shape = shape
.offset_left(2 + segment_str.len())
.max_width_error(shape.width, self.span())?;
} }
} }
Some(result) Ok(result)
} }
} }
@ -1114,7 +1168,7 @@ mod test {
struct Parser<'a> { struct Parser<'a> {
input: Peekable<Chars<'a>>, input: Peekable<Chars<'a>>,
version: Version, style_edition: StyleEdition,
} }
impl<'a> Parser<'a> { impl<'a> Parser<'a> {
@ -1132,7 +1186,7 @@ mod test {
buf: &mut String, buf: &mut String,
alias_buf: &mut Option<String>, alias_buf: &mut Option<String>,
) { ) {
let version = self.version; let style_edition = self.style_edition;
if !buf.is_empty() { if !buf.is_empty() {
let mut alias = None; let mut alias = None;
swap(alias_buf, &mut alias); swap(alias_buf, &mut alias);
@ -1140,19 +1194,28 @@ mod test {
match buf.as_ref() { match buf.as_ref() {
"self" => { "self" => {
let kind = UseSegmentKind::Slf(alias); let kind = UseSegmentKind::Slf(alias);
result.push(UseSegment { kind, version }); result.push(UseSegment {
kind,
style_edition,
});
*buf = String::new(); *buf = String::new();
*alias_buf = None; *alias_buf = None;
} }
"super" => { "super" => {
let kind = UseSegmentKind::Super(alias); let kind = UseSegmentKind::Super(alias);
result.push(UseSegment { kind, version }); result.push(UseSegment {
kind,
style_edition,
});
*buf = String::new(); *buf = String::new();
*alias_buf = None; *alias_buf = None;
} }
"crate" => { "crate" => {
let kind = UseSegmentKind::Crate(alias); let kind = UseSegmentKind::Crate(alias);
result.push(UseSegment { kind, version }); result.push(UseSegment {
kind,
style_edition,
});
*buf = String::new(); *buf = String::new();
*alias_buf = None; *alias_buf = None;
} }
@ -1160,7 +1223,10 @@ mod test {
let mut name = String::new(); let mut name = String::new();
swap(buf, &mut name); swap(buf, &mut name);
let kind = UseSegmentKind::Ident(name, alias); let kind = UseSegmentKind::Ident(name, alias);
result.push(UseSegment { kind, version }); result.push(UseSegment {
kind,
style_edition,
});
} }
} }
} }
@ -1178,7 +1244,7 @@ mod test {
let kind = UseSegmentKind::List(self.parse_list()); let kind = UseSegmentKind::List(self.parse_list());
result.push(UseSegment { result.push(UseSegment {
kind, kind,
version: self.version, style_edition: self.style_edition,
}); });
self.eat('}'); self.eat('}');
} }
@ -1188,7 +1254,7 @@ mod test {
let kind = UseSegmentKind::Glob; let kind = UseSegmentKind::Glob;
result.push(UseSegment { result.push(UseSegment {
kind, kind,
version: self.version, style_edition: self.style_edition,
}); });
} }
':' => { ':' => {
@ -1249,7 +1315,7 @@ mod test {
let mut parser = Parser { let mut parser = Parser {
input: s.chars().peekable(), input: s.chars().peekable(),
version: Version::One, style_edition: StyleEdition::Edition2015,
}; };
parser.parse_in_list() parser.parse_in_list()
} }

File diff suppressed because it is too large Load Diff

View File

@ -5,9 +5,6 @@
#![allow(clippy::match_like_matches_macro)] #![allow(clippy::match_like_matches_macro)]
#![allow(unreachable_pub)] #![allow(unreachable_pub)]
// #[macro_use]
// extern crate tracing;
// N.B. these crates are loaded from the sysroot, so they need extern crate. // N.B. these crates are loaded from the sysroot, so they need extern crate.
extern crate rustc_ast; extern crate rustc_ast;
extern crate rustc_ast_pretty; extern crate rustc_ast_pretty;
@ -48,8 +45,8 @@ use crate::shape::Indent;
use crate::utils::indent_next_line; use crate::utils::indent_next_line;
pub use crate::config::{ pub use crate::config::{
load_config, CliOptions, Color, Config, Edition, EmitMode, FileLines, FileName, NewlineStyle, CliOptions, Color, Config, Edition, EmitMode, FileLines, FileName, NewlineStyle, Range,
Range, Verbosity, StyleEdition, Verbosity, Version, load_config,
}; };
pub use crate::format_report_formatter::{FormatReportFormatter, FormatReportFormatterBuilder}; pub use crate::format_report_formatter::{FormatReportFormatter, FormatReportFormatterBuilder};
@ -94,6 +91,7 @@ mod rewrite;
pub(crate) mod rustfmt_diff; pub(crate) mod rustfmt_diff;
mod shape; mod shape;
mod skip; mod skip;
mod sort;
pub(crate) mod source_file; pub(crate) mod source_file;
pub(crate) mod source_map; pub(crate) mod source_map;
mod spanned; mod spanned;

View File

@ -5,10 +5,10 @@ use std::iter::Peekable;
use rustc_span::BytePos; use rustc_span::BytePos;
use crate::comment::{find_comment_end, rewrite_comment, FindUncommented}; use crate::comment::{FindUncommented, find_comment_end, rewrite_comment};
use crate::config::lists::*; use crate::config::lists::*;
use crate::config::{Config, IndentStyle}; use crate::config::{Config, IndentStyle};
use crate::rewrite::RewriteContext; use crate::rewrite::{RewriteContext, RewriteError, RewriteResult};
use crate::shape::{Indent, Shape}; use crate::shape::{Indent, Shape};
use crate::utils::{ use crate::utils::{
count_newlines, first_line_width, last_line_width, mk_sp, starts_with_newline, count_newlines, first_line_width, last_line_width, mk_sp, starts_with_newline,
@ -125,18 +125,18 @@ pub(crate) struct ListItem {
pub(crate) pre_comment_style: ListItemCommentStyle, pub(crate) pre_comment_style: ListItemCommentStyle,
// Item should include attributes and doc comments. None indicates a failed // Item should include attributes and doc comments. None indicates a failed
// rewrite. // rewrite.
pub(crate) item: Option<String>, pub(crate) item: RewriteResult,
pub(crate) post_comment: Option<String>, pub(crate) post_comment: Option<String>,
// Whether there is extra whitespace before this item. // Whether there is extra whitespace before this item.
pub(crate) new_lines: bool, pub(crate) new_lines: bool,
} }
impl ListItem { impl ListItem {
pub(crate) fn empty() -> ListItem { pub(crate) fn from_item(item: RewriteResult) -> ListItem {
ListItem { ListItem {
pre_comment: None, pre_comment: None,
pre_comment_style: ListItemCommentStyle::None, pre_comment_style: ListItemCommentStyle::None,
item: None, item: item,
post_comment: None, post_comment: None,
new_lines: false, new_lines: false,
} }
@ -185,7 +185,7 @@ impl ListItem {
ListItem { ListItem {
pre_comment: None, pre_comment: None,
pre_comment_style: ListItemCommentStyle::None, pre_comment_style: ListItemCommentStyle::None,
item: Some(s.into()), item: Ok(s.into()),
post_comment: None, post_comment: None,
new_lines: false, new_lines: false,
} }
@ -197,7 +197,11 @@ impl ListItem {
!matches!(*s, Some(ref s) if !s.is_empty()) !matches!(*s, Some(ref s) if !s.is_empty())
} }
!(empty(&self.pre_comment) && empty(&self.item) && empty(&self.post_comment)) fn empty_result(s: &RewriteResult) -> bool {
!matches!(*s, Ok(ref s) if !s.is_empty())
}
!(empty(&self.pre_comment) && empty_result(&self.item) && empty(&self.post_comment))
} }
} }
@ -257,7 +261,7 @@ where
} }
// Format a list of commented items into a string. // Format a list of commented items into a string.
pub(crate) fn write_list<I, T>(items: I, formatting: &ListFormatting<'_>) -> Option<String> pub(crate) fn write_list<I, T>(items: I, formatting: &ListFormatting<'_>) -> RewriteResult
where where
I: IntoIterator<Item = T> + Clone, I: IntoIterator<Item = T> + Clone,
T: AsRef<ListItem>, T: AsRef<ListItem>,
@ -281,7 +285,7 @@ where
let indent_str = &formatting.shape.indent.to_string(formatting.config); let indent_str = &formatting.shape.indent.to_string(formatting.config);
while let Some((i, item)) = iter.next() { while let Some((i, item)) = iter.next() {
let item = item.as_ref(); let item = item.as_ref();
let inner_item = item.item.as_ref()?; let inner_item = item.item.as_ref().or_else(|err| Err(err.clone()))?;
let first = i == 0; let first = i == 0;
let last = iter.peek().is_none(); let last = iter.peek().is_none();
let mut separate = match sep_place { let mut separate = match sep_place {
@ -516,7 +520,7 @@ where
prev_item_is_nested_import = inner_item.contains("::"); prev_item_is_nested_import = inner_item.contains("::");
} }
Some(result) Ok(result)
} }
fn max_width_of_item_with_post_comment<I, T>( fn max_width_of_item_with_post_comment<I, T>(
@ -741,7 +745,7 @@ where
I: Iterator<Item = T>, I: Iterator<Item = T>,
F1: Fn(&T) -> BytePos, F1: Fn(&T) -> BytePos,
F2: Fn(&T) -> BytePos, F2: Fn(&T) -> BytePos,
F3: Fn(&T) -> Option<String>, F3: Fn(&T) -> RewriteResult,
{ {
type Item = ListItem; type Item = ListItem;
@ -775,8 +779,9 @@ where
ListItem { ListItem {
pre_comment, pre_comment,
pre_comment_style, pre_comment_style,
// leave_last is set to true only for rewrite_items
item: if self.inner.peek().is_none() && self.leave_last { item: if self.inner.peek().is_none() && self.leave_last {
None Err(RewriteError::SkipFormatting)
} else { } else {
(self.get_item_string)(&item) (self.get_item_string)(&item)
}, },
@ -805,7 +810,7 @@ where
I: Iterator<Item = T>, I: Iterator<Item = T>,
F1: Fn(&T) -> BytePos, F1: Fn(&T) -> BytePos,
F2: Fn(&T) -> BytePos, F2: Fn(&T) -> BytePos,
F3: Fn(&T) -> Option<String>, F3: Fn(&T) -> RewriteResult,
{ {
ListItems { ListItems {
snippet_provider, snippet_provider,

View File

@ -10,35 +10,37 @@
// and those with brackets will be formatted as array literals. // and those with brackets will be formatted as array literals.
use std::collections::HashMap; use std::collections::HashMap;
use std::panic::{catch_unwind, AssertUnwindSafe}; use std::panic::{AssertUnwindSafe, catch_unwind};
use rustc_ast::token::{BinOpToken, Delimiter, Token, TokenKind}; use rustc_ast::token::{BinOpToken, Delimiter, Token, TokenKind};
use rustc_ast::tokenstream::{RefTokenTreeCursor, TokenStream, TokenTree}; use rustc_ast::tokenstream::{RefTokenTreeCursor, TokenStream, TokenTree};
use rustc_ast::{ast, ptr}; use rustc_ast::{ast, ptr};
use rustc_ast_pretty::pprust; use rustc_ast_pretty::pprust;
use rustc_span::{ use rustc_span::{
BytePos, DUMMY_SP, Span, Symbol,
symbol::{self, kw}, symbol::{self, kw},
BytePos, Span, Symbol, DUMMY_SP,
}; };
use tracing::debug; use tracing::debug;
use crate::comment::{ use crate::comment::{
contains_comment, CharClasses, FindUncommented, FullCodeCharKind, LineClasses, CharClasses, FindUncommented, FullCodeCharKind, LineClasses, contains_comment,
}; };
use crate::config::StyleEdition;
use crate::config::lists::*; use crate::config::lists::*;
use crate::config::Version; use crate::expr::{RhsAssignKind, rewrite_array, rewrite_assign_rhs};
use crate::expr::{rewrite_array, rewrite_assign_rhs, RhsAssignKind}; use crate::lists::{ListFormatting, itemize_list, write_list};
use crate::lists::{itemize_list, write_list, ListFormatting};
use crate::overflow; use crate::overflow;
use crate::parse::macros::lazy_static::parse_lazy_static; use crate::parse::macros::lazy_static::parse_lazy_static;
use crate::parse::macros::{parse_expr, parse_macro_args, ParsedMacroArgs}; use crate::parse::macros::{ParsedMacroArgs, parse_expr, parse_macro_args};
use crate::rewrite::{Rewrite, RewriteContext}; use crate::rewrite::{
MacroErrorKind, Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult,
};
use crate::shape::{Indent, Shape}; use crate::shape::{Indent, Shape};
use crate::source_map::SpanUtils; use crate::source_map::SpanUtils;
use crate::spanned::Spanned; use crate::spanned::Spanned;
use crate::utils::{ use crate::utils::{
filtered_str_fits, format_visibility, indent_next_line, is_empty_line, mk_sp, NodeIdExt, filtered_str_fits, format_visibility, indent_next_line, is_empty_line, mk_sp,
remove_trailing_white_spaces, rewrite_ident, trim_left_preserve_layout, NodeIdExt, remove_trailing_white_spaces, rewrite_ident, trim_left_preserve_layout,
}; };
use crate::visitor::FmtVisitor; use crate::visitor::FmtVisitor;
@ -72,22 +74,30 @@ impl MacroArg {
impl Rewrite for ast::Item { impl Rewrite for ast::Item {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
let mut visitor = crate::visitor::FmtVisitor::from_context(context); let mut visitor = crate::visitor::FmtVisitor::from_context(context);
visitor.block_indent = shape.indent; visitor.block_indent = shape.indent;
visitor.last_pos = self.span().lo(); visitor.last_pos = self.span().lo();
visitor.visit_item(self); visitor.visit_item(self);
Some(visitor.buffer.to_owned()) Ok(visitor.buffer.to_owned())
} }
} }
impl Rewrite for MacroArg { impl Rewrite for MacroArg {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
match *self { match *self {
MacroArg::Expr(ref expr) => expr.rewrite(context, shape), MacroArg::Expr(ref expr) => expr.rewrite_result(context, shape),
MacroArg::Ty(ref ty) => ty.rewrite(context, shape), MacroArg::Ty(ref ty) => ty.rewrite_result(context, shape),
MacroArg::Pat(ref pat) => pat.rewrite(context, shape), MacroArg::Pat(ref pat) => pat.rewrite_result(context, shape),
MacroArg::Item(ref item) => item.rewrite(context, shape), MacroArg::Item(ref item) => item.rewrite_result(context, shape),
MacroArg::Keyword(ident, _) => Some(ident.name.to_string()), MacroArg::Keyword(ident, _) => Ok(ident.name.to_string()),
} }
} }
} }
@ -111,12 +121,14 @@ fn rewrite_macro_name(
} }
// Use this on failing to format the macro call. // Use this on failing to format the macro call.
// TODO(ding-young) We should also report macro parse failure to tell users why given snippet
// is left unformatted. One possible improvement is appending formatting error to context.report
fn return_macro_parse_failure_fallback( fn return_macro_parse_failure_fallback(
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
indent: Indent, indent: Indent,
position: MacroPosition, position: MacroPosition,
span: Span, span: Span,
) -> Option<String> { ) -> RewriteResult {
// Mark this as a failure however we format it // Mark this as a failure however we format it
context.macro_rewrite_failure.replace(true); context.macro_rewrite_failure.replace(true);
@ -134,7 +146,8 @@ fn return_macro_parse_failure_fallback(
}) })
.unwrap_or(false); .unwrap_or(false);
if is_like_block_indent_style { if is_like_block_indent_style {
return trim_left_preserve_layout(context.snippet(span), indent, context.config); return trim_left_preserve_layout(context.snippet(span), indent, context.config)
.macro_error(MacroErrorKind::Unknown, span);
} }
context.skipped_range.borrow_mut().push(( context.skipped_range.borrow_mut().push((
@ -147,7 +160,7 @@ fn return_macro_parse_failure_fallback(
if position == MacroPosition::Item { if position == MacroPosition::Item {
snippet.push(';'); snippet.push(';');
} }
Some(snippet) Ok(snippet)
} }
pub(crate) fn rewrite_macro( pub(crate) fn rewrite_macro(
@ -156,13 +169,13 @@ pub(crate) fn rewrite_macro(
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
shape: Shape, shape: Shape,
position: MacroPosition, position: MacroPosition,
) -> Option<String> { ) -> RewriteResult {
let should_skip = context let should_skip = context
.skip_context .skip_context
.macros .macros
.skip(context.snippet(mac.path.span)); .skip(context.snippet(mac.path.span));
if should_skip { if should_skip {
None Err(RewriteError::SkipFormatting)
} else { } else {
let guard = context.enter_macro(); let guard = context.enter_macro();
let result = catch_unwind(AssertUnwindSafe(|| { let result = catch_unwind(AssertUnwindSafe(|| {
@ -176,9 +189,16 @@ pub(crate) fn rewrite_macro(
) )
})); }));
match result { match result {
Err(..) | Ok(None) => { Err(..) => {
context.macro_rewrite_failure.replace(true); context.macro_rewrite_failure.replace(true);
None Err(RewriteError::MacroFailure {
kind: MacroErrorKind::Unknown,
span: mac.span(),
})
}
Ok(Err(e)) => {
context.macro_rewrite_failure.replace(true);
Err(e)
} }
Ok(rw) => rw, Ok(rw) => rw,
} }
@ -192,11 +212,11 @@ fn rewrite_macro_inner(
shape: Shape, shape: Shape,
position: MacroPosition, position: MacroPosition,
is_nested_macro: bool, is_nested_macro: bool,
) -> Option<String> { ) -> RewriteResult {
if context.config.use_try_shorthand() { if context.config.use_try_shorthand() {
if let Some(expr) = convert_try_mac(mac, context) { if let Some(expr) = convert_try_mac(mac, context) {
context.leave_macro(); context.leave_macro();
return expr.rewrite(context, shape); return expr.rewrite_result(context, shape);
} }
} }
@ -216,21 +236,27 @@ fn rewrite_macro_inner(
if ts.is_empty() && !has_comment { if ts.is_empty() && !has_comment {
return match style { return match style {
Delimiter::Parenthesis if position == MacroPosition::Item => { Delimiter::Parenthesis if position == MacroPosition::Item => {
Some(format!("{macro_name}();")) Ok(format!("{macro_name}();"))
} }
Delimiter::Bracket if position == MacroPosition::Item => { Delimiter::Bracket if position == MacroPosition::Item => Ok(format!("{macro_name}[];")),
Some(format!("{macro_name}[];")) Delimiter::Parenthesis => Ok(format!("{macro_name}()")),
} Delimiter::Bracket => Ok(format!("{macro_name}[]")),
Delimiter::Parenthesis => Some(format!("{macro_name}()")), Delimiter::Brace => Ok(format!("{macro_name} {{}}")),
Delimiter::Bracket => Some(format!("{macro_name}[]")),
Delimiter::Brace => Some(format!("{macro_name} {{}}")),
_ => unreachable!(), _ => unreachable!(),
}; };
} }
// Format well-known macros which cannot be parsed as a valid AST. // Format well-known macros which cannot be parsed as a valid AST.
if macro_name == "lazy_static!" && !has_comment { if macro_name == "lazy_static!" && !has_comment {
if let success @ Some(..) = format_lazy_static(context, shape, ts.clone()) { match format_lazy_static(context, shape, ts.clone(), mac.span()) {
return success; Ok(rw) => return Ok(rw),
Err(err) => match err {
// We will move on to parsing macro args just like other macros
// if we could not parse lazy_static! with known syntax
RewriteError::MacroFailure { kind, span: _ }
if kind == MacroErrorKind::ParseFailure => {}
// If formatting fails even though parsing succeeds, return the err early
_ => return Err(err),
},
} }
} }
@ -267,7 +293,7 @@ fn rewrite_macro_inner(
Delimiter::Parenthesis => { Delimiter::Parenthesis => {
// Handle special case: `vec!(expr; expr)` // Handle special case: `vec!(expr; expr)`
if vec_with_semi { if vec_with_semi {
handle_vec_semi(context, shape, arg_vec, macro_name, style) handle_vec_semi(context, shape, arg_vec, macro_name, style, mac.span())
} else { } else {
// Format macro invocation as function call, preserve the trailing // Format macro invocation as function call, preserve the trailing
// comma because not all macros support them. // comma because not all macros support them.
@ -293,7 +319,7 @@ fn rewrite_macro_inner(
Delimiter::Bracket => { Delimiter::Bracket => {
// Handle special case: `vec![expr; expr]` // Handle special case: `vec![expr; expr]`
if vec_with_semi { if vec_with_semi {
handle_vec_semi(context, shape, arg_vec, macro_name, style) handle_vec_semi(context, shape, arg_vec, macro_name, style, mac.span())
} else { } else {
// If we are rewriting `vec!` macro or other special macros, // If we are rewriting `vec!` macro or other special macros,
// then we can rewrite this as a usual array literal. // then we can rewrite this as a usual array literal.
@ -323,7 +349,7 @@ fn rewrite_macro_inner(
_ => "", _ => "",
}; };
Some(format!("{rewrite}{comma}")) Ok(format!("{rewrite}{comma}"))
} }
} }
Delimiter::Brace => { Delimiter::Brace => {
@ -332,8 +358,8 @@ fn rewrite_macro_inner(
// anything in between the braces (for now). // anything in between the braces (for now).
let snippet = context.snippet(mac.span()).trim_start_matches(|c| c != '{'); let snippet = context.snippet(mac.span()).trim_start_matches(|c| c != '{');
match trim_left_preserve_layout(snippet, shape.indent, context.config) { match trim_left_preserve_layout(snippet, shape.indent, context.config) {
Some(macro_body) => Some(format!("{macro_name} {macro_body}")), Some(macro_body) => Ok(format!("{macro_name} {macro_body}")),
None => Some(format!("{macro_name} {snippet}")), None => Ok(format!("{macro_name} {snippet}")),
} }
} }
_ => unreachable!(), _ => unreachable!(),
@ -346,28 +372,32 @@ fn handle_vec_semi(
arg_vec: Vec<MacroArg>, arg_vec: Vec<MacroArg>,
macro_name: String, macro_name: String,
delim_token: Delimiter, delim_token: Delimiter,
) -> Option<String> { span: Span,
) -> RewriteResult {
let (left, right) = match delim_token { let (left, right) = match delim_token {
Delimiter::Parenthesis => ("(", ")"), Delimiter::Parenthesis => ("(", ")"),
Delimiter::Bracket => ("[", "]"), Delimiter::Bracket => ("[", "]"),
_ => unreachable!(), _ => unreachable!(),
}; };
let mac_shape = shape.offset_left(macro_name.len())?; // Should we return MaxWidthError, Or Macro failure
let mac_shape = shape
.offset_left(macro_name.len())
.max_width_error(shape.width, span)?;
// 8 = `vec![]` + `; ` or `vec!()` + `; ` // 8 = `vec![]` + `; ` or `vec!()` + `; `
let total_overhead = 8; let total_overhead = 8;
let nested_shape = mac_shape.block_indent(context.config.tab_spaces()); let nested_shape = mac_shape.block_indent(context.config.tab_spaces());
let lhs = arg_vec[0].rewrite(context, nested_shape)?; let lhs = arg_vec[0].rewrite_result(context, nested_shape)?;
let rhs = arg_vec[1].rewrite(context, nested_shape)?; let rhs = arg_vec[1].rewrite_result(context, nested_shape)?;
if !lhs.contains('\n') if !lhs.contains('\n')
&& !rhs.contains('\n') && !rhs.contains('\n')
&& lhs.len() + rhs.len() + total_overhead <= shape.width && lhs.len() + rhs.len() + total_overhead <= shape.width
{ {
// macro_name(lhs; rhs) or macro_name[lhs; rhs] // macro_name(lhs; rhs) or macro_name[lhs; rhs]
Some(format!("{macro_name}{left}{lhs}; {rhs}{right}")) Ok(format!("{macro_name}{left}{lhs}; {rhs}{right}"))
} else { } else {
// macro_name(\nlhs;\nrhs\n) or macro_name[\nlhs;\nrhs\n] // macro_name(\nlhs;\nrhs\n) or macro_name[\nlhs;\nrhs\n]
Some(format!( Ok(format!(
"{}{}{}{};{}{}{}{}", "{}{}{}{};{}{}{}{}",
macro_name, macro_name,
left, left,
@ -385,7 +415,7 @@ fn rewrite_empty_macro_def_body(
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
span: Span, span: Span,
shape: Shape, shape: Shape,
) -> Option<String> { ) -> RewriteResult {
// Create an empty, dummy `ast::Block` representing an empty macro body // Create an empty, dummy `ast::Block` representing an empty macro body
let block = ast::Block { let block = ast::Block {
stmts: vec![].into(), stmts: vec![].into(),
@ -395,7 +425,7 @@ fn rewrite_empty_macro_def_body(
tokens: None, tokens: None,
could_be_bare_literal: false, could_be_bare_literal: false,
}; };
block.rewrite(context, shape) block.rewrite_result(context, shape)
} }
pub(crate) fn rewrite_macro_def( pub(crate) fn rewrite_macro_def(
@ -406,8 +436,8 @@ pub(crate) fn rewrite_macro_def(
ident: symbol::Ident, ident: symbol::Ident,
vis: &ast::Visibility, vis: &ast::Visibility,
span: Span, span: Span,
) -> Option<String> { ) -> RewriteResult {
let snippet = Some(remove_trailing_white_spaces(context.snippet(span))); let snippet = Ok(remove_trailing_white_spaces(context.snippet(span)));
if snippet.as_ref().map_or(true, |s| s.ends_with(';')) { if snippet.as_ref().map_or(true, |s| s.ends_with(';')) {
return snippet; return snippet;
} }
@ -442,7 +472,7 @@ pub(crate) fn rewrite_macro_def(
let lo = context.snippet_provider.span_before(span, "{"); let lo = context.snippet_provider.span_before(span, "{");
result += " "; result += " ";
result += &rewrite_empty_macro_def_body(context, span.with_lo(lo), shape)?; result += &rewrite_empty_macro_def_body(context, span.with_lo(lo), shape)?;
return Some(result); return Ok(result);
} }
let branch_items = itemize_list( let branch_items = itemize_list(
@ -453,13 +483,14 @@ pub(crate) fn rewrite_macro_def(
|branch| branch.span.lo(), |branch| branch.span.lo(),
|branch| branch.span.hi(), |branch| branch.span.hi(),
|branch| match branch.rewrite(context, arm_shape, multi_branch_style) { |branch| match branch.rewrite(context, arm_shape, multi_branch_style) {
Some(v) => Some(v), Ok(v) => Ok(v),
// if the rewrite returned None because a macro could not be rewritten, then return the // if the rewrite returned None because a macro could not be rewritten, then return the
// original body // original body
None if context.macro_rewrite_failure.get() => { // TODO(ding-young) report rewrite error even if we return Ok with original snippet
Some(context.snippet(branch.body).trim().to_string()) Err(_) if context.macro_rewrite_failure.get() => {
Ok(context.snippet(branch.body).trim().to_string())
} }
None => None, Err(e) => Err(e),
}, },
context.snippet_provider.span_after(span, "{"), context.snippet_provider.span_after(span, "{"),
span.hi(), span.hi(),
@ -478,8 +509,8 @@ pub(crate) fn rewrite_macro_def(
} }
match write_list(&branch_items, &fmt) { match write_list(&branch_items, &fmt) {
Some(ref s) => result += s, Ok(ref s) => result += s,
None => return snippet, Err(_) => return snippet,
} }
if multi_branch_style { if multi_branch_style {
@ -487,7 +518,7 @@ pub(crate) fn rewrite_macro_def(
result += "}"; result += "}";
} }
Some(result) Ok(result)
} }
fn register_metavariable( fn register_metavariable(
@ -639,12 +670,13 @@ impl MacroArgKind {
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
shape: Shape, shape: Shape,
use_multiple_lines: bool, use_multiple_lines: bool,
) -> Option<String> { ) -> RewriteResult {
let rewrite_delimited_inner = |delim_tok, args| -> Option<(String, String, String)> { type DelimitedArgsRewrite = Result<(String, String, String), RewriteError>;
let rewrite_delimited_inner = |delim_tok, args| -> DelimitedArgsRewrite {
let inner = wrap_macro_args(context, args, shape)?; let inner = wrap_macro_args(context, args, shape)?;
let (lhs, rhs) = delim_token_to_str(context, delim_tok, shape, false, inner.is_empty()); let (lhs, rhs) = delim_token_to_str(context, delim_tok, shape, false, inner.is_empty());
if lhs.len() + inner.len() + rhs.len() <= shape.width { if lhs.len() + inner.len() + rhs.len() <= shape.width {
return Some((lhs, inner, rhs)); return Ok((lhs, inner, rhs));
} }
let (lhs, rhs) = delim_token_to_str(context, delim_tok, shape, true, false); let (lhs, rhs) = delim_token_to_str(context, delim_tok, shape, true, false);
@ -652,27 +684,27 @@ impl MacroArgKind {
.block_indent(context.config.tab_spaces()) .block_indent(context.config.tab_spaces())
.with_max_width(context.config); .with_max_width(context.config);
let inner = wrap_macro_args(context, args, nested_shape)?; let inner = wrap_macro_args(context, args, nested_shape)?;
Some((lhs, inner, rhs)) Ok((lhs, inner, rhs))
}; };
match *self { match *self {
MacroArgKind::MetaVariable(ty, ref name) => Some(format!("${name}:{ty}")), MacroArgKind::MetaVariable(ty, ref name) => Ok(format!("${name}:{ty}")),
MacroArgKind::Repeat(delim_tok, ref args, ref another, ref tok) => { MacroArgKind::Repeat(delim_tok, ref args, ref another, ref tok) => {
let (lhs, inner, rhs) = rewrite_delimited_inner(delim_tok, args)?; let (lhs, inner, rhs) = rewrite_delimited_inner(delim_tok, args)?;
let another = another let another = another
.as_ref() .as_ref()
.and_then(|a| a.rewrite(context, shape, use_multiple_lines)) .and_then(|a| a.rewrite(context, shape, use_multiple_lines).ok())
.unwrap_or_else(|| "".to_owned()); .unwrap_or_else(|| "".to_owned());
let repeat_tok = pprust::token_to_string(tok); let repeat_tok = pprust::token_to_string(tok);
Some(format!("${lhs}{inner}{rhs}{another}{repeat_tok}")) Ok(format!("${lhs}{inner}{rhs}{another}{repeat_tok}"))
} }
MacroArgKind::Delimited(delim_tok, ref args) => { MacroArgKind::Delimited(delim_tok, ref args) => {
rewrite_delimited_inner(delim_tok, args) rewrite_delimited_inner(delim_tok, args)
.map(|(lhs, inner, rhs)| format!("{}{}{}", lhs, inner, rhs)) .map(|(lhs, inner, rhs)| format!("{}{}{}", lhs, inner, rhs))
} }
MacroArgKind::Separator(ref sep, ref prefix) => Some(format!("{prefix}{sep} ")), MacroArgKind::Separator(ref sep, ref prefix) => Ok(format!("{prefix}{sep} ")),
MacroArgKind::Other(ref inner, ref prefix) => Some(format!("{prefix}{inner}")), MacroArgKind::Other(ref inner, ref prefix) => Ok(format!("{prefix}{inner}")),
} }
} }
} }
@ -688,7 +720,7 @@ impl ParsedMacroArg {
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
shape: Shape, shape: Shape,
use_multiple_lines: bool, use_multiple_lines: bool,
) -> Option<String> { ) -> RewriteResult {
self.kind.rewrite(context, shape, use_multiple_lines) self.kind.rewrite(context, shape, use_multiple_lines)
} }
} }
@ -966,9 +998,9 @@ fn wrap_macro_args(
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
args: &[ParsedMacroArg], args: &[ParsedMacroArg],
shape: Shape, shape: Shape,
) -> Option<String> { ) -> RewriteResult {
wrap_macro_args_inner(context, args, shape, false) wrap_macro_args_inner(context, args, shape, false)
.or_else(|| wrap_macro_args_inner(context, args, shape, true)) .or_else(|_| wrap_macro_args_inner(context, args, shape, true))
} }
fn wrap_macro_args_inner( fn wrap_macro_args_inner(
@ -976,7 +1008,7 @@ fn wrap_macro_args_inner(
args: &[ParsedMacroArg], args: &[ParsedMacroArg],
shape: Shape, shape: Shape,
use_multiple_lines: bool, use_multiple_lines: bool,
) -> Option<String> { ) -> RewriteResult {
let mut result = String::with_capacity(128); let mut result = String::with_capacity(128);
let mut iter = args.iter().peekable(); let mut iter = args.iter().peekable();
let indent_str = shape.indent.to_string_with_newline(context.config); let indent_str = shape.indent.to_string_with_newline(context.config);
@ -1002,9 +1034,9 @@ fn wrap_macro_args_inner(
} }
if !use_multiple_lines && result.len() >= shape.width { if !use_multiple_lines && result.len() >= shape.width {
None Err(RewriteError::Unknown)
} else { } else {
Some(result) Ok(result)
} }
} }
@ -1012,22 +1044,21 @@ fn wrap_macro_args_inner(
// for some common cases. I hope the basic logic is sufficient. Note that the // 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., `*` // meaning of some tokens is a bit different here from usual Rust, e.g., `*`
// and `(`/`)` have special meaning. // 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( fn format_macro_args(
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
token_stream: TokenStream, token_stream: TokenStream,
shape: Shape, shape: Shape,
) -> Option<String> { ) -> RewriteResult {
let span = span_for_token_stream(&token_stream);
if !context.config.format_macro_matchers() { if !context.config.format_macro_matchers() {
let span = span_for_token_stream(&token_stream); return Ok(match span {
return Some(match span {
Some(span) => context.snippet(span).to_owned(), Some(span) => context.snippet(span).to_owned(),
None => String::new(), None => String::new(),
}); });
} }
let parsed_args = MacroArgParser::new().parse(token_stream)?; let parsed_args = MacroArgParser::new()
.parse(token_stream)
.macro_error(MacroErrorKind::ParseFailure, span.unwrap())?;
wrap_macro_args(context, &parsed_args, shape) wrap_macro_args(context, &parsed_args, shape)
} }
@ -1132,9 +1163,9 @@ pub(crate) fn convert_try_mac(
pub(crate) fn macro_style(mac: &ast::MacCall, context: &RewriteContext<'_>) -> Delimiter { pub(crate) fn macro_style(mac: &ast::MacCall, context: &RewriteContext<'_>) -> Delimiter {
let snippet = context.snippet(mac.span()); let snippet = context.snippet(mac.span());
let paren_pos = snippet.find_uncommented("(").unwrap_or(usize::max_value()); let paren_pos = snippet.find_uncommented("(").unwrap_or(usize::MAX);
let bracket_pos = snippet.find_uncommented("[").unwrap_or(usize::max_value()); let bracket_pos = snippet.find_uncommented("[").unwrap_or(usize::MAX);
let brace_pos = snippet.find_uncommented("{").unwrap_or(usize::max_value()); let brace_pos = snippet.find_uncommented("{").unwrap_or(usize::MAX);
if paren_pos < bracket_pos && paren_pos < brace_pos { if paren_pos < bracket_pos && paren_pos < brace_pos {
Delimiter::Parenthesis Delimiter::Parenthesis
@ -1242,23 +1273,31 @@ impl MacroBranch {
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
shape: Shape, shape: Shape,
multi_branch_style: bool, multi_branch_style: bool,
) -> Option<String> { ) -> RewriteResult {
// Only attempt to format function-like macros. // Only attempt to format function-like macros.
if self.args_paren_kind != Delimiter::Parenthesis { if self.args_paren_kind != Delimiter::Parenthesis {
// FIXME(#1539): implement for non-sugared macros. // FIXME(#1539): implement for non-sugared macros.
return None; return Err(RewriteError::MacroFailure {
kind: MacroErrorKind::Unknown,
span: self.span,
});
} }
let old_body = context.snippet(self.body).trim(); let old_body = context.snippet(self.body).trim();
let has_block_body = old_body.starts_with('{'); let has_block_body = old_body.starts_with('{');
let mut prefix_width = 5; // 5 = " => {" let mut prefix_width = 5; // 5 = " => {"
if context.config.version() == Version::Two { if context.config.style_edition() >= StyleEdition::Edition2024 {
if has_block_body { if has_block_body {
prefix_width = 6; // 6 = " => {{" prefix_width = 6; // 6 = " => {{"
} }
} }
let mut result = let mut result = format_macro_args(
format_macro_args(context, self.args.clone(), shape.sub_width(prefix_width)?)?; context,
self.args.clone(),
shape
.sub_width(prefix_width)
.max_width_error(shape.width, self.span)?,
)?;
if multi_branch_style { if multi_branch_style {
result += " =>"; result += " =>";
@ -1267,7 +1306,7 @@ impl MacroBranch {
if !context.config.format_macro_bodies() { if !context.config.format_macro_bodies() {
result += " "; result += " ";
result += context.snippet(self.whole_body); result += context.snippet(self.whole_body);
return Some(result); return Ok(result);
} }
// The macro body is the most interesting part. It might end up as various // The macro body is the most interesting part. It might end up as various
@ -1276,7 +1315,8 @@ impl MacroBranch {
// `$$`). We'll try and format like an AST node, but we'll substitute // `$$`). We'll try and format like an AST node, but we'll substitute
// variables for new names with the same length first. // variables for new names with the same length first.
let (body_str, substs) = replace_names(old_body)?; let (body_str, substs) =
replace_names(old_body).macro_error(MacroErrorKind::ReplaceMacroVariable, self.span)?;
let mut config = context.config.clone(); let mut config = context.config.clone();
config.set().show_parse_errors(false); config.set().show_parse_errors(false);
@ -1299,13 +1339,21 @@ impl MacroBranch {
config.set().max_width(new_width); config.set().max_width(new_width);
match crate::format_code_block(&body_str, &config, true) { match crate::format_code_block(&body_str, &config, true) {
Some(new_body) => new_body, Some(new_body) => new_body,
None => return None, None => {
return Err(RewriteError::MacroFailure {
kind: MacroErrorKind::Unknown,
span: self.span,
});
}
} }
} }
}; };
if !filtered_str_fits(&new_body_snippet.snippet, config.max_width(), shape) { if !filtered_str_fits(&new_body_snippet.snippet, config.max_width(), shape) {
return None; return Err(RewriteError::ExceedsMaxWidth {
configured_width: shape.width,
span: self.span,
});
} }
// Indent the body since it is in a block. // Indent the body since it is in a block.
@ -1331,7 +1379,10 @@ impl MacroBranch {
for (old, new) in &substs { for (old, new) in &substs {
if old_body.contains(new) { if old_body.contains(new) {
debug!("rewrite_macro_def: bailing matching variable: `{}`", new); debug!("rewrite_macro_def: bailing matching variable: `{}`", new);
return None; return Err(RewriteError::MacroFailure {
kind: MacroErrorKind::ReplaceMacroVariable,
span: self.span,
});
} }
new_body = new_body.replace(new, old); new_body = new_body.replace(new, old);
} }
@ -1346,7 +1397,7 @@ impl MacroBranch {
result += "}"; result += "}";
Some(result) Ok(result)
} }
} }
@ -1366,7 +1417,8 @@ fn format_lazy_static(
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
shape: Shape, shape: Shape,
ts: TokenStream, ts: TokenStream,
) -> Option<String> { span: Span,
) -> RewriteResult {
let mut result = String::with_capacity(1024); let mut result = String::with_capacity(1024);
let nested_shape = shape let nested_shape = shape
.block_indent(context.config.tab_spaces()) .block_indent(context.config.tab_spaces())
@ -1375,7 +1427,8 @@ fn format_lazy_static(
result.push_str("lazy_static! {"); result.push_str("lazy_static! {");
result.push_str(&nested_shape.indent.to_string_with_newline(context.config)); result.push_str(&nested_shape.indent.to_string_with_newline(context.config));
let parsed_elems = parse_lazy_static(context, ts)?; let parsed_elems =
parse_lazy_static(context, ts).macro_error(MacroErrorKind::ParseFailure, span)?;
let last = parsed_elems.len() - 1; let last = parsed_elems.len() - 1;
for (i, (vis, id, ty, expr)) in parsed_elems.iter().enumerate() { for (i, (vis, id, ty, expr)) in parsed_elems.iter().enumerate() {
// Rewrite as a static item. // Rewrite as a static item.
@ -1385,14 +1438,16 @@ fn format_lazy_static(
"{}static ref {}: {} =", "{}static ref {}: {} =",
vis, vis,
id, id,
ty.rewrite(context, nested_shape)? ty.rewrite_result(context, nested_shape)?
)); ));
result.push_str(&rewrite_assign_rhs( result.push_str(&rewrite_assign_rhs(
context, context,
stmt, stmt,
&*expr, &*expr,
&RhsAssignKind::Expr(&expr.kind, expr.span), &RhsAssignKind::Expr(&expr.kind, expr.span),
nested_shape.sub_width(1)?, nested_shape
.sub_width(1)
.max_width_error(nested_shape.width, expr.span)?,
)?); )?);
result.push(';'); result.push(';');
if i != last { if i != last {
@ -1403,7 +1458,7 @@ fn format_lazy_static(
result.push_str(&shape.indent.to_string_with_newline(context.config)); result.push_str(&shape.indent.to_string_with_newline(context.config));
result.push('}'); result.push('}');
Some(result) Ok(result)
} }
fn rewrite_macro_with_items( fn rewrite_macro_with_items(
@ -1415,12 +1470,12 @@ fn rewrite_macro_with_items(
original_style: Delimiter, original_style: Delimiter,
position: MacroPosition, position: MacroPosition,
span: Span, span: Span,
) -> Option<String> { ) -> RewriteResult {
let style_to_delims = |style| match style { let style_to_delims = |style| match style {
Delimiter::Parenthesis => Some(("(", ")")), Delimiter::Parenthesis => Ok(("(", ")")),
Delimiter::Bracket => Some(("[", "]")), Delimiter::Bracket => Ok(("[", "]")),
Delimiter::Brace => Some((" {", "}")), Delimiter::Brace => Ok((" {", "}")),
_ => None, _ => Err(RewriteError::Unknown),
}; };
let (opener, closer) = style_to_delims(style)?; let (opener, closer) = style_to_delims(style)?;
@ -1442,7 +1497,7 @@ fn rewrite_macro_with_items(
for item in items { for item in items {
let item = match item { let item = match item {
MacroArg::Item(item) => item, MacroArg::Item(item) => item,
_ => return None, _ => return Err(RewriteError::Unknown),
}; };
visitor.visit_item(item); visitor.visit_item(item);
} }
@ -1455,5 +1510,5 @@ fn rewrite_macro_with_items(
result.push_str(&shape.indent.to_string_with_newline(context.config)); result.push_str(&shape.indent.to_string_with_newline(context.config));
result.push_str(closer); result.push_str(closer);
result.push_str(trailing_semicolon); result.push_str(trailing_semicolon);
Some(result) Ok(result)
} }

View File

@ -2,19 +2,19 @@
use std::iter::repeat; use std::iter::repeat;
use rustc_ast::{ast, ptr, MatchKind}; use rustc_ast::{MatchKind, ast, ptr};
use rustc_span::{BytePos, Span}; use rustc_span::{BytePos, Span};
use tracing::debug; use tracing::debug;
use crate::comment::{combine_strs_with_missing_comments, rewrite_comment, FindUncommented}; use crate::comment::{FindUncommented, combine_strs_with_missing_comments, rewrite_comment};
use crate::config::lists::*; use crate::config::lists::*;
use crate::config::{Config, ControlBraceStyle, IndentStyle, MatchArmLeadingPipe, Version}; use crate::config::{Config, ControlBraceStyle, IndentStyle, MatchArmLeadingPipe, StyleEdition};
use crate::expr::{ use crate::expr::{
format_expr, is_empty_block, is_simple_block, is_unsafe_block, prefer_next_line, rewrite_cond, ExprType, RhsTactics, format_expr, is_empty_block, is_simple_block, is_unsafe_block,
ExprType, RhsTactics, prefer_next_line, rewrite_cond,
}; };
use crate::lists::{itemize_list, write_list, ListFormatting}; use crate::lists::{ListFormatting, itemize_list, write_list};
use crate::rewrite::{Rewrite, RewriteContext}; use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult};
use crate::shape::Shape; use crate::shape::Shape;
use crate::source_map::SpanUtils; use crate::source_map::SpanUtils;
use crate::spanned::Spanned; use crate::spanned::Spanned;
@ -56,6 +56,10 @@ impl<'a> Spanned for ArmWrapper<'a> {
impl<'a> Rewrite for ArmWrapper<'a> { impl<'a> Rewrite for ArmWrapper<'a> {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
rewrite_match_arm( rewrite_match_arm(
context, context,
self.arm, self.arm,
@ -74,7 +78,7 @@ pub(crate) fn rewrite_match(
span: Span, span: Span,
attrs: &[ast::Attribute], attrs: &[ast::Attribute],
match_kind: MatchKind, match_kind: MatchKind,
) -> Option<String> { ) -> RewriteResult {
// Do not take the rhs overhead from the upper expressions into account // Do not take the rhs overhead from the upper expressions into account
// when rewriting match condition. // when rewriting match condition.
let cond_shape = Shape { let cond_shape = Shape {
@ -83,10 +87,14 @@ pub(crate) fn rewrite_match(
}; };
// 6 = `match ` // 6 = `match `
let cond_shape = match context.config.indent_style() { let cond_shape = match context.config.indent_style() {
IndentStyle::Visual => cond_shape.shrink_left(6)?, IndentStyle::Visual => cond_shape
IndentStyle::Block => cond_shape.offset_left(6)?, .shrink_left(6)
.max_width_error(shape.width, span)?,
IndentStyle::Block => cond_shape
.offset_left(6)
.max_width_error(shape.width, span)?,
}; };
let cond_str = cond.rewrite(context, cond_shape)?; let cond_str = cond.rewrite_result(context, cond_shape)?;
let alt_block_sep = &shape.indent.to_string_with_newline(context.config); let alt_block_sep = &shape.indent.to_string_with_newline(context.config);
let block_sep = match context.config.control_brace_style() { let block_sep = match context.config.control_brace_style() {
ControlBraceStyle::AlwaysNextLine => alt_block_sep, ControlBraceStyle::AlwaysNextLine => alt_block_sep,
@ -105,12 +113,13 @@ pub(crate) fn rewrite_match(
let inner_attrs_str = if inner_attrs.is_empty() { let inner_attrs_str = if inner_attrs.is_empty() {
String::new() String::new()
} else { } else {
let shape = match context.config.version() { let shape = if context.config.style_edition() <= StyleEdition::Edition2021 {
Version::One => shape, shape
_ => shape.block_indent(context.config.tab_spaces()), } else {
shape.block_indent(context.config.tab_spaces())
}; };
inner_attrs inner_attrs
.rewrite(context, shape) .rewrite_result(context, shape)
.map(|s| format!("{}{}\n", nested_indent_str, s))? .map(|s| format!("{}{}\n", nested_indent_str, s))?
}; };
@ -130,16 +139,16 @@ pub(crate) fn rewrite_match(
if arms.is_empty() { if arms.is_empty() {
let snippet = context.snippet(mk_sp(open_brace_pos, span.hi() - BytePos(1))); let snippet = context.snippet(mk_sp(open_brace_pos, span.hi() - BytePos(1)));
if snippet.trim().is_empty() { if snippet.trim().is_empty() {
Some(format!("match {cond_str} {{}}")) Ok(format!("match {cond_str} {{}}"))
} else { } else {
// Empty match with comments or inner attributes? We are not going to bother, sorry ;) // Empty match with comments or inner attributes? We are not going to bother, sorry ;)
Some(context.snippet(span).to_owned()) Ok(context.snippet(span).to_owned())
} }
} else { } else {
let span_after_cond = mk_sp(cond.span.hi(), span.hi()); let span_after_cond = mk_sp(cond.span.hi(), span.hi());
match match_kind { match match_kind {
MatchKind::Prefix => Some(format!( MatchKind::Prefix => Ok(format!(
"match {}{}{{\n{}{}{}\n{}}}", "match {}{}{{\n{}{}{}\n{}}}",
cond_str, cond_str,
block_sep, block_sep,
@ -148,7 +157,7 @@ pub(crate) fn rewrite_match(
rewrite_match_arms(context, arms, shape, span_after_cond, open_brace_pos)?, rewrite_match_arms(context, arms, shape, span_after_cond, open_brace_pos)?,
shape.indent.to_string(context.config), shape.indent.to_string(context.config),
)), )),
MatchKind::Postfix => Some(format!( MatchKind::Postfix => Ok(format!(
"{}.match{}{{\n{}{}{}\n{}}}", "{}.match{}{{\n{}{}{}\n{}}}",
cond_str, cond_str,
block_sep, block_sep,
@ -198,7 +207,7 @@ fn rewrite_match_arms(
shape: Shape, shape: Shape,
span: Span, span: Span,
open_brace_pos: BytePos, open_brace_pos: BytePos,
) -> Option<String> { ) -> RewriteResult {
let arm_shape = shape let arm_shape = shape
.block_indent(context.config.tab_spaces()) .block_indent(context.config.tab_spaces())
.with_max_width(context.config); .with_max_width(context.config);
@ -218,7 +227,7 @@ fn rewrite_match_arms(
"|", "|",
|arm| arm.span().lo(), |arm| arm.span().lo(),
|arm| arm.span().hi(), |arm| arm.span().hi(),
|arm| arm.rewrite(context, arm_shape), |arm| arm.rewrite_result(context, arm_shape),
open_brace_pos, open_brace_pos,
span.hi(), span.hi(),
false, false,
@ -238,19 +247,19 @@ fn rewrite_match_arm(
shape: Shape, shape: Shape,
is_last: bool, is_last: bool,
has_leading_pipe: bool, has_leading_pipe: bool,
) -> Option<String> { ) -> RewriteResult {
let (missing_span, attrs_str) = if !arm.attrs.is_empty() { let (missing_span, attrs_str) = if !arm.attrs.is_empty() {
if contains_skip(&arm.attrs) { if contains_skip(&arm.attrs) {
let (_, body) = flatten_arm_body(context, arm.body.as_deref()?, None); let (_, body) = flatten_arm_body(context, arm.body.as_deref().unknown_error()?, None);
// `arm.span()` does not include trailing comma, add it manually. // `arm.span()` does not include trailing comma, add it manually.
return Some(format!( return Ok(format!(
"{}{}", "{}{}",
context.snippet(arm.span()), context.snippet(arm.span()),
arm_comma(context.config, body, is_last), arm_comma(context.config, body, is_last),
)); ));
} }
let missing_span = mk_sp(arm.attrs[arm.attrs.len() - 1].span.hi(), arm.pat.span.lo()); let missing_span = mk_sp(arm.attrs[arm.attrs.len() - 1].span.hi(), arm.pat.span.lo());
(missing_span, arm.attrs.rewrite(context, shape)?) (missing_span, arm.attrs.rewrite_result(context, shape)?)
} else { } else {
(mk_sp(arm.span().lo(), arm.span().lo()), String::new()) (mk_sp(arm.span().lo(), arm.span().lo()), String::new())
}; };
@ -264,19 +273,25 @@ fn rewrite_match_arm(
}; };
// Patterns // Patterns
let pat_shape = match &arm.body.as_ref()?.kind { let pat_shape = match &arm.body.as_ref().unknown_error()?.kind {
ast::ExprKind::Block(_, Some(label)) => { ast::ExprKind::Block(_, Some(label)) => {
// Some block with a label ` => 'label: {` // Some block with a label ` => 'label: {`
// 7 = ` => : {` // 7 = ` => : {`
let label_len = label.ident.as_str().len(); let label_len = label.ident.as_str().len();
shape.sub_width(7 + label_len)?.offset_left(pipe_offset)? shape
.sub_width(7 + label_len)
.and_then(|s| s.offset_left(pipe_offset))
.max_width_error(shape.width, arm.span)?
} }
_ => { _ => {
// 5 = ` => {` // 5 = ` => {`
shape.sub_width(5)?.offset_left(pipe_offset)? shape
.sub_width(5)
.and_then(|s| s.offset_left(pipe_offset))
.max_width_error(shape.width, arm.span)?
} }
}; };
let pats_str = arm.pat.rewrite(context, pat_shape)?; let pats_str = arm.pat.rewrite_result(context, pat_shape)?;
// Guard // Guard
let block_like_pat = trimmed_last_line_width(&pats_str) <= context.config.tab_spaces(); let block_like_pat = trimmed_last_line_width(&pats_str) <= context.config.tab_spaces();
@ -298,10 +313,13 @@ fn rewrite_match_arm(
false, false,
)?; )?;
let arrow_span = mk_sp(arm.pat.span.hi(), arm.body.as_ref()?.span().lo()); let arrow_span = mk_sp(
arm.pat.span.hi(),
arm.body.as_ref().unknown_error()?.span().lo(),
);
rewrite_match_body( rewrite_match_body(
context, context,
arm.body.as_ref()?, arm.body.as_ref().unknown_error()?,
&lhs_str, &lhs_str,
shape, shape,
guard_str.contains('\n'), guard_str.contains('\n'),
@ -382,7 +400,7 @@ fn rewrite_match_body(
has_guard: bool, has_guard: bool,
arrow_span: Span, arrow_span: Span,
is_last: bool, is_last: bool,
) -> Option<String> { ) -> RewriteResult {
let (extend, body) = flatten_arm_body( let (extend, body) = flatten_arm_body(
context, context,
body, body,
@ -403,7 +421,7 @@ fn rewrite_match_body(
_ => " ", _ => " ",
}; };
Some(format!("{} =>{}{}{}", pats_str, block_sep, body_str, comma)) Ok(format!("{} =>{}{}{}", pats_str, block_sep, body_str, comma))
}; };
let next_line_indent = if !is_block || is_empty_block { let next_line_indent = if !is_block || is_empty_block {
@ -420,7 +438,7 @@ fn rewrite_match_body(
let arrow_snippet = context.snippet(arrow_span).trim(); let arrow_snippet = context.snippet(arrow_span).trim();
// search for the arrow starting from the end of the snippet since there may be a match // search for the arrow starting from the end of the snippet since there may be a match
// expression within the guard // expression within the guard
let arrow_index = if context.config.version() == Version::One { let arrow_index = if context.config.style_edition() <= StyleEdition::Edition2021 {
arrow_snippet.rfind("=>").unwrap() arrow_snippet.rfind("=>").unwrap()
} else { } else {
arrow_snippet.find_last_uncommented("=>").unwrap() arrow_snippet.find_last_uncommented("=>").unwrap()
@ -447,7 +465,7 @@ fn rewrite_match_body(
result.push_str(&nested_indent_str); result.push_str(&nested_indent_str);
result.push_str(body_str); result.push_str(body_str);
result.push_str(comma); result.push_str(comma);
return Some(result); return Ok(result);
} }
let indent_str = shape.indent.to_string_with_newline(context.config); let indent_str = shape.indent.to_string_with_newline(context.config);
@ -458,7 +476,7 @@ fn rewrite_match_body(
} else { } else {
"" ""
}; };
let semicolon = if context.config.version() == Version::One { let semicolon = if context.config.style_edition() <= StyleEdition::Edition2021 {
"" ""
} else { } else {
if semicolon_for_expr(context, body) { if semicolon_for_expr(context, body) {
@ -490,7 +508,7 @@ fn rewrite_match_body(
result.push_str(&block_sep); result.push_str(&block_sep);
result.push_str(body_str); result.push_str(body_str);
result.push_str(&body_suffix); result.push_str(&body_suffix);
Some(result) Ok(result)
}; };
// Let's try and get the arm body on the same line as the condition. // Let's try and get the arm body on the same line as the condition.
@ -499,7 +517,7 @@ fn rewrite_match_body(
.offset_left(extra_offset(pats_str, shape) + 4) .offset_left(extra_offset(pats_str, shape) + 4)
.and_then(|shape| shape.sub_width(comma.len())); .and_then(|shape| shape.sub_width(comma.len()));
let orig_body = if forbid_same_line || !arrow_comment.is_empty() { let orig_body = if forbid_same_line || !arrow_comment.is_empty() {
None Err(RewriteError::Unknown)
} else if let Some(body_shape) = orig_body_shape { } else if let Some(body_shape) = orig_body_shape {
let rewrite = nop_block_collapse( let rewrite = nop_block_collapse(
format_expr(body, ExprType::Statement, context, body_shape), format_expr(body, ExprType::Statement, context, body_shape),
@ -507,7 +525,7 @@ fn rewrite_match_body(
); );
match rewrite { match rewrite {
Some(ref body_str) Ok(ref body_str)
if is_block if is_block
|| (!body_str.contains('\n') || (!body_str.contains('\n')
&& unicode_str_width(body_str) <= body_shape.width) => && unicode_str_width(body_str) <= body_shape.width) =>
@ -517,7 +535,7 @@ fn rewrite_match_body(
_ => rewrite, _ => rewrite,
} }
} else { } else {
None Err(RewriteError::Unknown)
}; };
let orig_budget = orig_body_shape.map_or(0, |shape| shape.width); let orig_budget = orig_body_shape.map_or(0, |shape| shape.width);
@ -528,20 +546,23 @@ fn rewrite_match_body(
next_line_body_shape.width, next_line_body_shape.width,
); );
match (orig_body, next_line_body) { match (orig_body, next_line_body) {
(Some(ref orig_str), Some(ref next_line_str)) (Ok(ref orig_str), Ok(ref next_line_str))
if prefer_next_line(orig_str, next_line_str, RhsTactics::Default) => if prefer_next_line(orig_str, next_line_str, RhsTactics::Default) =>
{ {
combine_next_line_body(next_line_str) combine_next_line_body(next_line_str)
} }
(Some(ref orig_str), _) if extend && first_line_width(orig_str) <= orig_budget => { (Ok(ref orig_str), _) if extend && first_line_width(orig_str) <= orig_budget => {
combine_orig_body(orig_str) combine_orig_body(orig_str)
} }
(Some(ref orig_str), Some(ref next_line_str)) if orig_str.contains('\n') => { (Ok(ref orig_str), Ok(ref next_line_str)) if orig_str.contains('\n') => {
combine_next_line_body(next_line_str) combine_next_line_body(next_line_str)
} }
(None, Some(ref next_line_str)) => combine_next_line_body(next_line_str), (Err(_), Ok(ref next_line_str)) => combine_next_line_body(next_line_str),
(None, None) => None, // When both orig_body and next_line_body result in errors, we currently propagate the
(Some(ref orig_str), _) => combine_orig_body(orig_str), // error from the second attempt since it is more generous with width constraints.
// This decision is somewhat arbitrary and is open to change.
(Err(_), Err(next_line_err)) => Err(next_line_err),
(Ok(ref orig_str), _) => combine_orig_body(orig_str),
} }
} }
@ -554,7 +575,7 @@ fn rewrite_guard(
// the arm (excludes offset). // the arm (excludes offset).
pattern_width: usize, pattern_width: usize,
multiline_pattern: bool, multiline_pattern: bool,
) -> Option<String> { ) -> RewriteResult {
if let Some(ref guard) = *guard { if let Some(ref guard) = *guard {
// First try to fit the guard string on the same line as the pattern. // First try to fit the guard string on the same line as the pattern.
// 4 = ` if `, 5 = ` => {` // 4 = ` if `, 5 = ` => {`
@ -563,9 +584,9 @@ fn rewrite_guard(
.and_then(|s| s.sub_width(5)); .and_then(|s| s.sub_width(5));
if !multiline_pattern { if !multiline_pattern {
if let Some(cond_shape) = cond_shape { if let Some(cond_shape) = cond_shape {
if let Some(cond_str) = guard.rewrite(context, cond_shape) { if let Ok(cond_str) = guard.rewrite_result(context, cond_shape) {
if !cond_str.contains('\n') || pattern_width <= context.config.tab_spaces() { if !cond_str.contains('\n') || pattern_width <= context.config.tab_spaces() {
return Some(format!(" if {cond_str}")); return Ok(format!(" if {cond_str}"));
} }
} }
} }
@ -575,24 +596,20 @@ fn rewrite_guard(
// 3 = `if `, 5 = ` => {` // 3 = `if `, 5 = ` => {`
let cond_shape = Shape::indented(shape.indent.block_indent(context.config), context.config) let cond_shape = Shape::indented(shape.indent.block_indent(context.config), context.config)
.offset_left(3) .offset_left(3)
.and_then(|s| s.sub_width(5)); .and_then(|s| s.sub_width(5))
if let Some(cond_shape) = cond_shape { .max_width_error(shape.width, guard.span)?;
if let Some(cond_str) = guard.rewrite(context, cond_shape) { let cond_str = guard.rewrite_result(context, cond_shape)?;
return Some(format!( Ok(format!(
"{}if {}", "{}if {}",
cond_shape.indent.to_string_with_newline(context.config), cond_shape.indent.to_string_with_newline(context.config),
cond_str cond_str
)); ))
}
}
None
} else { } else {
Some(String::new()) Ok(String::new())
} }
} }
fn nop_block_collapse(block_str: Option<String>, budget: usize) -> Option<String> { fn nop_block_collapse(block_str: RewriteResult, budget: usize) -> RewriteResult {
debug!("nop_block_collapse {:?} {}", block_str, budget); debug!("nop_block_collapse {:?} {}", block_str, budget);
block_str.map(|block_str| { block_str.map(|block_str| {
if block_str.starts_with('{') if block_str.starts_with('{')

View File

@ -1,10 +1,10 @@
use rustc_span::{BytePos, Pos, Span}; use rustc_span::{BytePos, Pos, Span};
use tracing::debug; use tracing::debug;
use crate::comment::{is_last_comment_block, rewrite_comment, CodeCharKind, CommentCodeSlices}; use crate::comment::{CodeCharKind, CommentCodeSlices, is_last_comment_block, rewrite_comment};
use crate::config::file_lines::FileLines;
use crate::config::FileName; use crate::config::FileName;
use crate::config::Version; use crate::config::StyleEdition;
use crate::config::file_lines::FileLines;
use crate::coverage::transform_missing_snippet; use crate::coverage::transform_missing_snippet;
use crate::shape::{Indent, Shape}; use crate::shape::{Indent, Shape};
use crate::source_map::LineRangeUtils; use crate::source_map::LineRangeUtils;
@ -247,7 +247,9 @@ impl<'a> FmtVisitor<'a> {
let indent_str = self.block_indent.to_string(self.config); let indent_str = self.block_indent.to_string(self.config);
self.push_str(&indent_str); self.push_str(&indent_str);
self.block_indent self.block_indent
} else if self.config.version() == Version::Two && !snippet.starts_with('\n') { } else if self.config.style_edition() >= StyleEdition::Edition2024
&& !snippet.starts_with('\n')
{
// The comment appears on the same line as the previous formatted code. // The comment appears on the same line as the previous formatted code.
// Assuming that comment is logically associated with that code, we want to keep it on // Assuming that comment is logically associated with that code, we want to keep it on
// the same level and avoid mixing it with possible other comment. // the same level and avoid mixing it with possible other comment.
@ -283,13 +285,13 @@ impl<'a> FmtVisitor<'a> {
let other_lines = &subslice[offset + 1..]; let other_lines = &subslice[offset + 1..];
let comment_str = let comment_str =
rewrite_comment(other_lines, false, comment_shape, self.config) rewrite_comment(other_lines, false, comment_shape, self.config)
.unwrap_or_else(|| String::from(other_lines)); .unwrap_or_else(|_| String::from(other_lines));
self.push_str(&comment_str); self.push_str(&comment_str);
} }
} }
} else { } else {
let comment_str = rewrite_comment(subslice, false, comment_shape, self.config) let comment_str = rewrite_comment(subslice, false, comment_shape, self.config)
.unwrap_or_else(|| String::from(subslice)); .unwrap_or_else(|_| String::from(subslice));
self.push_str(&comment_str); self.push_str(&comment_str);
} }

View File

@ -4,8 +4,8 @@ use std::path::{Path, PathBuf};
use rustc_ast::ast; use rustc_ast::ast;
use rustc_ast::visit::Visitor; use rustc_ast::visit::Visitor;
use rustc_span::symbol::{self, sym, Symbol};
use rustc_span::Span; use rustc_span::Span;
use rustc_span::symbol::{self, Symbol, sym};
use thin_vec::ThinVec; use thin_vec::ThinVec;
use thiserror::Error; use thiserror::Error;

View File

@ -9,22 +9,22 @@ use rustc_span::Span;
use tracing::debug; use tracing::debug;
use crate::closures; use crate::closures;
use crate::config::Version; use crate::config::StyleEdition;
use crate::config::{lists::*, Config}; use crate::config::{Config, lists::*};
use crate::expr::{ use crate::expr::{
can_be_overflowed_expr, is_every_expr_simple, is_method_call, is_nested_call, is_simple_expr, can_be_overflowed_expr, is_every_expr_simple, is_method_call, is_nested_call, is_simple_expr,
rewrite_cond, rewrite_cond,
}; };
use crate::lists::{ use crate::lists::{
definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator, ListFormatting, ListItem, Separator, definitive_tactic, itemize_list, write_list,
}; };
use crate::macros::MacroArg; use crate::macros::MacroArg;
use crate::patterns::{can_be_overflowed_pat, TuplePatField}; use crate::patterns::{TuplePatField, can_be_overflowed_pat};
use crate::rewrite::{Rewrite, RewriteContext}; use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult};
use crate::shape::Shape; use crate::shape::Shape;
use crate::source_map::SpanUtils; use crate::source_map::SpanUtils;
use crate::spanned::Spanned; use crate::spanned::Spanned;
use crate::types::{can_be_overflowed_type, SegmentParam}; use crate::types::{SegmentParam, can_be_overflowed_type};
use crate::utils::{count_newlines, extra_offset, first_line_width, last_line_width, mk_sp}; use crate::utils::{count_newlines, extra_offset, first_line_width, last_line_width, mk_sp};
/// A list of `format!`-like macros, that take a long format string and a list of arguments to /// A list of `format!`-like macros, that take a long format string and a list of arguments to
@ -91,6 +91,10 @@ impl<'a> Rewrite for OverflowableItem<'a> {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.map(|item| item.rewrite(context, shape)) self.map(|item| item.rewrite(context, shape))
} }
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
self.map(|item| item.rewrite_result(context, shape))
}
} }
impl<'a> Spanned for OverflowableItem<'a> { impl<'a> Spanned for OverflowableItem<'a> {
@ -201,8 +205,12 @@ impl<'a> OverflowableItem<'a> {
OverflowableItem::NestedMetaItem(..) => SPECIAL_CASE_ATTR, OverflowableItem::NestedMetaItem(..) => SPECIAL_CASE_ATTR,
_ => &[], _ => &[],
}; };
let additional_cases = match (self, config.version()) { let additional_cases = match self {
(OverflowableItem::MacroArg(..), Version::Two) => SPECIAL_CASE_MACROS_V2, OverflowableItem::MacroArg(..)
if config.style_edition() >= StyleEdition::Edition2024 =>
{
SPECIAL_CASE_MACROS_V2
}
_ => &[], _ => &[],
}; };
base_cases.iter().chain(additional_cases) base_cases.iter().chain(additional_cases)
@ -278,7 +286,7 @@ pub(crate) fn rewrite_with_parens<'a, T: 'a + IntoOverflowableItem<'a>>(
span: Span, span: Span,
item_max_width: usize, item_max_width: usize,
force_separator_tactic: Option<SeparatorTactic>, force_separator_tactic: Option<SeparatorTactic>,
) -> Option<String> { ) -> RewriteResult {
Context::new( Context::new(
context, context,
items, items,
@ -300,7 +308,7 @@ pub(crate) fn rewrite_with_angle_brackets<'a, T: 'a + IntoOverflowableItem<'a>>(
items: impl Iterator<Item = &'a T>, items: impl Iterator<Item = &'a T>,
shape: Shape, shape: Shape,
span: Span, span: Span,
) -> Option<String> { ) -> RewriteResult {
Context::new( Context::new(
context, context,
items, items,
@ -324,7 +332,7 @@ pub(crate) fn rewrite_with_square_brackets<'a, T: 'a + IntoOverflowableItem<'a>>
span: Span, span: Span,
force_separator_tactic: Option<SeparatorTactic>, force_separator_tactic: Option<SeparatorTactic>,
delim_token: Option<Delimiter>, delim_token: Option<Delimiter>,
) -> Option<String> { ) -> RewriteResult {
let (lhs, rhs) = match delim_token { let (lhs, rhs) = match delim_token {
Some(Delimiter::Parenthesis) => ("(", ")"), Some(Delimiter::Parenthesis) => ("(", ")"),
Some(Delimiter::Brace) => ("{", "}"), Some(Delimiter::Brace) => ("{", "}"),
@ -428,7 +436,7 @@ impl<'a> Context<'a> {
if closures::args_have_many_closure(&self.items) { if closures::args_have_many_closure(&self.items) {
None None
} else { } else {
closures::rewrite_last_closure(self.context, expr, shape) closures::rewrite_last_closure(self.context, expr, shape).ok()
} }
} }
@ -457,7 +465,7 @@ impl<'a> Context<'a> {
if let Some(rewrite) = rewrite { if let Some(rewrite) = rewrite {
// splitn(2, *).next().unwrap() is always safe. // splitn(2, *).next().unwrap() is always safe.
let rewrite_first_line = Some(rewrite.splitn(2, '\n').next().unwrap().to_owned()); let rewrite_first_line = Ok(rewrite.splitn(2, '\n').next().unwrap().to_owned());
last_list_item.item = rewrite_first_line; last_list_item.item = rewrite_first_line;
Some(rewrite) Some(rewrite)
} else { } else {
@ -495,7 +503,7 @@ impl<'a> Context<'a> {
Some(OverflowableItem::MacroArg(MacroArg::Expr(expr))) Some(OverflowableItem::MacroArg(MacroArg::Expr(expr)))
if !combine_arg_with_callee if !combine_arg_with_callee
&& is_method_call(expr) && is_method_call(expr)
&& self.context.config.version() == Version::Two => && self.context.config.style_edition() >= StyleEdition::Edition2024 =>
{ {
self.context.force_one_line_chain.replace(true); self.context.force_one_line_chain.replace(true);
} }
@ -545,22 +553,23 @@ impl<'a> Context<'a> {
.and_then(|last_item| last_item.rewrite(self.context, self.nested_shape)); .and_then(|last_item| last_item.rewrite(self.context, self.nested_shape));
let no_newline = rw.as_ref().map_or(false, |s| !s.contains('\n')); let no_newline = rw.as_ref().map_or(false, |s| !s.contains('\n'));
if no_newline { if no_newline {
list_items[self.items.len() - 1].item = rw; list_items[self.items.len() - 1].item = rw.unknown_error();
} else { } else {
list_items[self.items.len() - 1].item = Some(overflowed.to_owned()); list_items[self.items.len() - 1].item = Ok(overflowed.to_owned());
} }
} else { } else {
list_items[self.items.len() - 1].item = Some(overflowed.to_owned()); list_items[self.items.len() - 1].item = Ok(overflowed.to_owned());
} }
} }
(true, DefinitiveListTactic::Horizontal, placeholder @ Some(..)) => { (true, DefinitiveListTactic::Horizontal, placeholder @ Some(..)) => {
list_items[self.items.len() - 1].item = placeholder; list_items[self.items.len() - 1].item = placeholder.unknown_error();
} }
_ if !self.items.is_empty() => { _ if !self.items.is_empty() => {
list_items[self.items.len() - 1].item = self list_items[self.items.len() - 1].item = self
.items .items
.last() .last()
.and_then(|last_item| last_item.rewrite(self.context, self.nested_shape)); .and_then(|last_item| last_item.rewrite(self.context, self.nested_shape))
.unknown_error();
// Use horizontal layout for a function with a single argument as long as // Use horizontal layout for a function with a single argument as long as
// everything fits in a single line. // everything fits in a single line.
@ -613,7 +622,7 @@ impl<'a> Context<'a> {
tactic tactic
} }
fn rewrite_items(&self) -> Option<(bool, String)> { fn rewrite_items(&self) -> Result<(bool, String), RewriteError> {
let span = self.items_span(); let span = self.items_span();
debug!("items: {:?}", self.items); debug!("items: {:?}", self.items);
@ -624,7 +633,7 @@ impl<'a> Context<'a> {
",", ",",
|item| item.span().lo(), |item| item.span().lo(),
|item| item.span().hi(), |item| item.span().hi(),
|item| item.rewrite(self.context, self.nested_shape), |item| item.rewrite_result(self.context, self.nested_shape),
span.lo(), span.lo(),
span.hi(), span.hi(),
true, true,
@ -689,7 +698,8 @@ impl<'a> Context<'a> {
); );
result.push_str(self.ident); result.push_str(self.ident);
result.push_str(prefix); result.push_str(prefix);
let force_single_line = if self.context.config.version() == Version::Two { let force_single_line = if self.context.config.style_edition() >= StyleEdition::Edition2024
{
!self.context.use_block_indent() || (is_extendable && extend_width <= shape.width) !self.context.use_block_indent() || (is_extendable && extend_width <= shape.width)
} else { } else {
// 2 = `()` // 2 = `()`
@ -711,7 +721,7 @@ impl<'a> Context<'a> {
result result
} }
fn rewrite(&self, shape: Shape) -> Option<String> { fn rewrite(&self, shape: Shape) -> RewriteResult {
let (extendable, items_str) = self.rewrite_items()?; let (extendable, items_str) = self.rewrite_items()?;
// If we are using visual indent style and failed to format, retry with block indent. // If we are using visual indent style and failed to format, retry with block indent.
@ -725,7 +735,7 @@ impl<'a> Context<'a> {
return result; return result;
} }
Some(self.wrap_items(&items_str, shape, extendable)) Ok(self.wrap_items(&items_str, shape, extendable))
} }
} }

View File

@ -1,9 +1,11 @@
use rustc_ast::ast; use rustc_ast::ast;
use rustc_span::Span;
use crate::config::lists::*;
use crate::config::IndentStyle; use crate::config::IndentStyle;
use crate::rewrite::{Rewrite, RewriteContext}; use crate::config::lists::*;
use crate::rewrite::{Rewrite, RewriteContext, RewriteErrorExt, RewriteResult};
use crate::shape::Shape; use crate::shape::Shape;
use crate::spanned::Spanned;
use crate::utils::{ use crate::utils::{
first_line_width, is_single_line, last_line_width, trimmed_last_line_width, wrap_str, first_line_width, is_single_line, last_line_width, trimmed_last_line_width, wrap_str,
}; };
@ -40,16 +42,19 @@ pub(crate) fn rewrite_all_pairs(
expr: &ast::Expr, expr: &ast::Expr,
shape: Shape, shape: Shape,
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
) -> Option<String> { ) -> RewriteResult {
expr.flatten(context, shape).and_then(|list| { expr.flatten(context, shape)
if list.let_chain_count() > 0 && !list.can_rewrite_let_chain_single_line() { .unknown_error()
rewrite_pairs_multiline(&list, shape, context) .and_then(|list| {
} else { if list.let_chain_count() > 0 && !list.can_rewrite_let_chain_single_line() {
// First we try formatting on one line. rewrite_pairs_multiline(&list, shape, context)
rewrite_pairs_one_line(&list, shape, context) } else {
.or_else(|| rewrite_pairs_multiline(&list, shape, context)) // First we try formatting on one line.
} rewrite_pairs_one_line(&list, shape, context)
}) .unknown_error()
.or_else(|_| rewrite_pairs_multiline(&list, shape, context))
}
})
} }
// This may return a multi-line result since we allow the last expression to go // This may return a multi-line result since we allow the last expression to go
@ -65,7 +70,7 @@ fn rewrite_pairs_one_line<T: Rewrite>(
let base_shape = shape.block(); let base_shape = shape.block();
for ((_, rewrite), s) in list.list.iter().zip(list.separators.iter()) { for ((_, rewrite), s) in list.list.iter().zip(list.separators.iter()) {
if let Some(rewrite) = rewrite { if let Ok(rewrite) = rewrite {
if !is_single_line(rewrite) || result.len() > shape.width { if !is_single_line(rewrite) || result.len() > shape.width {
return None; return None;
} }
@ -104,19 +109,20 @@ fn rewrite_pairs_multiline<T: Rewrite>(
list: &PairList<'_, '_, T>, list: &PairList<'_, '_, T>,
shape: Shape, shape: Shape,
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
) -> Option<String> { ) -> RewriteResult {
let rhs_offset = shape.rhs_overhead(context.config); let rhs_offset = shape.rhs_overhead(context.config);
let nested_shape = (match context.config.indent_style() { let nested_shape = (match context.config.indent_style() {
IndentStyle::Visual => shape.visual_indent(0), IndentStyle::Visual => shape.visual_indent(0),
IndentStyle::Block => shape.block_indent(context.config.tab_spaces()), IndentStyle::Block => shape.block_indent(context.config.tab_spaces()),
}) })
.with_max_width(context.config) .with_max_width(context.config)
.sub_width(rhs_offset)?; .sub_width(rhs_offset)
.max_width_error(shape.width, list.span)?;
let indent_str = nested_shape.indent.to_string_with_newline(context.config); let indent_str = nested_shape.indent.to_string_with_newline(context.config);
let mut result = String::new(); let mut result = String::new();
result.push_str(list.list[0].1.as_ref()?); result.push_str(list.list[0].1.as_ref().map_err(|err| err.clone())?);
for ((e, default_rw), s) in list.list[1..].iter().zip(list.separators.iter()) { for ((e, default_rw), s) in list.list[1..].iter().zip(list.separators.iter()) {
// The following test checks if we should keep two subexprs on the same // The following test checks if we should keep two subexprs on the same
@ -132,7 +138,7 @@ fn rewrite_pairs_multiline<T: Rewrite>(
if let Some(line_shape) = if let Some(line_shape) =
shape.offset_left(s.len() + 2 + trimmed_last_line_width(&result)) shape.offset_left(s.len() + 2 + trimmed_last_line_width(&result))
{ {
if let Some(rewrite) = e.rewrite(context, line_shape) { if let Ok(rewrite) = e.rewrite_result(context, line_shape) {
result.push(' '); result.push(' ');
result.push_str(s); result.push_str(s);
result.push(' '); result.push(' ');
@ -155,9 +161,9 @@ fn rewrite_pairs_multiline<T: Rewrite>(
} }
} }
result.push_str(default_rw.as_ref()?); result.push_str(default_rw.as_ref().map_err(|err| err.clone())?);
} }
Some(result) Ok(result)
} }
// Rewrites a single pair. // Rewrites a single pair.
@ -168,10 +174,10 @@ pub(crate) fn rewrite_pair<LHS, RHS>(
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
shape: Shape, shape: Shape,
separator_place: SeparatorPlace, separator_place: SeparatorPlace,
) -> Option<String> ) -> RewriteResult
where where
LHS: Rewrite, LHS: Rewrite + Spanned,
RHS: Rewrite, RHS: Rewrite + Spanned,
{ {
let tab_spaces = context.config.tab_spaces(); let tab_spaces = context.config.tab_spaces();
let lhs_overhead = match separator_place { let lhs_overhead = match separator_place {
@ -183,15 +189,17 @@ where
..shape ..shape
}; };
let lhs_result = lhs let lhs_result = lhs
.rewrite(context, lhs_shape) .rewrite_result(context, lhs_shape)
.map(|lhs_str| format!("{}{}", pp.prefix, lhs_str))?; .map(|lhs_str| format!("{}{}", pp.prefix, lhs_str))?;
// Try to put both lhs and rhs on the same line. // Try to put both lhs and rhs on the same line.
let rhs_orig_result = shape let rhs_orig_result = shape
.offset_left(last_line_width(&lhs_result) + pp.infix.len()) .offset_left(last_line_width(&lhs_result) + pp.infix.len())
.and_then(|s| s.sub_width(pp.suffix.len())) .and_then(|s| s.sub_width(pp.suffix.len()))
.and_then(|rhs_shape| rhs.rewrite(context, rhs_shape)); .max_width_error(shape.width, rhs.span())
if let Some(ref rhs_result) = rhs_orig_result { .and_then(|rhs_shape| rhs.rewrite_result(context, rhs_shape));
if let Ok(ref rhs_result) = rhs_orig_result {
// If the length of the lhs is equal to or shorter than the tab width or // If the length of the lhs is equal to or shorter than the tab width or
// the rhs looks like block expression, we put the rhs on the same // the rhs looks like block expression, we put the rhs on the same
// line with the lhs even if the rhs is multi-lined. // line with the lhs even if the rhs is multi-lined.
@ -207,7 +215,7 @@ where
+ first_line_width(rhs_result) + first_line_width(rhs_result)
+ pp.suffix.len(); + pp.suffix.len();
if one_line_width <= shape.width { if one_line_width <= shape.width {
return Some(format!( return Ok(format!(
"{}{}{}{}", "{}{}{}{}",
lhs_result, pp.infix, rhs_result, pp.suffix lhs_result, pp.infix, rhs_result, pp.suffix
)); ));
@ -219,13 +227,15 @@ where
// Re-evaluate the rhs because we have more space now: // Re-evaluate the rhs because we have more space now:
let mut rhs_shape = match context.config.indent_style() { let mut rhs_shape = match context.config.indent_style() {
IndentStyle::Visual => shape IndentStyle::Visual => shape
.sub_width(pp.suffix.len() + pp.prefix.len())? .sub_width(pp.suffix.len() + pp.prefix.len())
.max_width_error(shape.width, rhs.span())?
.visual_indent(pp.prefix.len()), .visual_indent(pp.prefix.len()),
IndentStyle::Block => { IndentStyle::Block => {
// Try to calculate the initial constraint on the right hand side. // Try to calculate the initial constraint on the right hand side.
let rhs_overhead = shape.rhs_overhead(context.config); let rhs_overhead = shape.rhs_overhead(context.config);
Shape::indented(shape.indent.block_indent(context.config), context.config) Shape::indented(shape.indent.block_indent(context.config), context.config)
.sub_width(rhs_overhead)? .sub_width(rhs_overhead)
.max_width_error(shape.width, rhs.span())?
} }
}; };
let infix = match separator_place { let infix = match separator_place {
@ -233,15 +243,17 @@ where
SeparatorPlace::Front => pp.infix.trim_start(), SeparatorPlace::Front => pp.infix.trim_start(),
}; };
if separator_place == SeparatorPlace::Front { if separator_place == SeparatorPlace::Front {
rhs_shape = rhs_shape.offset_left(infix.len())?; rhs_shape = rhs_shape
.offset_left(infix.len())
.max_width_error(rhs_shape.width, rhs.span())?;
} }
let rhs_result = rhs.rewrite(context, rhs_shape)?; let rhs_result = rhs.rewrite_result(context, rhs_shape)?;
let indent_str = rhs_shape.indent.to_string_with_newline(context.config); let indent_str = rhs_shape.indent.to_string_with_newline(context.config);
let infix_with_sep = match separator_place { let infix_with_sep = match separator_place {
SeparatorPlace::Back => format!("{infix}{indent_str}"), SeparatorPlace::Back => format!("{infix}{indent_str}"),
SeparatorPlace::Front => format!("{indent_str}{infix}"), SeparatorPlace::Front => format!("{indent_str}{infix}"),
}; };
Some(format!( Ok(format!(
"{}{}{}{}", "{}{}{}{}",
lhs_result, infix_with_sep, rhs_result, pp.suffix lhs_result, infix_with_sep, rhs_result, pp.suffix
)) ))
@ -255,8 +267,9 @@ trait FlattenPair: Rewrite + Sized {
} }
struct PairList<'a, 'b, T: Rewrite> { struct PairList<'a, 'b, T: Rewrite> {
list: Vec<(&'b T, Option<String>)>, list: Vec<(&'b T, RewriteResult)>,
separators: Vec<&'a str>, separators: Vec<&'a str>,
span: Span,
} }
fn is_ident(expr: &ast::Expr) -> bool { fn is_ident(expr: &ast::Expr) -> bool {
@ -303,7 +316,7 @@ impl FlattenPair for ast::Expr {
let default_rewrite = |node: &ast::Expr, sep: usize, is_first: bool| { let default_rewrite = |node: &ast::Expr, sep: usize, is_first: bool| {
if is_first { if is_first {
return node.rewrite(context, shape); return node.rewrite_result(context, shape);
} }
let nested_overhead = sep + 1; let nested_overhead = sep + 1;
let rhs_offset = shape.rhs_overhead(context.config); let rhs_offset = shape.rhs_overhead(context.config);
@ -312,12 +325,17 @@ impl FlattenPair for ast::Expr {
IndentStyle::Block => shape.block_indent(context.config.tab_spaces()), IndentStyle::Block => shape.block_indent(context.config.tab_spaces()),
}) })
.with_max_width(context.config) .with_max_width(context.config)
.sub_width(rhs_offset)?; .sub_width(rhs_offset)
.max_width_error(shape.width, node.span)?;
let default_shape = match context.config.binop_separator() { let default_shape = match context.config.binop_separator() {
SeparatorPlace::Back => nested_shape.sub_width(nested_overhead)?, SeparatorPlace::Back => nested_shape
SeparatorPlace::Front => nested_shape.offset_left(nested_overhead)?, .sub_width(nested_overhead)
.max_width_error(nested_shape.width, node.span)?,
SeparatorPlace::Front => nested_shape
.offset_left(nested_overhead)
.max_width_error(nested_shape.width, node.span)?,
}; };
node.rewrite(context, default_shape) node.rewrite_result(context, default_shape)
}; };
// Turn a tree of binop expressions into a list using a depth-first, // Turn a tree of binop expressions into a list using a depth-first,
@ -326,6 +344,7 @@ impl FlattenPair for ast::Expr {
let mut list = vec![]; let mut list = vec![];
let mut separators = vec![]; let mut separators = vec![];
let mut node = self; let mut node = self;
let span = self.span();
loop { loop {
match node.kind { match node.kind {
ast::ExprKind::Binary(op, ref lhs, _) if op.node == top_op => { ast::ExprKind::Binary(op, ref lhs, _) if op.node == top_op => {
@ -352,7 +371,11 @@ impl FlattenPair for ast::Expr {
} }
assert_eq!(list.len() - 1, separators.len()); assert_eq!(list.len() - 1, separators.len());
Some(PairList { list, separators }) Some(PairList {
list,
separators,
span,
})
} }
} }

View File

@ -1,5 +1,5 @@
use rustc_ast::ast; use rustc_ast::ast;
use rustc_builtin_macros::asm::{parse_asm_args, AsmArgs}; use rustc_builtin_macros::asm::{AsmArgs, parse_asm_args};
use crate::rewrite::RewriteContext; use crate::rewrite::RewriteContext;

View File

@ -1,4 +1,4 @@
use std::panic::{catch_unwind, AssertUnwindSafe}; use std::panic::{AssertUnwindSafe, catch_unwind};
use rustc_ast::ast; use rustc_ast::ast;
use rustc_ast::token::{Delimiter, TokenKind}; use rustc_ast::token::{Delimiter, TokenKind};

View File

@ -1,11 +1,11 @@
use rustc_ast::token::{Delimiter, NonterminalKind, NtExprKind::*, NtPatKind::*, TokenKind}; use rustc_ast::token::{Delimiter, NonterminalKind, NtExprKind::*, NtPatKind::*, TokenKind};
use rustc_ast::tokenstream::TokenStream; use rustc_ast::tokenstream::TokenStream;
use rustc_ast::{ast, ptr}; use rustc_ast::{ast, ptr};
use rustc_parse::parser::{ForceCollect, Parser, Recovery};
use rustc_parse::MACRO_ARGUMENTS; use rustc_parse::MACRO_ARGUMENTS;
use rustc_parse::parser::{ForceCollect, Parser, Recovery};
use rustc_session::parse::ParseSess; use rustc_session::parse::ParseSess;
use rustc_span::symbol::{self, kw};
use rustc_span::Symbol; use rustc_span::Symbol;
use rustc_span::symbol::{self, kw};
use crate::macros::MacroArg; use crate::macros::MacroArg;
use crate::rewrite::RewriteContext; use crate::rewrite::RewriteContext;

View File

@ -1,4 +1,4 @@
use std::panic::{catch_unwind, AssertUnwindSafe}; use std::panic::{AssertUnwindSafe, catch_unwind};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use rustc_ast::token::TokenKind; use rustc_ast::token::TokenKind;
@ -6,11 +6,11 @@ use rustc_ast::{ast, attr, ptr};
use rustc_errors::Diag; use rustc_errors::Diag;
use rustc_parse::parser::Parser as RawParser; use rustc_parse::parser::Parser as RawParser;
use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal}; use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal};
use rustc_span::{sym, Span}; use rustc_span::{Span, sym};
use thin_vec::ThinVec; use thin_vec::ThinVec;
use crate::parse::session::ParseSess;
use crate::Input; use crate::Input;
use crate::parse::session::ParseSess;
pub(crate) type DirectoryOwnership = rustc_expand::module::DirOwnership; pub(crate) type DirectoryOwnership = rustc_expand::module::DirOwnership;
pub(crate) type ModulePathSuccess = rustc_expand::module::ModulePathSuccess; pub(crate) type ModulePathSuccess = rustc_expand::module::ModulePathSuccess;

View File

@ -2,13 +2,14 @@ use std::path::Path;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use rustc_data_structures::sync::{IntoDynSyncSend, Lrc}; use rustc_data_structures::sync::{IntoDynSyncSend, Lrc};
use rustc_errors::emitter::{stderr_destination, DynEmitter, Emitter, HumanEmitter, SilentEmitter}; use rustc_errors::emitter::{DynEmitter, Emitter, HumanEmitter, SilentEmitter, stderr_destination};
use rustc_errors::translation::Translate; use rustc_errors::translation::Translate;
use rustc_errors::{ColorConfig, Diag, DiagCtxt, DiagInner, Level as DiagnosticLevel}; use rustc_errors::{ColorConfig, Diag, DiagCtxt, DiagInner, Level as DiagnosticLevel};
use rustc_session::parse::ParseSess as RawParseSess; use rustc_session::parse::ParseSess as RawParseSess;
use rustc_span::{ use rustc_span::{
BytePos, Span,
source_map::{FilePathMapping, SourceMap}, source_map::{FilePathMapping, SourceMap},
symbol, BytePos, Span, symbol,
}; };
use crate::config::file_lines::LineRange; use crate::config::file_lines::LineRange;
@ -394,7 +395,9 @@ mod tests {
} }
fn get_ignore_list(config: &str) -> IgnoreList { fn get_ignore_list(config: &str) -> IgnoreList {
Config::from_toml(config, Path::new("")).unwrap().ignore() Config::from_toml(config, Path::new("./rustfmt.toml"))
.unwrap()
.ignore()
} }
#[test] #[test]

View File

@ -2,22 +2,22 @@ use rustc_ast::ast::{self, BindingMode, ByRef, Pat, PatField, PatKind, RangeEnd,
use rustc_ast::ptr; use rustc_ast::ptr;
use rustc_span::{BytePos, Span}; use rustc_span::{BytePos, Span};
use crate::comment::{combine_strs_with_missing_comments, FindUncommented}; use crate::comment::{FindUncommented, combine_strs_with_missing_comments};
use crate::config::StyleEdition;
use crate::config::lists::*; use crate::config::lists::*;
use crate::config::Version;
use crate::expr::{can_be_overflowed_expr, rewrite_unary_prefix, wrap_struct_field}; use crate::expr::{can_be_overflowed_expr, rewrite_unary_prefix, wrap_struct_field};
use crate::lists::{ use crate::lists::{
definitive_tactic, itemize_list, shape_for_tactic, struct_lit_formatting, struct_lit_shape, ListFormatting, ListItem, Separator, definitive_tactic, itemize_list, shape_for_tactic,
struct_lit_tactic, write_list, ListFormatting, ListItem, Separator, struct_lit_formatting, struct_lit_shape, struct_lit_tactic, write_list,
}; };
use crate::macros::{rewrite_macro, MacroPosition}; use crate::macros::{MacroPosition, rewrite_macro};
use crate::overflow; use crate::overflow;
use crate::pairs::{rewrite_pair, PairParts}; use crate::pairs::{PairParts, rewrite_pair};
use crate::rewrite::{Rewrite, RewriteContext}; use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult};
use crate::shape::Shape; use crate::shape::Shape;
use crate::source_map::SpanUtils; use crate::source_map::SpanUtils;
use crate::spanned::Spanned; use crate::spanned::Spanned;
use crate::types::{rewrite_path, PathContext}; use crate::types::{PathContext, rewrite_path};
use crate::utils::{format_mutability, mk_sp, mk_sp_lo_plus_one, rewrite_ident}; use crate::utils::{format_mutability, mk_sp, mk_sp_lo_plus_one, rewrite_ident};
/// Returns `true` if the given pattern is "short". /// Returns `true` if the given pattern is "short".
@ -61,25 +61,36 @@ fn is_short_pattern_inner(pat: &ast::Pat) -> bool {
} }
} }
struct RangeOperand<'a>(&'a Option<ptr::P<ast::Expr>>); pub(crate) struct RangeOperand<'a> {
operand: &'a Option<ptr::P<ast::Expr>>,
pub(crate) span: Span,
}
impl<'a> Rewrite for RangeOperand<'a> { impl<'a> Rewrite for RangeOperand<'a> {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
match &self.0 { self.rewrite_result(context, shape).ok()
None => Some("".to_owned()), }
Some(ref exp) => exp.rewrite(context, shape),
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
match &self.operand {
None => Ok("".to_owned()),
Some(ref exp) => exp.rewrite_result(context, shape),
} }
} }
} }
impl Rewrite for Pat { impl Rewrite for Pat {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
match self.kind { match self.kind {
PatKind::Or(ref pats) => { PatKind::Or(ref pats) => {
let pat_strs = pats let pat_strs = pats
.iter() .iter()
.map(|p| p.rewrite(context, shape)) .map(|p| p.rewrite_result(context, shape))
.collect::<Option<Vec<_>>>()?; .collect::<Result<Vec<_>, RewriteError>>()?;
let use_mixed_layout = pats let use_mixed_layout = pats
.iter() .iter()
@ -115,14 +126,21 @@ impl Rewrite for Pat {
let sub_pat = match *sub_pat { let sub_pat = match *sub_pat {
Some(ref p) => { Some(ref p) => {
// 2 - `@ `. // 2 - `@ `.
let width = shape.width.checked_sub( let width = shape
mut_prefix.len() + ref_kw.len() + mut_infix.len() + id_str.len() + 2, .width
)?; .checked_sub(
mut_prefix.len()
+ ref_kw.len()
+ mut_infix.len()
+ id_str.len()
+ 2,
)
.max_width_error(shape.width, p.span())?;
let lo = context.snippet_provider.span_after(self.span, "@"); let lo = context.snippet_provider.span_after(self.span, "@");
combine_strs_with_missing_comments( combine_strs_with_missing_comments(
context, context,
"@", "@",
&p.rewrite(context, Shape::legacy(width, shape.indent))?, &p.rewrite_result(context, Shape::legacy(width, shape.indent))?,
mk_sp(lo, p.span.lo()), mk_sp(lo, p.span.lo()),
shape, shape,
true, true,
@ -207,19 +225,25 @@ impl Rewrite for Pat {
} }
PatKind::Wild => { PatKind::Wild => {
if 1 <= shape.width { if 1 <= shape.width {
Some("_".to_owned()) Ok("_".to_owned())
} else { } else {
None Err(RewriteError::ExceedsMaxWidth {
configured_width: 1,
span: self.span,
})
} }
} }
PatKind::Rest => { PatKind::Rest => {
if 1 <= shape.width { if 1 <= shape.width {
Some("..".to_owned()) Ok("..".to_owned())
} else { } else {
None Err(RewriteError::ExceedsMaxWidth {
configured_width: 1,
span: self.span,
})
} }
} }
PatKind::Never => None, PatKind::Never => Err(RewriteError::Unknown),
PatKind::Range(ref lhs, ref rhs, ref end_kind) => { PatKind::Range(ref lhs, ref rhs, ref end_kind) => {
let infix = match end_kind.node { let infix = match end_kind.node {
RangeEnd::Included(RangeSyntax::DotDotDot) => "...", RangeEnd::Included(RangeSyntax::DotDotDot) => "...",
@ -239,9 +263,17 @@ impl Rewrite for Pat {
} else { } else {
infix.to_owned() infix.to_owned()
}; };
let lspan = self.span.with_hi(end_kind.span.lo());
let rspan = self.span.with_lo(end_kind.span.hi());
rewrite_pair( rewrite_pair(
&RangeOperand(lhs), &RangeOperand {
&RangeOperand(rhs), operand: lhs,
span: lspan,
},
&RangeOperand {
operand: rhs,
span: rspan,
},
PairParts::infix(&infix), PairParts::infix(&infix),
context, context,
shape, shape,
@ -260,19 +292,21 @@ impl Rewrite for Pat {
let path_str = rewrite_path(context, PathContext::Expr, q_self, path, shape)?; let path_str = rewrite_path(context, PathContext::Expr, q_self, path, shape)?;
rewrite_tuple_pat(pat_vec, Some(path_str), self.span, context, shape) rewrite_tuple_pat(pat_vec, Some(path_str), self.span, context, shape)
} }
PatKind::Lit(ref expr) => expr.rewrite(context, shape), PatKind::Lit(ref expr) => expr.rewrite_result(context, shape),
PatKind::Slice(ref slice_pat) if context.config.version() == Version::One => { PatKind::Slice(ref slice_pat)
if context.config.style_edition() <= StyleEdition::Edition2021 =>
{
let rw: Vec<String> = slice_pat let rw: Vec<String> = slice_pat
.iter() .iter()
.map(|p| { .map(|p| {
if let Some(rw) = p.rewrite(context, shape) { if let Ok(rw) = p.rewrite_result(context, shape) {
rw rw
} else { } else {
context.snippet(p.span).to_string() context.snippet(p.span).to_string()
} }
}) })
.collect(); .collect();
Some(format!("[{}]", rw.join(", "))) Ok(format!("[{}]", rw.join(", ")))
} }
PatKind::Slice(ref slice_pat) => overflow::rewrite_with_square_brackets( PatKind::Slice(ref slice_pat) => overflow::rewrite_with_square_brackets(
context, context,
@ -296,10 +330,16 @@ impl Rewrite for Pat {
rewrite_macro(mac, None, context, shape, MacroPosition::Pat) rewrite_macro(mac, None, context, shape, MacroPosition::Pat)
} }
PatKind::Paren(ref pat) => pat PatKind::Paren(ref pat) => pat
.rewrite(context, shape.offset_left(1)?.sub_width(1)?) .rewrite_result(
context,
shape
.offset_left(1)
.and_then(|s| s.sub_width(1))
.max_width_error(shape.width, self.span)?,
)
.map(|inner_pat| format!("({})", inner_pat)), .map(|inner_pat| format!("({})", inner_pat)),
PatKind::Err(_) => None, PatKind::Err(_) => Err(RewriteError::Unknown),
PatKind::Deref(_) => None, PatKind::Deref(_) => Err(RewriteError::Unknown),
} }
} }
} }
@ -312,20 +352,21 @@ fn rewrite_struct_pat(
span: Span, span: Span,
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
shape: Shape, shape: Shape,
) -> Option<String> { ) -> RewriteResult {
// 2 = ` {` // 2 = ` {`
let path_shape = shape.sub_width(2)?; let path_shape = shape.sub_width(2).max_width_error(shape.width, span)?;
let path_str = rewrite_path(context, PathContext::Expr, qself, path, path_shape)?; let path_str = rewrite_path(context, PathContext::Expr, qself, path, path_shape)?;
if fields.is_empty() && !ellipsis { if fields.is_empty() && !ellipsis {
return Some(format!("{path_str} {{}}")); return Ok(format!("{path_str} {{}}"));
} }
let (ellipsis_str, terminator) = if ellipsis { (", ..", "..") } else { ("", "}") }; let (ellipsis_str, terminator) = if ellipsis { (", ..", "..") } else { ("", "}") };
// 3 = ` { `, 2 = ` }`. // 3 = ` { `, 2 = ` }`.
let (h_shape, v_shape) = let (h_shape, v_shape) =
struct_lit_shape(shape, context, path_str.len() + 3, ellipsis_str.len() + 2)?; struct_lit_shape(shape, context, path_str.len() + 3, ellipsis_str.len() + 2)
.max_width_error(shape.width, span)?;
let items = itemize_list( let items = itemize_list(
context.snippet_provider, context.snippet_provider,
@ -340,7 +381,7 @@ fn rewrite_struct_pat(
} }
}, },
|f| f.span.hi(), |f| f.span.hi(),
|f| f.rewrite(context, v_shape), |f| f.rewrite_result(context, v_shape),
context.snippet_provider.span_after(span, "{"), context.snippet_provider.span_after(span, "{"),
span.hi(), span.hi(),
false, false,
@ -379,11 +420,15 @@ fn rewrite_struct_pat(
// ast::Pat doesn't have attrs so use &[] // ast::Pat doesn't have attrs so use &[]
let fields_str = wrap_struct_field(context, &[], &fields_str, shape, v_shape, one_line_width)?; let fields_str = wrap_struct_field(context, &[], &fields_str, shape, v_shape, one_line_width)?;
Some(format!("{path_str} {{{fields_str}}}")) Ok(format!("{path_str} {{{fields_str}}}"))
} }
impl Rewrite for PatField { impl Rewrite for PatField {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
let hi_pos = if let Some(last) = self.attrs.last() { let hi_pos = if let Some(last) = self.attrs.last() {
last.span.hi() last.span.hi()
} else { } else {
@ -393,10 +438,10 @@ impl Rewrite for PatField {
let attrs_str = if self.attrs.is_empty() { let attrs_str = if self.attrs.is_empty() {
String::from("") String::from("")
} else { } else {
self.attrs.rewrite(context, shape)? self.attrs.rewrite_result(context, shape)?
}; };
let pat_str = self.pat.rewrite(context, shape)?; let pat_str = self.pat.rewrite_result(context, shape)?;
if self.is_shorthand { if self.is_shorthand {
combine_strs_with_missing_comments( combine_strs_with_missing_comments(
context, context,
@ -417,7 +462,7 @@ impl Rewrite for PatField {
"{}:\n{}{}", "{}:\n{}{}",
id_str, id_str,
nested_shape.indent.to_string(context.config), nested_shape.indent.to_string(context.config),
self.pat.rewrite(context, nested_shape)? self.pat.rewrite_result(context, nested_shape)?
) )
}; };
combine_strs_with_missing_comments( combine_strs_with_missing_comments(
@ -440,9 +485,13 @@ pub(crate) enum TuplePatField<'a> {
impl<'a> Rewrite for TuplePatField<'a> { impl<'a> Rewrite for TuplePatField<'a> {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
match *self { match *self {
TuplePatField::Pat(p) => p.rewrite(context, shape), TuplePatField::Pat(p) => p.rewrite_result(context, shape),
TuplePatField::Dotdot(_) => Some("..".to_string()), TuplePatField::Dotdot(_) => Ok("..".to_string()),
} }
} }
} }
@ -492,9 +541,9 @@ fn rewrite_tuple_pat(
span: Span, span: Span,
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
shape: Shape, shape: Shape,
) -> Option<String> { ) -> RewriteResult {
if pats.is_empty() { if pats.is_empty() {
return Some(format!("{}()", path_str.unwrap_or_default())); return Ok(format!("{}()", path_str.unwrap_or_default()));
} }
let mut pat_vec: Vec<_> = pats.iter().map(TuplePatField::Pat).collect(); let mut pat_vec: Vec<_> = pats.iter().map(TuplePatField::Pat).collect();
@ -548,7 +597,7 @@ fn count_wildcard_suffix_len(
",", ",",
|item| item.span().lo(), |item| item.span().lo(),
|item| item.span().hi(), |item| item.span().hi(),
|item| item.rewrite(context, shape), |item| item.rewrite_result(context, shape),
context.snippet_provider.span_after(span, "("), context.snippet_provider.span_after(span, "("),
span.hi() - BytePos(1), span.hi() - BytePos(1),
false, false,
@ -558,7 +607,7 @@ fn count_wildcard_suffix_len(
for item in items for item in items
.iter() .iter()
.rev() .rev()
.take_while(|i| matches!(i.item, Some(ref internal_string) if internal_string == "_")) .take_while(|i| matches!(i.item, Ok(ref internal_string) if internal_string == "_"))
{ {
suffix_len += 1; suffix_len += 1;

View File

@ -9,13 +9,13 @@
use std::cmp::Ordering; use std::cmp::Ordering;
use rustc_ast::{ast, attr}; use rustc_ast::{ast, attr};
use rustc_span::{symbol::sym, Span}; use rustc_span::{Span, symbol::sym};
use crate::config::{Config, GroupImportsTactic}; use crate::config::{Config, GroupImportsTactic};
use crate::imports::{normalize_use_trees_with_granularity, UseSegmentKind, UseTree}; use crate::imports::{UseSegmentKind, UseTree, normalize_use_trees_with_granularity};
use crate::items::{is_mod_decl, rewrite_extern_crate, rewrite_mod}; use crate::items::{is_mod_decl, rewrite_extern_crate, rewrite_mod};
use crate::lists::{itemize_list, write_list, ListFormatting, ListItem}; use crate::lists::{ListFormatting, ListItem, itemize_list, write_list};
use crate::rewrite::RewriteContext; use crate::rewrite::{RewriteContext, RewriteErrorExt};
use crate::shape::Shape; use crate::shape::Shape;
use crate::source_map::LineRangeUtils; use crate::source_map::LineRangeUtils;
use crate::spanned::Spanned; use crate::spanned::Spanned;
@ -59,7 +59,7 @@ fn wrap_reorderable_items(
let fmt = ListFormatting::new(shape, context.config) let fmt = ListFormatting::new(shape, context.config)
.separator("") .separator("")
.align_comments(false); .align_comments(false);
write_list(list_items, &fmt) write_list(list_items, &fmt).ok()
} }
fn rewrite_reorderable_item( fn rewrite_reorderable_item(
@ -99,7 +99,7 @@ fn rewrite_reorderable_or_regroupable_items(
";", ";",
|item| item.span().lo(), |item| item.span().lo(),
|item| item.span().hi(), |item| item.span().hi(),
|_item| Some("".to_owned()), |_item| Ok("".to_owned()),
span.lo(), span.lo(),
span.hi(), span.hi(),
false, false,
@ -131,9 +131,16 @@ fn rewrite_reorderable_or_regroupable_items(
.map(|use_group| { .map(|use_group| {
let item_vec: Vec<_> = use_group let item_vec: Vec<_> = use_group
.into_iter() .into_iter()
.map(|use_tree| ListItem { .map(|use_tree| {
item: use_tree.rewrite_top_level(context, nested_shape), let item = use_tree.rewrite_top_level(context, nested_shape);
..use_tree.list_item.unwrap_or_else(ListItem::empty) if let Some(list_item) = use_tree.list_item {
ListItem {
item: item,
..list_item
}
} else {
ListItem::from_item(item)
}
}) })
.collect(); .collect();
wrap_reorderable_items(context, &item_vec, nested_shape) wrap_reorderable_items(context, &item_vec, nested_shape)
@ -151,7 +158,7 @@ fn rewrite_reorderable_or_regroupable_items(
";", ";",
|item| item.span().lo(), |item| item.span().lo(),
|item| item.span().hi(), |item| item.span().hi(),
|item| rewrite_reorderable_item(context, item, shape), |item| rewrite_reorderable_item(context, item, shape).unknown_error(),
span.lo(), span.lo(),
span.hi(), span.hi(),
false, false,

View File

@ -5,17 +5,23 @@ use std::rc::Rc;
use rustc_ast::ptr; use rustc_ast::ptr;
use rustc_span::Span; use rustc_span::Span;
use thiserror::Error;
use crate::FormatReport;
use crate::config::{Config, IndentStyle}; use crate::config::{Config, IndentStyle};
use crate::parse::session::ParseSess; use crate::parse::session::ParseSess;
use crate::shape::Shape; use crate::shape::Shape;
use crate::skip::SkipContext; use crate::skip::SkipContext;
use crate::visitor::SnippetProvider; use crate::visitor::SnippetProvider;
use crate::FormatReport;
pub(crate) type RewriteResult = Result<String, RewriteError>;
pub(crate) trait Rewrite { pub(crate) trait Rewrite {
/// Rewrite self into shape. /// Rewrite self into shape.
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String>; fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String>;
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
self.rewrite(context, shape).unknown_error()
}
} }
impl<T: Rewrite> Rewrite for ptr::P<T> { impl<T: Rewrite> Rewrite for ptr::P<T> {
@ -24,6 +30,66 @@ impl<T: Rewrite> Rewrite for ptr::P<T> {
} }
} }
#[derive(Clone, Debug, PartialEq)]
pub(crate) enum MacroErrorKind {
ParseFailure,
ReplaceMacroVariable,
Unknown,
}
impl std::fmt::Display for MacroErrorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
MacroErrorKind::ParseFailure => write!(f, "(parse failure)"),
MacroErrorKind::ReplaceMacroVariable => write!(f, "(replacing macro variables with $)"),
MacroErrorKind::Unknown => write!(f, ""),
}
}
}
#[derive(Clone, Error, Debug)]
pub(crate) enum RewriteError {
#[error("Formatting was skipped due to skip attribute or out of file range.")]
SkipFormatting,
#[error("It exceeds the required width of {configured_width} for the span: {span:?}")]
ExceedsMaxWidth { configured_width: usize, span: Span },
#[error("Failed to format given macro{} at: {span:?}", kind)]
MacroFailure { kind: MacroErrorKind, span: Span },
/// Format failure that does not fit to above categories.
#[error("An unknown error occurred during formatting.")]
Unknown,
}
/// Extension trait used to conveniently convert to RewriteError
pub(crate) trait RewriteErrorExt<T> {
fn max_width_error(self, width: usize, span: Span) -> Result<T, RewriteError>;
fn macro_error(self, kind: MacroErrorKind, span: Span) -> Result<T, RewriteError>;
fn unknown_error(self) -> Result<T, RewriteError>;
}
impl<T> RewriteErrorExt<T> for Option<T> {
fn max_width_error(self, width: usize, span: Span) -> Result<T, RewriteError> {
self.ok_or_else(|| RewriteError::ExceedsMaxWidth {
configured_width: width,
span: span,
})
}
fn macro_error(self, kind: MacroErrorKind, span: Span) -> Result<T, RewriteError> {
self.ok_or_else(|| RewriteError::MacroFailure {
kind: kind,
span: span,
})
}
fn unknown_error(self) -> Result<T, RewriteError> {
self.ok_or_else(|| RewriteError::Unknown)
}
}
#[derive(Clone)] #[derive(Clone)]
pub(crate) struct RewriteContext<'a> { pub(crate) struct RewriteContext<'a> {
pub(crate) psess: &'a ParseSess, pub(crate) psess: &'a ParseSess,

View File

@ -282,7 +282,7 @@ where
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::DiffLine::*; use super::DiffLine::*;
use super::{make_diff, Mismatch}; use super::{Mismatch, make_diff};
use super::{ModifiedChunk, ModifiedLines}; use super::{ModifiedChunk, ModifiedLines};
#[test] #[test]

View File

@ -0,0 +1,368 @@
use itertools::EitherOrBoth;
use itertools::Itertools;
/// Iterator which breaks an identifier into various [VersionChunk]s.
struct VersionChunkIter<'a> {
ident: &'a str,
start: usize,
}
impl<'a> VersionChunkIter<'a> {
pub(crate) fn new(ident: &'a str) -> Self {
Self { ident, start: 0 }
}
fn parse_numeric_chunk(
&mut self,
mut chars: std::str::CharIndices<'a>,
) -> Option<VersionChunk<'a>> {
let mut end = self.start;
let mut is_end_of_chunk = false;
while let Some((idx, c)) = chars.next() {
end = self.start + idx;
if c.is_ascii_digit() {
continue;
}
is_end_of_chunk = true;
break;
}
let source = if is_end_of_chunk {
let value = &self.ident[self.start..end];
self.start = end;
value
} else {
let value = &self.ident[self.start..];
self.start = self.ident.len();
value
};
let zeros = source.chars().take_while(|c| *c == '0').count();
let value = source.parse::<usize>().ok()?;
Some(VersionChunk::Number {
value,
zeros,
source,
})
}
fn parse_str_chunk(
&mut self,
mut chars: std::str::CharIndices<'a>,
) -> Option<VersionChunk<'a>> {
let mut end = self.start;
let mut is_end_of_chunk = false;
while let Some((idx, c)) = chars.next() {
end = self.start + idx;
if c == '_' {
is_end_of_chunk = true;
break;
}
if !c.is_numeric() {
continue;
}
is_end_of_chunk = true;
break;
}
let source = if is_end_of_chunk {
let value = &self.ident[self.start..end];
self.start = end;
value
} else {
let value = &self.ident[self.start..];
self.start = self.ident.len();
value
};
Some(VersionChunk::Str(source))
}
}
impl<'a> Iterator for VersionChunkIter<'a> {
type Item = VersionChunk<'a>;
fn next(&mut self) -> Option<Self::Item> {
let mut chars = self.ident[self.start..].char_indices();
let (_, next) = chars.next()?;
if next == '_' {
self.start = self.start + next.len_utf8();
return Some(VersionChunk::Underscore);
}
if next.is_ascii_digit() {
return self.parse_numeric_chunk(chars);
}
self.parse_str_chunk(chars)
}
}
/// Represents a chunk in the version-sort algorithm
#[derive(Debug, PartialEq, Eq)]
enum VersionChunk<'a> {
/// A single `_` in an identifier. Underscores are sorted before all other characters.
Underscore,
/// A &str chunk in the version sort.
Str(&'a str),
/// A numeric chunk in the version sort. Keeps track of the numeric value and leading zeros.
Number {
value: usize,
zeros: usize,
source: &'a str,
},
}
/// Determine which side of the version-sort comparison had more leading zeros.
#[derive(Debug, PartialEq, Eq)]
enum MoreLeadingZeros {
Left,
Right,
Equal,
}
/// Compare two identifiers based on the version sorting algorithm described in [the style guide]
///
/// [the style guide]: https://doc.rust-lang.org/nightly/style-guide/#sorting
pub(crate) fn version_sort(a: &str, b: &str) -> std::cmp::Ordering {
let iter_a = VersionChunkIter::new(a);
let iter_b = VersionChunkIter::new(b);
let mut more_leading_zeros = MoreLeadingZeros::Equal;
for either_or_both in iter_a.zip_longest(iter_b) {
match either_or_both {
EitherOrBoth::Left(_) => return std::cmp::Ordering::Greater,
EitherOrBoth::Right(_) => return std::cmp::Ordering::Less,
EitherOrBoth::Both(a, b) => match (a, b) {
(VersionChunk::Underscore, VersionChunk::Underscore) => {
continue;
}
(VersionChunk::Underscore, _) => return std::cmp::Ordering::Less,
(_, VersionChunk::Underscore) => return std::cmp::Ordering::Greater,
(VersionChunk::Str(ca), VersionChunk::Str(cb))
| (VersionChunk::Str(ca), VersionChunk::Number { source: cb, .. })
| (VersionChunk::Number { source: ca, .. }, VersionChunk::Str(cb)) => {
match ca.cmp(&cb) {
std::cmp::Ordering::Equal => {
continue;
}
order @ _ => return order,
}
}
(
VersionChunk::Number {
value: va,
zeros: lza,
..
},
VersionChunk::Number {
value: vb,
zeros: lzb,
..
},
) => match va.cmp(&vb) {
std::cmp::Ordering::Equal => {
if lza == lzb {
continue;
}
if more_leading_zeros == MoreLeadingZeros::Equal && lza > lzb {
more_leading_zeros = MoreLeadingZeros::Left;
} else if more_leading_zeros == MoreLeadingZeros::Equal && lza < lzb {
more_leading_zeros = MoreLeadingZeros::Right;
}
continue;
}
order @ _ => return order,
},
},
}
}
match more_leading_zeros {
MoreLeadingZeros::Equal => std::cmp::Ordering::Equal,
MoreLeadingZeros::Left => std::cmp::Ordering::Less,
MoreLeadingZeros::Right => std::cmp::Ordering::Greater,
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_chunks() {
let mut iter = VersionChunkIter::new("x86_128");
assert_eq!(iter.next(), Some(VersionChunk::Str("x")));
assert_eq!(
iter.next(),
Some(VersionChunk::Number {
value: 86,
zeros: 0,
source: "86"
})
);
assert_eq!(iter.next(), Some(VersionChunk::Underscore));
assert_eq!(
iter.next(),
Some(VersionChunk::Number {
value: 128,
zeros: 0,
source: "128"
})
);
assert_eq!(iter.next(), None);
let mut iter = VersionChunkIter::new("w005s09t");
assert_eq!(iter.next(), Some(VersionChunk::Str("w")));
assert_eq!(
iter.next(),
Some(VersionChunk::Number {
value: 5,
zeros: 2,
source: "005"
})
);
assert_eq!(iter.next(), Some(VersionChunk::Str("s")));
assert_eq!(
iter.next(),
Some(VersionChunk::Number {
value: 9,
zeros: 1,
source: "09"
})
);
assert_eq!(iter.next(), Some(VersionChunk::Str("t")));
assert_eq!(iter.next(), None);
let mut iter = VersionChunkIter::new("ZY_WX");
assert_eq!(iter.next(), Some(VersionChunk::Str("ZY")));
assert_eq!(iter.next(), Some(VersionChunk::Underscore));
assert_eq!(iter.next(), Some(VersionChunk::Str("WX")));
let mut iter = VersionChunkIter::new("_v1");
assert_eq!(iter.next(), Some(VersionChunk::Underscore));
assert_eq!(iter.next(), Some(VersionChunk::Str("v")));
assert_eq!(
iter.next(),
Some(VersionChunk::Number {
value: 1,
zeros: 0,
source: "1"
})
);
let mut iter = VersionChunkIter::new("_1v");
assert_eq!(iter.next(), Some(VersionChunk::Underscore));
assert_eq!(
iter.next(),
Some(VersionChunk::Number {
value: 1,
zeros: 0,
source: "1"
})
);
assert_eq!(iter.next(), Some(VersionChunk::Str("v")));
let mut iter = VersionChunkIter::new("v009");
assert_eq!(iter.next(), Some(VersionChunk::Str("v")));
assert_eq!(
iter.next(),
Some(VersionChunk::Number {
value: 9,
zeros: 2,
source: "009"
})
);
}
#[test]
fn test_version_sort() {
let mut input = vec!["", "b", "a"];
let expected = vec!["", "a", "b"];
input.sort_by(|a, b| version_sort(a, b));
assert_eq!(input, expected);
let mut input = vec!["x7x", "xxx"];
let expected = vec!["x7x", "xxx"];
input.sort_by(|a, b| version_sort(a, b));
assert_eq!(input, expected);
let mut input = vec!["applesauce", "apple"];
let expected = vec!["apple", "applesauce"];
input.sort_by(|a, b| version_sort(a, b));
assert_eq!(input, expected);
let mut input = vec!["aaaaa", "aaa_a"];
let expected = vec!["aaa_a", "aaaaa"];
input.sort_by(|a, b| version_sort(a, b));
assert_eq!(input, expected);
let mut input = vec!["AAAAA", "AAA1A", "BBBBB", "BB_BB", "C3CCC"];
let expected = vec!["AAA1A", "AAAAA", "BB_BB", "BBBBB", "C3CCC"];
input.sort_by(|a, b| version_sort(a, b));
assert_eq!(input, expected);
let mut input = vec!["1_000_000", "1_010_001"];
let expected = vec!["1_000_000", "1_010_001"];
input.sort_by(|a, b| version_sort(a, b));
assert_eq!(input, expected);
let mut input = vec![
"5", "50", "500", "5_000", "5_005", "5_050", "5_500", "50_000", "50_005", "50_050",
"50_500",
];
let expected = vec![
"5", "5_000", "5_005", "5_050", "5_500", "50", "50_000", "50_005", "50_050", "50_500",
"500",
];
input.sort_by(|a, b| version_sort(a, b));
assert_eq!(input, expected);
let mut input = vec!["X86_64", "x86_64", "X86_128", "x86_128"];
let expected = vec!["X86_64", "X86_128", "x86_64", "x86_128"];
input.sort_by(|a, b| version_sort(a, b));
assert_eq!(input, expected);
let mut input = vec!["__", "_"];
let expected = vec!["_", "__"];
input.sort_by(|a, b| version_sort(a, b));
assert_eq!(input, expected);
let mut input = vec!["foo_", "foo"];
let expected = vec!["foo", "foo_"];
input.sort_by(|a, b| version_sort(a, b));
assert_eq!(input, expected);
let mut input = vec!["A", "AA", "B", "a", "aA", "aa", "b"];
let expected = vec!["A", "AA", "B", "a", "aA", "aa", "b"];
input.sort_by(|a, b| version_sort(a, b));
assert_eq!(input, expected);
let mut input = vec![
"x86_128", "usize", "uz", "v000", "v00", "v0", "v0s", "v00t", "v0u", "v001", "v01",
"v1", "v009", "x87", "zyxw", "_ZYXW", "_abcd", "A2", "ABCD", "Z_YXW", "ZY_XW", "ZY_XW",
"ZYXW", "v09", "v9", "v010", "v10", "w005s09t", "w5s009t", "x64", "x86", "x86_32",
"ua", "x86_64", "ZYXW_", "a1", "abcd", "u_zzz", "u8", "u16", "u32", "u64", "u128",
"u256",
];
let expected = vec![
"_ZYXW", "_abcd", "A2", "ABCD", "Z_YXW", "ZY_XW", "ZY_XW", "ZYXW", "ZYXW_", "a1",
"abcd", "u_zzz", "u8", "u16", "u32", "u64", "u128", "u256", "ua", "usize", "uz",
"v000", "v00", "v0", "v0s", "v00t", "v0u", "v001", "v01", "v1", "v009", "v09", "v9",
"v010", "v10", "w005s09t", "w5s009t", "x64", "x86", "x86_32", "x86_64", "x86_128",
"x87", "zyxw",
];
input.sort_by(|a, b| version_sort(a, b));
assert_eq!(input, expected)
}
}

View File

@ -2,10 +2,10 @@ use std::fs;
use std::io::{self, Write}; use std::io::{self, Write};
use std::path::Path; use std::path::Path;
use crate::NewlineStyle;
use crate::config::FileName; use crate::config::FileName;
use crate::emitter::{self, Emitter}; use crate::emitter::{self, Emitter};
use crate::parse::session::ParseSess; use crate::parse::session::ParseSess;
use crate::NewlineStyle;
#[cfg(test)] #[cfg(test)]
use crate::config::Config; use crate::config::Config;

View File

@ -1,9 +1,10 @@
use std::cmp::max; use std::cmp::max;
use rustc_ast::{ast, ptr}; use rustc_ast::{ast, ptr};
use rustc_span::{source_map, Span}; use rustc_span::{Span, source_map};
use crate::macros::MacroArg; use crate::macros::MacroArg;
use crate::patterns::RangeOperand;
use crate::utils::{mk_sp, outer_attributes}; use crate::utils::{mk_sp, outer_attributes};
/// Spanned returns a span including attributes, if available. /// Spanned returns a span including attributes, if available.
@ -212,3 +213,9 @@ impl Spanned for ast::PreciseCapturingArg {
} }
} }
} }
impl<'a> Spanned for RangeOperand<'a> {
fn span(&self) -> Span {
self.span
}
}

View File

@ -2,9 +2,9 @@ use rustc_ast::ast;
use rustc_span::Span; use rustc_span::Span;
use crate::comment::recover_comment_removed; use crate::comment::recover_comment_removed;
use crate::config::Version; use crate::config::StyleEdition;
use crate::expr::{format_expr, is_simple_block, ExprType}; use crate::expr::{ExprType, format_expr, is_simple_block};
use crate::rewrite::{Rewrite, RewriteContext}; use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult};
use crate::shape::Shape; use crate::shape::Shape;
use crate::source_map::LineRangeUtils; use crate::source_map::LineRangeUtils;
use crate::spanned::Spanned; use crate::spanned::Spanned;
@ -90,11 +90,20 @@ impl<'a> Stmt<'a> {
impl<'a> Rewrite for Stmt<'a> { impl<'a> Rewrite for Stmt<'a> {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
let expr_type = if context.config.version() == Version::Two && self.is_last_expr() { self.rewrite_result(context, shape).ok()
ExprType::SubExpression }
} else {
ExprType::Statement fn rewrite_result(
}; &self,
context: &RewriteContext<'_>,
shape: Shape,
) -> crate::rewrite::RewriteResult {
let expr_type =
if context.config.style_edition() >= StyleEdition::Edition2024 && self.is_last_expr() {
ExprType::SubExpression
} else {
ExprType::Statement
};
format_stmt( format_stmt(
context, context,
shape, shape,
@ -111,11 +120,11 @@ fn format_stmt(
stmt: &ast::Stmt, stmt: &ast::Stmt,
expr_type: ExprType, expr_type: ExprType,
is_last_expr: bool, is_last_expr: bool,
) -> Option<String> { ) -> RewriteResult {
skip_out_of_file_lines_range!(context, stmt.span()); skip_out_of_file_lines_range_err!(context, stmt.span());
let result = match stmt.kind { let result = match stmt.kind {
ast::StmtKind::Let(ref local) => local.rewrite(context, shape), ast::StmtKind::Let(ref local) => local.rewrite_result(context, shape),
ast::StmtKind::Expr(ref ex) | ast::StmtKind::Semi(ref ex) => { ast::StmtKind::Expr(ref ex) | ast::StmtKind::Semi(ref ex) => {
let suffix = if semicolon_for_stmt(context, stmt, is_last_expr) { let suffix = if semicolon_for_stmt(context, stmt, is_last_expr) {
";" ";"
@ -123,10 +132,14 @@ fn format_stmt(
"" ""
}; };
let shape = shape.sub_width(suffix.len())?; let shape = shape
.sub_width(suffix.len())
.max_width_error(shape.width, ex.span())?;
format_expr(ex, expr_type, context, shape).map(|s| s + suffix) format_expr(ex, expr_type, context, shape).map(|s| s + suffix)
} }
ast::StmtKind::MacCall(..) | ast::StmtKind::Item(..) | ast::StmtKind::Empty => None, ast::StmtKind::MacCall(..) | ast::StmtKind::Item(..) | ast::StmtKind::Empty => {
Err(RewriteError::Unknown)
}
}; };
result.and_then(|res| recover_comment_removed(res, stmt.span(), context)) result.map(|res| recover_comment_removed(res, stmt.span(), context))
} }

View File

@ -84,7 +84,7 @@ pub(crate) fn rewrite_string<'a>(
stripped_str stripped_str
.len() .len()
.checked_next_power_of_two() .checked_next_power_of_two()
.unwrap_or(usize::max_value()), .unwrap_or(usize::MAX),
); );
result.push_str(fmt.opener); result.push_str(fmt.opener);
@ -375,7 +375,7 @@ fn graphemes_width(graphemes: &[&str]) -> usize {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::{break_string, detect_url, rewrite_string, SnippetState, StringFormat}; use super::{SnippetState, StringFormat, break_string, detect_url, rewrite_string};
use crate::config::Config; use crate::config::Config;
use crate::shape::{Indent, Shape}; use crate::shape::{Indent, Shape};
use unicode_segmentation::UnicodeSegmentation; use unicode_segmentation::UnicodeSegmentation;

View File

@ -4,9 +4,9 @@ use std::io::{BufRead, BufReader, Write};
use std::iter::Enumerate; use std::iter::Enumerate;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use super::{print_mismatches, write_message, DIFF_CONTEXT_SIZE}; use super::{DIFF_CONTEXT_SIZE, print_mismatches, write_message};
use crate::config::{Config, EmitMode, Verbosity}; use crate::config::{Config, EmitMode, Verbosity};
use crate::rustfmt_diff::{make_diff, Mismatch}; use crate::rustfmt_diff::{Mismatch, make_diff};
use crate::{Input, Session}; use crate::{Input, Session};
const CONFIGURATIONS_FILE_NAME: &str = "Configurations.md"; const CONFIGURATIONS_FILE_NAME: &str = "Configurations.md";

View File

@ -6,14 +6,17 @@ use std::iter::Peekable;
use std::mem; use std::mem;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::process::{Command, Stdio}; use std::process::{Command, Stdio};
use std::str::Chars; use std::str::{Chars, FromStr};
use std::thread; use std::thread;
use crate::config::{Color, Config, EmitMode, FileName, NewlineStyle}; use crate::config::{Color, Config, EmitMode, FileName, NewlineStyle};
use crate::formatting::{ReportedErrors, SourceFile}; use crate::formatting::{ReportedErrors, SourceFile};
use crate::rustfmt_diff::{make_diff, print_diff, DiffLine, Mismatch, ModifiedChunk, OutputWriter}; use crate::rustfmt_diff::{DiffLine, Mismatch, ModifiedChunk, OutputWriter, make_diff, print_diff};
use crate::source_file; use crate::source_file;
use crate::{is_nightly_channel, FormatReport, FormatReportFormatterBuilder, Input, Session}; use crate::{
Edition, FormatReport, FormatReportFormatterBuilder, Input, Session, StyleEdition, Version,
is_nightly_channel,
};
use rustfmt_config_proc_macro::nightly_only_test; use rustfmt_config_proc_macro::nightly_only_test;
use tracing::{debug, warn}; use tracing::{debug, warn};
@ -102,10 +105,9 @@ fn is_file_skip(path: &Path) -> bool {
fn get_test_files(path: &Path, recursive: bool) -> Vec<PathBuf> { fn get_test_files(path: &Path, recursive: bool) -> Vec<PathBuf> {
let mut files = vec![]; let mut files = vec![];
if path.is_dir() { if path.is_dir() {
for entry in fs::read_dir(path).expect(&format!( for entry in
"couldn't read directory {}", fs::read_dir(path).expect(&format!("couldn't read directory {}", path.display()))
path.to_str().unwrap() {
)) {
let entry = entry.expect("couldn't get `DirEntry`"); let entry = entry.expect("couldn't get `DirEntry`");
let path = entry.path(); let path = entry.path();
if path.is_dir() && recursive { if path.is_dir() && recursive {
@ -119,10 +121,7 @@ fn get_test_files(path: &Path, recursive: bool) -> Vec<PathBuf> {
} }
fn verify_config_used(path: &Path, config_name: &str) { fn verify_config_used(path: &Path, config_name: &str) {
for entry in fs::read_dir(path).expect(&format!( for entry in fs::read_dir(path).expect(&format!("couldn't read {} directory", path.display())) {
"couldn't read {} directory",
path.to_str().unwrap()
)) {
let entry = entry.expect("couldn't get directory entry"); let entry = entry.expect("couldn't get directory entry");
let path = entry.path(); let path = entry.path();
if path.extension().map_or(false, |f| f == "rs") { if path.extension().map_or(false, |f| f == "rs") {
@ -711,13 +710,24 @@ fn print_mismatches<T: Fn(u32) -> String>(
fn read_config(filename: &Path) -> Config { fn read_config(filename: &Path) -> Config {
let sig_comments = read_significant_comments(filename); let sig_comments = read_significant_comments(filename);
let (edition, style_edition, version) = get_editions_from_comments(&sig_comments);
// Look for a config file. If there is a 'config' property in the significant comments, use // 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 // that. Otherwise, if there are no significant comments at all, look for a config file with
// the same name as the test file. // the same name as the test file.
let mut config = if !sig_comments.is_empty() { let mut config = if !sig_comments.is_empty() {
get_config(sig_comments.get("config").map(Path::new)) get_config(
sig_comments.get("config").map(Path::new),
edition,
style_edition,
version,
)
} else { } else {
get_config(filename.with_extension("toml").file_name().map(Path::new)) get_config(
filename.with_extension("toml").file_name().map(Path::new),
edition,
style_edition,
version,
)
}; };
for (key, val) in &sig_comments { for (key, val) in &sig_comments {
@ -748,13 +758,31 @@ enum IdempotentCheckError {
Parse, Parse,
} }
fn get_editions_from_comments(
comments: &HashMap<String, String>,
) -> (Option<Edition>, Option<StyleEdition>, Option<Version>) {
(
comments
.get("edition")
.map(|e| Edition::from_str(e).expect(&format!("invalid edition value: '{}'", e))),
comments.get("style_edition").map(|se| {
StyleEdition::from_str(se).expect(&format!("invalid style_edition value: '{}'", se))
}),
comments
.get("version")
.map(|v| Version::from_str(v).expect(&format!("invalid version value: '{}'", v))),
)
}
fn idempotent_check( fn idempotent_check(
filename: &PathBuf, filename: &PathBuf,
opt_config: &Option<PathBuf>, opt_config: &Option<PathBuf>,
) -> Result<FormatReport, IdempotentCheckError> { ) -> Result<FormatReport, IdempotentCheckError> {
let sig_comments = read_significant_comments(filename); let sig_comments = read_significant_comments(filename);
let config = if let Some(ref config_file_path) = opt_config { let config = if let Some(ref config_file_path) = opt_config {
Config::from_toml_path(config_file_path).expect("`rustfmt.toml` not found") let (edition, style_edition, version) = get_editions_from_comments(&sig_comments);
Config::from_toml_path(config_file_path, edition, style_edition, version)
.expect("`rustfmt.toml` not found")
} else { } else {
read_config(filename) read_config(filename)
}; };
@ -778,14 +806,19 @@ fn idempotent_check(
// Reads test config file using the supplied (optional) file name. If there's no file name or the // 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 // file doesn't exist, just return the default config. Otherwise, the file must be read
// successfully. // successfully.
fn get_config(config_file: Option<&Path>) -> Config { fn get_config(
config_file: Option<&Path>,
edition: Option<Edition>,
style_edition: Option<StyleEdition>,
version: Option<Version>,
) -> Config {
let config_file_name = match config_file { let config_file_name = match config_file {
None => return Default::default(), None => return Config::default_for_possible_style_edition(style_edition, edition, version),
Some(file_name) => { Some(file_name) => {
let mut full_path = PathBuf::from("tests/config/"); let mut full_path = PathBuf::from("tests/config/");
full_path.push(file_name); full_path.push(file_name);
if !full_path.exists() { if !full_path.exists() {
return Default::default(); return Config::default_for_possible_style_edition(style_edition, edition, version);
}; };
full_path full_path
} }
@ -797,7 +830,14 @@ fn get_config(config_file: Option<&Path>) -> Config {
.read_to_string(&mut def_config) .read_to_string(&mut def_config)
.expect("Couldn't read config"); .expect("Couldn't read config");
Config::from_toml(&def_config, Path::new("tests/config/")).expect("invalid TOML") Config::from_toml_for_style_edition(
&def_config,
Path::new("tests/config/"),
edition,
style_edition,
version,
)
.expect("invalid TOML")
} }
// Reads significant comments of the form: `// rustfmt-key: value` into a hash map. // Reads significant comments of the form: `// rustfmt-key: value` into a hash map.

View File

@ -2,23 +2,23 @@ use std::ops::Deref;
use rustc_ast::ast::{self, FnRetTy, Mutability, Term}; use rustc_ast::ast::{self, FnRetTy, Mutability, Term};
use rustc_ast::ptr; use rustc_ast::ptr;
use rustc_span::{symbol::kw, BytePos, Pos, Span}; use rustc_span::{BytePos, Pos, Span, symbol::kw};
use tracing::debug; use tracing::debug;
use crate::comment::{combine_strs_with_missing_comments, contains_comment}; use crate::comment::{combine_strs_with_missing_comments, contains_comment};
use crate::config::lists::*; use crate::config::lists::*;
use crate::config::{IndentStyle, TypeDensity, Version}; use crate::config::{IndentStyle, StyleEdition, TypeDensity};
use crate::expr::{ use crate::expr::{
format_expr, rewrite_assign_rhs, rewrite_call, rewrite_tuple, rewrite_unary_prefix, ExprType, ExprType, RhsAssignKind, format_expr, rewrite_assign_rhs, rewrite_call, rewrite_tuple,
RhsAssignKind, rewrite_unary_prefix,
}; };
use crate::lists::{ use crate::lists::{
definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator, ListFormatting, ListItem, Separator, definitive_tactic, itemize_list, write_list,
}; };
use crate::macros::{rewrite_macro, MacroPosition}; use crate::macros::{MacroPosition, rewrite_macro};
use crate::overflow; use crate::overflow;
use crate::pairs::{rewrite_pair, PairParts}; use crate::pairs::{PairParts, rewrite_pair};
use crate::rewrite::{Rewrite, RewriteContext}; use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult};
use crate::shape::Shape; use crate::shape::Shape;
use crate::source_map::SpanUtils; use crate::source_map::SpanUtils;
use crate::spanned::Spanned; use crate::spanned::Spanned;
@ -41,7 +41,7 @@ pub(crate) fn rewrite_path(
qself: &Option<ptr::P<ast::QSelf>>, qself: &Option<ptr::P<ast::QSelf>>,
path: &ast::Path, path: &ast::Path,
shape: Shape, shape: Shape,
) -> Option<String> { ) -> RewriteResult {
let skip_count = qself.as_ref().map_or(0, |x| x.position); let skip_count = qself.as_ref().map_or(0, |x| x.position);
// 32 covers almost all path lengths measured when compiling core, and there isn't a big // 32 covers almost all path lengths measured when compiling core, and there isn't a big
@ -57,7 +57,7 @@ pub(crate) fn rewrite_path(
if let Some(qself) = qself { if let Some(qself) = qself {
result.push('<'); result.push('<');
let fmt_ty = qself.ty.rewrite(context, shape)?; let fmt_ty = qself.ty.rewrite_result(context, shape)?;
result.push_str(&fmt_ty); result.push_str(&fmt_ty);
if skip_count > 0 { if skip_count > 0 {
@ -67,7 +67,7 @@ pub(crate) fn rewrite_path(
} }
// 3 = ">::".len() // 3 = ">::".len()
let shape = shape.sub_width(3)?; let shape = shape.sub_width(3).max_width_error(shape.width, path.span)?;
result = rewrite_path_segments( result = rewrite_path_segments(
PathContext::Type, PathContext::Type,
@ -103,7 +103,7 @@ fn rewrite_path_segments<'a, I>(
span_hi: BytePos, span_hi: BytePos,
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
shape: Shape, shape: Shape,
) -> Option<String> ) -> RewriteResult
where where
I: Iterator<Item = &'a ast::PathSegment>, I: Iterator<Item = &'a ast::PathSegment>,
{ {
@ -122,7 +122,9 @@ where
} }
let extra_offset = extra_offset(&buffer, shape); let extra_offset = extra_offset(&buffer, shape);
let new_shape = shape.shrink_left(extra_offset)?; let new_shape = shape
.shrink_left(extra_offset)
.max_width_error(shape.width, mk_sp(span_lo, span_hi))?;
let segment_string = rewrite_segment( let segment_string = rewrite_segment(
path_context, path_context,
segment, segment,
@ -135,7 +137,7 @@ where
buffer.push_str(&segment_string); buffer.push_str(&segment_string);
} }
Some(buffer) Ok(buffer)
} }
#[derive(Debug)] #[derive(Debug)]
@ -169,19 +171,27 @@ impl<'a> Spanned for SegmentParam<'a> {
impl<'a> Rewrite for SegmentParam<'a> { impl<'a> Rewrite for SegmentParam<'a> {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
match *self { match *self {
SegmentParam::Const(const_) => const_.rewrite(context, shape), SegmentParam::Const(const_) => const_.rewrite_result(context, shape),
SegmentParam::LifeTime(lt) => lt.rewrite(context, shape), SegmentParam::LifeTime(lt) => lt.rewrite_result(context, shape),
SegmentParam::Type(ty) => ty.rewrite(context, shape), SegmentParam::Type(ty) => ty.rewrite_result(context, shape),
SegmentParam::Binding(atc) => atc.rewrite(context, shape), SegmentParam::Binding(atc) => atc.rewrite_result(context, shape),
} }
} }
} }
impl Rewrite for ast::PreciseCapturingArg { impl Rewrite for ast::PreciseCapturingArg {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
match self { match self {
ast::PreciseCapturingArg::Lifetime(lt) => lt.rewrite(context, shape), ast::PreciseCapturingArg::Lifetime(lt) => lt.rewrite_result(context, shape),
ast::PreciseCapturingArg::Arg(p, _) => { ast::PreciseCapturingArg::Arg(p, _) => {
rewrite_path(context, PathContext::Type, &None, p, shape) rewrite_path(context, PathContext::Type, &None, p, shape)
} }
@ -191,13 +201,20 @@ impl Rewrite for ast::PreciseCapturingArg {
impl Rewrite for ast::AssocItemConstraint { impl Rewrite for ast::AssocItemConstraint {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
use ast::AssocItemConstraintKind::{Bound, Equality}; use ast::AssocItemConstraintKind::{Bound, Equality};
let mut result = String::with_capacity(128); let mut result = String::with_capacity(128);
result.push_str(rewrite_ident(context, self.ident)); result.push_str(rewrite_ident(context, self.ident));
if let Some(ref gen_args) = self.gen_args { if let Some(ref gen_args) = self.gen_args {
let budget = shape.width.checked_sub(result.len())?; let budget = shape
.width
.checked_sub(result.len())
.max_width_error(shape.width, self.span)?;
let shape = Shape::legacy(budget, shape.indent + result.len()); let shape = Shape::legacy(budget, shape.indent + result.len());
let gen_str = rewrite_generic_args(gen_args, context, shape, gen_args.span())?; let gen_str = rewrite_generic_args(gen_args, context, shape, gen_args.span())?;
result.push_str(&gen_str); result.push_str(&gen_str);
@ -210,23 +227,30 @@ impl Rewrite for ast::AssocItemConstraint {
}; };
result.push_str(infix); result.push_str(infix);
let budget = shape.width.checked_sub(result.len())?; let budget = shape
.width
.checked_sub(result.len())
.max_width_error(shape.width, self.span)?;
let shape = Shape::legacy(budget, shape.indent + result.len()); let shape = Shape::legacy(budget, shape.indent + result.len());
let rewrite = self.kind.rewrite(context, shape)?; let rewrite = self.kind.rewrite_result(context, shape)?;
result.push_str(&rewrite); result.push_str(&rewrite);
Some(result) Ok(result)
} }
} }
impl Rewrite for ast::AssocItemConstraintKind { impl Rewrite for ast::AssocItemConstraintKind {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
match self { match self {
ast::AssocItemConstraintKind::Equality { term } => match term { ast::AssocItemConstraintKind::Equality { term } => match term {
Term::Ty(ty) => ty.rewrite(context, shape), Term::Ty(ty) => ty.rewrite_result(context, shape),
Term::Const(c) => c.rewrite(context, shape), Term::Const(c) => c.rewrite_result(context, shape),
}, },
ast::AssocItemConstraintKind::Bound { bounds } => bounds.rewrite(context, shape), ast::AssocItemConstraintKind::Bound { bounds } => bounds.rewrite_result(context, shape),
} }
} }
} }
@ -248,16 +272,17 @@ fn rewrite_segment(
span_hi: BytePos, span_hi: BytePos,
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
shape: Shape, shape: Shape,
) -> Option<String> { ) -> RewriteResult {
let mut result = String::with_capacity(128); let mut result = String::with_capacity(128);
result.push_str(rewrite_ident(context, segment.ident)); result.push_str(rewrite_ident(context, segment.ident));
let ident_len = result.len(); let ident_len = result.len();
let shape = if context.use_block_indent() { let shape = if context.use_block_indent() {
shape.offset_left(ident_len)? shape.offset_left(ident_len)
} else { } else {
shape.shrink_left(ident_len)? shape.shrink_left(ident_len)
}; }
.max_width_error(shape.width, mk_sp(*span_lo, span_hi))?;
if let Some(ref args) = segment.args { if let Some(ref args) = segment.args {
let generics_str = rewrite_generic_args(args, context, shape, mk_sp(*span_lo, span_hi))?; let generics_str = rewrite_generic_args(args, context, shape, mk_sp(*span_lo, span_hi))?;
@ -288,7 +313,7 @@ fn rewrite_segment(
result.push_str(&generics_str) result.push_str(&generics_str)
} }
Some(result) Ok(result)
} }
fn format_function_type<'a, I>( fn format_function_type<'a, I>(
@ -298,7 +323,7 @@ fn format_function_type<'a, I>(
span: Span, span: Span,
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
shape: Shape, shape: Shape,
) -> Option<String> ) -> RewriteResult
where where
I: ExactSizeIterator, I: ExactSizeIterator,
<I as Iterator>::Item: Deref, <I as Iterator>::Item: Deref,
@ -308,12 +333,12 @@ where
let ty_shape = match context.config.indent_style() { let ty_shape = match context.config.indent_style() {
// 4 = " -> " // 4 = " -> "
IndentStyle::Block => shape.offset_left(4)?, IndentStyle::Block => shape.offset_left(4).max_width_error(shape.width, span)?,
IndentStyle::Visual => shape.block_left(4)?, IndentStyle::Visual => shape.block_left(4).max_width_error(shape.width, span)?,
}; };
let output = match *output { let output = match *output {
FnRetTy::Ty(ref ty) => { FnRetTy::Ty(ref ty) => {
let type_str = ty.rewrite(context, ty_shape)?; let type_str = ty.rewrite_result(context, ty_shape)?;
format!(" -> {type_str}") format!(" -> {type_str}")
} }
FnRetTy::Default(..) => String::new(), FnRetTy::Default(..) => String::new(),
@ -326,7 +351,10 @@ where
) )
} else { } else {
// 2 for () // 2 for ()
let budget = shape.width.checked_sub(2)?; let budget = shape
.width
.checked_sub(2)
.max_width_error(shape.width, span)?;
// 1 for ( // 1 for (
let offset = shape.indent + 1; let offset = shape.indent + 1;
Shape::legacy(budget, offset) Shape::legacy(budget, offset)
@ -339,7 +367,8 @@ where
let list_hi = context.snippet_provider.span_before(span, ")"); let list_hi = context.snippet_provider.span_before(span, ")");
let comment = context let comment = context
.snippet_provider .snippet_provider
.span_to_snippet(mk_sp(list_lo, list_hi))? .span_to_snippet(mk_sp(list_lo, list_hi))
.unknown_error()?
.trim(); .trim();
let comment = if comment.starts_with("//") { let comment = if comment.starts_with("//") {
format!( format!(
@ -360,7 +389,7 @@ where
",", ",",
|arg| arg.span().lo(), |arg| arg.span().lo(),
|arg| arg.span().hi(), |arg| arg.span().hi(),
|arg| arg.rewrite(context, list_shape), |arg| arg.rewrite_result(context, list_shape),
list_lo, list_lo,
span.hi(), span.hi(),
false, false,
@ -396,9 +425,9 @@ where
) )
}; };
if output.is_empty() || last_line_width(&args) + first_line_width(&output) <= shape.width { if output.is_empty() || last_line_width(&args) + first_line_width(&output) <= shape.width {
Some(format!("{args}{output}")) Ok(format!("{args}{output}"))
} else { } else {
Some(format!( Ok(format!(
"{}\n{}{}", "{}\n{}{}",
args, args,
list_shape.indent.to_string(context.config), list_shape.indent.to_string(context.config),
@ -429,6 +458,10 @@ fn get_tactics(item_vec: &[ListItem], output: &str, shape: Shape) -> DefinitiveL
impl Rewrite for ast::WherePredicate { impl Rewrite for ast::WherePredicate {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
// FIXME: dead spans? // FIXME: dead spans?
let result = match *self { let result = match *self {
ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate { ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate {
@ -437,7 +470,7 @@ impl Rewrite for ast::WherePredicate {
ref bounds, ref bounds,
.. ..
}) => { }) => {
let type_str = bounded_ty.rewrite(context, shape)?; let type_str = bounded_ty.rewrite_result(context, shape)?;
let colon = type_bound_colon(context).trim_end(); let colon = type_bound_colon(context).trim_end();
let lhs = if let Some(binder_str) = let lhs = if let Some(binder_str) =
rewrite_bound_params(context, shape, bound_generic_params) rewrite_bound_params(context, shape, bound_generic_params)
@ -452,28 +485,34 @@ impl Rewrite for ast::WherePredicate {
ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate { ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate {
ref lifetime, ref lifetime,
ref bounds, ref bounds,
.. span,
}) => rewrite_bounded_lifetime(lifetime, bounds, context, shape)?, }) => rewrite_bounded_lifetime(lifetime, bounds, span, context, shape)?,
ast::WherePredicate::EqPredicate(ast::WhereEqPredicate { ast::WherePredicate::EqPredicate(ast::WhereEqPredicate {
ref lhs_ty, ref lhs_ty,
ref rhs_ty, ref rhs_ty,
.. ..
}) => { }) => {
let lhs_ty_str = lhs_ty.rewrite(context, shape).map(|lhs| lhs + " =")?; let lhs_ty_str = lhs_ty
.rewrite_result(context, shape)
.map(|lhs| lhs + " =")?;
rewrite_assign_rhs(context, lhs_ty_str, &**rhs_ty, &RhsAssignKind::Ty, shape)? rewrite_assign_rhs(context, lhs_ty_str, &**rhs_ty, &RhsAssignKind::Ty, shape)?
} }
}; };
Some(result) Ok(result)
} }
} }
impl Rewrite for ast::GenericArg { impl Rewrite for ast::GenericArg {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
match *self { match *self {
ast::GenericArg::Lifetime(ref lt) => lt.rewrite(context, shape), ast::GenericArg::Lifetime(ref lt) => lt.rewrite_result(context, shape),
ast::GenericArg::Type(ref ty) => ty.rewrite(context, shape), ast::GenericArg::Type(ref ty) => ty.rewrite_result(context, shape),
ast::GenericArg::Const(ref const_) => const_.rewrite(context, shape), ast::GenericArg::Const(ref const_) => const_.rewrite_result(context, shape),
} }
} }
} }
@ -483,11 +522,11 @@ fn rewrite_generic_args(
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
shape: Shape, shape: Shape,
span: Span, span: Span,
) -> Option<String> { ) -> RewriteResult {
match gen_args { match gen_args {
ast::GenericArgs::AngleBracketed(ref data) => { ast::GenericArgs::AngleBracketed(ref data) => {
if data.args.is_empty() { if data.args.is_empty() {
Some("".to_owned()) Ok("".to_owned())
} else { } else {
let args = data let args = data
.args .args
@ -513,47 +552,63 @@ fn rewrite_generic_args(
context, context,
shape, shape,
), ),
ast::GenericArgs::ParenthesizedElided(..) => Some("(..)".to_owned()), ast::GenericArgs::ParenthesizedElided(..) => Ok("(..)".to_owned()),
} }
} }
fn rewrite_bounded_lifetime( fn rewrite_bounded_lifetime(
lt: &ast::Lifetime, lt: &ast::Lifetime,
bounds: &[ast::GenericBound], bounds: &[ast::GenericBound],
span: Span,
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
shape: Shape, shape: Shape,
) -> Option<String> { ) -> RewriteResult {
let result = lt.rewrite(context, shape)?; let result = lt.rewrite_result(context, shape)?;
if bounds.is_empty() { if bounds.is_empty() {
Some(result) Ok(result)
} else { } else {
let colon = type_bound_colon(context); let colon = type_bound_colon(context);
let overhead = last_line_width(&result) + colon.len(); let overhead = last_line_width(&result) + colon.len();
let shape = shape
.sub_width(overhead)
.max_width_error(shape.width, span)?;
let result = format!( let result = format!(
"{}{}{}", "{}{}{}",
result, result,
colon, colon,
join_bounds(context, shape.sub_width(overhead)?, bounds, true)? join_bounds(context, shape, bounds, true)?
); );
Some(result) Ok(result)
} }
} }
impl Rewrite for ast::AnonConst { impl Rewrite for ast::AnonConst {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
format_expr(&self.value, ExprType::SubExpression, context, shape) format_expr(&self.value, ExprType::SubExpression, context, shape)
} }
} }
impl Rewrite for ast::Lifetime { impl Rewrite for ast::Lifetime {
fn rewrite(&self, context: &RewriteContext<'_>, _: Shape) -> Option<String> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
Some(context.snippet(self.ident.span).to_owned()) self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, _: Shape) -> RewriteResult {
Ok(context.snippet(self.ident.span).to_owned())
} }
} }
impl Rewrite for ast::GenericBound { impl Rewrite for ast::GenericBound {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
match *self { match *self {
ast::GenericBound::Trait( ast::GenericBound::Trait(
ref poly_trait_ref, ref poly_trait_ref,
@ -574,24 +629,30 @@ impl Rewrite for ast::GenericBound {
asyncness.push(' '); asyncness.push(' ');
} }
let polarity = polarity.as_str(); let polarity = polarity.as_str();
let shape = shape.offset_left(constness.len() + polarity.len())?; let shape = shape
.offset_left(constness.len() + polarity.len())
.max_width_error(shape.width, self.span())?;
poly_trait_ref poly_trait_ref
.rewrite(context, shape) .rewrite_result(context, shape)
.map(|s| format!("{constness}{asyncness}{polarity}{s}")) .map(|s| format!("{constness}{asyncness}{polarity}{s}"))
.map(|s| if has_paren { format!("({})", s) } else { s }) .map(|s| if has_paren { format!("({})", s) } else { s })
} }
ast::GenericBound::Use(ref args, span) => { ast::GenericBound::Use(ref args, span) => {
overflow::rewrite_with_angle_brackets(context, "use", args.iter(), shape, span) overflow::rewrite_with_angle_brackets(context, "use", args.iter(), shape, span)
} }
ast::GenericBound::Outlives(ref lifetime) => lifetime.rewrite(context, shape), ast::GenericBound::Outlives(ref lifetime) => lifetime.rewrite_result(context, shape),
} }
} }
} }
impl Rewrite for ast::GenericBounds { impl Rewrite for ast::GenericBounds {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
if self.is_empty() { if self.is_empty() {
return Some(String::new()); return Ok(String::new());
} }
join_bounds(context, shape, self, true) join_bounds(context, shape, self, true)
@ -600,8 +661,15 @@ impl Rewrite for ast::GenericBounds {
impl Rewrite for ast::GenericParam { impl Rewrite for ast::GenericParam {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
// FIXME: If there are more than one attributes, this will force multiline. // FIXME: If there are more than one attributes, this will force multiline.
let mut result = self.attrs.rewrite(context, shape).unwrap_or(String::new()); let mut result = self
.attrs
.rewrite_result(context, shape)
.unwrap_or(String::new());
let has_attrs = !result.is_empty(); let has_attrs = !result.is_empty();
let mut param = String::with_capacity(128); let mut param = String::with_capacity(128);
@ -615,15 +683,19 @@ impl Rewrite for ast::GenericParam {
param.push_str("const "); param.push_str("const ");
param.push_str(rewrite_ident(context, self.ident)); param.push_str(rewrite_ident(context, self.ident));
param.push_str(": "); param.push_str(": ");
param.push_str(&ty.rewrite(context, shape)?); param.push_str(&ty.rewrite_result(context, shape)?);
if let Some(default) = default { if let Some(default) = default {
let eq_str = match context.config.type_punctuation_density() { let eq_str = match context.config.type_punctuation_density() {
TypeDensity::Compressed => "=", TypeDensity::Compressed => "=",
TypeDensity::Wide => " = ", TypeDensity::Wide => " = ",
}; };
param.push_str(eq_str); param.push_str(eq_str);
let budget = shape.width.checked_sub(param.len())?; let budget = shape
let rewrite = default.rewrite(context, Shape::legacy(budget, shape.indent))?; .width
.checked_sub(param.len())
.max_width_error(shape.width, self.span())?;
let rewrite =
default.rewrite_result(context, Shape::legacy(budget, shape.indent))?;
param.push_str(&rewrite); param.push_str(&rewrite);
} }
kw_span.lo() kw_span.lo()
@ -634,7 +706,7 @@ impl Rewrite for ast::GenericParam {
if !self.bounds.is_empty() { if !self.bounds.is_empty() {
param.push_str(type_bound_colon(context)); param.push_str(type_bound_colon(context));
param.push_str(&self.bounds.rewrite(context, shape)?) param.push_str(&self.bounds.rewrite_result(context, shape)?)
} }
if let ast::GenericParamKind::Type { if let ast::GenericParamKind::Type {
default: Some(ref def), default: Some(ref def),
@ -645,9 +717,12 @@ impl Rewrite for ast::GenericParam {
TypeDensity::Wide => " = ", TypeDensity::Wide => " = ",
}; };
param.push_str(eq_str); param.push_str(eq_str);
let budget = shape.width.checked_sub(param.len())?; let budget = shape
.width
.checked_sub(param.len())
.max_width_error(shape.width, self.span())?;
let rewrite = let rewrite =
def.rewrite(context, Shape::legacy(budget, shape.indent + param.len()))?; def.rewrite_result(context, Shape::legacy(budget, shape.indent + param.len()))?;
param.push_str(&rewrite); param.push_str(&rewrite);
} }
@ -673,44 +748,67 @@ impl Rewrite for ast::GenericParam {
result.push_str(&param); result.push_str(&param);
} }
Some(result) Ok(result)
} }
} }
impl Rewrite for ast::PolyTraitRef { impl Rewrite for ast::PolyTraitRef {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
if let Some(lifetime_str) = rewrite_bound_params(context, shape, &self.bound_generic_params) if let Some(lifetime_str) = rewrite_bound_params(context, shape, &self.bound_generic_params)
{ {
// 6 is "for<> ".len() // 6 is "for<> ".len()
let extra_offset = lifetime_str.len() + 6; let extra_offset = lifetime_str.len() + 6;
let path_str = self let shape = shape
.trait_ref .offset_left(extra_offset)
.rewrite(context, shape.offset_left(extra_offset)?)?; .max_width_error(shape.width, self.span)?;
let path_str = self.trait_ref.rewrite_result(context, shape)?;
Some(format!("for<{lifetime_str}> {path_str}")) Ok(format!("for<{lifetime_str}> {path_str}"))
} else { } else {
self.trait_ref.rewrite(context, shape) self.trait_ref.rewrite_result(context, shape)
} }
} }
} }
impl Rewrite for ast::TraitRef { impl Rewrite for ast::TraitRef {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
rewrite_path(context, PathContext::Type, &None, &self.path, shape) rewrite_path(context, PathContext::Type, &None, &self.path, shape)
} }
} }
impl Rewrite for ast::Ty { impl Rewrite for ast::Ty {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
self.rewrite_result(context, shape).ok()
}
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
match self.kind { match self.kind {
ast::TyKind::TraitObject(ref bounds, tobj_syntax) => { ast::TyKind::TraitObject(ref bounds, tobj_syntax) => {
// we have to consider 'dyn' keyword is used or not!!! // we have to consider 'dyn' keyword is used or not!!!
let (shape, prefix) = match tobj_syntax { let (shape, prefix) = match tobj_syntax {
ast::TraitObjectSyntax::Dyn => (shape.offset_left(4)?, "dyn "), ast::TraitObjectSyntax::Dyn => {
ast::TraitObjectSyntax::DynStar => (shape.offset_left(5)?, "dyn* "), let shape = shape
.offset_left(4)
.max_width_error(shape.width, self.span())?;
(shape, "dyn ")
}
ast::TraitObjectSyntax::DynStar => {
let shape = shape
.offset_left(5)
.max_width_error(shape.width, self.span())?;
(shape, "dyn* ")
}
ast::TraitObjectSyntax::None => (shape, ""), ast::TraitObjectSyntax::None => (shape, ""),
}; };
let mut res = bounds.rewrite(context, shape)?; let mut res = bounds.rewrite_result(context, shape)?;
// We may have falsely removed a trailing `+` inside macro call. // We may have falsely removed a trailing `+` inside macro call.
if context.inside_macro() if context.inside_macro()
&& bounds.len() == 1 && bounds.len() == 1
@ -719,7 +817,7 @@ impl Rewrite for ast::Ty {
{ {
res.push('+'); res.push('+');
} }
Some(format!("{prefix}{res}")) Ok(format!("{prefix}{res}"))
} }
ast::TyKind::Ptr(ref mt) => { ast::TyKind::Ptr(ref mt) => {
let prefix = match mt.mutbl { let prefix = match mt.mutbl {
@ -738,8 +836,11 @@ impl Rewrite for ast::Ty {
let mut cmnt_lo = ref_hi; let mut cmnt_lo = ref_hi;
if let Some(ref lifetime) = *lifetime { if let Some(ref lifetime) = *lifetime {
let lt_budget = shape.width.checked_sub(2 + mut_len)?; let lt_budget = shape
let lt_str = lifetime.rewrite( .width
.checked_sub(2 + mut_len)
.max_width_error(shape.width, self.span())?;
let lt_str = lifetime.rewrite_result(
context, context,
Shape::legacy(lt_budget, shape.indent + 2 + mut_len), Shape::legacy(lt_budget, shape.indent + 2 + mut_len),
)?; )?;
@ -783,39 +884,46 @@ impl Rewrite for ast::Ty {
result = combine_strs_with_missing_comments( result = combine_strs_with_missing_comments(
context, context,
result.trim_end(), result.trim_end(),
&mt.ty.rewrite(context, shape)?, &mt.ty.rewrite_result(context, shape)?,
before_ty_span, before_ty_span,
shape, shape,
true, true,
)?; )?;
} else { } else {
let used_width = last_line_width(&result); let used_width = last_line_width(&result);
let budget = shape.width.checked_sub(used_width)?; let budget = shape
let ty_str = mt .width
.ty .checked_sub(used_width)
.rewrite(context, Shape::legacy(budget, shape.indent + used_width))?; .max_width_error(shape.width, self.span())?;
let ty_str = mt.ty.rewrite_result(
context,
Shape::legacy(budget, shape.indent + used_width),
)?;
result.push_str(&ty_str); result.push_str(&ty_str);
} }
Some(result) Ok(result)
} }
// FIXME: we drop any comments here, even though it's a silly place to put // FIXME: we drop any comments here, even though it's a silly place to put
// comments. // comments.
ast::TyKind::Paren(ref ty) => { ast::TyKind::Paren(ref ty) => {
if context.config.version() == Version::One if context.config.style_edition() <= StyleEdition::Edition2021
|| context.config.indent_style() == IndentStyle::Visual || context.config.indent_style() == IndentStyle::Visual
{ {
let budget = shape.width.checked_sub(2)?; let budget = shape
.width
.checked_sub(2)
.max_width_error(shape.width, self.span())?;
return ty return ty
.rewrite(context, Shape::legacy(budget, shape.indent + 1)) .rewrite_result(context, Shape::legacy(budget, shape.indent + 1))
.map(|ty_str| format!("({})", ty_str)); .map(|ty_str| format!("({})", ty_str));
} }
// 2 = () // 2 = ()
if let Some(sh) = shape.sub_width(2) { if let Some(sh) = shape.sub_width(2) {
if let Some(ref s) = ty.rewrite(context, sh) { if let Ok(ref s) = ty.rewrite_result(context, sh) {
if !s.contains('\n') { if !s.contains('\n') {
return Some(format!("({s})")); return Ok(format!("({s})"));
} }
} }
} }
@ -824,8 +932,8 @@ impl Rewrite for ast::Ty {
let shape = shape let shape = shape
.block_indent(context.config.tab_spaces()) .block_indent(context.config.tab_spaces())
.with_max_width(context.config); .with_max_width(context.config);
let rw = ty.rewrite(context, shape)?; let rw = ty.rewrite_result(context, shape)?;
Some(format!( Ok(format!(
"({}{}{})", "({}{}{})",
shape.to_string_with_newline(context.config), shape.to_string_with_newline(context.config),
rw, rw,
@ -833,15 +941,18 @@ impl Rewrite for ast::Ty {
)) ))
} }
ast::TyKind::Slice(ref ty) => { ast::TyKind::Slice(ref ty) => {
let budget = shape.width.checked_sub(4)?; let budget = shape
ty.rewrite(context, Shape::legacy(budget, shape.indent + 1)) .width
.checked_sub(4)
.max_width_error(shape.width, self.span())?;
ty.rewrite_result(context, Shape::legacy(budget, shape.indent + 1))
.map(|ty_str| format!("[{}]", ty_str)) .map(|ty_str| format!("[{}]", ty_str))
} }
ast::TyKind::Tup(ref items) => { ast::TyKind::Tup(ref items) => {
rewrite_tuple(context, items.iter(), self.span, shape, items.len() == 1) rewrite_tuple(context, items.iter(), self.span, shape, items.len() == 1)
} }
ast::TyKind::AnonStruct(..) => Some(context.snippet(self.span).to_owned()), ast::TyKind::AnonStruct(..) => Ok(context.snippet(self.span).to_owned()),
ast::TyKind::AnonUnion(..) => Some(context.snippet(self.span).to_owned()), ast::TyKind::AnonUnion(..) => Ok(context.snippet(self.span).to_owned()),
ast::TyKind::Path(ref q_self, ref path) => { ast::TyKind::Path(ref q_self, ref path) => {
rewrite_path(context, PathContext::Type, q_self, path, shape) rewrite_path(context, PathContext::Type, q_self, path, shape)
} }
@ -855,24 +966,27 @@ impl Rewrite for ast::Ty {
), ),
ast::TyKind::Infer => { ast::TyKind::Infer => {
if shape.width >= 1 { if shape.width >= 1 {
Some("_".to_owned()) Ok("_".to_owned())
} else { } else {
None Err(RewriteError::ExceedsMaxWidth {
configured_width: shape.width,
span: self.span(),
})
} }
} }
ast::TyKind::BareFn(ref bare_fn) => rewrite_bare_fn(bare_fn, self.span, context, shape), ast::TyKind::BareFn(ref bare_fn) => rewrite_bare_fn(bare_fn, self.span, context, shape),
ast::TyKind::Never => Some(String::from("!")), ast::TyKind::Never => Ok(String::from("!")),
ast::TyKind::MacCall(ref mac) => { ast::TyKind::MacCall(ref mac) => {
rewrite_macro(mac, None, context, shape, MacroPosition::Expression) rewrite_macro(mac, None, context, shape, MacroPosition::Expression)
} }
ast::TyKind::ImplicitSelf => Some(String::from("")), ast::TyKind::ImplicitSelf => Ok(String::from("")),
ast::TyKind::ImplTrait(_, ref it) => { ast::TyKind::ImplTrait(_, ref it) => {
// Empty trait is not a parser error. // Empty trait is not a parser error.
if it.is_empty() { if it.is_empty() {
return Some("impl".to_owned()); return Ok("impl".to_owned());
} }
let rw = if context.config.version() == Version::One { let rw = if context.config.style_edition() <= StyleEdition::Edition2021 {
it.rewrite(context, shape) it.rewrite_result(context, shape)
} else { } else {
join_bounds(context, shape, it, false) join_bounds(context, shape, it, false)
}; };
@ -881,8 +995,8 @@ impl Rewrite for ast::Ty {
format!("impl{}{}", space, it_str) format!("impl{}{}", space, it_str)
}) })
} }
ast::TyKind::CVarArgs => Some("...".to_owned()), ast::TyKind::CVarArgs => Ok("...".to_owned()),
ast::TyKind::Dummy | ast::TyKind::Err(_) => Some(context.snippet(self.span).to_owned()), ast::TyKind::Dummy | ast::TyKind::Err(_) => Ok(context.snippet(self.span).to_owned()),
ast::TyKind::Typeof(ref anon_const) => rewrite_call( ast::TyKind::Typeof(ref anon_const) => rewrite_call(
context, context,
"typeof", "typeof",
@ -891,9 +1005,9 @@ impl Rewrite for ast::Ty {
shape, shape,
), ),
ast::TyKind::Pat(ref ty, ref pat) => { ast::TyKind::Pat(ref ty, ref pat) => {
let ty = ty.rewrite(context, shape)?; let ty = ty.rewrite_result(context, shape)?;
let pat = pat.rewrite(context, shape)?; let pat = pat.rewrite_result(context, shape)?;
Some(format!("{ty} is {pat}")) Ok(format!("{ty} is {pat}"))
} }
} }
} }
@ -904,7 +1018,7 @@ fn rewrite_bare_fn(
span: Span, span: Span,
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
shape: Shape, shape: Shape,
) -> Option<String> { ) -> RewriteResult {
debug!("rewrite_bare_fn {:#?}", shape); debug!("rewrite_bare_fn {:#?}", shape);
let mut result = String::with_capacity(128); let mut result = String::with_capacity(128);
@ -928,9 +1042,14 @@ fn rewrite_bare_fn(
result.push_str("fn"); result.push_str("fn");
let func_ty_shape = if context.use_block_indent() { let func_ty_shape = if context.use_block_indent() {
shape.offset_left(result.len())? shape
.offset_left(result.len())
.max_width_error(shape.width, span)?
} else { } else {
shape.visual_indent(result.len()).sub_width(result.len())? shape
.visual_indent(result.len())
.sub_width(result.len())
.max_width_error(shape.width, span)?
}; };
let rewrite = format_function_type( let rewrite = format_function_type(
@ -944,7 +1063,7 @@ fn rewrite_bare_fn(
result.push_str(&rewrite); result.push_str(&rewrite);
Some(result) Ok(result)
} }
fn is_generic_bounds_in_order(generic_bounds: &[ast::GenericBound]) -> bool { fn is_generic_bounds_in_order(generic_bounds: &[ast::GenericBound]) -> bool {
@ -968,7 +1087,7 @@ fn join_bounds(
shape: Shape, shape: Shape,
items: &[ast::GenericBound], items: &[ast::GenericBound],
need_indent: bool, need_indent: bool,
) -> Option<String> { ) -> RewriteResult {
join_bounds_inner(context, shape, items, need_indent, false) join_bounds_inner(context, shape, items, need_indent, false)
} }
@ -978,7 +1097,7 @@ fn join_bounds_inner(
items: &[ast::GenericBound], items: &[ast::GenericBound],
need_indent: bool, need_indent: bool,
force_newline: bool, force_newline: bool,
) -> Option<String> { ) -> RewriteResult {
debug_assert!(!items.is_empty()); debug_assert!(!items.is_empty());
let generic_bounds_in_order = is_generic_bounds_in_order(items); let generic_bounds_in_order = is_generic_bounds_in_order(items);
@ -1073,10 +1192,10 @@ fn join_bounds_inner(
}; };
let (extendable, trailing_str) = if i == 0 { let (extendable, trailing_str) = if i == 0 {
let bound_str = item.rewrite(context, shape)?; let bound_str = item.rewrite_result(context, shape)?;
(is_bound_extendable(&bound_str, item), bound_str) (is_bound_extendable(&bound_str, item), bound_str)
} else { } else {
let bound_str = &item.rewrite(context, shape)?; let bound_str = &item.rewrite_result(context, shape)?;
match leading_span { match leading_span {
Some(ls) if has_leading_comment => ( Some(ls) if has_leading_comment => (
is_bound_extendable(bound_str, item), is_bound_extendable(bound_str, item),
@ -1100,7 +1219,7 @@ fn join_bounds_inner(
true, true,
) )
.map(|v| (v, trailing_span, extendable)), .map(|v| (v, trailing_span, extendable)),
_ => Some((strs + &trailing_str, trailing_span, extendable)), _ => Ok((strs + &trailing_str, trailing_span, extendable)),
} }
}, },
)?; )?;
@ -1110,22 +1229,22 @@ fn join_bounds_inner(
// and either there is more than one item; // and either there is more than one item;
// or the single item is of type `Trait`, // or the single item is of type `Trait`,
// and any of the internal arrays contains more than one item; // and any of the internal arrays contains more than one item;
let retry_with_force_newline = match context.config.version() { let retry_with_force_newline = match context.config.style_edition() {
Version::One => { style_edition @ _ if style_edition <= StyleEdition::Edition2021 => {
!force_newline !force_newline
&& items.len() > 1 && items.len() > 1
&& (result.0.contains('\n') || result.0.len() > shape.width) && (result.0.contains('\n') || result.0.len() > shape.width)
} }
Version::Two if force_newline => false, _ if force_newline => false,
Version::Two if (!result.0.contains('\n') && result.0.len() <= shape.width) => false, _ if (!result.0.contains('\n') && result.0.len() <= shape.width) => false,
Version::Two if items.len() > 1 => true, _ if items.len() > 1 => true,
Version::Two => is_item_with_multi_items_array(&items[0]), _ => is_item_with_multi_items_array(&items[0]),
}; };
if retry_with_force_newline { if retry_with_force_newline {
join_bounds_inner(context, shape, items, need_indent, true) join_bounds_inner(context, shape, items, need_indent, true)
} else { } else {
Some(result.0) Ok(result.0)
} }
} }

View File

@ -6,11 +6,11 @@ use rustc_ast::ast::{
}; };
use rustc_ast::ptr; use rustc_ast::ptr;
use rustc_ast_pretty::pprust; use rustc_ast_pretty::pprust;
use rustc_span::{sym, symbol, BytePos, LocalExpnId, Span, Symbol, SyntaxContext}; use rustc_span::{BytePos, LocalExpnId, Span, Symbol, SyntaxContext, sym, symbol};
use unicode_width::UnicodeWidthStr; use unicode_width::UnicodeWidthStr;
use crate::comment::{filter_normal_code, CharClasses, FullCodeCharKind, LineClasses}; use crate::comment::{CharClasses, FullCodeCharKind, LineClasses, filter_normal_code};
use crate::config::{Config, Version}; use crate::config::{Config, StyleEdition};
use crate::rewrite::RewriteContext; use crate::rewrite::RewriteContext;
use crate::shape::{Indent, Shape}; use crate::shape::{Indent, Shape};
@ -367,10 +367,10 @@ macro_rules! out_of_file_lines_range {
}; };
} }
macro_rules! skip_out_of_file_lines_range { macro_rules! skip_out_of_file_lines_range_err {
($self:ident, $span:expr) => { ($self:ident, $span:expr) => {
if out_of_file_lines_range!($self, $span) { if out_of_file_lines_range!($self, $span) {
return None; return Err(RewriteError::SkipFormatting);
} }
}; };
} }
@ -596,7 +596,7 @@ pub(crate) fn trim_left_preserve_layout(
// just InString{Commented} in order to allow the start of a string to be indented // just InString{Commented} in order to allow the start of a string to be indented
let new_veto_trim_value = (kind == FullCodeCharKind::InString let new_veto_trim_value = (kind == FullCodeCharKind::InString
|| (config.version() == Version::Two || (config.style_edition() >= StyleEdition::Edition2024
&& kind == FullCodeCharKind::InStringCommented)) && kind == FullCodeCharKind::InStringCommented))
&& !line.ends_with('\\'); && !line.ends_with('\\');
let line = if veto_trim || new_veto_trim_value { let line = if veto_trim || new_veto_trim_value {
@ -612,7 +612,7 @@ pub(crate) fn trim_left_preserve_layout(
// such lines should not be taken into account when computing the minimum. // such lines should not be taken into account when computing the minimum.
match kind { match kind {
FullCodeCharKind::InStringCommented | FullCodeCharKind::EndStringCommented FullCodeCharKind::InStringCommented | FullCodeCharKind::EndStringCommented
if config.version() == Version::Two => if config.style_edition() >= StyleEdition::Edition2024 =>
{ {
None None
} }
@ -656,7 +656,7 @@ pub(crate) fn indent_next_line(kind: FullCodeCharKind, line: &str, config: &Conf
// formatting the code block, therefore the string's indentation needs // formatting the code block, therefore the string's indentation needs
// to be adjusted for the code surrounding the code block. // to be adjusted for the code surrounding the code block.
config.format_strings() && line.ends_with('\\') config.format_strings() && line.ends_with('\\')
} else if config.version() == Version::Two { } else if config.style_edition() >= StyleEdition::Edition2024 {
!kind.is_commented_string() !kind.is_commented_string()
} else { } else {
true true

View File

@ -11,9 +11,9 @@ use crate::config::lists::*;
use crate::expr::rewrite_field; use crate::expr::rewrite_field;
use crate::items::{rewrite_struct_field, rewrite_struct_field_prefix}; use crate::items::{rewrite_struct_field, rewrite_struct_field_prefix};
use crate::lists::{ use crate::lists::{
definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator, ListFormatting, ListItem, Separator, definitive_tactic, itemize_list, write_list,
}; };
use crate::rewrite::{Rewrite, RewriteContext}; use crate::rewrite::{Rewrite, RewriteContext, RewriteResult};
use crate::shape::{Indent, Shape}; use crate::shape::{Indent, Shape};
use crate::source_map::SpanUtils; use crate::source_map::SpanUtils;
use crate::spanned::Spanned; use crate::spanned::Spanned;
@ -24,13 +24,13 @@ use crate::utils::{
pub(crate) trait AlignedItem { pub(crate) trait AlignedItem {
fn skip(&self) -> bool; fn skip(&self) -> bool;
fn get_span(&self) -> Span; fn get_span(&self) -> Span;
fn rewrite_prefix(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String>; fn rewrite_prefix(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult;
fn rewrite_aligned_item( fn rewrite_aligned_item(
&self, &self,
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
shape: Shape, shape: Shape,
prefix_max_width: usize, prefix_max_width: usize,
) -> Option<String>; ) -> RewriteResult;
} }
impl AlignedItem for ast::FieldDef { impl AlignedItem for ast::FieldDef {
@ -42,24 +42,23 @@ impl AlignedItem for ast::FieldDef {
self.span() self.span()
} }
fn rewrite_prefix(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { fn rewrite_prefix(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
let attrs_str = self.attrs.rewrite(context, shape)?; let attrs_str = self.attrs.rewrite_result(context, shape)?;
let missing_span = if self.attrs.is_empty() { let missing_span = if self.attrs.is_empty() {
mk_sp(self.span.lo(), self.span.lo()) mk_sp(self.span.lo(), self.span.lo())
} else { } else {
mk_sp(self.attrs.last().unwrap().span.hi(), self.span.lo()) mk_sp(self.attrs.last().unwrap().span.hi(), self.span.lo())
}; };
let attrs_extendable = self.ident.is_none() && is_attributes_extendable(&attrs_str); let attrs_extendable = self.ident.is_none() && is_attributes_extendable(&attrs_str);
rewrite_struct_field_prefix(context, self).and_then(|field_str| { let field_str = rewrite_struct_field_prefix(context, self)?;
combine_strs_with_missing_comments( combine_strs_with_missing_comments(
context, context,
&attrs_str, &attrs_str,
&field_str, &field_str,
missing_span, missing_span,
shape, shape,
attrs_extendable, attrs_extendable,
) )
})
} }
fn rewrite_aligned_item( fn rewrite_aligned_item(
@ -67,7 +66,7 @@ impl AlignedItem for ast::FieldDef {
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
shape: Shape, shape: Shape,
prefix_max_width: usize, prefix_max_width: usize,
) -> Option<String> { ) -> RewriteResult {
rewrite_struct_field(context, self, shape, prefix_max_width) rewrite_struct_field(context, self, shape, prefix_max_width)
} }
} }
@ -81,8 +80,8 @@ impl AlignedItem for ast::ExprField {
self.span() self.span()
} }
fn rewrite_prefix(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { fn rewrite_prefix(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
let attrs_str = self.attrs.rewrite(context, shape)?; let attrs_str = self.attrs.rewrite_result(context, shape)?;
let name = rewrite_ident(context, self.ident); let name = rewrite_ident(context, self.ident);
let missing_span = if self.attrs.is_empty() { let missing_span = if self.attrs.is_empty() {
mk_sp(self.span.lo(), self.span.lo()) mk_sp(self.span.lo(), self.span.lo())
@ -104,7 +103,7 @@ impl AlignedItem for ast::ExprField {
context: &RewriteContext<'_>, context: &RewriteContext<'_>,
shape: Shape, shape: Shape,
prefix_max_width: usize, prefix_max_width: usize,
) -> Option<String> { ) -> RewriteResult {
rewrite_field(context, self, shape, prefix_max_width) rewrite_field(context, self, shape, prefix_max_width)
} }
} }
@ -199,7 +198,7 @@ fn struct_field_prefix_max_min_width<T: AlignedItem>(
.rewrite_prefix(context, shape) .rewrite_prefix(context, shape)
.map(|field_str| trimmed_last_line_width(&field_str)) .map(|field_str| trimmed_last_line_width(&field_str))
}) })
.fold_options((0, ::std::usize::MAX), |(max_len, min_len), len| { .fold_ok((0, ::std::usize::MAX), |(max_len, min_len), len| {
(cmp::max(max_len, len), cmp::min(min_len, len)) (cmp::max(max_len, len), cmp::min(min_len, len))
}) })
.unwrap_or((0, 0)) .unwrap_or((0, 0))
@ -246,12 +245,12 @@ fn rewrite_aligned_items_inner<T: AlignedItem>(
if tactic == DefinitiveListTactic::Horizontal { if tactic == DefinitiveListTactic::Horizontal {
// since the items fits on a line, there is no need to align them // since the items fits on a line, there is no need to align them
let do_rewrite = let do_rewrite =
|field: &T| -> Option<String> { field.rewrite_aligned_item(context, item_shape, 0) }; |field: &T| -> RewriteResult { field.rewrite_aligned_item(context, item_shape, 0) };
fields fields
.iter() .iter()
.zip(items.iter_mut()) .zip(items.iter_mut())
.for_each(|(field, list_item): (&T, &mut ListItem)| { .for_each(|(field, list_item): (&T, &mut ListItem)| {
if list_item.item.is_some() { if list_item.item.is_ok() {
list_item.item = do_rewrite(field); list_item.item = do_rewrite(field);
} }
}); });
@ -267,7 +266,7 @@ fn rewrite_aligned_items_inner<T: AlignedItem>(
.tactic(tactic) .tactic(tactic)
.trailing_separator(separator_tactic) .trailing_separator(separator_tactic)
.preserve_newline(true); .preserve_newline(true);
write_list(&items, &fmt) write_list(&items, &fmt).ok()
} }
/// Returns the index in `fields` up to which a field belongs to the current group. /// Returns the index in `fields` up to which a field belongs to the current group.

View File

@ -3,24 +3,23 @@ use std::rc::Rc;
use rustc_ast::{ast, token::Delimiter, visit}; use rustc_ast::{ast, token::Delimiter, visit};
use rustc_data_structures::sync::Lrc; use rustc_data_structures::sync::Lrc;
use rustc_span::{symbol, BytePos, Pos, Span}; use rustc_span::{BytePos, Pos, Span, symbol};
use tracing::debug; use tracing::debug;
use crate::attr::*; use crate::attr::*;
use crate::comment::{contains_comment, rewrite_comment, CodeCharKind, CommentCodeSlices}; use crate::comment::{CodeCharKind, CommentCodeSlices, contains_comment, rewrite_comment};
use crate::config::Version; use crate::config::{BraceStyle, Config, MacroSelector, StyleEdition};
use crate::config::{BraceStyle, Config, MacroSelector};
use crate::coverage::transform_missing_snippet; use crate::coverage::transform_missing_snippet;
use crate::items::{ use crate::items::{
format_impl, format_trait, format_trait_alias, is_mod_decl, is_use_item, rewrite_extern_crate, FnBraceStyle, FnSig, ItemVisitorKind, StaticParts, StructParts, format_impl, format_trait,
rewrite_type_alias, FnBraceStyle, FnSig, ItemVisitorKind, StaticParts, StructParts, format_trait_alias, is_mod_decl, is_use_item, rewrite_extern_crate, rewrite_type_alias,
}; };
use crate::macros::{macro_style, rewrite_macro, rewrite_macro_def, MacroPosition}; use crate::macros::{MacroPosition, macro_style, rewrite_macro, rewrite_macro_def};
use crate::modules::Module; use crate::modules::Module;
use crate::parse::session::ParseSess; use crate::parse::session::ParseSess;
use crate::rewrite::{Rewrite, RewriteContext}; use crate::rewrite::{Rewrite, RewriteContext};
use crate::shape::{Indent, Shape}; use crate::shape::{Indent, Shape};
use crate::skip::{is_skip_attr, SkipContext}; use crate::skip::{SkipContext, is_skip_attr};
use crate::source_map::{LineRangeUtils, SpanUtils}; use crate::source_map::{LineRangeUtils, SpanUtils};
use crate::spanned::Spanned; use crate::spanned::Spanned;
use crate::stmt::Stmt; use crate::stmt::Stmt;
@ -291,7 +290,9 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
let mut comment_shape = let mut comment_shape =
Shape::indented(self.block_indent, config).comment(config); Shape::indented(self.block_indent, config).comment(config);
if self.config.version() == Version::Two && comment_on_same_line { if self.config.style_edition() >= StyleEdition::Edition2024
&& comment_on_same_line
{
self.push_str(" "); self.push_str(" ");
// put the first line of the comment on the same line as the // put the first line of the comment on the same line as the
// block's last line // block's last line
@ -312,8 +313,8 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
let comment_str = let comment_str =
rewrite_comment(other_lines, false, comment_shape, config); rewrite_comment(other_lines, false, comment_shape, config);
match comment_str { match comment_str {
Some(ref s) => self.push_str(s), Ok(ref s) => self.push_str(s),
None => self.push_str(other_lines), Err(_) => self.push_str(other_lines),
} }
} }
} }
@ -342,8 +343,8 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
let comment_str = rewrite_comment(&sub_slice, false, comment_shape, config); let comment_str = rewrite_comment(&sub_slice, false, comment_shape, config);
match comment_str { match comment_str {
Some(ref s) => self.push_str(s), Ok(ref s) => self.push_str(s),
None => self.push_str(&sub_slice), Err(_) => self.push_str(&sub_slice),
} }
} }
} }
@ -561,9 +562,11 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
) )
} else { } else {
let indent = self.block_indent; let indent = self.block_indent;
let rewrite = self.rewrite_required_fn( let rewrite = self
indent, item.ident, sig, &item.vis, generics, item.span, .rewrite_required_fn(
); indent, item.ident, sig, &item.vis, generics, item.span,
)
.ok();
self.push_rewrite(item.span, rewrite); self.push_rewrite(item.span, rewrite);
} }
} }
@ -584,7 +587,8 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
item.ident, item.ident,
&item.vis, &item.vis,
item.span, item.span,
); )
.ok();
self.push_rewrite(item.span, rewrite); self.push_rewrite(item.span, rewrite);
} }
ast::ItemKind::Delegation(..) | ast::ItemKind::DelegationMac(..) => { ast::ItemKind::Delegation(..) | ast::ItemKind::DelegationMac(..) => {
@ -609,7 +613,8 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
self.block_indent, self.block_indent,
visitor_kind, visitor_kind,
span, span,
); )
.ok();
self.push_rewrite(span, rewrite); self.push_rewrite(span, rewrite);
} }
@ -655,8 +660,9 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
); );
} else { } else {
let indent = self.block_indent; let indent = self.block_indent;
let rewrite = let rewrite = self
self.rewrite_required_fn(indent, ai.ident, sig, &ai.vis, generics, ai.span); .rewrite_required_fn(indent, ai.ident, sig, &ai.vis, generics, ai.span)
.ok();
self.push_rewrite(ai.span, rewrite); self.push_rewrite(ai.span, rewrite);
} }
} }
@ -683,7 +689,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
// 1 = ; // 1 = ;
let shape = self.shape().saturating_sub_width(1); let shape = self.shape().saturating_sub_width(1);
let rewrite = self.with_context(|ctx| rewrite_macro(mac, ident, ctx, shape, pos)); let rewrite = self.with_context(|ctx| rewrite_macro(mac, ident, ctx, shape, pos).ok());
// As of v638 of the rustc-ap-* crates, the associated span no longer includes // 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 // the trailing semicolon. This determines the correct span to ensure scenarios
// with whitespace between the delimiters and trailing semi (i.e. `foo!(abc) ;`) // with whitespace between the delimiters and trailing semi (i.e. `foo!(abc) ;`)

View File

@ -1,3 +1,3 @@
max_width = 120 max_width = 120
version = "Two" version = "Two"
attr_fn_like_width = 120 attr_fn_like_width = 120

View File

@ -0,0 +1 @@
edition = 2019

View File

@ -0,0 +1 @@
style_edition = "2024"

View File

@ -0,0 +1 @@
version = "Two"

View File

@ -0,0 +1,2 @@
style_edition = "2024"
overflow_delimited_expr = false

View File

@ -0,0 +1,2 @@
style_edition = "2021"
edition = "2024"

View File

@ -0,0 +1,2 @@
version = "Two"
edition = "2018"

View File

@ -0,0 +1,3 @@
version = "Two"
edition = "2018"
style_edition = "2021"

View File

@ -0,0 +1,2 @@
version = "Two"
style_edition = "2021"

View File

@ -5,7 +5,7 @@ use std::fs::remove_file;
use std::path::Path; use std::path::Path;
use std::process::Command; use std::process::Command;
use rustfmt_config_proc_macro::rustfmt_only_ci_test; use rustfmt_config_proc_macro::{nightly_only_test, rustfmt_only_ci_test};
/// Run the rustfmt executable and return its output. /// Run the rustfmt executable and return its output.
fn rustfmt(args: &[&str]) -> (String, String) { fn rustfmt(args: &[&str]) -> (String, String) {
@ -207,3 +207,28 @@ fn rustfmt_emits_error_when_control_brace_style_is_always_next_line() {
let (_stdout, stderr) = rustfmt(&args); let (_stdout, stderr) = rustfmt(&args);
assert!(!stderr.contains("error[internal]: left behind trailing whitespace")) assert!(!stderr.contains("error[internal]: left behind trailing whitespace"))
} }
#[nightly_only_test]
#[test]
fn rustfmt_generates_no_error_if_failed_format_code_in_doc_comments() {
// See also https://github.com/rust-lang/rustfmt/issues/6109
let file = "tests/target/issue-6109.rs";
let args = ["--config", "format_code_in_doc_comments=true", file];
let (stdout, stderr) = rustfmt(&args);
assert!(stderr.is_empty());
assert!(stdout.is_empty());
}
#[test]
fn rustfmt_error_improvement_regarding_invalid_toml() {
// See also https://github.com/rust-lang/rustfmt/issues/6302
let invalid_toml_config = "tests/config/issue-6302.toml";
let args = ["--config-path", invalid_toml_config];
let (_stdout, stderr) = rustfmt(&args);
let toml_path = Path::new(invalid_toml_config).canonicalize().unwrap();
let expected_error_message = format!("The file `{}` failed to parse", toml_path.display());
assert!(stderr.contains(&expected_error_message));
}

View File

@ -1,4 +1,4 @@
// rustfmt-version: Two // rustfmt-style_edition: 2024
fn main() { fn main() {
match a { match a {
_ => _ =>

View File

@ -1,4 +1,4 @@
// rustfmt-version: Two // rustfmt-style_edition: 2024
fn main() { fn main() {
match a { match a {
_ => // comment with => _ => // comment with =>

View File

@ -1,4 +1,4 @@
// rustfmt-version: One // rustfmt-style_edition: 2015
// rustfmt-error_on_line_overflow: false // rustfmt-error_on_line_overflow: false
// rustfmt-indent_style: Block // rustfmt-indent_style: Block

View File

@ -1,4 +1,4 @@
// rustfmt-version: Two // rustfmt-style_edition: 2024
// rustfmt-error_on_line_overflow: false // rustfmt-error_on_line_overflow: false
// rustfmt-indent_style: Block // rustfmt-indent_style: Block

View File

@ -0,0 +1,155 @@
// rustfmt-style_edition: 2015
fn combine_blocklike() {
do_thing(
|param| {
action();
foo(param)
},
);
do_thing(
x,
|param| {
action();
foo(param)
},
);
do_thing(
x,
// I'll be discussing the `action` with your para(m)legal counsel
|param| {
action();
foo(param)
},
);
do_thing(
Bar {
x: value,
y: value2,
},
);
do_thing(
x,
Bar {
x: value,
y: value2,
},
);
do_thing(
x,
// Let me tell you about that one time at the `Bar`
Bar {
x: value,
y: value2,
},
);
do_thing(
&[
value_with_longer_name,
value2_with_longer_name,
value3_with_longer_name,
value4_with_longer_name,
],
);
do_thing(
x,
&[
value_with_longer_name,
value2_with_longer_name,
value3_with_longer_name,
value4_with_longer_name,
],
);
do_thing(
x,
// Just admit it; my list is longer than can be folded on to one line
&[
value_with_longer_name,
value2_with_longer_name,
value3_with_longer_name,
value4_with_longer_name,
],
);
do_thing(
vec![
value_with_longer_name,
value2_with_longer_name,
value3_with_longer_name,
value4_with_longer_name,
],
);
do_thing(
x,
vec![
value_with_longer_name,
value2_with_longer_name,
value3_with_longer_name,
value4_with_longer_name,
],
);
do_thing(
x,
// Just admit it; my list is longer than can be folded on to one line
vec![
value_with_longer_name,
value2_with_longer_name,
value3_with_longer_name,
value4_with_longer_name,
],
);
do_thing(
x,
(
1,
2,
3,
|param| {
action();
foo(param)
},
),
);
}
fn combine_struct_sample() {
let identity = verify(
&ctx,
VerifyLogin {
type_: LoginType::Username,
username: args.username.clone(),
password: Some(args.password.clone()),
domain: None,
},
)?;
}
fn combine_macro_sample() {
rocket::ignite()
.mount(
"/",
routes![
http::auth::login,
http::auth::logout,
http::cors::options,
http::action::dance,
http::action::sleep,
],
)
.launch();
}

View File

@ -0,0 +1,155 @@
// rustfmt-style_edition: 2024
fn combine_blocklike() {
do_thing(
|param| {
action();
foo(param)
},
);
do_thing(
x,
|param| {
action();
foo(param)
},
);
do_thing(
x,
// I'll be discussing the `action` with your para(m)legal counsel
|param| {
action();
foo(param)
},
);
do_thing(
Bar {
x: value,
y: value2,
},
);
do_thing(
x,
Bar {
x: value,
y: value2,
},
);
do_thing(
x,
// Let me tell you about that one time at the `Bar`
Bar {
x: value,
y: value2,
},
);
do_thing(
&[
value_with_longer_name,
value2_with_longer_name,
value3_with_longer_name,
value4_with_longer_name,
],
);
do_thing(
x,
&[
value_with_longer_name,
value2_with_longer_name,
value3_with_longer_name,
value4_with_longer_name,
],
);
do_thing(
x,
// Just admit it; my list is longer than can be folded on to one line
&[
value_with_longer_name,
value2_with_longer_name,
value3_with_longer_name,
value4_with_longer_name,
],
);
do_thing(
vec![
value_with_longer_name,
value2_with_longer_name,
value3_with_longer_name,
value4_with_longer_name,
],
);
do_thing(
x,
vec![
value_with_longer_name,
value2_with_longer_name,
value3_with_longer_name,
value4_with_longer_name,
],
);
do_thing(
x,
// Just admit it; my list is longer than can be folded on to one line
vec![
value_with_longer_name,
value2_with_longer_name,
value3_with_longer_name,
value4_with_longer_name,
],
);
do_thing(
x,
(
1,
2,
3,
|param| {
action();
foo(param)
},
),
);
}
fn combine_struct_sample() {
let identity = verify(
&ctx,
VerifyLogin {
type_: LoginType::Username,
username: args.username.clone(),
password: Some(args.password.clone()),
domain: None,
},
)?;
}
fn combine_macro_sample() {
rocket::ignite()
.mount(
"/",
routes![
http::auth::login,
http::auth::logout,
http::cors::options,
http::action::dance,
http::action::sleep,
],
)
.launch();
}

View File

@ -1,5 +1,5 @@
// rustfmt-fn_single_line: true // rustfmt-fn_single_line: true
// rustfmt-version: One // rustfmt-style_edition: 2015
// Test single-line functions. // Test single-line functions.
fn foo_expr() { fn foo_expr() {

View File

@ -1,5 +1,5 @@
// rustfmt-fn_single_line: true // rustfmt-fn_single_line: true
// rustfmt-version: Two // rustfmt-style_edition: 2024
// Test single-line functions. // Test single-line functions.
fn foo_expr() { fn foo_expr() {

View File

@ -1,4 +1,4 @@
// rustfmt-version: One // rustfmt-style_edition: 2015
// rustfmt-error_on_line_overflow: false // rustfmt-error_on_line_overflow: false
fn issue_2179() { fn issue_2179() {

View File

@ -1,4 +1,4 @@
// rustfmt-version: Two // rustfmt-style_edition: 2024
// rustfmt-error_on_line_overflow: false // rustfmt-error_on_line_overflow: false
fn issue_2179() { fn issue_2179() {

View File

@ -1,4 +1,4 @@
// rustfmt-version: One // rustfmt-style_edition: 2015
fn foo() { fn foo() {
match 0 { match 0 {

View File

@ -1,4 +1,4 @@
// rustfmt-version: Two // rustfmt-style_edition: 2024
fn foo() { fn foo() {
match 0 { match 0 {

View File

@ -1,4 +1,4 @@
// rustfmt-version: Two // rustfmt-style_edition: 2024
fn main() { fn main() {
thread::spawn(|| { thread::spawn(|| {

View File

@ -1,4 +1,4 @@
// rustfmt-version: One // rustfmt-style_edition: 2015
pub fn main() { pub fn main() {
/* let s = String::from( /* let s = String::from(

View File

@ -1,4 +1,4 @@
// rustfmt-version: Two // rustfmt-style_edition: 2024
pub fn main() { pub fn main() {
/* let s = String::from( /* let s = String::from(

View File

@ -1,4 +1,4 @@
// rustfmt-version: One // rustfmt-style_edition: 2015
fn main() { fn main() {
assert!(HAYSTACK assert!(HAYSTACK

View File

@ -1,4 +1,4 @@
// rustfmt-version: Two // rustfmt-style_edition: 2024
fn main() { fn main() {
assert!(HAYSTACK assert!(HAYSTACK

Some files were not shown because too many files have changed in this diff Show More