diff --git a/Cargo.lock b/Cargo.lock index 1497d2d463a..e973d7f9a2a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4630,7 +4630,7 @@ dependencies = [ [[package]] name = "rustfmt-nightly" -version = "1.7.1" +version = "1.8.0" dependencies = [ "annotate-snippets 0.9.2", "anyhow", diff --git a/src/tools/rustfmt/.github/workflows/check_diff.yml b/src/tools/rustfmt/.github/workflows/check_diff.yml index 2f2beb76915..99daa0addf5 100644 --- a/src/tools/rustfmt/.github/workflows/check_diff.yml +++ b/src/tools/rustfmt/.github/workflows/check_diff.yml @@ -21,7 +21,7 @@ jobs: steps: - name: checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: install rustup run: | diff --git a/src/tools/rustfmt/.github/workflows/integration.yml b/src/tools/rustfmt/.github/workflows/integration.yml index f0dd0cf73bb..bda374562bc 100644 --- a/src/tools/rustfmt/.github/workflows/integration.yml +++ b/src/tools/rustfmt/.github/workflows/integration.yml @@ -64,7 +64,7 @@ jobs: steps: - name: checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Run build - name: install rustup diff --git a/src/tools/rustfmt/.github/workflows/linux.yml b/src/tools/rustfmt/.github/workflows/linux.yml index bce9b0c8d5a..3a5e6ab5404 100644 --- a/src/tools/rustfmt/.github/workflows/linux.yml +++ b/src/tools/rustfmt/.github/workflows/linux.yml @@ -26,7 +26,7 @@ jobs: steps: - name: checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Run build - name: install rustup diff --git a/src/tools/rustfmt/.github/workflows/mac.yml b/src/tools/rustfmt/.github/workflows/mac.yml index 89a980c42c5..2c766d0573b 100644 --- a/src/tools/rustfmt/.github/workflows/mac.yml +++ b/src/tools/rustfmt/.github/workflows/mac.yml @@ -8,7 +8,6 @@ on: jobs: test: # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/virtual-environments-for-github-hosted-runners#supported-runners-and-hardware-resources - # macOS Catalina 10.15 runs-on: macos-latest name: (${{ matrix.target }}, ${{ matrix.cfg_release_channel }}) env: @@ -23,7 +22,7 @@ jobs: steps: - name: checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Run build - name: install rustup diff --git a/src/tools/rustfmt/.github/workflows/rustdoc_check.yml b/src/tools/rustfmt/.github/workflows/rustdoc_check.yml index cd0c3218971..6e8a7ecd7ad 100644 --- a/src/tools/rustfmt/.github/workflows/rustdoc_check.yml +++ b/src/tools/rustfmt/.github/workflows/rustdoc_check.yml @@ -11,7 +11,7 @@ jobs: name: rustdoc check steps: - name: checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: install rustup run: | diff --git a/src/tools/rustfmt/.github/workflows/upload-assets.yml b/src/tools/rustfmt/.github/workflows/upload-assets.yml index 7dfaa4b9204..7a639b469e8 100644 --- a/src/tools/rustfmt/.github/workflows/upload-assets.yml +++ b/src/tools/rustfmt/.github/workflows/upload-assets.yml @@ -31,7 +31,7 @@ jobs: target: x86_64-pc-windows-msvc runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 # Run build - name: install rustup diff --git a/src/tools/rustfmt/.github/workflows/windows.yml b/src/tools/rustfmt/.github/workflows/windows.yml index ec37c714b08..728f1b90b13 100644 --- a/src/tools/rustfmt/.github/workflows/windows.yml +++ b/src/tools/rustfmt/.github/workflows/windows.yml @@ -33,7 +33,7 @@ jobs: - name: disable git eol translation run: git config --global core.autocrlf false - name: checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Run build - name: Install Rustup using win.rustup.rs diff --git a/src/tools/rustfmt/CHANGELOG.md b/src/tools/rustfmt/CHANGELOG.md index 89e90fb17dd..8af60f60dc6 100644 --- a/src/tools/rustfmt/CHANGELOG.md +++ b/src/tools/rustfmt/CHANGELOG.md @@ -1,6 +1,66 @@ # 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 @@ -238,7 +298,7 @@ ### 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 diff --git a/src/tools/rustfmt/Cargo.lock b/src/tools/rustfmt/Cargo.lock index 2a1fffa50fe..e2ceb668ebd 100644 --- a/src/tools/rustfmt/Cargo.lock +++ b/src/tools/rustfmt/Cargo.lock @@ -499,7 +499,7 @@ dependencies = [ [[package]] name = "rustfmt-nightly" -version = "1.7.1" +version = "1.8.0" dependencies = [ "annotate-snippets", "anyhow", @@ -710,21 +710,9 @@ checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if", "pin-project-lite", - "tracing-attributes", "tracing-core", ] -[[package]] -name = "tracing-attributes" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "tracing-core" version = "0.1.31" diff --git a/src/tools/rustfmt/Cargo.toml b/src/tools/rustfmt/Cargo.toml index a16620ed99b..e497b792342 100644 --- a/src/tools/rustfmt/Cargo.toml +++ b/src/tools/rustfmt/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rustfmt-nightly" -version = "1.7.1" +version = "1.8.0" description = "Tool to find and fix Rust formatting issues" repository = "https://github.com/rust-lang/rustfmt" readme = "README.md" @@ -50,7 +50,7 @@ serde_json = "1.0" term = "0.7" thiserror = "1.0.40" 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"] } unicode-segmentation = "1.9" unicode-width = "0.1" diff --git a/src/tools/rustfmt/Configurations.md b/src/tools/rustfmt/Configurations.md index f52c2573154..b1f54060392 100644 --- a/src/tools/rustfmt/Configurations.md +++ b/src/tools/rustfmt/Configurations.md @@ -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. - **Default value**: `"2015"` -- **Possible values**: `"2015"`, `"2018"`, `"2021"` +- **Possible values**: `"2015"`, `"2018"`, `"2021"`, `"2024"` - **Stable**: Yes 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) +## `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` Number of spaces per tab @@ -3051,9 +3062,7 @@ fn main() { ## `version` -Which version of the formatting rules to use. `Version::One` is backwards-compatible -with Rustfmt 1.0. Other versions are only backwards compatible within a major -version number. +This option is deprecated and has been replaced by [`style_edition`](#style_edition) - **Default value**: `One` - **Possible values**: `One`, `Two` diff --git a/src/tools/rustfmt/Contributing.md b/src/tools/rustfmt/Contributing.md index 2f2ccfb175a..85754a8658a 100644 --- a/src/tools/rustfmt/Contributing.md +++ b/src/tools/rustfmt/Contributing.md @@ -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 ``` -### Version-gate formatting changes +### Gate formatting changes -A change that introduces a different code-formatting should be gated on the -`version` configuration. This is to ensure the formatting of the current major -release is preserved, while allowing fixes to be implemented for the next -release. +A change that introduces a different code-formatting must be gated on the +`style_edition` configuration. This is to ensure rustfmt upholds its formatting +stability guarantees and adheres to the Style Edition process set in [RFC 3338] -This is done by conditionally guarding the change like so: +This can be done by conditionally guarding the formatting change, e.g.: ```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 } else { // 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 configuration, while being stable by default. -When the next major release is done, the code block of the previous formatting -can be deleted, e.g., the first block in the example above when going from `1.x` -to `2.x`. +This can then be enhanced as needed if and when there are +new Style Editions with differing formatting prescriptions. | 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 Rustfmt is basically a pretty printer - that is, its mode of operation is to diff --git a/src/tools/rustfmt/build.rs b/src/tools/rustfmt/build.rs index 9a8bb77a8ed..696c713d726 100644 --- a/src/tools/rustfmt/build.rs +++ b/src/tools/rustfmt/build.rs @@ -25,7 +25,7 @@ fn main() { // (git not installed or if this is not a git repository) just return an empty string. fn commit_info() -> String { match (channel(), commit_hash(), commit_date()) { - (channel, Some(hash), Some(date)) => format!("{} ({} {})", channel, hash.trim_end(), date), + (channel, Some(hash), Some(date)) => format!("{} ({} {})", channel, hash, date), _ => String::new(), } } @@ -39,17 +39,20 @@ fn channel() -> String { } fn commit_hash() -> Option { - Command::new("git") - .args(["rev-parse", "--short", "HEAD"]) + let output = Command::new("git") + .args(["rev-parse", "HEAD"]) .output() - .ok() - .and_then(|r| String::from_utf8(r.stdout).ok()) + .ok()?; + let mut stdout = output.status.success().then_some(output.stdout)?; + stdout.truncate(10); + String::from_utf8(stdout).ok() } fn commit_date() -> Option { - Command::new("git") + let output = Command::new("git") .args(["log", "-1", "--date=short", "--pretty=format:%cd"]) .output() - .ok() - .and_then(|r| String::from_utf8(r.stdout).ok()) + .ok()?; + let stdout = output.status.success().then_some(output.stdout)?; + String::from_utf8(stdout).ok() } diff --git a/src/tools/rustfmt/check_diff/Cargo.lock b/src/tools/rustfmt/check_diff/Cargo.lock index 6716ccdf9a0..2abf5af2f98 100644 --- a/src/tools/rustfmt/check_diff/Cargo.lock +++ b/src/tools/rustfmt/check_diff/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. 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]] name = "anstream" version = "0.6.14" @@ -51,11 +60,26 @@ dependencies = [ "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]] name = "check_diff" version = "0.1.0" dependencies = [ "clap", + "tempfile", + "tracing", + "tracing-subscriber", ] [[package]] @@ -104,6 +128,22 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "heck" version = "0.5.0" @@ -116,6 +156,73 @@ version = "1.70.0" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "proc-macro2" version = "1.0.83" @@ -134,6 +241,78 @@ dependencies = [ "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]] name = "strsim" version = "0.11.1" @@ -151,6 +330,89 @@ dependencies = [ "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]] name = "unicode-ident" version = "1.0.12" @@ -163,6 +425,34 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "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]] name = "windows-sys" version = "0.52.0" diff --git a/src/tools/rustfmt/check_diff/Cargo.toml b/src/tools/rustfmt/check_diff/Cargo.toml index a1ed154481a..4ae8a5f1f3a 100644 --- a/src/tools/rustfmt/check_diff/Cargo.toml +++ b/src/tools/rustfmt/check_diff/Cargo.toml @@ -7,3 +7,7 @@ edition = "2021" [dependencies] clap = { version = "4.4.2", features = ["derive"] } +tracing = "0.1.37" +tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } +[dev-dependencies] +tempfile = "3" diff --git a/src/tools/rustfmt/check_diff/src/lib.rs b/src/tools/rustfmt/check_diff/src/lib.rs new file mode 100644 index 00000000000..b83d67c8b6e --- /dev/null +++ b/src/tools/rustfmt/check_diff/src/lib.rs @@ -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, stderr: Vec }, + IO(std::io::Error), +} + +impl From 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(()); +} diff --git a/src/tools/rustfmt/check_diff/src/main.rs b/src/tools/rustfmt/check_diff/src/main.rs index 6d07c1b0df6..01c5926c490 100644 --- a/src/tools/rustfmt/check_diff/src/main.rs +++ b/src/tools/rustfmt/check_diff/src/main.rs @@ -1,4 +1,5 @@ use clap::Parser; + /// Inputs for the check_diff script #[derive(Parser)] struct CliInputs { @@ -16,10 +17,5 @@ struct CliInputs { } fn main() { - 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 - ); + let _args = CliInputs::parse(); } diff --git a/src/tools/rustfmt/check_diff/tests/bash_commands.rs b/src/tools/rustfmt/check_diff/tests/bash_commands.rs new file mode 100644 index 00000000000..38ee34ef503 --- /dev/null +++ b/src/tools/rustfmt/check_diff/tests/bash_commands.rs @@ -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); +} diff --git a/src/tools/rustfmt/check_diff/tests/git.rs b/src/tools/rustfmt/check_diff/tests/git.rs new file mode 100644 index 00000000000..677c3840e1e --- /dev/null +++ b/src/tools/rustfmt/check_diff/tests/git.rs @@ -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()); +} diff --git a/src/tools/rustfmt/ci/build_and_test.bat b/src/tools/rustfmt/ci/build_and_test.bat index 16608a4aaa7..b6b5ca21364 100755 --- a/src/tools/rustfmt/ci/build_and_test.bat +++ b/src/tools/rustfmt/ci/build_and_test.bat @@ -13,7 +13,13 @@ if "%CFG_RELEASE_CHANNEL%"=="nightly" ( ) cargo test || exit /b 1 -:: Build and test other crates +:: Build and test config_proc_macro cd config_proc_macro || exit /b 1 cargo build --locked || 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 diff --git a/src/tools/rustfmt/ci/build_and_test.sh b/src/tools/rustfmt/ci/build_and_test.sh index 207da362fd6..dd9a0c0fd9b 100755 --- a/src/tools/rustfmt/ci/build_and_test.sh +++ b/src/tools/rustfmt/ci/build_and_test.sh @@ -17,7 +17,13 @@ else fi cargo test -# Build and test other crates +# Build and test config_proc_macro cd config_proc_macro cargo build --locked cargo test + +# Build and test check_diff +cd .. +cd check_diff +cargo build --locked +cargo test diff --git a/src/tools/rustfmt/config_proc_macro/Cargo.toml b/src/tools/rustfmt/config_proc_macro/Cargo.toml index eda8a7fce81..ec0db49d71c 100644 --- a/src/tools/rustfmt/config_proc_macro/Cargo.toml +++ b/src/tools/rustfmt/config_proc_macro/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rustfmt-config_proc_macro" version = "0.3.0" -edition = "2018" +edition = "2021" description = "A collection of procedural macros for rustfmt" license = "Apache-2.0 OR MIT" categories = ["development-tools::procedural-macro-helpers"] @@ -13,7 +13,7 @@ proc-macro = true [dependencies] proc-macro2 = "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] serde = { version = "1.0.160", features = ["derive"] } diff --git a/src/tools/rustfmt/config_proc_macro/src/utils.rs b/src/tools/rustfmt/config_proc_macro/src/utils.rs index f5cba87b07b..1f5b5cdb604 100644 --- a/src/tools/rustfmt/config_proc_macro/src/utils.rs +++ b/src/tools/rustfmt/config_proc_macro/src/utils.rs @@ -1,5 +1,5 @@ use proc_macro2::TokenStream; -use quote::{quote, ToTokens}; +use quote::{ToTokens, quote}; pub fn fold_quote(input: impl Iterator, f: F) -> TokenStream where diff --git a/src/tools/rustfmt/rust-toolchain b/src/tools/rustfmt/rust-toolchain index 25e3961d32a..80723123274 100644 --- a/src/tools/rustfmt/rust-toolchain +++ b/src/tools/rustfmt/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2024-06-13" +channel = "nightly-2024-09-10" components = ["llvm-tools", "rustc-dev"] diff --git a/src/tools/rustfmt/rustfmt.toml b/src/tools/rustfmt/rustfmt.toml index eccd5f9bd19..86447eac2e6 100644 --- a/src/tools/rustfmt/rustfmt.toml +++ b/src/tools/rustfmt/rustfmt.toml @@ -1,3 +1,4 @@ error_on_line_overflow = true error_on_unformatted = true -version = "Two" +style_edition = "2024" +overflow_delimited_expr = false diff --git a/src/tools/rustfmt/src/attr.rs b/src/tools/rustfmt/src/attr.rs index a2c0a28d66e..5c2068b6a22 100644 --- a/src/tools/rustfmt/src/attr.rs +++ b/src/tools/rustfmt/src/attr.rs @@ -1,21 +1,21 @@ //! Format attributes and meta items. -use rustc_ast::ast; use rustc_ast::HasAttrs; -use rustc_span::{symbol::sym, Span}; +use rustc_ast::ast; +use rustc_span::{Span, symbol::sym}; use tracing::debug; use self::doc_comment::DocCommentFormatter; -use crate::comment::{contains_comment, rewrite_doc_comment, CommentStyle}; -use crate::config::lists::*; +use crate::comment::{CommentStyle, contains_comment, rewrite_doc_comment}; use crate::config::IndentStyle; +use crate::config::lists::*; 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::rewrite::{Rewrite, RewriteContext}; +use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult}; use crate::shape::Shape; use crate::source_map::SpanUtils; -use crate::types::{rewrite_path, PathContext}; +use crate::types::{PathContext, rewrite_path}; use crate::utils::{count_newlines, mk_sp}; mod doc_comment; @@ -100,7 +100,7 @@ fn format_derive( ",", |span| span.lo(), |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 '(' // This helps us focus parsing to just what's inside #[derive(...)] context.snippet_provider.span_after(attr.span, "("), @@ -148,7 +148,7 @@ fn format_derive( .tactic(tactic) .trailing_separator(trailing_separator) .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); @@ -218,9 +218,9 @@ fn rewrite_initial_doc_comments( context: &RewriteContext<'_>, attrs: &[ast::Attribute], shape: Shape, -) -> Option<(usize, Option)> { +) -> Result<(usize, Option), RewriteError> { if attrs.is_empty() { - return Some((0, None)); + return Ok((0, None)); } // Rewrite doc comments 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)) .collect::>() .join("\n"); - return Some(( + return Ok(( sugared_docs.len(), Some(rewrite_doc_comment( &snippet, @@ -240,13 +240,19 @@ fn rewrite_initial_doc_comments( )); } - Some((0, None)) + Ok((0, None)) } impl Rewrite for ast::NestedMetaItem { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { 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) => { 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 { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { - 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 => { rewrite_path(context, PathContext::Type, &None, &self.path, shape)? } @@ -287,7 +297,7 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option &path, list.iter(), // 1 = "]" - shape.sub_width(1)?, + shape.sub_width(1).max_width_error(shape.width, self.span)?, self.span, context.config.attr_fn_like_width(), Some(if has_trailing_comma { @@ -300,7 +310,9 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option ast::MetaItemKind::NameValue(ref lit) => { let path = rewrite_path(context, PathContext::Type, &None, &self.path, shape)?; // 3 = ` = ` - let lit_shape = shape.shrink_left(path.len() + 3)?; + let lit_shape = shape + .shrink_left(path.len() + 3) + .max_width_error(shape.width, self.span)?; // `rewrite_literal` returns `None` when `lit` exceeds max // width. Since a literal is basically unformattable unless it // is a string literal (and only if `format_strings` is set), @@ -308,7 +320,7 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option // is longer than the max width and continue on formatting. // See #2479 for example. let value = rewrite_literal(context, lit.as_token_lit(), lit.span, lit_shape) - .unwrap_or_else(|| context.snippet(lit.span).to_owned()); + .unwrap_or_else(|_| context.snippet(lit.span).to_owned()); format!("{path} = {value}") } }) @@ -317,6 +329,10 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option impl Rewrite for ast::Attribute { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { let snippet = context.snippet(self.span); if self.is_doc_comment() { rewrite_doc_comment(snippet, shape.comment(context.config), context.config) @@ -328,7 +344,7 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option let prefix = attr_prefix(self); if should_skip || contains_comment(snippet) { - return Some(snippet.to_owned()); + return Ok(snippet.to_owned()); } if let Some(ref meta) = self.meta() { @@ -353,9 +369,11 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option } // 1 = `[` - let shape = shape.offset_left(prefix.len() + 1)?; - Some(meta.rewrite(context, shape).map_or_else( - || snippet.to_owned(), + let shape = shape + .offset_left(prefix.len() + 1) + .max_width_error(shape.width, self.span)?; + Ok(meta.rewrite_result(context, shape).map_or_else( + |_| snippet.to_owned(), |rw| match &self.kind { ast::AttrKind::Normal(normal_attr) => match normal_attr.item.unsafety { // For #![feature(unsafe_attributes)] @@ -367,7 +385,7 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option }, )) } else { - Some(snippet.to_owned()) + Ok(snippet.to_owned()) } } } @@ -375,8 +393,12 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option impl Rewrite for [ast::Attribute] { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { if self.is_empty() { - return Some(String::new()); + return Ok(String::new()); } // The current remaining attributes. @@ -392,7 +414,7 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option // merging derives into a single attribute. loop { if attrs.is_empty() { - return Some(result); + return Ok(result); } // Handle doc comments. @@ -431,7 +453,7 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option // Handle derives if we will merge them. if !skip_derives && context.config.merge_derives() && is_derive(&attrs[0]) { let derives = take_while_with_pred(context, attrs, is_derive); - let derive_str = format_derive(derives, shape, context)?; + let derive_str = format_derive(derives, shape, context).unknown_error()?; result.push_str(&derive_str); let missing_span = attrs @@ -464,7 +486,7 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option // If we get here, then we have a regular attribute, just handle one // 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); let missing_span = attrs diff --git a/src/tools/rustfmt/src/bin/main.rs b/src/tools/rustfmt/src/bin/main.rs index 88281d296be..c7d3a060d54 100644 --- a/src/tools/rustfmt/src/bin/main.rs +++ b/src/tools/rustfmt/src/bin/main.rs @@ -1,6 +1,6 @@ #![feature(rustc_private)] -use anyhow::{format_err, Result}; +use anyhow::{Result, format_err}; use io::Error as IoError; use thiserror::Error; @@ -11,15 +11,15 @@ use std::collections::HashMap; use std::env; 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::str::FromStr; use getopts::{Matches, Options}; use crate::rustfmt::{ - load_config, CliOptions, Color, Config, Edition, EmitMode, FileLines, FileName, - FormatReportFormatterBuilder, Input, Session, Verbosity, + CliOptions, Color, Config, Edition, EmitMode, FileLines, FileName, + FormatReportFormatterBuilder, Input, Session, StyleEdition, Verbosity, Version, load_config, }; 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", "[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( "", "color", @@ -181,6 +186,12 @@ fn make_opts() -> Options { "skip-children", "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"); @@ -263,24 +274,35 @@ fn format_string(input: String, options: GetOptsOptions) -> Result { let (mut config, _) = load_config(Some(Path::new(".")), Some(options.clone()))?; if options.check { - config.set().emit_mode(EmitMode::Diff); + config.set_cli().emit_mode(EmitMode::Diff); } else { match options.emit_mode { // Emit modes which work with standard input // None means default, which is Stdout. - None | Some(EmitMode::Stdout) | Some(EmitMode::Checkstyle) | Some(EmitMode::Json) => {} + 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) => { return Err(OperationError::StdinBadEmit(emit_mode).into()); } } - config - .set() - .emit_mode(options.emit_mode.unwrap_or(EmitMode::Stdout)); } config.set().verbose(Verbosity::Quiet); // parse file_lines - config.set().file_lines(options.file_lines); + 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() { match *f { FileName::Stdin => {} @@ -319,10 +341,10 @@ fn format( for file in files { 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(); } 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(); } else { // Check the file directory if the config-path could not be read or not provided @@ -428,27 +450,27 @@ fn print_help_file_lines() { } fn print_version() { - let version_info = format!( - "{}-{}", - option_env!("CARGO_PKG_VERSION").unwrap_or("unknown"), - include_str!(concat!(env!("OUT_DIR"), "/commit-info.txt")) - ); + let version_number = option_env!("CARGO_PKG_VERSION").unwrap_or("unknown"); + let commit_info = 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 { if matches.opt_present("h") { - let topic = matches.opt_str("h"); - if topic.is_none() { + let Some(topic) = matches.opt_str("h") else { return Ok(Operation::Help(HelpOp::None)); - } else if topic == Some("config".to_owned()) { - return Ok(Operation::Help(HelpOp::Config)); - } else if topic == Some("file-lines".to_owned()) && is_nightly() { - return Ok(Operation::Help(HelpOp::FileLines)); - } else { - return Err(OperationError::UnknownHelpTopic(topic.unwrap())); - } + }; + + return match topic.as_str() { + "config" => Ok(Operation::Help(HelpOp::Config)), + "file-lines" if is_nightly() => Ok(Operation::Help(HelpOp::FileLines)), + _ => Err(OperationError::UnknownHelpTopic(topic)), + }; } let mut free_matches = matches.free.iter(); @@ -514,6 +536,7 @@ struct GetOptsOptions { backup: bool, check: bool, edition: Option, + style_edition: Option, color: Option, file_lines: FileLines, // Default is all lines in all files. unstable_features: bool, @@ -545,6 +568,10 @@ pub fn from_matches(matches: &Matches) -> Result { if let Some(ref file_lines) = matches.opt_str("file-lines") { 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 { let mut unstable_options = vec![]; if matches.opt_present("skip-children") { @@ -556,6 +583,9 @@ pub fn from_matches(matches: &Matches) -> Result { if matches.opt_present("file-lines") { unstable_options.push("`--file-lines`"); } + if matches.opt_present("style-edition") { + unstable_options.push("`--style-edition`"); + } if !unstable_options.is_empty() { let s = if unstable_options.len() == 1 { "" } else { "s" }; return Err(format_err!( @@ -650,36 +680,49 @@ fn verify_file_lines(&self, files: &[PathBuf]) { impl CliOptions for GetOptsOptions { fn apply_to(self, config: &mut Config) { if self.verbose { - config.set().verbose(Verbosity::Verbose); + config.set_cli().verbose(Verbosity::Verbose); } else if self.quiet { - config.set().verbose(Verbosity::Quiet); + config.set_cli().verbose(Verbosity::Quiet); } else { 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 { - config.set().skip_children(skip_children); + config.set_cli().skip_children(skip_children); } 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 { - 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 { - config.set().emit_mode(EmitMode::Diff); + config.set_cli().emit_mode(EmitMode::Diff); } 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 { - config.set().make_backup(true); + config.set_cli().make_backup(true); } if let Some(color) = self.color { - config.set().color(color); + config.set_cli().color(color); } 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 { @@ -690,6 +733,25 @@ fn apply_to(self, config: &mut Config) { fn config_path(&self) -> Option<&Path> { self.config_path.as_deref() } + + fn edition(&self) -> Option { + self.inline_config + .get("edition") + .map_or(self.edition, |e| Edition::from_str(e).ok()) + } + + fn style_edition(&self) -> Option { + self.inline_config + .get("style_edition") + .map_or(self.style_edition, |se| StyleEdition::from_str(se).ok()) + } + + fn version(&self) -> Option { + self.inline_config + .get("version") + .map(|version| Version::from_str(version).ok()) + .flatten() + } } fn edition_from_edition_str(edition_str: &str) -> Result { @@ -702,6 +764,16 @@ fn edition_from_edition_str(edition_str: &str) -> Result { } } +fn style_edition_from_style_edition_str(edition_str: &str) -> Result { + 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 { match emit_str { "files" => Ok(EmitMode::Files), @@ -712,3 +784,185 @@ fn emit_mode_from_emit_str(emit_str: &str) -> Result { _ => 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(path: Option<&Path>, options: Option) -> 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); + } +} diff --git a/src/tools/rustfmt/src/chains.rs b/src/tools/rustfmt/src/chains.rs index 96fbe7a963a..fd2ef9cb1db 100644 --- a/src/tools/rustfmt/src/chains.rs +++ b/src/tools/rustfmt/src/chains.rs @@ -59,15 +59,15 @@ use std::cmp::min; use rustc_ast::{ast, ptr}; -use rustc_span::{symbol, BytePos, Span}; +use rustc_span::{BytePos, Span, symbol}; use tracing::debug; -use crate::comment::{rewrite_comment, CharClasses, FullCodeCharKind, RichChar}; -use crate::config::{IndentStyle, Version}; +use crate::comment::{CharClasses, FullCodeCharKind, RichChar, rewrite_comment}; +use crate::config::{IndentStyle, StyleEdition}; use crate::expr::rewrite_call; use crate::lists::extract_pre_comment; 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::source_map::SpanUtils; use crate::utils::{ @@ -80,6 +80,9 @@ /// Provides the original input contents from the span /// of a chain element with trailing spaces trimmed. fn format_overflow_style(span: Span, context: &RewriteContext<'_>) -> Option { + // 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| { s.lines() .map(|l| l.trim_end()) @@ -93,12 +96,16 @@ fn format_chain_item( context: &RewriteContext<'_>, rewrite_shape: Shape, allow_overflow: bool, -) -> Option { +) -> RewriteResult { if allow_overflow { - item.rewrite(context, rewrite_shape) - .or_else(|| format_overflow_style(item.span, context)) + // TODO(ding-young): Consider calling format_overflow_style() + // 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 { - item.rewrite(context, rewrite_shape) + item.rewrite_result(context, rewrite_shape) } } @@ -135,17 +142,17 @@ pub(crate) fn rewrite_chain( expr: &ast::Expr, context: &RewriteContext<'_>, shape: Shape, -) -> Option { +) -> RewriteResult { let chain = Chain::from_ast(expr, context); debug!("rewrite_chain {:?} {:?}", chain, shape); // If this is just an expression with some `?`s, then format it trivially and // return early. if chain.children.is_empty() { - return chain.parent.rewrite(context, shape); + return chain.parent.rewrite_result(context, shape); } - chain.rewrite(context, shape) + chain.rewrite_result(context, shape) } #[derive(Debug)] @@ -203,7 +210,7 @@ fn is_block_like(&self, context: &RewriteContext<'_>, reps: &str) -> bool { fn is_tup_field_access(expr: &ast::Expr) -> bool { match expr.kind { ast::ExprKind::Field(_, ref field) => { - field.name.to_string().chars().all(|c| c.is_digit(10)) + field.name.as_str().chars().all(|c| c.is_digit(10)) } _ => false, } @@ -269,7 +276,13 @@ fn from_ast( impl Rewrite for ChainItem { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { - 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 { ChainItemKind::Parent { ref expr, @@ -278,14 +291,14 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option ChainItemKind::Parent { ref expr, parens: false, - } => expr.rewrite(context, shape)?, + } => expr.rewrite_result(context, shape)?, ChainItemKind::MethodCall(ref segment, ref types, ref exprs) => { Self::rewrite_method_call(segment.ident, types, exprs, self.span, context, shape)? } ChainItemKind::StructField(ident) => format!(".{}", rewrite_ident(context, ident)), ChainItemKind::TupleField(ident, nested) => format!( "{}.{}", - if nested && context.config.version() == Version::One { + if nested && context.config.style_edition() <= StyleEdition::Edition2021 { " " } else { "" @@ -297,7 +310,7 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option rewrite_comment(comment, false, shape, context.config)? } }; - Some(format!("{rewrite}{}", "?".repeat(self.tries))) + Ok(format!("{rewrite}{}", "?".repeat(self.tries))) } } @@ -327,14 +340,14 @@ fn rewrite_method_call( span: Span, context: &RewriteContext<'_>, shape: Shape, - ) -> Option { + ) -> RewriteResult { let type_str = if types.is_empty() { String::new() } else { let type_list = types .iter() - .map(|ty| ty.rewrite(context, shape)) - .collect::>>()?; + .map(|ty| ty.rewrite_result(context, shape)) + .collect::, RewriteError>>()?; format!("::<{}>", type_list.join(", ")) }; @@ -519,6 +532,10 @@ fn convert_try(expr: &ast::Expr, context: &RewriteContext<'_>) -> ast::Expr { impl Rewrite for Chain { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { debug!("rewrite chain {:?} {:?}", self, shape); let mut formatter = match context.config.indent_style() { @@ -532,17 +549,25 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option formatter.format_root(&self.parent, context, shape)?; 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. - 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_last_child(context, shape, 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 @@ fn format_root( parent: &ChainItem, context: &RewriteContext<'_>, shape: Shape, - ) -> Option<()>; + ) -> Result<(), RewriteError>; fn child_shape(&self, context: &RewriteContext<'_>, shape: Shape) -> Option; - 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( &mut self, context: &RewriteContext<'_>, shape: Shape, child_shape: Shape, - ) -> Option<()>; - fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> Option; + ) -> Result<(), RewriteError>; + fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> RewriteResult; // Returns `Some` if the chain is only a root, None otherwise. fn pure_root(&mut self) -> Option; } @@ -616,12 +645,16 @@ fn pure_root(&mut self) -> Option { } } - 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] { let rewrite = format_chain_item(item, context, child_shape, self.allow_overflow)?; self.rewrites.push(rewrite); } - Some(()) + Ok(()) } // Rewrite the last child. The last child of a chain requires special treatment. We need to @@ -662,8 +695,8 @@ fn format_last_child( context: &RewriteContext<'_>, shape: Shape, child_shape: Shape, - ) -> Option<()> { - let last = self.children.last()?; + ) -> Result<(), RewriteError> { + let last = self.children.last().unknown_error()?; let extendable = may_extend && last_line_extendable(&self.rewrites[0]); let prev_last_line_width = last_line_width(&self.rewrites[0]); @@ -687,11 +720,17 @@ fn format_last_child( && self.rewrites.iter().all(|s| !s.contains('\n')) && one_line_budget > 0; let last_shape = if all_in_one_line { - shape.sub_width(last.tries)? + shape + .sub_width(last.tries) + .max_width_error(shape.width, last.span)? } else if extendable { - child_shape.sub_width(last.tries)? + child_shape + .sub_width(last.tries) + .max_width_error(child_shape.width, last.span)? } 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; @@ -707,7 +746,7 @@ fn format_last_child( }; 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: // 1. The entire chain fits in a single line except the last child. // 2. `last_child_str.lines().count() >= 5`. @@ -722,17 +761,18 @@ fn format_last_child( // last child on its own line, and compare two rewrites to choose which is // better. let last_shape = child_shape - .sub_width(shape.rhs_overhead(context.config) + last.tries)?; - match last.rewrite(context, last_shape) { - Some(ref new_rw) if !could_fit_single_line => { + .sub_width(shape.rhs_overhead(context.config) + last.tries) + .max_width_error(child_shape.width, last.span)?; + match last.rewrite_result(context, last_shape) { + Ok(ref new_rw) if !could_fit_single_line => { last_subexpr_str = Some(new_rw.clone()); } - Some(ref new_rw) if new_rw.lines().count() >= line_count => { + Ok(ref new_rw) if new_rw.lines().count() >= line_count => { last_subexpr_str = Some(rw); self.fits_single_line = could_fit_single_line && all_in_one_line; } - new_rw @ Some(..) => { - last_subexpr_str = new_rw; + Ok(new_rw) => { + last_subexpr_str = Some(new_rw); } _ => { last_subexpr_str = Some(rw); @@ -747,22 +787,28 @@ fn format_last_child( let last_shape = if context.use_block_indent() { last_shape } 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)); - self.rewrites.push(last_subexpr_str?); - Some(()) + let last_subexpr_str = + last_subexpr_str.unwrap_or(last.rewrite_result(context, last_shape)?); + self.rewrites.push(last_subexpr_str); + Ok(()) } - fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> Option { + fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> RewriteResult { let connector = if self.fits_single_line { // Yay, we can put everything on one line. Cow::from("") } else { // Use new lines. if context.force_one_line_chain.get() { - return None; + return Err(RewriteError::ExceedsMaxWidth { + configured_width: child_shape.width, + span: self.children.last().unknown_error()?.span, + }); } child_shape.to_string_with_newline(context.config) }; @@ -781,7 +827,7 @@ fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> Opt result.push_str(rewrite); } - Some(result) + Ok(result) } } @@ -806,8 +852,8 @@ fn format_root( parent: &ChainItem, context: &RewriteContext<'_>, shape: Shape, - ) -> Option<()> { - let mut root_rewrite: String = parent.rewrite(context, shape)?; + ) -> Result<(), RewriteError> { + 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 tab_width = context.config.tab_spaces().saturating_sub(shape.offset); @@ -817,10 +863,12 @@ fn format_root( if let ChainItemKind::Comment(..) = item.kind { break; } - let shape = shape.offset_left(root_rewrite.len())?; - match &item.rewrite(context, shape) { - Some(rewrite) => root_rewrite.push_str(rewrite), - None => break, + let shape = shape + .offset_left(root_rewrite.len()) + .max_width_error(shape.width, item.span)?; + match &item.rewrite_result(context, shape) { + Ok(rewrite) => root_rewrite.push_str(rewrite), + Err(_) => break, } root_ends_with_block = last_line_extendable(&root_rewrite); @@ -832,7 +880,7 @@ fn format_root( } self.shared.rewrites.push(root_rewrite); self.root_ends_with_block = root_ends_with_block; - Some(()) + Ok(()) } fn child_shape(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { @@ -840,7 +888,11 @@ fn child_shape(&self, context: &RewriteContext<'_>, shape: Shape) -> Option, child_shape: Shape) -> Option<()> { + fn format_children( + &mut self, + context: &RewriteContext<'_>, + child_shape: Shape, + ) -> Result<(), RewriteError> { self.shared.format_children(context, child_shape) } @@ -849,12 +901,12 @@ fn format_last_child( context: &RewriteContext<'_>, shape: Shape, child_shape: Shape, - ) -> Option<()> { + ) -> Result<(), RewriteError> { self.shared .format_last_child(true, context, shape, child_shape) } - fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> Option { + fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> RewriteResult { self.shared.join_rewrites(context, child_shape) } @@ -885,9 +937,9 @@ fn format_root( parent: &ChainItem, context: &RewriteContext<'_>, shape: Shape, - ) -> Option<()> { + ) -> Result<(), RewriteError> { 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'); self.offset = if multiline { last_line_width(&root_rewrite).saturating_sub(shape.used_width()) @@ -899,18 +951,19 @@ fn format_root( let item = &self.shared.children[0]; if let ChainItemKind::Comment(..) = item.kind { self.shared.rewrites.push(root_rewrite); - return Some(()); + return Ok(()); } let child_shape = parent_shape .visual_indent(self.offset) - .sub_width(self.offset)?; - let rewrite = item.rewrite(context, child_shape)?; + .sub_width(self.offset) + .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) { root_rewrite.push_str(&rewrite); } else { // We couldn't fit in at the visual indent, try the last // indent. - let rewrite = item.rewrite(context, parent_shape)?; + let rewrite = item.rewrite_result(context, parent_shape)?; root_rewrite.push_str(&rewrite); self.offset = 0; } @@ -919,7 +972,7 @@ fn format_root( } self.shared.rewrites.push(root_rewrite); - Some(()) + Ok(()) } fn child_shape(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { @@ -932,7 +985,11 @@ fn child_shape(&self, context: &RewriteContext<'_>, shape: Shape) -> Option, child_shape: Shape) -> Option<()> { + fn format_children( + &mut self, + context: &RewriteContext<'_>, + child_shape: Shape, + ) -> Result<(), RewriteError> { self.shared.format_children(context, child_shape) } @@ -941,12 +998,12 @@ fn format_last_child( context: &RewriteContext<'_>, shape: Shape, child_shape: Shape, - ) -> Option<()> { + ) -> Result<(), RewriteError> { self.shared .format_last_child(false, context, shape, child_shape) } - fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> Option { + fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> RewriteResult { self.shared.join_rewrites(context, child_shape) } diff --git a/src/tools/rustfmt/src/closures.rs b/src/tools/rustfmt/src/closures.rs index b5c26235e77..a37b47e3bc9 100644 --- a/src/tools/rustfmt/src/closures.rs +++ b/src/tools/rustfmt/src/closures.rs @@ -4,17 +4,17 @@ use tracing::debug; use crate::attr::get_attrs_from_stmt; +use crate::config::StyleEdition; use crate::config::lists::*; -use crate::config::Version; use crate::expr::{block_contains_comment, is_simple_block, is_unsafe_block, rewrite_cond}; use crate::items::{span_hi_for_param, span_lo_for_param}; -use crate::lists::{definitive_tactic, itemize_list, write_list, ListFormatting, Separator}; +use crate::lists::{ListFormatting, Separator, definitive_tactic, itemize_list, write_list}; use crate::overflow::OverflowableItem; -use crate::rewrite::{Rewrite, RewriteContext}; +use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult}; use crate::shape::Shape; use crate::source_map::SpanUtils; 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: // FIXME - the below is probably no longer true in full. @@ -37,7 +37,7 @@ pub(crate) fn rewrite_closure( span: Span, context: &RewriteContext<'_>, shape: Shape, -) -> Option { +) -> RewriteResult { debug!("rewrite_closure {:?}", body); let (prefix, extra_offset) = rewrite_closure_fn_decl( @@ -53,13 +53,15 @@ pub(crate) fn rewrite_closure( shape, )?; // 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 { // The body of the closure is an empty block. if block.stmts.is_empty() && !block_contains_comment(context, block) { return body - .rewrite(context, shape) + .rewrite_result(context, shape) .map(|s| format!("{} {}", prefix, s)); } @@ -67,15 +69,15 @@ pub(crate) fn rewrite_closure( ast::FnRetTy::Default(_) if !context.inside_macro() => { 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. rewrite_closure_block(block, &prefix, context, body_shape) }) } 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 // one line, so we'll insert a block. rewrite_closure_with_block(body, &prefix, context, body_shape) @@ -89,7 +91,7 @@ fn try_rewrite_without_block( context: &RewriteContext<'_>, shape: Shape, body_shape: Shape, -) -> Option { +) -> RewriteResult { let expr = get_inner_expr(expr, prefix, context); if is_block_closure_forced(context, expr) { @@ -153,11 +155,11 @@ fn rewrite_closure_with_block( prefix: &str, context: &RewriteContext<'_>, shape: Shape, -) -> Option { +) -> RewriteResult { let left_most = left_most_sub_expr(body); let veto_block = veto_block(body) && !expr_requires_semi_to_be_stmt(left_most); if veto_block { - return None; + return Err(RewriteError::Unknown); } let block = ast::Block { @@ -185,7 +187,7 @@ fn rewrite_closure_with_block( shape, false, )?; - Some(format!("{prefix} {block}")) + Ok(format!("{prefix} {block}")) } // Rewrite closure with a single expression without wrapping its body with block. @@ -194,7 +196,7 @@ fn rewrite_closure_expr( prefix: &str, context: &RewriteContext<'_>, shape: Shape, -) -> Option { +) -> RewriteResult { fn allow_multi_line(expr: &ast::Expr) -> bool { match expr.kind { ast::ExprKind::Match(..) @@ -217,12 +219,12 @@ fn allow_multi_line(expr: &ast::Expr) -> bool { // unless it is a block-like expression or we are inside macro call. let veto_multiline = (!allow_multi_line(expr) && !context.inside_macro()) || context.config.force_multiline_blocks(); - expr.rewrite(context, shape) + expr.rewrite_result(context, shape) .and_then(|rw| { if veto_multiline && rw.contains('\n') { - None + Err(RewriteError::Unknown) } else { - Some(rw) + Ok(rw) } }) .map(|rw| format!("{} {}", prefix, rw)) @@ -234,8 +236,12 @@ fn rewrite_closure_block( prefix: &str, context: &RewriteContext<'_>, shape: Shape, -) -> Option { - Some(format!("{} {}", prefix, block.rewrite(context, shape)?)) +) -> RewriteResult { + Ok(format!( + "{} {}", + prefix, + block.rewrite_result(context, shape)? + )) } // Return type is (prefix, extra_offset) @@ -250,13 +256,14 @@ fn rewrite_closure_fn_decl( span: Span, context: &RewriteContext<'_>, shape: Shape, -) -> Option<(String, usize)> { +) -> Result<(String, usize), RewriteError> { let binder = match binder { ast::ClosureBinder::For { generic_params, .. } if generic_params.is_empty() => { "for<> ".to_owned() } ast::ClosureBinder::For { generic_params, .. } => { - let lifetime_str = rewrite_bound_params(context, shape, generic_params)?; + let lifetime_str = + rewrite_bound_params(context, shape, generic_params).unknown_error()?; format!("for<{lifetime_str}> ") } ast::ClosureBinder::NotPresent => "".to_owned(), @@ -287,13 +294,17 @@ fn rewrite_closure_fn_decl( // 4 = "|| {".len(), which is overconservative when the closure consists of // a single expression. let nested_shape = shape - .shrink_left(binder.len() + const_.len() + immovable.len() + coro.len() + mover.len())? - .sub_width(4)?; + .shrink_left(binder.len() + const_.len() + immovable.len() + coro.len() + mover.len()) + .and_then(|shape| shape.sub_width(4)) + .max_width_error(shape.width, span)?; // 1 = | let param_offset = nested_shape.indent + 1; - let param_shape = nested_shape.offset_left(1)?.visual_indent(0); - let ret_str = fn_decl.output.rewrite(context, param_shape)?; + let param_shape = nested_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( context.snippet_provider, @@ -302,7 +313,7 @@ fn rewrite_closure_fn_decl( ",", |param| span_lo_for_param(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, "|"), body.span.lo(), false, @@ -317,7 +328,9 @@ fn rewrite_closure_fn_decl( horizontal_budget, ); 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, }; @@ -339,7 +352,7 @@ fn rewrite_closure_fn_decl( // 1 = space between `|...|` and body. 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. @@ -348,7 +361,7 @@ pub(crate) fn rewrite_last_closure( context: &RewriteContext<'_>, expr: &ast::Expr, shape: Shape, -) -> Option { +) -> RewriteResult { if let ast::ExprKind::Closure(ref closure) = expr.kind { let ast::Closure { 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 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. 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 // keep the blocks. 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') => { single_line_body_str @@ -424,9 +439,9 @@ pub(crate) fn rewrite_last_closure( } // 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`. @@ -443,18 +458,18 @@ fn is_block_closure_forced(context: &RewriteContext<'_>, expr: &ast::Expr) -> bo if context.inside_macro() { false } 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 { 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::Try(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, } } diff --git a/src/tools/rustfmt/src/comment.rs b/src/tools/rustfmt/src/comment.rs index e76be0fd162..b7d7a396a67 100644 --- a/src/tools/rustfmt/src/comment.rs +++ b/src/tools/rustfmt/src/comment.rs @@ -2,14 +2,14 @@ use std::{borrow::Cow, iter}; -use itertools::{multipeek, MultiPeek}; +use itertools::{Itertools as _, MultiPeek, multipeek}; use rustc_span::Span; use tracing::{debug, trace}; use crate::config::Config; -use crate::rewrite::RewriteContext; +use crate::rewrite::{RewriteContext, RewriteErrorExt, RewriteResult}; use crate::shape::{Indent, Shape}; -use crate::string::{rewrite_string, StringFormat}; +use crate::string::{StringFormat, rewrite_string}; use crate::utils::{ count_newlines, first_line_width, last_line_width, trim_left_preserve_layout, trimmed_last_line_width, unicode_str_width, @@ -158,7 +158,7 @@ pub(crate) fn combine_strs_with_missing_comments( span: Span, shape: Shape, allow_extend: bool, -) -> Option { +) -> RewriteResult { trace!( "combine_strs_with_missing_comments `{}` `{}` {:?} {:?}", 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(next_str); - return Some(result); + return Ok(result); } // 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(next_str); - Some(result) + Ok(result) } -pub(crate) fn rewrite_doc_comment(orig: &str, shape: Shape, config: &Config) -> Option { +pub(crate) fn rewrite_doc_comment(orig: &str, shape: Shape, config: &Config) -> RewriteResult { identify_comment(orig, false, shape, config, true) } @@ -245,7 +245,7 @@ pub(crate) fn rewrite_comment( block_style: bool, shape: Shape, config: &Config, -) -> Option { +) -> RewriteResult { identify_comment(orig, block_style, shape, config, false) } @@ -255,7 +255,7 @@ fn identify_comment( shape: Shape, config: &Config, is_doc_comment: bool, -) -> Option { +) -> RewriteResult { let style = comment_style(orig, false); // Computes the byte length of line taking into account a newline if the line is part of a @@ -347,7 +347,7 @@ fn consume_same_line_comments( let (first_group, rest) = orig.split_at(first_group_ending); let rewritten_first_group = if !config.normalize_comments() && has_bare_lines && style.is_block_comment() { - trim_left_preserve_layout(first_group, shape.indent, config)? + trim_left_preserve_layout(first_group, shape.indent, config).unknown_error()? } else if !config.normalize_comments() && !config.wrap_comments() && !( @@ -368,7 +368,7 @@ fn consume_same_line_comments( )? }; if rest.is_empty() { - Some(rewritten_first_group) + Ok(rewritten_first_group) } else { identify_comment( rest.trim_start(), @@ -534,10 +534,11 @@ fn add_line(&mut self, line: &str) -> bool { /// Returns the block as a string, with each line trimmed at the start. fn trimmed_block_as_string(&self) -> String { - self.lines - .iter() - .map(|line| format!("{} ", line.trim_start())) - .collect::() + self.lines.iter().fold(String::new(), |mut acc, line| { + acc.push_str(line.trim_start()); + acc.push(' '); + acc + }) } /// Returns the block as a string under its original form. @@ -900,7 +901,7 @@ fn rewrite_comment_inner( shape: Shape, config: &Config, is_doc_comment: bool, -) -> Option { +) -> RewriteResult { let mut rewriter = CommentRewrite::new(orig, block_style, shape, config); 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 = "//#### "; @@ -999,7 +1000,7 @@ pub(crate) fn rewrite_missing_comment( span: Span, shape: Shape, context: &RewriteContext<'_>, -) -> Option { +) -> RewriteResult { let missing_snippet = context.snippet(span); let trimmed_snippet = missing_snippet.trim(); // 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() { rewrite_comment(trimmed_snippet, false, shape, context.config) } else { - Some(String::new()) + Ok(String::new()) } } @@ -1019,13 +1020,13 @@ pub(crate) fn recover_missing_comment_in_span( shape: Shape, context: &RewriteContext<'_>, used_width: usize, -) -> Option { +) -> RewriteResult { let missing_comment = rewrite_missing_comment(span, shape, context)?; if missing_comment.is_empty() { - Some(String::new()) + Ok(String::new()) } else { let missing_snippet = context.snippet(span); - let pos = missing_snippet.find('/')?; + let pos = missing_snippet.find('/').unknown_error()?; // 1 = ` ` let total_width = missing_comment.len() + used_width + 1; let force_new_line_before_comment = @@ -1035,7 +1036,7 @@ pub(crate) fn recover_missing_comment_in_span( } else { Cow::from(" ") }; - Some(format!("{sep}{missing_comment}")) + Ok(format!("{sep}{missing_comment}")) } } @@ -1055,8 +1056,7 @@ fn light_rewrite_comment( config: &Config, is_doc_comment: bool, ) -> String { - let lines: Vec<&str> = orig - .lines() + orig.lines() .map(|l| { // This is basically just l.trim(), but in the case that a line starts // with `*` we want to leave one space before it, so it aligns with the @@ -1074,8 +1074,7 @@ fn light_rewrite_comment( // Preserve markdown's double-space line break syntax in doc comment. trim_end_unless_two_whitespaces(left_trimmed, is_doc_comment) }) - .collect(); - lines.join(&format!("\n{}", offset.to_string(config))) + .join(&format!("\n{}", offset.to_string(config))) } /// Trims comment characters and possibly a single space from the left of a string. @@ -1704,12 +1703,11 @@ fn next(&mut self) -> Option { } /// Checks is `new` didn't miss any comment from `span`, if it removed any, return previous text -/// (if it fits in the width/offset, else return `None`), else return `new` pub(crate) fn recover_comment_removed( new: String, span: Span, context: &RewriteContext<'_>, -) -> Option { +) -> String { let snippet = context.snippet(span); if snippet != new && changed_comment_content(snippet, &new) { // We missed some comments. Warn and keep the original text. @@ -1723,9 +1721,9 @@ pub(crate) fn recover_comment_removed( )], ); } - Some(snippet.to_owned()) + snippet.to_owned() } else { - Some(new) + new } } diff --git a/src/tools/rustfmt/src/config/config_type.rs b/src/tools/rustfmt/src/config/config_type.rs index f7cff1a1729..14217caba0a 100644 --- a/src/tools/rustfmt/src/config/config_type.rs +++ b/src/tools/rustfmt/src/config/config_type.rs @@ -70,15 +70,15 @@ macro_rules! create_config { // // - $i: the ident name of the option // - $ty: the type of the option value - // - $def: the default value of the option // - $stb: true if the option is stable // - $dstring: description of the option - ($($i:ident: $ty:ty, $def:expr, $stb:expr, $( $dstring:expr ),+ );+ $(;)*) => ( + ($($i:ident: $ty:ty, $stb:expr, $( $dstring:expr ),+ );+ $(;)*) => ( #[cfg(test)] use std::collections::HashSet; use std::io::Write; use serde::{Deserialize, Serialize}; + use $crate::config::style_edition::StyleEditionDefault; #[derive(Clone)] #[allow(unreachable_pub)] @@ -89,7 +89,10 @@ pub struct Config { // - 1: true if the option was manually initialized // - 2: the option value // - 3: true if the option is unstable - $($i: (Cell, 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, <$ty as StyleEditionDefault>::ConfigType, bool, bool)),+ } // Just like the Config struct but with each property wrapped @@ -100,7 +103,7 @@ pub struct Config { #[derive(Deserialize, Serialize, Clone)] #[allow(unreachable_pub)] 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 @@ -114,7 +117,7 @@ pub struct PartialConfig { impl<'a> ConfigSetter<'a> { $( #[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; match stringify!($i) { "max_width" @@ -130,6 +133,37 @@ pub fn $i(&mut self, value: $ty) { "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(), + &_ => (), + } + } + )+ + } + + #[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 @@ pub fn $i(&self) -> bool { )+ } + // 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 { $( #[allow(unreachable_pub)] - pub fn $i(&self) -> $ty { + pub fn $i(&self) -> <$ty as StyleEditionDefault>::ConfigType { self.$i.0.set(true); 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)] pub fn set(&mut self) -> ConfigSetter<'_> { ConfigSetter(self) } + #[allow(unreachable_pub)] + pub fn set_cli(&mut self) -> CliConfigSetter<'_> { + CliConfigSetter(self) + } + #[allow(unreachable_pub)] pub fn was_set(&self) -> ConfigWasSet<'_> { 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 { $( if let Some(option_value) = parsed.$i { @@ -186,6 +261,7 @@ fn fill_from_parsed_config(mut self, parsed: PartialConfig, dir: &Path) -> Confi self.set_merge_imports(); self.set_fn_args_layout(); self.set_hide_parse_errors(); + self.set_version(); self } @@ -212,7 +288,9 @@ pub(crate) fn is_valid_name(name: &str) -> bool { pub fn is_valid_key_val(key: &str, val: &str) -> bool { match key { $( - stringify!($i) => val.parse::<$ty>().is_ok(), + stringify!($i) => { + val.parse::<<$ty as StyleEditionDefault>::ConfigType>().is_ok() + } )+ _ => false, } @@ -246,11 +324,15 @@ pub fn override_value(&mut self, key: &str, val: &str) match key { $( stringify!($i) => { - let option_value = val.parse::<$ty>() - .expect(&format!("Failed to parse override for {} (\"{}\") as a {}", - stringify!($i), - val, - stringify!($ty))); + let value = val.parse::<<$ty as StyleEditionDefault>::ConfigType>() + .expect( + &format!( + "Failed to parse override for {} (\"{}\") as a {}", + stringify!($i), + val, + stringify!(<$ty as StyleEditionDefault>::ConfigType) + ) + ); // Users are currently allowed to set unstable // options/variants via the `--config` options override. @@ -261,7 +343,7 @@ pub fn override_value(&mut self, key: &str, val: &str) // For now, do not validate whether the option or value is stable, // just always set it. self.$i.1 = true; - self.$i.2 = option_value; + self.$i.2 = value; } )+ _ => panic!("Unknown config key in override: {}", key) @@ -281,6 +363,7 @@ pub fn override_value(&mut self, key: &str, val: &str) "merge_imports" => self.set_merge_imports(), "fn_args_layout" => self.set_fn_args_layout(), "hide_parse_errors" => self.set_hide_parse_errors(), + "version" => self.set_version(), &_ => (), } } @@ -301,6 +384,7 @@ pub fn is_hidden_option(name: &str) -> bool { #[allow(unreachable_pub)] pub fn print_docs(out: &mut dyn Write, include_unstable: bool) { + let style_edition = StyleEdition::Edition2015; use std::cmp; let max = 0; $( let max = cmp::max(max, stringify!($i).len()+1); )+ @@ -317,14 +401,17 @@ pub fn print_docs(out: &mut dyn Write, include_unstable: bool) { } name_out.push_str(name_raw); 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() { default_str = String::from("\"\""); } writeln!(out, "{}{} Default: {}{}", name_out, - <$ty>::doc_hint(), + <<$ty as StyleEditionDefault>::ConfigType>::doc_hint(), default_str, if !$stb { " (unstable)" } else { "" }).unwrap(); $( @@ -477,12 +564,36 @@ fn set_hide_parse_errors(&mut self) { } } + 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)] /// Returns `true` if the config key was explicitly set and is the default value. 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 { - return self.$i.1 && self.$i.2 == $def; + return self.$i.1 && self.$i.2 == default_value; } )+ false @@ -492,11 +603,7 @@ pub fn is_default(&self, key: &str) -> bool { // Template for the default configuration impl Default for Config { fn default() -> Config { - Config { - $( - $i: (Cell::new(false), false, $def, $stb), - )+ - } + Config::default_with_style_edition(StyleEdition::Edition2015) } } ) diff --git a/src/tools/rustfmt/src/config/file_lines.rs b/src/tools/rustfmt/src/config/file_lines.rs index 224864393d3..c53ec6371e9 100644 --- a/src/tools/rustfmt/src/config/file_lines.rs +++ b/src/tools/rustfmt/src/config/file_lines.rs @@ -7,7 +7,7 @@ use rustc_data_structures::sync::Lrc; use rustc_span::SourceFile; -use serde::{ser, Deserialize, Deserializer, Serialize, Serializer}; +use serde::{Deserialize, Deserializer, Serialize, Serializer, ser}; use serde_json as json; use thiserror::Error; @@ -38,7 +38,7 @@ fn from(name: rustc_span::FileName) -> FileName { impl fmt::Display for FileName { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - FileName::Real(p) => write!(f, "{}", p.to_str().unwrap()), + FileName::Real(p) => write!(f, "{}", p.display()), FileName::Stdin => write!(f, ""), } } @@ -201,7 +201,7 @@ pub(crate) fn all() -> FileLines { } /// 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() } diff --git a/src/tools/rustfmt/src/config/mod.rs b/src/tools/rustfmt/src/config/mod.rs index 9484b2e5829..ea257c0a8ba 100644 --- a/src/tools/rustfmt/src/config/mod.rs +++ b/src/tools/rustfmt/src/config/mod.rs @@ -10,9 +10,7 @@ #[allow(unreachable_pub)] pub use crate::config::file_lines::{FileLines, FileName, Range}; #[allow(unreachable_pub)] -pub use crate::config::lists::*; -#[allow(unreachable_pub)] -pub use crate::config::macro_names::{MacroSelector, MacroSelectors}; +pub use crate::config::macro_names::MacroSelector; #[allow(unreachable_pub)] pub use crate::config::options::*; @@ -34,161 +32,169 @@ // `name: value type, default value, is stable, description;` create_config! { // Fundamental stuff - max_width: usize, 100, true, "Maximum width of each line"; - hard_tabs: bool, false, true, "Use tab characters for indentation, spaces for alignment"; - tab_spaces: usize, 4, true, "Number of spaces per tab"; - newline_style: NewlineStyle, NewlineStyle::Auto, true, "Unix or Windows line endings"; - indent_style: IndentStyle, IndentStyle::Block, false, "How do we indent expressions or items"; + max_width: MaxWidth, true, "Maximum width of each line"; + hard_tabs: HardTabs, true, "Use tab characters for indentation, spaces for alignment"; + tab_spaces: TabSpaces, true, "Number of spaces per tab"; + newline_style: NewlineStyleConfig, true, "Unix or Windows line endings"; + indent_style: IndentStyleConfig, false, "How do we indent expressions or items"; // 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'"; - width_heuristics: WidthHeuristics, WidthHeuristics::scaled(100), false, - "'small' heuristic values"; - fn_call_width: usize, 60, true, "Maximum width of the args of a function call before \ + width_heuristics: WidthHeuristicsConfig, false, "'small' heuristic values"; + fn_call_width: FnCallWidth, true, "Maximum width of the args of a function call before \ falling back to vertical formatting."; - attr_fn_like_width: usize, 70, true, "Maximum width of the args of a function-like \ + attr_fn_like_width: AttrFnLikeWidth, true, "Maximum width of the args of a function-like \ attributes before falling back to vertical formatting."; - struct_lit_width: usize, 18, true, "Maximum width in the body of a struct lit before \ + struct_lit_width: StructLitWidth, true, "Maximum width in the body of a struct lit before \ falling back to vertical formatting."; - struct_variant_width: usize, 35, true, "Maximum width in the body of a struct variant before \ - falling back to vertical formatting."; - array_width: usize, 60, true, "Maximum width of an array literal before falling \ + struct_variant_width: StructVariantWidth, true, "Maximum width in the body of a struct variant \ + before falling back to vertical formatting."; + array_width: ArrayWidth, true, "Maximum width of an array literal before falling \ back to vertical formatting."; - chain_width: usize, 60, true, "Maximum length of a chain to fit on a single line."; - single_line_if_else_max_width: usize, 50, true, "Maximum line length for single line if-else \ - expressions. A value of zero means always break if-else expressions."; - single_line_let_else_max_width: usize, 50, true, "Maximum line length for single line \ - let-else statements. A value of zero means always format the divergent `else` block \ - over multiple lines."; + chain_width: ChainWidth, true, "Maximum length of a chain to fit on a single line."; + single_line_if_else_max_width: SingleLineIfElseMaxWidth, true, "Maximum line length for single \ + line if-else expressions. A value of zero means always break if-else expressions."; + single_line_let_else_max_width: SingleLineLetElseMaxWidth, true, "Maximum line length for \ + single line let-else statements. A value of zero means always format the divergent `else` \ + block over multiple lines."; // Comments. macros, and strings - wrap_comments: bool, false, false, "Break comments to fit on the line"; - format_code_in_doc_comments: bool, false, false, "Format the code snippet in doc comments."; - doc_comment_code_block_width: usize, 100, false, "Maximum width for code snippets in doc \ - comments. No effect unless format_code_in_doc_comments = true"; - comment_width: usize, 80, false, + wrap_comments: WrapComments, false, "Break comments to fit on the line"; + format_code_in_doc_comments: FormatCodeInDocComments, false, "Format the code snippet in \ + doc comments."; + doc_comment_code_block_width: DocCommentCodeBlockWidth, false, "Maximum width for code \ + 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"; - normalize_comments: bool, false, false, "Convert /* */ comments to // comments where possible"; - normalize_doc_attributes: bool, false, false, "Normalize doc attributes as doc comments"; - format_strings: bool, false, false, "Format string literals where necessary"; - format_macro_matchers: bool, false, false, + normalize_comments: NormalizeComments, false, "Convert /* */ comments to // comments where \ + possible"; + normalize_doc_attributes: NormalizeDocAttributes, false, "Normalize doc attributes as doc \ + comments"; + format_strings: FormatStrings, false, "Format string literals where necessary"; + format_macro_matchers: FormatMacroMatchers, false, "Format the metavariable matching patterns in macros"; - format_macro_bodies: bool, true, false, "Format the bodies of declarative macro definitions"; - skip_macro_invocations: MacroSelectors, MacroSelectors::default(), false, + format_macro_bodies: FormatMacroBodies, false, + "Format the bodies of declarative macro definitions"; + skip_macro_invocations: SkipMacroInvocations, false, "Skip formatting the bodies of macros invoked with the following names."; - hex_literal_case: HexLiteralCase, HexLiteralCase::Preserve, false, - "Format hexadecimal integer literals"; + hex_literal_case: HexLiteralCaseConfig, false, "Format hexadecimal integer literals"; // 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"; - struct_lit_single_line: bool, true, false, + struct_lit_single_line: StructLitSingleLine, false, "Put small struct literals on a single line"; - fn_single_line: bool, false, false, "Put single-expression functions on a single line"; - where_single_line: bool, false, false, "Force where-clauses to be on a single line"; + fn_single_line: FnSingleLine, false, "Put single-expression functions on a single line"; + where_single_line: WhereSingleLine, false, "Force where-clauses to be on a single line"; // Imports - imports_indent: IndentStyle, IndentStyle::Block, false, "Indent of imports"; - imports_layout: ListTactic, ListTactic::Mixed, false, "Item layout inside a import block"; - imports_granularity: ImportGranularity, ImportGranularity::Preserve, false, + imports_indent: ImportsIndent, false, "Indent of imports"; + imports_layout: ImportsLayout, false, "Item layout inside a import block"; + imports_granularity: ImportsGranularityConfig, false, "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"; - merge_imports: bool, false, false, "(deprecated: use imports_granularity instead)"; + merge_imports: MergeImports, false, "(deprecated: use imports_granularity instead)"; // Ordering - reorder_imports: bool, true, true, "Reorder import and extern crate statements alphabetically"; - reorder_modules: bool, true, true, "Reorder module statements alphabetically in group"; - reorder_impl_items: bool, false, false, "Reorder impl items"; + reorder_imports: ReorderImports, true, "Reorder import and extern crate statements \ + alphabetically"; + reorder_modules: ReorderModules, true, "Reorder module statements alphabetically in group"; + reorder_impl_items: ReorderImplItems, false, "Reorder impl items"; // 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"; - space_before_colon: bool, false, false, "Leave a space before the colon"; - space_after_colon: bool, true, false, "Leave a space after the colon"; - spaces_around_ranges: bool, false, false, "Put spaces around the .. and ..= range operators"; - binop_separator: SeparatorPlace, SeparatorPlace::Front, false, + space_before_colon: SpaceBeforeColon, false, "Leave a space before the colon"; + space_after_colon: SpaceAfterColon, false, "Leave a space after the colon"; + spaces_around_ranges: SpacesAroundRanges, false, "Put spaces around the .. and ..= range \ + operators"; + binop_separator: BinopSeparator, false, "Where to put a binary operator when a binary expression goes multiline"; // Misc. - remove_nested_parens: bool, true, true, "Remove nested parens"; - combine_control_expr: bool, true, false, "Combine control expressions with function calls"; - short_array_element_width_threshold: usize, 10, true, + remove_nested_parens: RemoveNestedParens, true, "Remove nested parens"; + combine_control_expr: CombineControlExpr, false, "Combine control expressions with function \ + calls"; + short_array_element_width_threshold: ShortArrayElementWidthThreshold, true, "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"; - struct_field_align_threshold: usize, 0, false, + struct_field_align_threshold: StructFieldAlignThreshold, false, "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"; - match_arm_blocks: bool, true, false, "Wrap the body of arms in blocks when it does not fit on \ - the same line with the pattern of arms"; - match_arm_leading_pipes: MatchArmLeadingPipe, MatchArmLeadingPipe::Never, true, + match_arm_blocks: MatchArmBlocks, false, "Wrap the body of arms in blocks when it does not fit \ + on the same line with the pattern of arms"; + match_arm_leading_pipes: MatchArmLeadingPipeConfig, true, "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"; - fn_args_layout: Density, Density::Tall, true, + fn_args_layout: FnArgsLayout, true, "(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."; - brace_style: BraceStyle, BraceStyle::SameLineWhere, false, "Brace style for items"; - control_brace_style: ControlBraceStyle, ControlBraceStyle::AlwaysSameLine, false, + brace_style: BraceStyleConfig, false, "Brace style for items"; + control_brace_style: ControlBraceStyleConfig, false, "Brace style for control flow constructs"; - trailing_semicolon: bool, true, false, + trailing_semicolon: TrailingSemicolon, false, "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"; - 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)"; - blank_lines_upper_bound: usize, 1, false, + blank_lines_upper_bound: BlankLinesUpperBound, false, "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"; - edition: Edition, Edition::Edition2015, true, "The edition of the parser (RFC 2052)"; - version: Version, Version::One, false, "Version of formatting rules"; - inline_attribute_width: usize, 0, false, + edition: EditionConfig, true, "The edition of the parser (RFC 2052)"; + style_edition: StyleEditionConfig, false, "The edition of the Style Guide (RFC 3338)"; + version: VersionConfig, false, "Version of formatting rules"; + inline_attribute_width: InlineAttributeWidth, false, "Write an item and its attribute on the same line \ if their combined width is below a threshold"; - format_generated_files: bool, true, false, "Format generated files"; - generated_marker_line_search_limit: usize, 5, false, "Number of lines to check for a \ - `@generated` marker when `format_generated_files` is enabled"; + format_generated_files: FormatGeneratedFiles, false, "Format generated files"; + generated_marker_line_search_limit: GeneratedMarkerLineSearchLimit, false, "Number of lines to \ + check for a `@generated` marker when `format_generated_files` is enabled"; // Options that can change the source code beyond whitespace/blocks (somewhat linty things) - merge_derives: bool, true, true, "Merge multiple `#[derive(...)]` into a single one"; - use_try_shorthand: bool, false, true, "Replace uses of the try! macro by the ? shorthand"; - use_field_init_shorthand: bool, false, true, "Use field initialization shorthand if possible"; - force_explicit_abi: bool, true, true, "Always print the abi for extern items"; - condense_wildcard_suffixes: bool, false, false, "Replace strings of _ wildcards by a single .. \ - in tuple patterns"; + merge_derives: MergeDerives, true, "Merge multiple `#[derive(...)]` into a single one"; + use_try_shorthand: UseTryShorthand, true, "Replace uses of the try! macro by the ? shorthand"; + use_field_init_shorthand: UseFieldInitShorthand, true, "Use field initialization shorthand if \ + possible"; + force_explicit_abi: ForceExplicitAbi, true, "Always print the abi for extern items"; + 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) - color: Color, Color::Auto, false, + color: ColorConfig, false, "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"; - unstable_features: bool, false, false, + unstable_features: UnstableFeatures, false, "Enables unstable features. Only available on nightly channel"; - disable_all_formatting: bool, false, true, "Don't reformat anything"; - skip_children: bool, false, false, "Don't reformat out of line modules"; - hide_parse_errors: bool, false, false, "(deprecated: use show_parse_errors instead)"; - show_parse_errors: bool, true, 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_unformatted: bool, false, false, + disable_all_formatting: DisableAllFormatting, true, "Don't reformat anything"; + skip_children: SkipChildren, false, "Don't reformat out of line modules"; + hide_parse_errors: HideParseErrors, false, "Hide errors from the parser"; + show_parse_errors: ShowParseErrors, false, "Show errors from the parser (unstable)"; + error_on_line_overflow: ErrorOnLineOverflow, false, "Error if unable to get all lines within \ + max_width"; + error_on_unformatted: ErrorOnUnformatted, false, "Error if unable to get comments or string literals within max_width, \ or they are left with trailing whitespaces"; - ignore: IgnoreList, IgnoreList::default(), false, + ignore: Ignore, false, "Skip formatting the specified files and directories"; // Not user-facing - verbose: Verbosity, Verbosity::Normal, false, "How much to information to emit to the user"; - file_lines: FileLines, FileLines::all(), false, + verbose: Verbose, false, "How much to information to emit to the user"; + file_lines: FileLinesConfig, false, "Lines to format; this is not supported in rustfmt.toml, and can only be specified \ via the --file-lines option"; - emit_mode: EmitMode, EmitMode::Files, false, + emit_mode: EmitModeConfig, false, "What emit Mode to use when none is supplied"; - make_backup: bool, false, false, "Backup changed files"; - print_misformatted_file_names: bool, false, true, + make_backup: MakeBackup, false, "Backup changed files"; + print_misformatted_file_names: PrintMisformattedFileNames, true, "Prints the names of mismatched files that were formatted. Prints the names of \ files that would be formatted when used with `--check` mode. "; } @@ -211,9 +217,48 @@ pub fn to_toml(&self) -> Result { ::toml::to_string(&cloned).map_err(ToTomlError) } + + pub(super) fn to_parsed_config( + self, + style_edition_override: Option, + edition_override: Option, + version_override: Option, + 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 { + pub fn default_for_possible_style_edition( + style_edition: Option, + edition: Option, + version: Option, + ) -> 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 { if self.was_set().required_version() { let version = env!("CARGO_PKG_VERSION"); @@ -237,11 +282,16 @@ pub(crate) fn version_meets_requirement(&self) -> bool { /// /// Returns a `Config` if the config could be read and parsed from /// the file, otherwise errors. - pub(super) fn from_toml_path(file_path: &Path) -> Result { + pub(super) fn from_toml_path( + file_path: &Path, + edition: Option, + style_edition: Option, + version: Option, + ) -> Result { let mut file = File::open(&file_path)?; let mut toml = String::new(); file.read_to_string(&mut toml)?; - Config::from_toml(&toml, file_path.parent().unwrap()) + Config::from_toml_for_style_edition(&toml, file_path, edition, style_edition, version) .map_err(|err| Error::new(ErrorKind::InvalidData, err)) } @@ -254,9 +304,14 @@ pub(super) fn from_toml_path(file_path: &Path) -> Result { /// /// Returns the `Config` to use, and the path of the project file if there was /// one. - pub(super) fn from_resolved_toml_path(dir: &Path) -> Result<(Config, Option), Error> { + pub(super) fn from_resolved_toml_path( + dir: &Path, + edition: Option, + style_edition: Option, + version: Option, + ) -> Result<(Config, Option), Error> { /// Try to find a project file in the given directory and its parents. - /// Returns the path of a the nearest project file if one exists, + /// Returns the path of the nearest project file if one exists, /// or `None` if no project file was found. fn resolve_project_file(dir: &Path) -> Result, Error> { let mut current = if dir.is_relative() { @@ -299,12 +354,27 @@ fn resolve_project_file(dir: &Path) -> Result, Error> { } match resolve_project_file(dir)? { - None => Ok((Config::default(), None)), - Some(path) => Config::from_toml_path(&path).map(|config| (config, Some(path))), + None => Ok(( + 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 { + #[allow(dead_code)] + pub(super) fn from_toml(toml: &str, file_path: &Path) -> Result { + 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, + style_edition: Option, + version: Option, + ) -> Result { let parsed: ::toml::Value = toml .parse() .map_err(|e| format!("Could not parse TOML: {}", e))?; @@ -318,18 +388,25 @@ pub(crate) fn from_toml(toml: &str, dir: &Path) -> Result { err.push_str(msg) } } - match parsed.try_into() { + + match parsed.try_into::() { Ok(parsed_config) => { if !err.is_empty() { 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.push_str("Error: Decoding config file failed:\n"); - err.push_str(format!("{e}\n").as_str()); - err.push_str("Please check your config file."); - Err(err) + let err_msg = format!( + "The file `{}` failed to parse.\nError details: {e}", + file_path.display() + ); + err.push_str(&err_msg); + Err(err_msg) } } } @@ -341,17 +418,26 @@ pub fn load_config( file_path: Option<&Path>, options: Option, ) -> Result<(Config, Option), Error> { - let over_ride = match options { - Some(ref opts) => config_path(opts)?, - None => None, + let (over_ride, edition, style_edition, version) = match options { + Some(ref opts) => ( + config_path(opts)?, + opts.edition(), + opts.style_edition(), + opts.version(), + ), + None => (None, None, None, None), }; let result = if let Some(over_ride) = over_ride { - Config::from_toml_path(over_ride.as_ref()).map(|p| (p, Some(over_ride.to_owned()))) + 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 { - Config::from_resolved_toml_path(file_path) + Config::from_resolved_toml_path(file_path, edition, style_edition, version) } else { - Ok((Config::default(), None)) + Ok(( + Config::default_for_possible_style_edition(style_edition, edition, version), + None, + )) }; result.map(|(mut c, p)| { @@ -362,7 +448,7 @@ pub fn load_config( }) } -// 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 fn get_toml_path(dir: &Path) -> Result, Error> { @@ -372,7 +458,7 @@ fn get_toml_path(dir: &Path) -> Result, Error> { match fs::metadata(&config_file) { // Only return if it's a file to handle the unlikely situation of a directory named // `rustfmt.toml`. - Ok(ref md) if md.is_file() => return Ok(Some(config_file)), + 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 // find the project file yet, and continue searching. Err(e) => { @@ -411,7 +497,11 @@ fn config_path(options: &dyn CliOptions) -> Result, Error> { 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 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}; #[allow(dead_code)] mod mock { use super::super::*; + use crate::config_option_with_style_edition_default; use rustfmt_config_proc_macro::config_type; #[config_type] @@ -436,66 +527,69 @@ pub(crate) enum PartiallyUnstableOption { V3, } + config_option_with_style_edition_default!( + StableOption, bool, _ => false; + UnstableOption, bool, _ => false; + PartiallyUnstable, PartiallyUnstableOption, _ => PartiallyUnstableOption::V1; + ); + create_config! { // Options that are used by the generated functions - max_width: usize, 100, true, "Maximum width of each line"; - required_version: String, env!("CARGO_PKG_VERSION").to_owned(), false, - "Require a specific version of rustfmt."; - ignore: IgnoreList, IgnoreList::default(), false, - "Skip formatting the specified files and directories."; - verbose: Verbosity, Verbosity::Normal, false, - "How much to information to emit to the user"; - file_lines: FileLines, FileLines::all(), false, + max_width: MaxWidth, true, "Maximum width of each line"; + required_version: RequiredVersion, false, "Require a specific version of rustfmt."; + ignore: Ignore, false, "Skip formatting the specified files and directories."; + verbose: Verbose, false, "How much to information to emit to the user"; + file_lines: FileLinesConfig, false, "Lines to format; this is not supported in rustfmt.toml, and can only be specified \ via the --file-lines option"; // merge_imports deprecation - imports_granularity: ImportGranularity, ImportGranularity::Preserve, false, - "Merge imports"; - merge_imports: bool, false, false, "(deprecated: use imports_granularity instead)"; + imports_granularity: ImportsGranularityConfig, false, "Merge imports"; + merge_imports: MergeImports, false, "(deprecated: use imports_granularity instead)"; // fn_args_layout renamed to fn_params_layout - fn_args_layout: Density, Density::Tall, true, - "(deprecated: use fn_params_layout instead)"; - fn_params_layout: Density, Density::Tall, true, + fn_args_layout: FnArgsLayout, true, "(deprecated: use fn_params_layout instead)"; + fn_params_layout: FnParamsLayout, true, "Control the layout of parameters in a function signatures."; // 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)"; - show_parse_errors: bool, true, false, + show_parse_errors: ShowParseErrors, false, "Show errors from the parser (unstable)"; // Width Heuristics - use_small_heuristics: Heuristics, Heuristics::Default, true, + use_small_heuristics: UseSmallHeuristics, true, "Whether to use different formatting for items and \ expressions if they satisfy a heuristic notion of 'small'."; - width_heuristics: WidthHeuristics, WidthHeuristics::scaled(100), false, - "'small' heuristic values"; + width_heuristics: WidthHeuristicsConfig, false, "'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."; - attr_fn_like_width: usize, 70, true, "Maximum width of the args of a function-like \ - attributes before falling back to vertical formatting."; - struct_lit_width: usize, 18, true, "Maximum width in the body of a struct lit before \ - falling back to vertical formatting."; - struct_variant_width: usize, 35, true, "Maximum width in the body of a struct \ + attr_fn_like_width: AttrFnLikeWidth, true, "Maximum width of the args of a \ + function-like attributes before falling back to vertical formatting."; + struct_lit_width: StructLitWidth, true, "Maximum width in the body of a struct lit \ + before falling back to vertical formatting."; + struct_variant_width: StructVariantWidth, true, "Maximum width in the body of a struct \ variant before falling back to vertical formatting."; - array_width: usize, 60, true, "Maximum width of an array literal before falling \ + array_width: ArrayWidth, true, "Maximum width of an array literal before falling \ back to vertical formatting."; - chain_width: usize, 60, true, "Maximum length of a chain to fit on a single line."; - single_line_if_else_max_width: usize, 50, true, "Maximum line length for single \ - line if-else expressions. A value of zero means always break if-else expressions."; - single_line_let_else_max_width: usize, 50, false, "Maximum line length for single \ - line let-else statements. A value of zero means always format the divergent \ - `else` block over multiple lines."; + chain_width: ChainWidth, true, "Maximum length of a chain to fit on a single line."; + single_line_if_else_max_width: SingleLineIfElseMaxWidth, true, "Maximum line length \ + for single line if-else expressions. A value of zero means always break if-else \ + expressions."; + single_line_let_else_max_width: SingleLineLetElseMaxWidth, false, "Maximum line length \ + for single line let-else statements. A value of zero means always format the \ + divergent `else` block over multiple lines."; // Options that are used by the tests - stable_option: bool, false, true, "A stable option"; - unstable_option: bool, false, false, "An unstable option"; - partially_unstable_option: PartiallyUnstableOption, PartiallyUnstableOption::V1, true, - "A partially unstable option"; + stable_option: StableOption, true, "A stable option"; + unstable_option: UnstableOption, false, "An unstable option"; + partially_unstable_option: PartiallyUnstable, true, "A partially unstable option"; + edition: EditionConfig, true, "blah"; + style_edition: StyleEditionConfig, true, "blah"; + version: VersionConfig, false, "blah blah" } #[cfg(test)] @@ -580,7 +674,7 @@ fn test_config_used_to_toml() { #[test] 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().verbose(), false); @@ -679,6 +773,7 @@ fn test_dump_default_config() { blank_lines_upper_bound = 1 blank_lines_lower_bound = 0 edition = "2015" +style_edition = "2015" version = "One" inline_attribute_width = 0 format_generated_files = true @@ -706,6 +801,114 @@ fn test_dump_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] #[test] fn test_as_not_nightly_channel() { @@ -730,11 +933,26 @@ fn test_as_nightly_channel() { #[nightly_only_test] #[test] 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.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)] mod deprecated_option_merge_imports { use super::*; @@ -746,7 +964,7 @@ fn test_old_option_set() { unstable_features = 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); } @@ -758,7 +976,7 @@ fn test_both_set() { merge_imports = true 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); } @@ -769,7 +987,7 @@ fn test_new_overridden() { unstable_features = 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"); assert_eq!(config.imports_granularity(), ImportGranularity::Preserve); } @@ -781,7 +999,7 @@ fn test_old_overridden() { unstable_features = true 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"); // no effect: the new option always takes precedence assert_eq!(config.imports_granularity(), ImportGranularity::Module); @@ -798,7 +1016,7 @@ fn test_default_sets_correct_widths() { use_small_heuristics = "Default" 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.attr_fn_like_width(), 140); assert_eq!(config.chain_width(), 120); @@ -814,7 +1032,7 @@ fn test_max_sets_correct_widths() { use_small_heuristics = "Max" 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.attr_fn_like_width(), 120); assert_eq!(config.chain_width(), 120); @@ -830,11 +1048,11 @@ fn test_off_sets_correct_widths() { use_small_heuristics = "Off" max_width = 100 "#; - let config = Config::from_toml(toml, Path::new("")).unwrap(); - assert_eq!(config.array_width(), usize::max_value()); - assert_eq!(config.attr_fn_like_width(), usize::max_value()); - assert_eq!(config.chain_width(), usize::max_value()); - assert_eq!(config.fn_call_width(), usize::max_value()); + let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap(); + assert_eq!(config.array_width(), usize::MAX); + assert_eq!(config.attr_fn_like_width(), usize::MAX); + assert_eq!(config.chain_width(), usize::MAX); + assert_eq!(config.fn_call_width(), usize::MAX); assert_eq!(config.single_line_if_else_max_width(), 0); assert_eq!(config.struct_lit_width(), 0); assert_eq!(config.struct_variant_width(), 0); @@ -852,7 +1070,7 @@ fn test_override_works_with_default() { struct_lit_width = 30 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.attr_fn_like_width(), 40); assert_eq!(config.chain_width(), 20); @@ -874,7 +1092,7 @@ fn test_override_with_max() { struct_lit_width = 30 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.attr_fn_like_width(), 40); assert_eq!(config.chain_width(), 20); @@ -896,7 +1114,7 @@ fn test_override_with_off() { struct_lit_width = 30 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.attr_fn_like_width(), 40); assert_eq!(config.chain_width(), 20); @@ -912,7 +1130,7 @@ fn test_fn_call_width_config_exceeds_max_width() { max_width = 90 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); } @@ -922,7 +1140,7 @@ fn test_attr_fn_like_width_config_exceeds_max_width() { max_width = 80 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); } @@ -932,7 +1150,7 @@ fn test_struct_lit_config_exceeds_max_width() { max_width = 78 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); } @@ -942,7 +1160,7 @@ fn test_struct_variant_width_config_exceeds_max_width() { max_width = 80 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); } @@ -952,7 +1170,7 @@ fn test_array_width_config_exceeds_max_width() { max_width = 60 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); } @@ -962,7 +1180,7 @@ fn test_chain_width_config_exceeds_max_width() { max_width = 80 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); } @@ -972,7 +1190,7 @@ fn test_single_line_if_else_max_width_config_exceeds_max_width() { max_width = 70 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); } diff --git a/src/tools/rustfmt/src/config/options.rs b/src/tools/rustfmt/src/config/options.rs index 3c5c713a33a..c526b5c678f 100644 --- a/src/tools/rustfmt/src/config/options.rs +++ b/src/tools/rustfmt/src/config/options.rs @@ -1,6 +1,6 @@ #![allow(unused_imports)] -use std::collections::{hash_set, HashSet}; +use std::collections::{HashSet, hash_set}; use std::fmt; use std::path::{Path, PathBuf}; use std::str::FromStr; @@ -11,8 +11,10 @@ use serde::ser::SerializeSeq; use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use crate::config::lists::*; use crate::config::Config; +use crate::config::file_lines::FileLines; +use crate::config::lists::*; +use crate::config::macro_names::MacroSelectors; #[config_type] pub enum NewlineStyle { @@ -253,12 +255,12 @@ impl WidthHeuristics { // Using this WidthHeuristics means we ignore heuristics. pub fn null() -> WidthHeuristics { WidthHeuristics { - fn_call_width: usize::max_value(), - attr_fn_like_width: usize::max_value(), + fn_call_width: usize::MAX, + attr_fn_like_width: usize::MAX, struct_lit_width: 0, struct_variant_width: 0, - array_width: usize::max_value(), - chain_width: usize::max_value(), + array_width: usize::MAX, + chain_width: usize::MAX, single_line_if_else_max_width: 0, single_line_let_else_max_width: 0, } @@ -413,7 +415,13 @@ fn from_str(_: &str) -> Result { /// values in a config with values from the command line. pub trait CliOptions { 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 edition(&self) -> Option; + fn style_edition(&self) -> Option; + fn version(&self) -> Option; } /// The edition of the syntax and semantics of code (RFC 2052). @@ -454,6 +462,17 @@ fn from(edition: Edition) -> Self { } } +impl From 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 { fn partial_cmp(&self, other: &Edition) -> Option { rustc_span::edition::Edition::partial_cmp(&(*self).into(), &(*other).into()) @@ -471,10 +490,11 @@ pub enum MatchArmLeadingPipe { Preserve, } -/// Defines the default values for each config according to [the style guide]. -/// rustfmt output may differ between style editions. +/// Defines the default values for each config according to the edition of the +/// [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] pub enum StyleEdition { #[value = "2015"] @@ -491,6 +511,169 @@ pub enum StyleEdition { Edition2021, #[value = "2024"] #[doc_hint = "2024"] + #[unstable_variant] /// [Edition 2024](). Edition2024, } + +impl From 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 { + 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; +); diff --git a/src/tools/rustfmt/src/config/style_edition.rs b/src/tools/rustfmt/src/config/style_edition.rs index 7b3ea3bc119..c34eca9c8e1 100644 --- a/src/tools/rustfmt/src/config/style_edition.rs +++ b/src/tools/rustfmt/src/config/style_edition.rs @@ -2,7 +2,7 @@ /// Defines the default value for the given style edition #[allow(dead_code)] -pub(crate) trait StyleEditionDefault { +pub trait StyleEditionDefault { type ConfigType; fn style_edition_default(style_edition: StyleEdition) -> Self::ConfigType; } diff --git a/src/tools/rustfmt/src/emitter/checkstyle.rs b/src/tools/rustfmt/src/emitter/checkstyle.rs index 9385ae59a06..c320c16bd1d 100644 --- a/src/tools/rustfmt/src/emitter/checkstyle.rs +++ b/src/tools/rustfmt/src/emitter/checkstyle.rs @@ -1,6 +1,6 @@ use self::xml::XmlEscaped; use super::*; -use crate::rustfmt_diff::{make_diff, DiffLine, Mismatch}; +use crate::rustfmt_diff::{DiffLine, Mismatch, make_diff}; mod xml; diff --git a/src/tools/rustfmt/src/emitter/json.rs b/src/tools/rustfmt/src/emitter/json.rs index 084f565804c..a99626f783d 100644 --- a/src/tools/rustfmt/src/emitter/json.rs +++ b/src/tools/rustfmt/src/emitter/json.rs @@ -1,5 +1,5 @@ use super::*; -use crate::rustfmt_diff::{make_diff, DiffLine, Mismatch}; +use crate::rustfmt_diff::{DiffLine, Mismatch, make_diff}; use serde::Serialize; use serde_json::to_string as to_json_string; diff --git a/src/tools/rustfmt/src/emitter/modified_lines.rs b/src/tools/rustfmt/src/emitter/modified_lines.rs index 81f0a31b974..0d5124bc33e 100644 --- a/src/tools/rustfmt/src/emitter/modified_lines.rs +++ b/src/tools/rustfmt/src/emitter/modified_lines.rs @@ -1,5 +1,5 @@ use super::*; -use crate::rustfmt_diff::{make_diff, ModifiedLines}; +use crate::rustfmt_diff::{ModifiedLines, make_diff}; #[derive(Debug, Default)] pub(crate) struct ModifiedLinesEmitter; diff --git a/src/tools/rustfmt/src/expr.rs b/src/tools/rustfmt/src/expr.rs index 75c75c523b0..77c9818b66b 100644 --- a/src/tools/rustfmt/src/expr.rs +++ b/src/tools/rustfmt/src/expr.rs @@ -3,33 +3,33 @@ use itertools::Itertools; use rustc_ast::token::{Delimiter, Lit, LitKind}; -use rustc_ast::{ast, ptr, token, ForLoopKind, MatchKind}; +use rustc_ast::{ForLoopKind, MatchKind, ast, ptr, token}; use rustc_span::{BytePos, Span}; use tracing::debug; use crate::chains::rewrite_chain; use crate::closures; use crate::comment::{ - combine_strs_with_missing_comments, contains_comment, recover_comment_removed, rewrite_comment, - rewrite_missing_comment, CharClasses, FindUncommented, + CharClasses, FindUncommented, combine_strs_with_missing_comments, contains_comment, + recover_comment_removed, rewrite_comment, rewrite_missing_comment, }; use crate::config::lists::*; -use crate::config::{Config, ControlBraceStyle, HexLiteralCase, IndentStyle, Version}; +use crate::config::{Config, ControlBraceStyle, HexLiteralCase, IndentStyle, StyleEdition}; use crate::lists::{ - definitive_tactic, itemize_list, shape_for_tactic, struct_lit_formatting, struct_lit_shape, - struct_lit_tactic, write_list, ListFormatting, Separator, + ListFormatting, Separator, definitive_tactic, itemize_list, shape_for_tactic, + 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::matches::rewrite_match; use crate::overflow::{self, IntoOverflowableItem, OverflowableItem}; -use crate::pairs::{rewrite_all_pairs, rewrite_pair, PairParts}; -use crate::rewrite::{Rewrite, RewriteContext}; +use crate::pairs::{PairParts, rewrite_all_pairs, rewrite_pair}; +use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult}; use crate::shape::{Indent, Shape}; use crate::source_map::{LineRangeUtils, SpanUtils}; use crate::spanned::Spanned; use crate::stmt; -use crate::string::{rewrite_string, StringFormat}; -use crate::types::{rewrite_path, PathContext}; +use crate::string::{StringFormat, rewrite_string}; +use crate::types::{PathContext, rewrite_path}; use crate::utils::{ colon_spaces, contains_skip, count_newlines, filtered_str_fits, first_line_ends_with, inner_attributes, last_line_extendable, last_line_width, mk_sp, outer_attributes, @@ -40,6 +40,10 @@ impl Rewrite for ast::Expr { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { format_expr(self, ExprType::SubExpression, context, shape) } } @@ -59,14 +63,14 @@ pub(crate) fn format_expr( expr_type: ExprType, context: &RewriteContext<'_>, shape: Shape, -) -> Option { - skip_out_of_file_lines_range!(context, expr.span); +) -> RewriteResult { + skip_out_of_file_lines_range_err!(context, expr.span); if contains_skip(&*expr.attrs) { - return Some(context.snippet(expr.span()).to_owned()); + return Ok(context.snippet(expr.span()).to_owned()); } let shape = if expr_type == ExprType::Statement && semicolon_for_expr(context, expr) { - shape.sub_width(1)? + shape.sub_width(1).max_width_error(shape.width, expr.span)? } else { shape }; @@ -82,25 +86,25 @@ pub(crate) fn format_expr( None, ), ast::ExprKind::Lit(token_lit) => { - if let Some(expr_rw) = rewrite_literal(context, token_lit, expr.span, shape) { - Some(expr_rw) + if let Ok(expr_rw) = rewrite_literal(context, token_lit, expr.span, shape) { + Ok(expr_rw) } else { if let LitKind::StrRaw(_) = token_lit.kind { - Some(context.snippet(expr.span).trim().into()) + Ok(context.snippet(expr.span).trim().into()) } else { - None + Err(RewriteError::Unknown) } } } ast::ExprKind::Call(ref callee, ref args) => { let inner_span = mk_sp(callee.span.hi(), expr.span.hi()); - let callee_str = callee.rewrite(context, shape)?; + let callee_str = callee.rewrite_result(context, shape)?; rewrite_call(context, &callee_str, args, inner_span, shape) } ast::ExprKind::Paren(ref subexpr) => rewrite_paren(context, subexpr, shape, expr.span), ast::ExprKind::Binary(op, ref lhs, ref rhs) => { // FIXME: format comments between operands and operator - rewrite_all_pairs(expr, shape, context).or_else(|| { + rewrite_all_pairs(expr, shape, context).or_else(|_| { rewrite_pair( &**lhs, &**rhs, @@ -138,7 +142,8 @@ pub(crate) fn format_expr( | ast::ExprKind::ForLoop { .. } | ast::ExprKind::Loop(..) | ast::ExprKind::While(..) => to_control_flow(expr, expr_type) - .and_then(|control_flow| control_flow.rewrite(context, shape)), + .unknown_error() + .and_then(|control_flow| control_flow.rewrite_result(context, shape)), ast::ExprKind::ConstBlock(ref anon_const) => { let rewrite = match anon_const.value.kind { ast::ExprKind::Block(ref block, opt_label) => { @@ -148,20 +153,20 @@ pub(crate) fn format_expr( // See https://github.com/rust-lang/rustfmt/issues/6158 rewrite_block(block, Some(&expr.attrs), opt_label, context, shape)? } - _ => anon_const.rewrite(context, shape)?, + _ => anon_const.rewrite_result(context, shape)?, }; - Some(format!("const {}", rewrite)) + Ok(format!("const {}", rewrite)) } ast::ExprKind::Block(ref block, opt_label) => { match expr_type { ExprType::Statement => { if is_unsafe_block(block) { rewrite_block(block, Some(&expr.attrs), opt_label, context, shape) - } else if let rw @ Some(_) = + } else if let Some(rw) = rewrite_empty_block(context, block, Some(&expr.attrs), opt_label, "", shape) { // Rewrite block without trying to put it in a single line. - rw + Ok(rw) } else { let prefix = block_prefix(context, block, shape)?; @@ -198,7 +203,7 @@ pub(crate) fn format_expr( Some(label) => format!(" {}", label.ident), None => String::new(), }; - Some(format!("continue{id_str}")) + Ok(format!("continue{id_str}")) } ast::ExprKind::Break(ref opt_label, ref opt_expr) => { let id_str = match *opt_label { @@ -209,14 +214,14 @@ pub(crate) fn format_expr( if let Some(ref expr) = *opt_expr { rewrite_unary_prefix(context, &format!("break{id_str} "), &**expr, shape) } else { - Some(format!("break{id_str}")) + Ok(format!("break{id_str}")) } } ast::ExprKind::Yield(ref opt_expr) => { if let Some(ref expr) = *opt_expr { rewrite_unary_prefix(context, "yield ", &**expr, shape) } else { - Some("yield".to_string()) + Ok("yield".to_string()) } } ast::ExprKind::Closure(ref cl) => closures::rewrite_closure( @@ -236,20 +241,21 @@ pub(crate) fn format_expr( | ast::ExprKind::MethodCall(..) | ast::ExprKind::Await(_, _) => rewrite_chain(expr, context, shape), ast::ExprKind::MacCall(ref mac) => { - rewrite_macro(mac, None, context, shape, MacroPosition::Expression).or_else(|| { + rewrite_macro(mac, None, context, shape, MacroPosition::Expression).or_else(|_| { wrap_str( context.snippet(expr.span).to_owned(), context.config.max_width(), shape, ) + .max_width_error(shape.width, expr.span) }) } - ast::ExprKind::Ret(None) => Some("return".to_owned()), + ast::ExprKind::Ret(None) => Ok("return".to_owned()), ast::ExprKind::Ret(Some(ref expr)) => { rewrite_unary_prefix(context, "return ", &**expr, shape) } ast::ExprKind::Become(ref expr) => rewrite_unary_prefix(context, "become ", &**expr, shape), - ast::ExprKind::Yeet(None) => Some("do yeet".to_owned()), + ast::ExprKind::Yeet(None) => Ok("do yeet".to_owned()), ast::ExprKind::Yeet(Some(ref expr)) => { rewrite_unary_prefix(context, "do yeet ", &**expr, shape) } @@ -344,23 +350,23 @@ fn needs_space_after_range(rhs: &ast::Expr) -> bool { }; rewrite_unary_suffix(context, &sp_delim, &*lhs, shape) } - (None, None) => Some(delim.to_owned()), + (None, None) => Ok(delim.to_owned()), } } // We do not format these expressions yet, but they should still // satisfy our width restrictions. // Style Guide RFC for InlineAsm variant pending // https://github.com/rust-dev-tools/fmt-rfcs/issues/152 - ast::ExprKind::InlineAsm(..) => Some(context.snippet(expr.span).to_owned()), + ast::ExprKind::InlineAsm(..) => Ok(context.snippet(expr.span).to_owned()), ast::ExprKind::TryBlock(ref block) => { - if let rw @ Some(_) = + if let rw @ Ok(_) = rewrite_single_line_block(context, "try ", block, Some(&expr.attrs), None, shape) { rw } else { // 9 = `try ` let budget = shape.width.saturating_sub(9); - Some(format!( + Ok(format!( "{}{}", "try ", rewrite_block( @@ -379,7 +385,7 @@ fn needs_space_after_range(rhs: &ast::Expr) -> bool { } else { "" }; - if let rw @ Some(_) = rewrite_single_line_block( + if let rw @ Ok(_) = rewrite_single_line_block( context, format!("{kind} {mover}").as_str(), block, @@ -391,7 +397,7 @@ fn needs_space_after_range(rhs: &ast::Expr) -> bool { } else { // 6 = `async ` let budget = shape.width.saturating_sub(6); - Some(format!( + Ok(format!( "{kind} {mover}{}", rewrite_block( block, @@ -403,7 +409,7 @@ fn needs_space_after_range(rhs: &ast::Expr) -> bool { )) } } - ast::ExprKind::Underscore => Some("_".to_owned()), + ast::ExprKind::Underscore => Ok("_".to_owned()), ast::ExprKind::FormatArgs(..) | ast::ExprKind::Type(..) | ast::ExprKind::IncludedBytes(..) @@ -412,16 +418,16 @@ fn needs_space_after_range(rhs: &ast::Expr) -> bool { // rustfmt tries to parse macro arguments when formatting macros, so it's not totally // impossible for rustfmt to come across one of these nodes when formatting a file. // Also, rustfmt might get passed the output from `-Zunpretty=expanded`. - None + Err(RewriteError::Unknown) } - ast::ExprKind::Err(_) | ast::ExprKind::Dummy => None, + ast::ExprKind::Err(_) | ast::ExprKind::Dummy => Err(RewriteError::Unknown), }; expr_rw - .and_then(|expr_str| recover_comment_removed(expr_str, expr.span, context)) + .map(|expr_str| recover_comment_removed(expr_str, expr.span, context)) .and_then(|expr_str| { let attrs = outer_attributes(&expr.attrs); - let attrs_str = attrs.rewrite(context, shape)?; + let attrs_str = attrs.rewrite_result(context, shape)?; let span = mk_sp( attrs.last().map_or(expr.span.lo(), |attr| attr.span.hi()), expr.span.lo(), @@ -438,7 +444,7 @@ pub(crate) fn rewrite_array<'a, T: 'a + IntoOverflowableItem<'a>>( shape: Shape, force_separator_tactic: Option, delim_token: Option, -) -> Option { +) -> RewriteResult { overflow::rewrite_with_square_brackets( context, name, @@ -488,17 +494,20 @@ fn rewrite_empty_block( None } -fn block_prefix(context: &RewriteContext<'_>, block: &ast::Block, shape: Shape) -> Option { - Some(match block.rules { +fn block_prefix(context: &RewriteContext<'_>, block: &ast::Block, shape: Shape) -> RewriteResult { + Ok(match block.rules { ast::BlockCheckMode::Unsafe(..) => { let snippet = context.snippet(block.span); - let open_pos = snippet.find_uncommented("{")?; + let open_pos = snippet.find_uncommented("{").unknown_error()?; // Extract comment between unsafe and block start. let trimmed = &snippet[6..open_pos].trim(); if !trimmed.is_empty() { // 9 = "unsafe {".len(), 7 = "unsafe ".len() - let budget = shape.width.checked_sub(9)?; + let budget = shape + .width + .checked_sub(9) + .max_width_error(shape.width, block.span)?; format!( "unsafe {} ", rewrite_comment( @@ -523,17 +532,19 @@ fn rewrite_single_line_block( attrs: Option<&[ast::Attribute]>, label: Option, shape: Shape, -) -> Option { +) -> RewriteResult { if let Some(block_expr) = stmt::Stmt::from_simple_block(context, block, attrs) { - let expr_shape = shape.offset_left(last_line_width(prefix))?; - let expr_str = block_expr.rewrite(context, expr_shape)?; + let expr_shape = shape + .offset_left(last_line_width(prefix)) + .max_width_error(shape.width, block_expr.span())?; + let expr_str = block_expr.rewrite_result(context, expr_shape)?; let label_str = rewrite_label(context, label); let result = format!("{prefix}{label_str}{{ {expr_str} }}"); if result.len() <= shape.width && !result.contains('\n') { - return Some(result); + return Ok(result); } } - None + Err(RewriteError::Unknown) } pub(crate) fn rewrite_block_with_visitor( @@ -544,9 +555,9 @@ pub(crate) fn rewrite_block_with_visitor( label: Option, shape: Shape, has_braces: bool, -) -> Option { - if let rw @ Some(_) = rewrite_empty_block(context, block, attrs, label, prefix, shape) { - return rw; +) -> RewriteResult { + if let Some(rw_str) = rewrite_empty_block(context, block, attrs, label, prefix, shape) { + return Ok(rw_str); } let mut visitor = FmtVisitor::from_context(context); @@ -555,7 +566,7 @@ pub(crate) fn rewrite_block_with_visitor( match (block.rules, label) { (ast::BlockCheckMode::Unsafe(..), _) | (ast::BlockCheckMode::Default, Some(_)) => { let snippet = context.snippet(block.span); - let open_pos = snippet.find_uncommented("{")?; + let open_pos = snippet.find_uncommented("{").unknown_error()?; visitor.last_pos = block.span.lo() + BytePos(open_pos as u32) } (ast::BlockCheckMode::Default, None) => visitor.last_pos = block.span.lo(), @@ -569,11 +580,15 @@ pub(crate) fn rewrite_block_with_visitor( .skipped_range .borrow_mut() .append(&mut visitor_context.skipped_range.borrow_mut()); - Some(format!("{}{}{}", prefix, label_str, visitor.buffer)) + Ok(format!("{}{}{}", prefix, label_str, visitor.buffer)) } impl Rewrite for ast::Block { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { rewrite_block(self, None, None, context, shape) } } @@ -584,7 +599,7 @@ fn rewrite_block( label: Option, context: &RewriteContext<'_>, shape: Shape, -) -> Option { +) -> RewriteResult { rewrite_block_inner(block, attrs, label, true, context, shape) } @@ -595,27 +610,24 @@ fn rewrite_block_inner( allow_single_line: bool, context: &RewriteContext<'_>, shape: Shape, -) -> Option { +) -> RewriteResult { let prefix = block_prefix(context, block, shape)?; // shape.width is used only for the single line case: either the empty block `{}`, // or an unsafe expression `unsafe { e }`. - if let rw @ Some(_) = rewrite_empty_block(context, block, attrs, label, &prefix, shape) { - return rw; + if let Some(rw_str) = rewrite_empty_block(context, block, attrs, label, &prefix, shape) { + return Ok(rw_str); } - let result = rewrite_block_with_visitor(context, &prefix, block, attrs, label, shape, true); - if let Some(ref result_str) = result { - if allow_single_line && result_str.lines().count() <= 3 { - if let rw @ Some(_) = - rewrite_single_line_block(context, &prefix, block, attrs, label, shape) - { - return rw; - } + let result_str = + rewrite_block_with_visitor(context, &prefix, block, attrs, label, shape, true)?; + if allow_single_line && result_str.lines().count() <= 3 { + if let rw @ Ok(_) = rewrite_single_line_block(context, &prefix, block, attrs, label, shape) + { + return rw; } } - - result + Ok(result_str) } /// Rewrite the divergent block of a `let-else` statement. @@ -624,7 +636,7 @@ pub(crate) fn rewrite_let_else_block( allow_single_line: bool, context: &RewriteContext<'_>, shape: Shape, -) -> Option { +) -> RewriteResult { rewrite_block_inner(block, None, None, allow_single_line, context, shape) } @@ -648,6 +660,7 @@ pub(crate) fn rewrite_cond( String::from("\n") + &shape.indent.block_only().to_string(context.config); control_flow .rewrite_cond(context, shape, &alt_block_sep) + .ok() .map(|rw| rw.0) }), } @@ -872,10 +885,12 @@ fn rewrite_pat_expr( expr: &ast::Expr, shape: Shape, offset: usize, - ) -> Option { + ) -> RewriteResult { debug!("rewrite_pat_expr {:?} {:?} {:?}", shape, self.pat, expr); - let cond_shape = shape.offset_left(offset)?; + let cond_shape = shape + .offset_left(offset) + .max_width_error(shape.width, expr.span)?; if let Some(pat) = self.pat { let matcher = if self.matcher.is_empty() { self.matcher.to_owned() @@ -883,9 +898,10 @@ fn rewrite_pat_expr( format!("{} ", self.matcher) }; let pat_shape = cond_shape - .offset_left(matcher.len())? - .sub_width(self.connector.len())?; - let pat_string = pat.rewrite(context, pat_shape)?; + .offset_left(matcher.len()) + .and_then(|s| s.sub_width(self.connector.len())) + .max_width_error(cond_shape.width, pat.span)?; + let pat_string = pat.rewrite_result(context, pat_shape)?; let comments_lo = context .snippet_provider .span_after(self.span.with_lo(pat.span.hi()), self.connector.trim()); @@ -902,10 +918,10 @@ fn rewrite_pat_expr( ); } - let expr_rw = expr.rewrite(context, cond_shape); + let expr_rw = expr.rewrite_result(context, cond_shape); // The expression may (partially) fit on the current line. // We do not allow splitting between `if` and condition. - if self.keyword == "if" || expr_rw.is_some() { + if self.keyword == "if" || expr_rw.is_ok() { return expr_rw; } @@ -914,7 +930,7 @@ fn rewrite_pat_expr( .block_indent(context.config.tab_spaces()) .with_max_width(context.config); let nested_indent_str = nested_shape.indent.to_string_with_newline(context.config); - expr.rewrite(context, nested_shape) + expr.rewrite_result(context, nested_shape) .map(|expr_rw| format!("{}{}", nested_indent_str, expr_rw)) } @@ -923,7 +939,7 @@ fn rewrite_cond( context: &RewriteContext<'_>, shape: Shape, alt_block_sep: &str, - ) -> Option<(String, usize)> { + ) -> Result<(String, usize), RewriteError> { // Do not take the rhs overhead from the upper expressions into account // when rewriting pattern. let new_width = context.budget(shape.used_width()); @@ -934,7 +950,9 @@ fn rewrite_cond( let constr_shape = if self.nested_if { // We are part of an if-elseif-else chain. Our constraints are tightened. // 7 = "} else " .len() - fresh_shape.offset_left(7)? + fresh_shape + .offset_left(7) + .max_width_error(fresh_shape.width, self.span)? } else { fresh_shape }; @@ -970,7 +988,7 @@ fn rewrite_cond( if let Some(cond_str) = trial { if cond_str.len() <= context.config.single_line_if_else_max_width() { - return Some((cond_str, 0)); + return Ok((cond_str, 0)); } } } @@ -1023,7 +1041,7 @@ fn rewrite_cond( label_string.len() + self.keyword.len() + pat_expr_string.len() + 2 }; - Some(( + Ok(( format!( "{}{}{}{}{}", label_string, @@ -1089,13 +1107,17 @@ pub(crate) fn rewrite_else_kw_with_comments( impl<'a> Rewrite for ControlFlow<'a> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { debug!("ControlFlow::rewrite {:?} {:?}", self, shape); let alt_block_sep = &shape.indent.to_string_with_newline(context.config); let (cond_str, used_width) = self.rewrite_cond(context, shape, alt_block_sep)?; // If `used_width` is 0, it indicates that whole control flow is written in a single line. if used_width == 0 { - return Some(cond_str); + return Ok(cond_str); } let block_width = shape.width.saturating_sub(used_width); @@ -1139,7 +1161,7 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option true, mk_sp(else_block.span.lo(), self.span.hi()), ) - .rewrite(context, shape) + .rewrite_result(context, shape) } _ => { last_in_chain = true; @@ -1164,7 +1186,7 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option result.push_str(&rewrite?); } - Some(result) + Ok(result) } } @@ -1177,7 +1199,7 @@ fn rewrite_label(context: &RewriteContext<'_>, opt_label: Option) -> fn extract_comment(span: Span, context: &RewriteContext<'_>, shape: Shape) -> Option { match rewrite_missing_comment(span, shape, context) { - Some(ref comment) if !comment.is_empty() => Some(format!( + Ok(ref comment) if !comment.is_empty() => Some(format!( "{indent}{comment}{indent}", indent = shape.indent.to_string_with_newline(context.config) )), @@ -1248,7 +1270,7 @@ pub(crate) fn rewrite_literal( token_lit: token::Lit, span: Span, shape: Shape, -) -> Option { +) -> RewriteResult { match token_lit.kind { token::LitKind::Str => rewrite_string_lit(context, span, shape), token::LitKind::Integer => rewrite_int_lit(context, token_lit, span, shape), @@ -1256,11 +1278,12 @@ pub(crate) fn rewrite_literal( context.snippet(span).to_owned(), context.config.max_width(), shape, - ), + ) + .max_width_error(shape.width, span), } } -fn rewrite_string_lit(context: &RewriteContext<'_>, span: Span, shape: Shape) -> Option { +fn rewrite_string_lit(context: &RewriteContext<'_>, span: Span, shape: Shape) -> RewriteResult { let string_lit = context.snippet(span); if !context.config.format_strings() { @@ -1268,11 +1291,12 @@ fn rewrite_string_lit(context: &RewriteContext<'_>, span: Span, shape: Shape) -> .lines() .dropping_back(1) .all(|line| line.ends_with('\\')) - && context.config.version() == Version::Two + && context.config.style_edition() >= StyleEdition::Edition2024 { - return Some(string_lit.to_owned()); + return Ok(string_lit.to_owned()); } else { - return wrap_str(string_lit.to_owned(), context.config.max_width(), shape); + return wrap_str(string_lit.to_owned(), context.config.max_width(), shape) + .max_width_error(shape.width, span); } } @@ -1284,6 +1308,7 @@ fn rewrite_string_lit(context: &RewriteContext<'_>, span: Span, shape: Shape) -> &StringFormat::new(shape.visual_indent(0), context.config), shape.width.saturating_sub(2), ) + .max_width_error(shape.width, span) } fn rewrite_int_lit( @@ -1291,7 +1316,7 @@ fn rewrite_int_lit( token_lit: token::Lit, span: Span, shape: Shape, -) -> Option { +) -> RewriteResult { let symbol = token_lit.symbol.as_str(); if let Some(symbol_stripped) = symbol.strip_prefix("0x") { @@ -1305,11 +1330,12 @@ fn rewrite_int_lit( format!( "0x{}{}", hex_lit, - token_lit.suffix.map_or(String::new(), |s| s.to_string()) + token_lit.suffix.as_ref().map_or("", |s| s.as_str()) ), context.config.max_width(), shape, - ); + ) + .max_width_error(shape.width, span); } } @@ -1318,6 +1344,7 @@ fn rewrite_int_lit( context.config.max_width(), shape, ) + .max_width_error(shape.width, span) } fn choose_separator_tactic(context: &RewriteContext<'_>, span: Span) -> Option { @@ -1338,7 +1365,7 @@ pub(crate) fn rewrite_call( args: &[ptr::P], span: Span, shape: Shape, -) -> Option { +) -> RewriteResult { overflow::rewrite_with_parens( context, callee, @@ -1458,7 +1485,7 @@ pub(crate) fn rewrite_paren( mut subexpr: &ast::Expr, shape: Shape, mut span: Span, -) -> Option { +) -> RewriteResult { debug!("rewrite_paren, shape: {:?}", shape); // Extract comments within parens. @@ -1487,11 +1514,14 @@ pub(crate) fn rewrite_paren( } // 1 = `(` and `)` - let sub_shape = shape.offset_left(1)?.sub_width(1)?; - let subexpr_str = subexpr.rewrite(context, sub_shape)?; + let sub_shape = shape + .offset_left(1) + .and_then(|s| s.sub_width(1)) + .max_width_error(shape.width, span)?; + let subexpr_str = subexpr.rewrite_result(context, sub_shape)?; let fits_single_line = !pre_comment.contains("//") && !post_comment.contains("//"); if fits_single_line { - Some(format!("({pre_comment}{subexpr_str}{post_comment})")) + Ok(format!("({pre_comment}{subexpr_str}{post_comment})")) } else { rewrite_paren_in_multi_line(context, subexpr, shape, pre_span, post_span) } @@ -1503,12 +1533,12 @@ fn rewrite_paren_in_multi_line( shape: Shape, pre_span: Span, post_span: Span, -) -> Option { +) -> RewriteResult { let nested_indent = shape.indent.block_indent(context.config); let nested_shape = Shape::indented(nested_indent, context.config); let pre_comment = rewrite_missing_comment(pre_span, nested_shape, context)?; let post_comment = rewrite_missing_comment(post_span, nested_shape, context)?; - let subexpr_str = subexpr.rewrite(context, nested_shape)?; + let subexpr_str = subexpr.rewrite_result(context, nested_shape)?; let mut result = String::with_capacity(subexpr_str.len() * 2); result.push('('); @@ -1525,7 +1555,7 @@ fn rewrite_paren_in_multi_line( result.push_str(&shape.indent.to_string_with_newline(context.config)); result.push(')'); - Some(result) + Ok(result) } fn rewrite_index( @@ -1533,8 +1563,8 @@ fn rewrite_index( index: &ast::Expr, context: &RewriteContext<'_>, shape: Shape, -) -> Option { - let expr_str = expr.rewrite(context, shape)?; +) -> RewriteResult { + let expr_str = expr.rewrite_result(context, shape)?; let offset = last_line_width(&expr_str) + 1; let rhs_overhead = shape.rhs_overhead(context.config); @@ -1549,37 +1579,45 @@ fn rewrite_index( .and_then(|shape| shape.sub_width(1)), IndentStyle::Visual => shape.visual_indent(offset).sub_width(offset + 1), } - }; - let orig_index_rw = index_shape.and_then(|s| index.rewrite(context, s)); + } + .max_width_error(shape.width, index.span()); + let orig_index_rw = index_shape.and_then(|s| index.rewrite_result(context, s)); // Return if index fits in a single line. match orig_index_rw { - Some(ref index_str) if !index_str.contains('\n') => { - return Some(format!("{expr_str}[{index_str}]")); + Ok(ref index_str) if !index_str.contains('\n') => { + return Ok(format!("{expr_str}[{index_str}]")); } _ => (), } // Try putting index on the next line and see if it fits in a single line. let indent = shape.indent.block_indent(context.config); - let index_shape = Shape::indented(indent, context.config).offset_left(1)?; - let index_shape = index_shape.sub_width(1 + rhs_overhead)?; - let new_index_rw = index.rewrite(context, index_shape); + let index_shape = Shape::indented(indent, context.config) + .offset_left(1) + .max_width_error(shape.width, index.span())?; + let index_shape = index_shape + .sub_width(1 + rhs_overhead) + .max_width_error(index_shape.width, index.span())?; + let new_index_rw = index.rewrite_result(context, index_shape); match (orig_index_rw, new_index_rw) { - (_, Some(ref new_index_str)) if !new_index_str.contains('\n') => Some(format!( + (_, Ok(ref new_index_str)) if !new_index_str.contains('\n') => Ok(format!( "{}{}[{}]", expr_str, indent.to_string_with_newline(context.config), new_index_str, )), - (None, Some(ref new_index_str)) => Some(format!( + (Err(_), Ok(ref new_index_str)) => Ok(format!( "{}{}[{}]", expr_str, indent.to_string_with_newline(context.config), new_index_str, )), - (Some(ref index_str), _) => Some(format!("{expr_str}[{index_str}]")), - _ => None, + (Ok(ref index_str), _) => Ok(format!("{expr_str}[{index_str}]")), + // When both orig_index_rw and new_index_rw result in errors, we currently propagate the + // 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(new_index_rw_err)) => Err(new_index_rw_err), } } @@ -1596,7 +1634,7 @@ fn rewrite_struct_lit<'a>( attrs: &[ast::Attribute], span: Span, shape: Shape, -) -> Option { +) -> RewriteResult { debug!("rewrite_struct_lit: shape {:?}", shape); enum StructLitField<'a> { @@ -1606,20 +1644,21 @@ enum StructLitField<'a> { } // 2 = " {".len() - 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 has_base_or_rest = match struct_rest { - ast::StructRest::None if fields.is_empty() => return Some(format!("{path_str} {{}}")), + ast::StructRest::None if fields.is_empty() => return Ok(format!("{path_str} {{}}")), ast::StructRest::Rest(_) if fields.is_empty() => { - return Some(format!("{path_str} {{ .. }}")); + return Ok(format!("{path_str} {{ .. }}")); } ast::StructRest::Rest(_) | ast::StructRest::Base(_) => true, _ => false, }; // Foo { a: Foo } - indent is +3, width is -5. - let (h_shape, v_shape) = struct_lit_shape(shape, context, path_str.len() + 3, 2)?; + let (h_shape, v_shape) = struct_lit_shape(shape, context, path_str.len() + 3, 2) + .max_width_error(shape.width, span)?; let one_line_width = h_shape.map_or(0, |shape| shape.width); let body_lo = context.snippet_provider.span_after(span, "{"); @@ -1632,7 +1671,8 @@ enum StructLitField<'a> { v_shape, mk_sp(body_lo, span.hi()), one_line_width, - )? + ) + .unknown_error()? } else { let field_iter = fields.iter().map(StructLitField::Regular).chain( match struct_rest { @@ -1661,14 +1701,24 @@ enum StructLitField<'a> { let rewrite = |item: &StructLitField<'_>| match *item { StructLitField::Regular(field) => { // The 1 taken from the v_budget is for the comma. - rewrite_field(context, field, v_shape.sub_width(1)?, 0) + rewrite_field( + context, + field, + v_shape.sub_width(1).max_width_error(v_shape.width, span)?, + 0, + ) } StructLitField::Base(expr) => { // 2 = .. - expr.rewrite(context, v_shape.offset_left(2)?) - .map(|s| format!("..{}", s)) + expr.rewrite_result( + context, + v_shape + .offset_left(2) + .max_width_error(v_shape.width, span)?, + ) + .map(|s| format!("..{}", s)) } - StructLitField::Rest(_) => Some("..".to_owned()), + StructLitField::Rest(_) => Ok("..".to_owned()), }; let items = itemize_list( @@ -1703,7 +1753,7 @@ enum StructLitField<'a> { let fields_str = wrap_struct_field(context, attrs, &fields_str, shape, v_shape, one_line_width)?; - Some(format!("{path_str} {{{fields_str}}}")) + Ok(format!("{path_str} {{{fields_str}}}")) // FIXME if context.config.indent_style() == Visual, but we run out // of space, we should fall back to BlockIndent. @@ -1716,7 +1766,7 @@ pub(crate) fn wrap_struct_field( shape: Shape, nested_shape: Shape, one_line_width: usize, -) -> Option { +) -> RewriteResult { let should_vertical = context.config.indent_style() == IndentStyle::Block && (fields_str.contains('\n') || !context.config.struct_lit_single_line() @@ -1725,7 +1775,7 @@ pub(crate) fn wrap_struct_field( let inner_attrs = &inner_attributes(attrs); if inner_attrs.is_empty() { if should_vertical { - Some(format!( + Ok(format!( "{}{}{}", nested_shape.indent.to_string_with_newline(context.config), fields_str, @@ -1733,13 +1783,13 @@ pub(crate) fn wrap_struct_field( )) } else { // One liner or visual indent. - Some(format!(" {fields_str} ")) + Ok(format!(" {fields_str} ")) } } else { - Some(format!( + Ok(format!( "{}{}{}{}{}", nested_shape.indent.to_string_with_newline(context.config), - inner_attrs.rewrite(context, shape)?, + inner_attrs.rewrite_result(context, shape)?, nested_shape.indent.to_string_with_newline(context.config), fields_str, shape.indent.to_string_with_newline(context.config) @@ -1756,38 +1806,40 @@ pub(crate) fn rewrite_field( field: &ast::ExprField, shape: Shape, prefix_max_width: usize, -) -> Option { +) -> RewriteResult { if contains_skip(&field.attrs) { - return Some(context.snippet(field.span()).to_owned()); + return Ok(context.snippet(field.span()).to_owned()); } - let mut attrs_str = field.attrs.rewrite(context, shape)?; + let mut attrs_str = field.attrs.rewrite_result(context, shape)?; if !attrs_str.is_empty() { attrs_str.push_str(&shape.indent.to_string_with_newline(context.config)); }; let name = context.snippet(field.ident.span); if field.is_shorthand { - Some(attrs_str + name) + Ok(attrs_str + name) } else { let mut separator = String::from(struct_lit_field_separator(context.config)); for _ in 0..prefix_max_width.saturating_sub(name.len()) { separator.push(' '); } let overhead = name.len() + separator.len(); - let expr_shape = shape.offset_left(overhead)?; - let expr = field.expr.rewrite(context, expr_shape); + let expr_shape = shape + .offset_left(overhead) + .max_width_error(shape.width, field.span)?; + let expr = field.expr.rewrite_result(context, expr_shape); let is_lit = matches!(field.expr.kind, ast::ExprKind::Lit(_)); match expr { - Some(ref e) + Ok(ref e) if !is_lit && e.as_str() == name && context.config.use_field_init_shorthand() => { - Some(attrs_str + name) + Ok(attrs_str + name) } - Some(e) => Some(format!("{attrs_str}{name}{separator}{e}")), - None => { + Ok(e) => Ok(format!("{attrs_str}{name}{separator}{e}")), + Err(_) => { let expr_offset = shape.indent.block_indent(context.config); let expr = field .expr - .rewrite(context, Shape::indented(expr_offset, context.config)); + .rewrite_result(context, Shape::indented(expr_offset, context.config)); expr.map(|s| { format!( "{}{}:\n{}{}", @@ -1808,21 +1860,27 @@ fn rewrite_tuple_in_visual_indent_style<'a, T: 'a + IntoOverflowableItem<'a>>( span: Span, shape: Shape, is_singleton_tuple: bool, -) -> Option { +) -> RewriteResult { // In case of length 1, need a trailing comma debug!("rewrite_tuple_in_visual_indent_style {:?}", shape); if is_singleton_tuple { // 3 = "(" + ",)" - let nested_shape = shape.sub_width(3)?.visual_indent(1); + let nested_shape = shape + .sub_width(3) + .max_width_error(shape.width, span)? + .visual_indent(1); return items .next() .unwrap() - .rewrite(context, nested_shape) + .rewrite_result(context, nested_shape) .map(|s| format!("({},)", s)); } let list_lo = context.snippet_provider.span_after(span, "("); - let nested_shape = shape.sub_width(2)?.visual_indent(1); + let nested_shape = shape + .sub_width(2) + .max_width_error(shape.width, span)? + .visual_indent(1); let items = itemize_list( context.snippet_provider, items, @@ -1830,7 +1888,7 @@ fn rewrite_tuple_in_visual_indent_style<'a, T: 'a + IntoOverflowableItem<'a>>( ",", |item| item.span().lo(), |item| item.span().hi(), - |item| item.rewrite(context, nested_shape), + |item| item.rewrite_result(context, nested_shape), list_lo, span.hi() - BytePos(1), false, @@ -1847,7 +1905,7 @@ fn rewrite_tuple_in_visual_indent_style<'a, T: 'a + IntoOverflowableItem<'a>>( .ends_with_newline(false); let list_str = write_list(&item_vec, &fmt)?; - Some(format!("({list_str})")) + Ok(format!("({list_str})")) } fn rewrite_let( @@ -1855,14 +1913,16 @@ fn rewrite_let( shape: Shape, pat: &ast::Pat, expr: &ast::Expr, -) -> Option { +) -> RewriteResult { let mut result = "let ".to_owned(); // TODO(ytmimi) comments could appear between `let` and the `pat` // 4 = "let ".len() - let pat_shape = shape.offset_left(4)?; - let pat_str = pat.rewrite(context, pat_shape)?; + let pat_shape = shape + .offset_left(4) + .max_width_error(shape.width, pat.span)?; + let pat_str = pat.rewrite_result(context, pat_shape)?; result.push_str(&pat_str); // TODO(ytmimi) comments could appear between `pat` and `=` @@ -1890,7 +1950,7 @@ pub(crate) fn rewrite_tuple<'a, T: 'a + IntoOverflowableItem<'a>>( span: Span, shape: Shape, is_singleton_tuple: bool, -) -> Option { +) -> RewriteResult { debug!("rewrite_tuple {:?}", shape); if context.use_block_indent() { // We use the same rule as function calls for rewriting tuples. @@ -1919,31 +1979,35 @@ pub(crate) fn rewrite_tuple<'a, T: 'a + IntoOverflowableItem<'a>>( } } -pub(crate) fn rewrite_unary_prefix( +pub(crate) fn rewrite_unary_prefix( context: &RewriteContext<'_>, prefix: &str, rewrite: &R, shape: Shape, -) -> Option { +) -> RewriteResult { + let shape = shape + .offset_left(prefix.len()) + .max_width_error(shape.width, rewrite.span())?; rewrite - .rewrite(context, shape.offset_left(prefix.len())?) + .rewrite_result(context, shape) .map(|r| format!("{}{}", prefix, r)) } // FIXME: this is probably not correct for multi-line Rewrites. we should // subtract suffix.len() from the last line budget, not the first! -pub(crate) fn rewrite_unary_suffix( +pub(crate) fn rewrite_unary_suffix( context: &RewriteContext<'_>, suffix: &str, rewrite: &R, shape: Shape, -) -> Option { - rewrite - .rewrite(context, shape.sub_width(suffix.len())?) - .map(|mut r| { - r.push_str(suffix); - r - }) +) -> RewriteResult { + let shape = shape + .sub_width(suffix.len()) + .max_width_error(shape.width, rewrite.span())?; + rewrite.rewrite_result(context, shape).map(|mut r| { + r.push_str(suffix); + r + }) } fn rewrite_unary_op( @@ -1951,7 +2015,7 @@ fn rewrite_unary_op( op: ast::UnOp, expr: &ast::Expr, shape: Shape, -) -> Option { +) -> RewriteResult { // For some reason, an UnOp is not spanned like BinOp! rewrite_unary_prefix(context, op.as_str(), expr, shape) } @@ -1990,15 +2054,21 @@ fn rewrite_assignment( rhs: &ast::Expr, op: Option<&ast::BinOp>, shape: Shape, -) -> Option { +) -> RewriteResult { let operator_str = match op { Some(op) => context.snippet(op.span), None => "=", }; // 1 = space between lhs and operator. - let lhs_shape = shape.sub_width(operator_str.len() + 1)?; - let lhs_str = format!("{} {}", lhs.rewrite(context, lhs_shape)?, operator_str); + let lhs_shape = shape + .sub_width(operator_str.len() + 1) + .max_width_error(shape.width, lhs.span())?; + let lhs_str = format!( + "{} {}", + lhs.rewrite_result(context, lhs_shape)?, + operator_str + ); rewrite_assign_rhs( context, @@ -2029,7 +2099,7 @@ pub(crate) fn rewrite_assign_rhs, R: Rewrite>( ex: &R, rhs_kind: &RhsAssignKind<'_>, shape: Shape, -) -> Option { +) -> RewriteResult { rewrite_assign_rhs_with(context, lhs, ex, shape, rhs_kind, RhsTactics::Default) } @@ -2040,7 +2110,7 @@ pub(crate) fn rewrite_assign_rhs_expr( shape: Shape, rhs_kind: &RhsAssignKind<'_>, rhs_tactics: RhsTactics, -) -> Option { +) -> RewriteResult { let last_line_width = last_line_width(lhs).saturating_sub(if lhs.contains('\n') { shape.indent.width() } else { @@ -2062,7 +2132,7 @@ pub(crate) fn rewrite_assign_rhs_expr( context, ex, orig_shape, - ex.rewrite(context, orig_shape), + ex.rewrite_result(context, orig_shape), rhs_kind, rhs_tactics, has_rhs_comment, @@ -2076,13 +2146,13 @@ pub(crate) fn rewrite_assign_rhs_with, R: Rewrite>( shape: Shape, rhs_kind: &RhsAssignKind<'_>, rhs_tactics: RhsTactics, -) -> Option { +) -> RewriteResult { let lhs = lhs.into(); let rhs = rewrite_assign_rhs_expr(context, &lhs, ex, shape, rhs_kind, rhs_tactics)?; - Some(lhs + &rhs) + Ok(lhs + &rhs) } -pub(crate) fn rewrite_assign_rhs_with_comments, R: Rewrite>( +pub(crate) fn rewrite_assign_rhs_with_comments, R: Rewrite + Spanned>( context: &RewriteContext<'_>, lhs: S, ex: &R, @@ -2091,21 +2161,22 @@ pub(crate) fn rewrite_assign_rhs_with_comments, R: Rewrite>( rhs_tactics: RhsTactics, between_span: Span, allow_extend: bool, -) -> Option { +) -> RewriteResult { let lhs = lhs.into(); let contains_comment = contains_comment(context.snippet(between_span)); let shape = if contains_comment { - shape.block_left(context.config.tab_spaces())? + shape + .block_left(context.config.tab_spaces()) + .max_width_error(shape.width, between_span.with_hi(ex.span().hi()))? } else { shape }; let rhs = rewrite_assign_rhs_expr(context, &lhs, ex, shape, rhs_kind, rhs_tactics)?; - if contains_comment { let rhs = rhs.trim_start(); combine_strs_with_missing_comments(context, &lhs, rhs, between_span, shape, allow_extend) } else { - Some(lhs + &rhs) + Ok(lhs + &rhs) } } @@ -2113,23 +2184,25 @@ fn choose_rhs( context: &RewriteContext<'_>, expr: &R, shape: Shape, - orig_rhs: Option, + orig_rhs: RewriteResult, _rhs_kind: &RhsAssignKind<'_>, rhs_tactics: RhsTactics, has_rhs_comment: bool, -) -> Option { +) -> RewriteResult { match orig_rhs { - Some(ref new_str) if new_str.is_empty() => Some(String::new()), - Some(ref new_str) - if !new_str.contains('\n') && unicode_str_width(new_str) <= shape.width => - { - Some(format!(" {new_str}")) + Ok(ref new_str) if new_str.is_empty() => Ok(String::new()), + Ok(ref new_str) if !new_str.contains('\n') && unicode_str_width(new_str) <= shape.width => { + Ok(format!(" {new_str}")) } _ => { // Expression did not fit on the same line as the identifier. // Try splitting the line and see if that works better. - let new_shape = shape_from_rhs_tactic(context, shape, rhs_tactics)?; - let new_rhs = expr.rewrite(context, new_shape); + let new_shape = shape_from_rhs_tactic(context, shape, rhs_tactics) + // TODO(ding-young) Ideally, we can replace unknown_error() with max_width_error(), + // but this requires either implementing the Spanned trait for ast::GenericBounds + // or grabbing the span from the call site. + .unknown_error()?; + let new_rhs = expr.rewrite_result(context, new_shape); let new_indent_str = &shape .indent .block_indent(context.config) @@ -2137,24 +2210,27 @@ fn choose_rhs( let before_space_str = if has_rhs_comment { "" } else { " " }; match (orig_rhs, new_rhs) { - (Some(ref orig_rhs), Some(ref new_rhs)) + (Ok(ref orig_rhs), Ok(ref new_rhs)) if !filtered_str_fits(&new_rhs, context.config.max_width(), new_shape) => { - Some(format!("{before_space_str}{orig_rhs}")) + Ok(format!("{before_space_str}{orig_rhs}")) } - (Some(ref orig_rhs), Some(ref new_rhs)) + (Ok(ref orig_rhs), Ok(ref new_rhs)) if prefer_next_line(orig_rhs, new_rhs, rhs_tactics) => { - Some(format!("{new_indent_str}{new_rhs}")) + Ok(format!("{new_indent_str}{new_rhs}")) } - (None, Some(ref new_rhs)) => Some(format!("{new_indent_str}{new_rhs}")), - (None, None) if rhs_tactics == RhsTactics::AllowOverflow => { + (Err(_), Ok(ref new_rhs)) => Ok(format!("{new_indent_str}{new_rhs}")), + (Err(_), Err(_)) if rhs_tactics == RhsTactics::AllowOverflow => { let shape = shape.infinite_width(); - expr.rewrite(context, shape) + expr.rewrite_result(context, shape) .map(|s| format!("{}{}", before_space_str, s)) } - (None, None) => None, - (Some(orig_rhs), _) => Some(format!("{before_space_str}{orig_rhs}")), + // When both orig_rhs and new_rhs result in errors, we currently propagate + // the 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(new_rhs_err)) => Err(new_rhs_err), + (Ok(orig_rhs), _) => Ok(format!("{before_space_str}{orig_rhs}")), } } } @@ -2204,7 +2280,7 @@ fn rewrite_expr_addrof( mutability: ast::Mutability, expr: &ast::Expr, shape: Shape, -) -> Option { +) -> RewriteResult { let operator_str = match (mutability, borrow_kind) { (ast::Mutability::Not, ast::BorrowKind::Ref) => "&", (ast::Mutability::Not, ast::BorrowKind::Raw) => "&raw const ", diff --git a/src/tools/rustfmt/src/formatting.rs b/src/tools/rustfmt/src/formatting.rs index 5e71fe107eb..1e1e329f624 100644 --- a/src/tools/rustfmt/src/formatting.rs +++ b/src/tools/rustfmt/src/formatting.rs @@ -17,7 +17,7 @@ use crate::parse::session::ParseSess; use crate::utils::{contains_skip, count_newlines}; use crate::visitor::FmtVisitor; -use crate::{modules, source_file, ErrorKind, FormatReport, Input, Session}; +use crate::{ErrorKind, FormatReport, Input, Session, modules, source_file}; mod generated; mod newline_style; diff --git a/src/tools/rustfmt/src/git-rustfmt/main.rs b/src/tools/rustfmt/src/git-rustfmt/main.rs index b8b0432aa95..64458420f59 100644 --- a/src/tools/rustfmt/src/git-rustfmt/main.rs +++ b/src/tools/rustfmt/src/git-rustfmt/main.rs @@ -13,7 +13,9 @@ use tracing::debug; 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> { let prefixes: Vec<_> = files @@ -87,6 +89,15 @@ fn apply_to(self, _: &mut rustfmt::Config) { fn config_path(&self) -> Option<&Path> { unreachable!(); } + fn edition(&self) -> Option { + unreachable!(); + } + fn style_edition(&self) -> Option { + unreachable!(); + } + fn version(&self) -> Option { + unreachable!(); + } } fn uncommitted_files() -> Vec { diff --git a/src/tools/rustfmt/src/ignore_path.rs b/src/tools/rustfmt/src/ignore_path.rs index 5c25f233ce3..2f804ef5a25 100644 --- a/src/tools/rustfmt/src/ignore_path.rs +++ b/src/tools/rustfmt/src/ignore_path.rs @@ -41,8 +41,11 @@ fn test_ignore_path_set() { use crate::ignore_path::IgnorePathSet; use std::path::{Path, PathBuf}; - let config = - Config::from_toml(r#"ignore = ["foo.rs", "bar_dir/*"]"#, Path::new("")).unwrap(); + let config = Config::from_toml( + r#"ignore = ["foo.rs", "bar_dir/*"]"#, + Path::new("./rustfmt.toml"), + ) + .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")))); @@ -59,7 +62,7 @@ fn test_negated_ignore_path_set() { let config = Config::from_toml( r#"ignore = ["foo.rs", "bar_dir/*", "!bar_dir/*/what.rs"]"#, - Path::new(""), + Path::new("./rustfmt.toml"), ) .unwrap(); let ignore_path_set = IgnorePathSet::from_ignore_list(&config.ignore()).unwrap(); diff --git a/src/tools/rustfmt/src/imports.rs b/src/tools/rustfmt/src/imports.rs index 12c178e136f..b741dd9b5da 100644 --- a/src/tools/rustfmt/src/imports.rs +++ b/src/tools/rustfmt/src/imports.rs @@ -8,19 +8,20 @@ use rustc_ast::ast::{self, UseTreeKind}; use rustc_span::{ + BytePos, DUMMY_SP, Span, symbol::{self, sym}, - BytePos, Span, DUMMY_SP, }; use crate::comment::combine_strs_with_missing_comments; -use crate::config::lists::*; use crate::config::ImportGranularity; -use crate::config::{Edition, IndentStyle, Version}; +use crate::config::lists::*; +use crate::config::{Edition, IndentStyle, StyleEdition}; 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::sort::version_sort; use crate::source_map::SpanUtils; use crate::spanned::Spanned; use crate::utils::{is_same_visibility, mk_sp, rewrite_ident}; @@ -44,7 +45,8 @@ pub(crate) fn format_import(&mut self, item: &ast::Item, tree: &ast::UseTree) { Some(item.span.lo()), Some(item.attrs.clone()), ) - .rewrite_top_level(&self.get_context(), shape); + .rewrite_top_level(&self.get_context(), shape) + .ok(); match rw { Some(ref s) if s.is_empty() => { // Format up to last newline @@ -104,7 +106,7 @@ pub(crate) enum UseSegmentKind { #[derive(Clone, Eq, PartialEq)] pub(crate) struct UseSegment { pub(crate) kind: UseSegmentKind, - pub(crate) version: Version, + pub(crate) style_edition: StyleEdition, } #[derive(Clone)] @@ -149,7 +151,7 @@ fn remove_alias(&self) -> UseSegment { }; UseSegment { kind, - version: self.version, + style_edition: self.style_edition, } } @@ -197,7 +199,7 @@ fn from_path_segment( Some(UseSegment { kind, - version: context.config.version(), + style_edition: context.config.style_edition(), }) } @@ -331,12 +333,17 @@ pub(crate) fn rewrite_top_level( &self, context: &RewriteContext<'_>, shape: Shape, - ) -> Option { + ) -> RewriteResult { let vis = self.visibility.as_ref().map_or(Cow::from(""), |vis| { crate::utils::format_visibility(context, vis) }); let use_str = self - .rewrite(context, shape.offset_left(vis.len())?) + .rewrite_result( + context, + shape + .offset_left(vis.len()) + .max_width_error(shape.width, self.span())?, + ) .map(|s| { if s.is_empty() { s @@ -346,8 +353,8 @@ pub(crate) fn rewrite_top_level( })?; match self.attrs { Some(ref attrs) if !attrs.is_empty() => { - let attr_str = attrs.rewrite(context, shape)?; - let lo = attrs.last().as_ref()?.span.hi(); + let attr_str = attrs.rewrite_result(context, shape)?; + let lo = attrs.last().unknown_error()?.span.hi(); let hi = self.span.lo(); let span = mk_sp(lo, hi); @@ -368,7 +375,7 @@ pub(crate) fn rewrite_top_level( allow_extend, ) } - _ => Some(use_str), + _ => Ok(use_str), } } @@ -444,18 +451,21 @@ fn from_ast( } } - let version = context.config.version(); + let style_edition = context.config.style_edition(); match a.kind { UseTreeKind::Glob => { // in case of a global path and the glob starts at the root, e.g., "::*" if a.prefix.segments.len() == 1 && leading_modsep { let kind = UseSegmentKind::Ident("".to_owned(), None); - result.path.push(UseSegment { kind, version }); + result.path.push(UseSegment { + kind, + style_edition, + }); } result.path.push(UseSegment { kind: UseSegmentKind::Glob, - version, + style_edition, }); } UseTreeKind::Nested { @@ -470,7 +480,7 @@ fn from_ast( ",", |tree| tree.span.lo(), |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, "{"), a.span.hi(), false, @@ -480,7 +490,10 @@ fn from_ast( // e.g., "::{foo, bar}" if a.prefix.segments.len() == 1 && leading_modsep { let kind = UseSegmentKind::Ident("".to_owned(), None); - result.path.push(UseSegment { kind, version }); + result.path.push(UseSegment { + kind, + style_edition, + }); } let kind = UseSegmentKind::List( list.iter() @@ -490,7 +503,10 @@ fn from_ast( }) .collect(), ); - result.path.push(UseSegment { kind, version }); + result.path.push(UseSegment { + kind, + style_edition, + }); } UseTreeKind::Simple(ref rename) => { // If the path has leading double colons and is composed of only 2 segments, then we @@ -519,7 +535,10 @@ fn from_ast( _ => UseSegmentKind::Ident(name, alias), }; - let segment = UseSegment { kind, version }; + let segment = UseSegment { + kind, + style_edition, + }; // `name` is already in result. result.path.pop(); @@ -614,7 +633,7 @@ pub(crate) fn normalize(mut self) -> UseTree { list.sort(); last = UseSegment { kind: UseSegmentKind::List(list), - version: last.version, + style_edition: last.style_edition, }; } @@ -732,9 +751,12 @@ fn nest_trailing_self(mut self) -> UseTree { }) = self.path.last() { 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)]); - self.path.push(UseSegment { kind, version }); + self.path.push(UseSegment { + kind, + style_edition, + }); } self } @@ -750,7 +772,7 @@ fn merge_rest( return None; } 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 { let mut list = list.clone(); merge_use_trees_inner( @@ -760,7 +782,10 @@ fn merge_rest( ); let mut new_path = b[..len].to_vec(); let kind = UseSegmentKind::List(list); - new_path.push(UseSegment { kind, version }); + new_path.push(UseSegment { + kind, + style_edition, + }); return Some(new_path); } } else if len == 1 { @@ -770,9 +795,12 @@ fn merge_rest( (&b[0], &a[1..]) }; 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( - vec![UseSegment { kind, version }], + vec![UseSegment { + kind, + style_edition, + }], DUMMY_SP, )]; match rest { @@ -788,7 +816,7 @@ fn merge_rest( b[0].clone(), UseSegment { kind: UseSegmentKind::List(list), - version, + style_edition, }, ]); } else { @@ -801,8 +829,11 @@ fn merge_rest( list.sort(); let mut new_path = b[..len].to_vec(); let kind = UseSegmentKind::List(list); - let version = a[0].version; - new_path.push(UseSegment { kind, version }); + let style_edition = a[0].style_edition; + new_path.push(UseSegment { + kind, + style_edition, + }); Some(new_path) } @@ -892,8 +923,8 @@ fn is_upper_snake_case(s: &str) -> bool { | (Super(ref a), Super(ref b)) | (Crate(ref a), Crate(ref b)) => match (a, b) { (Some(sa), Some(sb)) => { - if self.version == Version::Two { - sa.trim_start_matches("r#").cmp(sb.trim_start_matches("r#")) + if self.style_edition >= StyleEdition::Edition2024 { + version_sort(sa.trim_start_matches("r#"), sb.trim_start_matches("r#")) } else { a.cmp(b) } @@ -902,25 +933,31 @@ fn is_upper_snake_case(s: &str) -> bool { }, (Glob, Glob) => Ordering::Equal, (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#")) } else { (pia.as_str(), pib.as_str()) }; - // snake_case < CamelCase < UPPER_SNAKE_CASE - if ia.starts_with(char::is_uppercase) && ib.starts_with(char::is_lowercase) { - return Ordering::Greater; - } - if ia.starts_with(char::is_lowercase) && ib.starts_with(char::is_uppercase) { - return Ordering::Less; - } - if is_upper_snake_case(ia) && !is_upper_snake_case(ib) { - return Ordering::Greater; - } - if !is_upper_snake_case(ia) && is_upper_snake_case(ib) { - return Ordering::Less; - } - let ident_ord = ia.cmp(ib); + + let ident_ord = if self.style_edition >= StyleEdition::Edition2024 { + version_sort(ia, ib) + } else { + // snake_case < CamelCase < UPPER_SNAKE_CASE + if ia.starts_with(char::is_uppercase) && ib.starts_with(char::is_lowercase) { + return Ordering::Greater; + } + if ia.starts_with(char::is_lowercase) && ib.starts_with(char::is_uppercase) { + return Ordering::Less; + } + if is_upper_snake_case(ia) && !is_upper_snake_case(ib) { + return Ordering::Greater; + } + if !is_upper_snake_case(ia) && is_upper_snake_case(ib) { + return Ordering::Less; + } + ia.cmp(ib) + }; + if ident_ord != Ordering::Equal { return ident_ord; } @@ -928,9 +965,8 @@ fn is_upper_snake_case(s: &str) -> bool { (None, Some(_)) => Ordering::Less, (Some(_), None) => Ordering::Greater, (Some(aas), Some(abs)) => { - if self.version == Version::Two { - aas.trim_start_matches("r#") - .cmp(abs.trim_start_matches("r#")) + if self.style_edition >= StyleEdition::Edition2024 { + version_sort(aas.trim_start_matches("r#"), abs.trim_start_matches("r#")) } else { aas.cmp(abs) } @@ -982,21 +1018,24 @@ fn rewrite_nested_use_tree( context: &RewriteContext<'_>, use_tree_list: &[UseTree], shape: Shape, -) -> Option { +) -> RewriteResult { let mut list_items = Vec::with_capacity(use_tree_list.len()); let nested_shape = match context.config.imports_indent() { IndentStyle::Block => shape .block_indent(context.config.tab_spaces()) .with_max_width(context.config) - .sub_width(1)?, + .sub_width(1) + .unknown_error()?, IndentStyle::Visual => shape.visual_indent(0), }; for use_tree in use_tree_list { if let Some(mut list_item) = use_tree.list_item.clone() { - list_item.item = use_tree.rewrite(context, nested_shape); + list_item.item = use_tree.rewrite_result(context, nested_shape); list_items.push(list_item); } 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| { @@ -1049,12 +1088,16 @@ fn rewrite_nested_use_tree( format!("{{{list_str}}}") }; - Some(result) + Ok(result) } impl Rewrite for UseSegment { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { - 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)) => { format!("{ident} as {rename}") } @@ -1066,31 +1109,42 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option UseSegmentKind::Crate(Some(ref rename)) => format!("crate as {rename}"), UseSegmentKind::Crate(None) => "crate".to_owned(), UseSegmentKind::Glob => "*".to_owned(), - UseSegmentKind::List(ref use_tree_list) => rewrite_nested_use_tree( - context, - use_tree_list, - // 1 = "{" and "}" - shape.offset_left(1)?.sub_width(1)?, - )?, + UseSegmentKind::List(ref use_tree_list) => { + rewrite_nested_use_tree( + context, + use_tree_list, + // 1 = "{" and "}" + shape + .offset_left(1) + .and_then(|s| s.sub_width(1)) + .unknown_error()?, + )? + } }) } } impl Rewrite for UseTree { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + self.rewrite_result(context, shape).ok() + } + // This does NOT format attributes and visibility or add a trailing `;`. - fn rewrite(&self, context: &RewriteContext<'_>, mut shape: Shape) -> Option { + fn rewrite_result(&self, context: &RewriteContext<'_>, mut shape: Shape) -> RewriteResult { let mut result = String::with_capacity(256); let mut iter = self.path.iter().peekable(); while let Some(segment) = iter.next() { - let segment_str = segment.rewrite(context, shape)?; + let segment_str = segment.rewrite_result(context, shape)?; result.push_str(&segment_str); if iter.peek().is_some() { result.push_str("::"); // 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 @@ fn parse_use_tree(s: &str) -> UseTree { struct Parser<'a> { input: Peekable>, - version: Version, + style_edition: StyleEdition, } impl<'a> Parser<'a> { @@ -1132,7 +1186,7 @@ fn push_segment( buf: &mut String, alias_buf: &mut Option, ) { - let version = self.version; + let style_edition = self.style_edition; if !buf.is_empty() { let mut alias = None; swap(alias_buf, &mut alias); @@ -1140,19 +1194,28 @@ fn push_segment( match buf.as_ref() { "self" => { let kind = UseSegmentKind::Slf(alias); - result.push(UseSegment { kind, version }); + result.push(UseSegment { + kind, + style_edition, + }); *buf = String::new(); *alias_buf = None; } "super" => { let kind = UseSegmentKind::Super(alias); - result.push(UseSegment { kind, version }); + result.push(UseSegment { + kind, + style_edition, + }); *buf = String::new(); *alias_buf = None; } "crate" => { let kind = UseSegmentKind::Crate(alias); - result.push(UseSegment { kind, version }); + result.push(UseSegment { + kind, + style_edition, + }); *buf = String::new(); *alias_buf = None; } @@ -1160,7 +1223,10 @@ fn push_segment( let mut name = String::new(); swap(buf, &mut name); let kind = UseSegmentKind::Ident(name, alias); - result.push(UseSegment { kind, version }); + result.push(UseSegment { + kind, + style_edition, + }); } } } @@ -1178,7 +1244,7 @@ fn parse_in_list(&mut self) -> UseTree { let kind = UseSegmentKind::List(self.parse_list()); result.push(UseSegment { kind, - version: self.version, + style_edition: self.style_edition, }); self.eat('}'); } @@ -1188,7 +1254,7 @@ fn parse_in_list(&mut self) -> UseTree { let kind = UseSegmentKind::Glob; result.push(UseSegment { kind, - version: self.version, + style_edition: self.style_edition, }); } ':' => { @@ -1249,7 +1315,7 @@ fn parse_list(&mut self) -> Vec { let mut parser = Parser { input: s.chars().peekable(), - version: Version::One, + style_edition: StyleEdition::Edition2015, }; parser.parse_in_list() } diff --git a/src/tools/rustfmt/src/items.rs b/src/tools/rustfmt/src/items.rs index cbf7ce90e37..fc043a697e0 100644 --- a/src/tools/rustfmt/src/items.rs +++ b/src/tools/rustfmt/src/items.rs @@ -1,31 +1,30 @@ // Formatting top-level items - functions, structs, enums, traits, impls. use std::borrow::Cow; -use std::cmp::{max, min, Ordering}; +use std::cmp::{Ordering, max, min}; use regex::Regex; use rustc_ast::visit; use rustc_ast::{ast, ptr}; -use rustc_span::{symbol, BytePos, Span, DUMMY_SP}; +use rustc_span::{BytePos, DUMMY_SP, Span, symbol}; use tracing::debug; use crate::attr::filter_inline_attrs; use crate::comment::{ - combine_strs_with_missing_comments, contains_comment, is_last_comment_block, + FindUncommented, combine_strs_with_missing_comments, contains_comment, is_last_comment_block, recover_comment_removed, recover_missing_comment_in_span, rewrite_missing_comment, - FindUncommented, }; use crate::config::lists::*; -use crate::config::{BraceStyle, Config, IndentStyle, Version}; +use crate::config::{BraceStyle, Config, IndentStyle, StyleEdition}; use crate::expr::{ - is_empty_block, is_simple_block_stmt, rewrite_assign_rhs, rewrite_assign_rhs_with, - rewrite_assign_rhs_with_comments, rewrite_else_kw_with_comments, rewrite_let_else_block, - RhsAssignKind, RhsTactics, + RhsAssignKind, RhsTactics, is_empty_block, is_simple_block_stmt, rewrite_assign_rhs, + rewrite_assign_rhs_with, rewrite_assign_rhs_with_comments, rewrite_else_kw_with_comments, + rewrite_let_else_block, }; -use crate::lists::{definitive_tactic, itemize_list, write_list, ListFormatting, Separator}; -use crate::macros::{rewrite_macro, MacroPosition}; +use crate::lists::{ListFormatting, Separator, definitive_tactic, itemize_list, write_list}; +use crate::macros::{MacroPosition, rewrite_macro}; use crate::overflow; -use crate::rewrite::{Rewrite, RewriteContext}; +use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult}; use crate::shape::{Indent, Shape}; use crate::source_map::{LineRangeUtils, SpanUtils}; use crate::spanned::Spanned; @@ -49,18 +48,22 @@ fn type_annotation_separator(config: &Config) -> &str { // let pat: ty = init; or let pat: ty = init else { .. }; impl Rewrite for ast::Local { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { debug!( "Local::rewrite {:?} {} {:?}", self, shape.width, shape.indent ); - skip_out_of_file_lines_range!(context, self.span); + skip_out_of_file_lines_range_err!(context, self.span); if contains_skip(&self.attrs) { - return None; + return Err(RewriteError::SkipFormatting); } - let attrs_str = self.attrs.rewrite(context, shape)?; + let attrs_str = self.attrs.rewrite_result(context, shape)?; let mut result = if attrs_str.is_empty() { "let ".to_owned() } else { @@ -79,10 +82,15 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option let let_kw_offset = result.len() - "let ".len(); // 4 = "let ".len() - let pat_shape = shape.offset_left(4)?; + let pat_shape = shape + .offset_left(4) + .max_width_error(shape.width, self.span())?; // 1 = ; - let pat_shape = pat_shape.sub_width(1)?; - let pat_str = self.pat.rewrite(context, pat_shape)?; + let pat_shape = pat_shape + .sub_width(1) + .max_width_error(shape.width, self.span())?; + let pat_str = self.pat.rewrite_result(context, pat_shape)?; + result.push_str(&pat_str); // String that is placed within the assignment pattern and expression. @@ -96,11 +104,13 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option } else { shape } - .offset_left(last_line_width(&result) + separator.len())? + .offset_left(last_line_width(&result) + separator.len()) + .max_width_error(shape.width, self.span())? // 2 = ` =` - .sub_width(2)?; + .sub_width(2) + .max_width_error(shape.width, self.span())?; - let rewrite = ty.rewrite(context, ty_shape)?; + let rewrite = ty.rewrite_result(context, ty_shape)?; infix.push_str(separator); infix.push_str(&rewrite); @@ -117,7 +127,9 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option if let Some((init, else_block)) = self.kind.init_else_opt() { // 1 = trailing semicolon; - let nested_shape = shape.sub_width(1)?; + let nested_shape = shape + .sub_width(1) + .max_width_error(shape.width, self.span())?; result = rewrite_assign_rhs( context, @@ -131,7 +143,8 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option let else_kw_span = init.span.between(block.span); // Strip attributes and comments to check if newline is needed before the else // keyword from the initializer part. (#5901) - let init_str = if context.config.version() == Version::Two { + let style_edition = context.config.style_edition(); + let init_str = if style_edition >= StyleEdition::Edition2024 { &result[let_kw_offset..] } else { result.as_str() @@ -155,7 +168,8 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option std::cmp::min(shape.width, context.config.single_line_let_else_max_width()); // If available_space hits zero we know for sure this will be a multi-lined block - let assign_str_with_else_kw = if context.config.version() == Version::Two { + let style_edition = context.config.style_edition(); + let assign_str_with_else_kw = if style_edition >= StyleEdition::Edition2024 { &result[let_kw_offset..] } else { result.as_str() @@ -184,7 +198,7 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option } result.push(';'); - Some(result) + Ok(result) } } @@ -420,7 +434,7 @@ pub(crate) fn rewrite_fn_before_block( let mut fn_brace_style = newline_for_brace(self.config, &fn_sig.generics.where_clause); let (result, _, force_newline_brace) = - rewrite_fn_base(&context, indent, ident, fn_sig, span, fn_brace_style)?; + rewrite_fn_base(&context, indent, ident, fn_sig, span, fn_brace_style).ok()?; // 2 = ` {` if self.config.brace_style() == BraceStyle::AlwaysNextLine @@ -441,7 +455,7 @@ pub(crate) fn rewrite_required_fn( vis: &ast::Visibility, generics: &ast::Generics, span: Span, - ) -> Option { + ) -> RewriteResult { // Drop semicolon or it will be interpreted as comment. let span = mk_sp(span.lo(), span.hi() - BytePos(1)); let context = self.get_context(); @@ -463,7 +477,7 @@ pub(crate) fn rewrite_required_fn( // Re-attach semicolon result.push(';'); - Some(result) + Ok(result) } pub(crate) fn single_line_fn( @@ -615,7 +629,10 @@ fn format_variant_list( } }, |f| f.span.hi(), - |f| self.format_variant(f, one_line_width, pad_discrim_ident_to), + |f| { + self.format_variant(f, one_line_width, pad_discrim_ident_to) + .unknown_error() + }, body_lo, body_hi, false, @@ -636,7 +653,7 @@ fn format_variant_list( .trailing_separator(self.config.trailing_comma()) .preserve_newline(true); - let list = write_list(&items, &fmt)?; + let list = write_list(&items, &fmt).ok()?; result.push_str(&list); result.push_str(&original_offset.to_string_with_newline(self.config)); result.push('}'); @@ -658,10 +675,10 @@ fn format_variant( let context = self.get_context(); let shape = self.shape(); - let attrs_str = if context.config.version() == Version::Two { + let attrs_str = if context.config.style_edition() >= StyleEdition::Edition2024 { field.attrs.rewrite(&context, shape)? } else { - // Version::One formatting that was off by 1. See issue #5801 + // StyleEdition::Edition20{15|18|21} formatting that was off by 1. See issue #5801 field.attrs.rewrite(&context, shape.sub_width(1)?)? }; // sub_width(1) to take the trailing comma into account @@ -693,12 +710,14 @@ fn format_variant( shape, &RhsAssignKind::Expr(&ex.kind, ex.span), RhsTactics::AllowOverflow, - )? + ) + .ok()? } else { variant_body }; combine_strs_with_missing_comments(&context, &attrs_str, &variant_body, span, shape, false) + .ok() } fn visit_impl_items(&mut self, items: &[ptr::P]) { @@ -822,7 +841,8 @@ pub(crate) fn format_impl( where_span_end, self_ty.span.hi(), option, - )?; + ) + .ok()?; // If there is no where-clause, we may have missing comments between the trait name and // the opening brace. @@ -834,7 +854,7 @@ pub(crate) fn format_impl( context, last_line_width(&result), ) { - Some(ref missing_comment) if !missing_comment.is_empty() => { + Ok(ref missing_comment) if !missing_comment.is_empty() => { result.push_str(missing_comment); } _ => (), @@ -949,7 +969,7 @@ fn format_impl_ref_and_type( result.push_str(format_defaultness(defaultness)); result.push_str(format_safety(safety)); - let shape = if context.config.version() == Version::Two { + let shape = if context.config.style_edition() >= StyleEdition::Edition2024 { Shape::indented(offset + last_line_width(&result), context.config) } else { generics_shape_from_config( @@ -958,7 +978,7 @@ fn format_impl_ref_and_type( 0, )? }; - let generics_str = rewrite_generics(context, "impl", generics, shape)?; + let generics_str = rewrite_generics(context, "impl", generics, shape).ok()?; result.push_str(&generics_str); result.push_str(format_constness_right(constness)); @@ -1166,7 +1186,7 @@ pub(crate) fn format_trait( let shape = Shape::indented(offset, context.config).offset_left(result.len())?; let generics_str = - rewrite_generics(context, rewrite_ident(context, item.ident), generics, shape)?; + rewrite_generics(context, rewrite_ident(context, item.ident), generics, shape).ok()?; result.push_str(&generics_str); // FIXME(#2055): rustfmt fails to format when there are comments between trait bounds. @@ -1187,7 +1207,8 @@ pub(crate) fn format_trait( shape, &RhsAssignKind::Bounds, RhsTactics::ForceNextLineWithoutIndent, - )?; + ) + .ok()?; } // Rewrite where-clause. @@ -1212,7 +1233,8 @@ pub(crate) fn format_trait( None, pos_before_where, option, - )?; + ) + .ok()?; // If the where-clause cannot fit on the same line, // put the where-clause on a new line if !where_clause_str.contains('\n') @@ -1241,7 +1263,7 @@ pub(crate) fn format_trait( context, last_line_width(&result), ) { - Some(ref missing_comment) if !missing_comment.is_empty() => { + Ok(ref missing_comment) if !missing_comment.is_empty() => { result.push_str(missing_comment); } _ => (), @@ -1317,7 +1339,11 @@ pub(crate) struct TraitAliasBounds<'a> { impl<'a> Rewrite for TraitAliasBounds<'a> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { - let generic_bounds_str = self.generic_bounds.rewrite(context, shape)?; + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { + let generic_bounds_str = self.generic_bounds.rewrite_result(context, shape)?; let mut option = WhereClauseOption::new(true, WhereClauseSpace::None); option.allow_single_line(); @@ -1346,7 +1372,7 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option shape.indent.to_string_with_newline(context.config) }; - Some(format!("{generic_bounds_str}{space}{where_str}")) + Ok(format!("{generic_bounds_str}{space}{where_str}")) } } @@ -1361,7 +1387,7 @@ pub(crate) fn format_trait_alias( let alias = rewrite_ident(context, ident); // 6 = "trait ", 2 = " =" let g_shape = shape.offset_left(6)?.sub_width(2)?; - let generics_str = rewrite_generics(context, alias, generics, g_shape)?; + let generics_str = rewrite_generics(context, alias, generics, g_shape).ok()?; let vis_str = format_visibility(context, vis); let lhs = format!("{vis_str}trait {generics_str} ="); // 1 = ";" @@ -1377,6 +1403,7 @@ pub(crate) fn format_trait_alias( shape.sub_width(1)?, ) .map(|s| s + ";") + .ok() } fn format_unit_struct( @@ -1531,8 +1558,8 @@ fn format_empty_struct_or_tuple( // indented shape for proper indenting of multi-line comments let shape = Shape::indented(offset.block_indent(context.config), context.config); match rewrite_missing_comment(span, shape, context) { - Some(ref s) if s.is_empty() => (), - Some(ref s) => { + Ok(ref s) if s.is_empty() => (), + Ok(ref s) => { let is_multi_line = !is_single_line(s); if is_multi_line || first_line_contains_single_line_comment(s) { let nested_indent_str = offset @@ -1545,7 +1572,7 @@ fn format_empty_struct_or_tuple( result.push_str(&offset.to_string_with_newline(context.config)); } } - None => result.push_str(context.snippet(span)), + Err(_) => result.push_str(context.snippet(span)), } result.push_str(closer); } @@ -1587,7 +1614,7 @@ fn format_tuple_struct( Some(generics) => { let budget = context.budget(last_line_width(&header_str)); let shape = Shape::legacy(budget, offset); - let generics_str = rewrite_generics(context, "", generics, shape)?; + let generics_str = rewrite_generics(context, "", generics, shape).ok()?; result.push_str(&generics_str); let where_budget = context.budget(last_line_width(&result)); @@ -1603,7 +1630,8 @@ fn format_tuple_struct( None, body_hi, option, - )? + ) + .ok()? } None => "".to_owned(), }; @@ -1629,7 +1657,8 @@ fn format_tuple_struct( mk_sp(lo, span.hi()), context.config.fn_call_width(), None, - )?; + ) + .ok()?; } if !where_clause_str.is_empty() @@ -1672,7 +1701,7 @@ pub(crate) fn rewrite_type_alias<'a, 'b>( indent: Indent, visitor_kind: &ItemVisitorKind<'b>, span: Span, -) -> Option { +) -> RewriteResult { use ItemVisitorKind::*; let ast::TyAlias { @@ -1683,6 +1712,9 @@ pub(crate) fn rewrite_type_alias<'a, 'b>( where_clauses, } = *ty_alias_kind; let ty_opt = ty.as_ref(); + let rhs_hi = ty + .as_ref() + .map_or(where_clauses.before.span.hi(), |ty| ty.span.hi()); let (ident, vis) = match visitor_kind { Item(i) => (i.ident, &i.vis), AssocTraitItem(i) | AssocImplItem(i) => (i.ident, &i.vis), @@ -1697,21 +1729,27 @@ pub(crate) fn rewrite_type_alias<'a, 'b>( match (visitor_kind, &op_ty) { (Item(_) | AssocTraitItem(_) | ForeignItem(_), Some(op_bounds)) => { let op = OpaqueType { bounds: op_bounds }; - rewrite_ty(rw_info, Some(bounds), Some(&op), vis) + rewrite_ty(rw_info, Some(bounds), Some(&op), rhs_hi, vis) } (Item(_) | AssocTraitItem(_) | ForeignItem(_), None) => { - rewrite_ty(rw_info, Some(bounds), ty_opt, vis) + rewrite_ty(rw_info, Some(bounds), ty_opt, rhs_hi, vis) } (AssocImplItem(_), _) => { let result = if let Some(op_bounds) = op_ty { let op = OpaqueType { bounds: op_bounds }; - rewrite_ty(rw_info, Some(bounds), Some(&op), &DEFAULT_VISIBILITY) + rewrite_ty( + rw_info, + Some(bounds), + Some(&op), + rhs_hi, + &DEFAULT_VISIBILITY, + ) } else { - rewrite_ty(rw_info, Some(bounds), ty_opt, vis) + rewrite_ty(rw_info, Some(bounds), ty_opt, rhs_hi, vis) }?; match defaultness { - ast::Defaultness::Default(..) => Some(format!("default {result}")), - _ => Some(result), + ast::Defaultness::Default(..) => Ok(format!("default {result}")), + _ => Ok(result), } } } @@ -1721,17 +1759,16 @@ fn rewrite_ty( rw_info: &TyAliasRewriteInfo<'_, '_>, generic_bounds_opt: Option<&ast::GenericBounds>, rhs: Option<&R>, + // the span of the end of the RHS (or the end of the generics, if there is no RHS) + rhs_hi: BytePos, vis: &ast::Visibility, -) -> Option { +) -> RewriteResult { let mut result = String::with_capacity(128); let TyAliasRewriteInfo(context, indent, generics, where_clauses, ident, span) = *rw_info; let (before_where_predicates, after_where_predicates) = generics .where_clause .predicates .split_at(where_clauses.split); - if !after_where_predicates.is_empty() { - return None; - } result.push_str(&format!("{}type ", format_visibility(context, vis))); let ident_str = rewrite_ident(context, ident); @@ -1739,9 +1776,11 @@ fn rewrite_ty( result.push_str(ident_str) } else { // 2 = `= ` - let g_shape = Shape::indented(indent, context.config) - .offset_left(result.len())? - .sub_width(2)?; + let g_shape = Shape::indented(indent, context.config); + let g_shape = g_shape + .offset_left(result.len()) + .and_then(|s| s.sub_width(2)) + .max_width_error(g_shape.width, span)?; let generics_str = rewrite_generics(context, ident_str, generics, g_shape)?; result.push_str(&generics_str); } @@ -1749,8 +1788,13 @@ fn rewrite_ty( if let Some(bounds) = generic_bounds_opt { if !bounds.is_empty() { // 2 = `: ` - let shape = Shape::indented(indent, context.config).offset_left(result.len() + 2)?; - let type_bounds = bounds.rewrite(context, shape).map(|s| format!(": {}", s))?; + let shape = Shape::indented(indent, context.config); + let shape = shape + .offset_left(result.len() + 2) + .max_width_error(shape.width, span)?; + let type_bounds = bounds + .rewrite_result(context, shape) + .map(|s| format!(": {}", s))?; result.push_str(&type_bounds); } } @@ -1760,7 +1804,7 @@ fn rewrite_ty( if rhs.is_none() { option.suppress_comma(); } - let where_clause_str = rewrite_where_clause( + let before_where_clause_str = rewrite_where_clause( context, before_where_predicates, where_clauses.before.span, @@ -1772,14 +1816,20 @@ fn rewrite_ty( generics.span.hi(), option, )?; - result.push_str(&where_clause_str); + result.push_str(&before_where_clause_str); - if let Some(ty) = rhs { - // If there's a where clause, add a newline before the assignment. Otherwise just add a - // space. - let has_where = !before_where_predicates.is_empty(); - if has_where { + let mut result = if let Some(ty) = rhs { + // If there are any where clauses, add a newline before the assignment. + // If there is a before where clause, do not indent, but if there is + // only an after where clause, additionally indent the type. + if !before_where_predicates.is_empty() { result.push_str(&indent.to_string_with_newline(context.config)); + } else if !after_where_predicates.is_empty() { + result.push_str( + &indent + .block_indent(context.config) + .to_string_with_newline(context.config), + ); } else { result.push(' '); } @@ -1791,13 +1841,20 @@ fn rewrite_ty( let lhs = match comment_span { Some(comment_span) - if contains_comment(context.snippet_provider.span_to_snippet(comment_span)?) => + if contains_comment( + context + .snippet_provider + .span_to_snippet(comment_span) + .unknown_error()?, + ) => { - let comment_shape = if has_where { + let comment_shape = if !before_where_predicates.is_empty() { Shape::indented(indent, context.config) } else { - Shape::indented(indent, context.config) - .block_left(context.config.tab_spaces())? + let shape = Shape::indented(indent, context.config); + shape + .block_left(context.config.tab_spaces()) + .max_width_error(shape.width, span)? }; combine_strs_with_missing_comments( @@ -1812,12 +1869,39 @@ fn rewrite_ty( _ => format!("{result}="), }; - // 1 = `;` - let shape = Shape::indented(indent, context.config).sub_width(1)?; - rewrite_assign_rhs(context, lhs, &*ty, &RhsAssignKind::Ty, shape).map(|s| s + ";") + // 1 = `;` unless there's a trailing where clause + let shape = Shape::indented(indent, context.config); + let shape = if after_where_predicates.is_empty() { + Shape::indented(indent, context.config) + .sub_width(1) + .max_width_error(shape.width, span)? + } else { + shape + }; + rewrite_assign_rhs(context, lhs, &*ty, &RhsAssignKind::Ty, shape)? } else { - Some(format!("{result};")) + result + }; + + if !after_where_predicates.is_empty() { + let option = WhereClauseOption::new(true, WhereClauseSpace::Newline); + let after_where_clause_str = rewrite_where_clause( + context, + after_where_predicates, + where_clauses.after.span, + context.config.brace_style(), + Shape::indented(indent, context.config), + false, + ";", + None, + rhs_hi, + option, + )?; + result.push_str(&after_where_clause_str); } + + result += ";"; + Ok(result) } fn type_annotation_spacing(config: &Config) -> (&str, &str) { @@ -1830,10 +1914,10 @@ fn type_annotation_spacing(config: &Config) -> (&str, &str) { pub(crate) fn rewrite_struct_field_prefix( context: &RewriteContext<'_>, field: &ast::FieldDef, -) -> Option { +) -> RewriteResult { let vis = format_visibility(context, &field.vis); let type_annotation_spacing = type_annotation_spacing(context.config); - Some(match field.ident { + Ok(match field.ident { Some(name) => format!( "{}{}{}:", vis, @@ -1846,6 +1930,10 @@ pub(crate) fn rewrite_struct_field_prefix( impl Rewrite for ast::FieldDef { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { rewrite_struct_field(context, self, shape, 0) } } @@ -1855,15 +1943,15 @@ pub(crate) fn rewrite_struct_field( field: &ast::FieldDef, shape: Shape, lhs_max_width: usize, -) -> Option { +) -> RewriteResult { if contains_skip(&field.attrs) { - return Some(context.snippet(field.span()).to_owned()); + return Ok(context.snippet(field.span()).to_owned()); } let type_annotation_spacing = type_annotation_spacing(context.config); let prefix = rewrite_struct_field_prefix(context, field)?; - let attrs_str = field.attrs.rewrite(context, shape)?; + let attrs_str = field.attrs.rewrite_result(context, shape)?; let attrs_extendable = field.ident.is_none() && is_attributes_extendable(&attrs_str); let missing_span = if field.attrs.is_empty() { mk_sp(field.span.lo(), field.span.lo()) @@ -1893,12 +1981,14 @@ pub(crate) fn rewrite_struct_field( if prefix.is_empty() && !attrs_str.is_empty() && attrs_extendable && spacing.is_empty() { spacing.push(' '); } + let orig_ty = shape .offset_left(overhead + spacing.len()) - .and_then(|ty_shape| field.ty.rewrite(context, ty_shape)); + .and_then(|ty_shape| field.ty.rewrite_result(context, ty_shape).ok()); + if let Some(ref ty) = orig_ty { if !ty.contains('\n') && !contains_comment(context.snippet(missing_span)) { - return Some(attr_prefix + &spacing + ty); + return Ok(attr_prefix + &spacing + ty); } } @@ -2045,7 +2135,8 @@ fn rewrite_static( comments_span, true, ) - .and_then(|res| recover_comment_removed(res, static_parts.span, context)) + .ok() + .map(|res| recover_comment_removed(res, static_parts.span, context)) .map(|s| if s.ends_with(';') { s } else { s + ";" }) } else { Some(format!("{prefix}{ty_str};")) @@ -2072,19 +2163,34 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option impl Rewrite for ast::FnRetTy { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { match *self { - ast::FnRetTy::Default(_) => Some(String::new()), + ast::FnRetTy::Default(_) => Ok(String::new()), ast::FnRetTy::Ty(ref ty) => { - if context.config.version() == Version::One + let arrow_width = "-> ".len(); + if context.config.style_edition() <= StyleEdition::Edition2021 || context.config.indent_style() == IndentStyle::Visual { - let inner_width = shape.width.checked_sub(3)?; + let inner_width = shape + .width + .checked_sub(arrow_width) + .max_width_error(shape.width, self.span())?; return ty - .rewrite(context, Shape::legacy(inner_width, shape.indent + 3)) + .rewrite_result( + context, + Shape::legacy(inner_width, shape.indent + arrow_width), + ) .map(|r| format!("-> {}", r)); } - ty.rewrite(context, shape.offset_left(3)?) + let shape = shape + .offset_left(arrow_width) + .max_width_error(shape.width, self.span())?; + + ty.rewrite_result(context, shape) .map(|s| format!("-> {}", s)) } } @@ -2126,9 +2232,11 @@ fn get_missing_param_comments( }; let comment_before_colon = rewrite_missing_comment(span_before_colon, shape, context) + .ok() .filter(|comment| !comment.is_empty()) .map_or(String::new(), |comment| format!(" {}", comment)); let comment_after_colon = rewrite_missing_comment(span_after_colon, shape, context) + .ok() .filter(|comment| !comment.is_empty()) .map_or(String::new(), |comment| format!("{} ", comment)); (comment_before_colon, comment_after_colon) @@ -2136,9 +2244,13 @@ fn get_missing_param_comments( impl Rewrite for ast::Param { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { let param_attrs_result = self .attrs - .rewrite(context, Shape::legacy(shape.width, shape.indent))?; + .rewrite_result(context, Shape::legacy(shape.width, shape.indent))?; // N.B. Doc comments aren't typically valid syntax, but could appear // in the presence of certain macros - https://github.com/rust-lang/rustfmt/issues/4936 let (span, has_multiple_attr_lines, has_doc_comments) = if !self.attrs.is_empty() { @@ -2164,7 +2276,7 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option } else if is_named_param(self) { let param_name = &self .pat - .rewrite(context, Shape::legacy(shape.width, shape.indent))?; + .rewrite_result(context, Shape::legacy(shape.width, shape.indent))?; let mut result = combine_strs_with_missing_comments( context, ¶m_attrs_result, @@ -2181,10 +2293,13 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option result.push_str(colon_spaces(context.config)); result.push_str(&after_comment); let overhead = last_line_width(&result); - let max_width = shape.width.checked_sub(overhead)?; - if let Some(ty_str) = self + let max_width = shape + .width + .checked_sub(overhead) + .max_width_error(shape.width, self.span())?; + if let Ok(ty_str) = self .ty - .rewrite(context, Shape::legacy(max_width, shape.indent)) + .rewrite_result(context, Shape::legacy(max_width, shape.indent)) { result.push_str(&ty_str); } else { @@ -2206,17 +2321,20 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option result.push_str(colon_spaces(context.config)); result.push_str(&after_comment); let overhead = last_line_width(&result); - let max_width = shape.width.checked_sub(overhead)?; + let max_width = shape + .width + .checked_sub(overhead) + .max_width_error(shape.width, self.span())?; let ty_str = self .ty - .rewrite(context, Shape::legacy(max_width, shape.indent))?; + .rewrite_result(context, Shape::legacy(max_width, shape.indent))?; result.push_str(&ty_str); } } - Some(result) + Ok(result) } else { - self.ty.rewrite(context, shape) + self.ty.rewrite_result(context, shape) } } } @@ -2228,17 +2346,17 @@ fn rewrite_explicit_self( span: Span, shape: Shape, has_multiple_attr_lines: bool, -) -> Option { +) -> RewriteResult { match explicit_self.node { ast::SelfKind::Region(lt, m) => { let mut_str = format_mutability(m); match lt { Some(ref l) => { - let lifetime_str = l.rewrite( + let lifetime_str = l.rewrite_result( context, Shape::legacy(context.config.max_width(), Indent::empty()), )?; - Some(combine_strs_with_missing_comments( + Ok(combine_strs_with_missing_comments( context, param_attrs, &format!("&{lifetime_str} {mut_str}self"), @@ -2247,7 +2365,7 @@ fn rewrite_explicit_self( !has_multiple_attr_lines, )?) } - None => Some(combine_strs_with_missing_comments( + None => Ok(combine_strs_with_missing_comments( context, param_attrs, &format!("&{mut_str}self"), @@ -2258,12 +2376,12 @@ fn rewrite_explicit_self( } } ast::SelfKind::Explicit(ref ty, mutability) => { - let type_str = ty.rewrite( + let type_str = ty.rewrite_result( context, Shape::legacy(context.config.max_width(), Indent::empty()), )?; - Some(combine_strs_with_missing_comments( + Ok(combine_strs_with_missing_comments( context, param_attrs, &format!("{}self: {}", format_mutability(mutability), type_str), @@ -2272,7 +2390,7 @@ fn rewrite_explicit_self( !has_multiple_attr_lines, )?) } - ast::SelfKind::Value(mutability) => Some(combine_strs_with_missing_comments( + ast::SelfKind::Value(mutability) => Ok(combine_strs_with_missing_comments( context, param_attrs, &format!("{}self", format_mutability(mutability)), @@ -2326,7 +2444,7 @@ fn rewrite_fn_base( fn_sig: &FnSig<'_>, span: Span, fn_brace_style: FnBraceStyle, -) -> Option<(String, bool, bool)> { +) -> Result<(String, bool, bool), RewriteError> { let mut force_new_line_for_brace = false; let where_clause = &fn_sig.generics.where_clause; @@ -2370,7 +2488,7 @@ fn rewrite_fn_base( // return type later anyway. let ret_str = fd .output - .rewrite(context, Shape::indented(indent, context.config))?; + .rewrite_result(context, Shape::indented(indent, context.config))?; let multi_line_ret_str = ret_str.contains('\n'); let ret_str_len = if multi_line_ret_str { 0 } else { ret_str.len() }; @@ -2383,7 +2501,7 @@ fn rewrite_fn_base( ret_str_len, fn_brace_style, multi_line_ret_str, - )?; + ); debug!( "rewrite_fn_base: one_line_budget: {}, multi_line_budget: {}, param_indent: {:?}", @@ -2452,7 +2570,7 @@ fn rewrite_fn_base( .last() .map_or(false, |last_line| last_line.contains("//")); - if context.config.version() == Version::Two { + if context.config.style_edition() >= StyleEdition::Edition2024 { if closing_paren_overflow_max_width { result.push(')'); result.push_str(&indent.to_string_with_newline(context.config)); @@ -2495,7 +2613,7 @@ fn rewrite_fn_base( } }; let ret_shape = if ret_should_indent { - if context.config.version() == Version::One + if context.config.style_edition() <= StyleEdition::Edition2021 || context.config.indent_style() == IndentStyle::Visual { let indent = if param_str.is_empty() { @@ -2528,7 +2646,7 @@ fn rewrite_fn_base( ret_shape } } else { - if context.config.version() == Version::Two { + if context.config.style_edition() >= StyleEdition::Edition2024 { if !param_str.is_empty() || !no_params_and_over_max_width { result.push(' '); } @@ -2545,7 +2663,7 @@ fn rewrite_fn_base( if multi_line_ret_str || ret_should_indent { // Now that we know the proper indent and width, we need to // re-layout the return type. - let ret_str = fd.output.rewrite(context, ret_shape)?; + let ret_str = fd.output.rewrite_result(context, ret_shape)?; result.push_str(&ret_str); } else { result.push_str(&ret_str); @@ -2617,7 +2735,7 @@ fn rewrite_fn_base( context, last_line_width(&result), ) { - Some(ref missing_comment) if !missing_comment.is_empty() => { + Ok(ref missing_comment) if !missing_comment.is_empty() => { result.push_str(missing_comment); force_new_line_for_brace = true; } @@ -2632,7 +2750,7 @@ fn rewrite_fn_base( force_new_line_for_brace |= ends_with_comment; force_new_line_for_brace |= is_params_multi_lined && context.config.where_single_line() && !where_clause_str.is_empty(); - Some((result, ends_with_comment, force_new_line_for_brace)) + Ok((result, ends_with_comment, force_new_line_for_brace)) } /// Kind of spaces to put before `where`. @@ -2703,7 +2821,7 @@ fn rewrite_params( param_indent: Indent, span: Span, variadic: bool, -) -> Option { +) -> RewriteResult { if params.is_empty() { let comment = context .snippet(mk_sp( @@ -2712,7 +2830,7 @@ fn rewrite_params( span.hi() - BytePos(1), )) .trim(); - return Some(comment.to_owned()); + return Ok(comment.to_owned()); } let param_items: Vec<_> = itemize_list( context.snippet_provider, @@ -2723,8 +2841,8 @@ fn rewrite_params( |param| param.ty.span.hi(), |param| { param - .rewrite(context, Shape::legacy(multi_line_budget, param_indent)) - .or_else(|| Some(context.snippet(param.span()).to_owned())) + .rewrite_result(context, Shape::legacy(multi_line_budget, param_indent)) + .or_else(|_| Ok(context.snippet(param.span()).to_owned())) }, span.lo(), span.hi(), @@ -2772,7 +2890,7 @@ fn compute_budgets_for_params( ret_str_len: usize, fn_brace_style: FnBraceStyle, force_vertical_layout: bool, -) -> Option<(usize, usize, Indent)> { +) -> (usize, usize, Indent) { debug!( "compute_budgets_for_params {} {:?}, {}, {:?}", result.len(), @@ -2809,7 +2927,7 @@ fn compute_budgets_for_params( } }; - return Some((one_line_budget, multi_line_budget, indent)); + return (one_line_budget, multi_line_budget, indent); } } @@ -2821,7 +2939,7 @@ fn compute_budgets_for_params( // Account for `)` and possibly ` {`. IndentStyle::Visual => new_indent.width() + if ret_str_len == 0 { 1 } else { 3 }, }; - Some((0, context.budget(used_space), new_indent)) + (0, context.budget(used_space), new_indent) } fn newline_for_brace(config: &Config, where_clause: &ast::WhereClause) -> FnBraceStyle { @@ -2846,12 +2964,12 @@ fn rewrite_generics( ident: &str, generics: &ast::Generics, shape: Shape, -) -> Option { +) -> RewriteResult { // FIXME: convert bounds to where-clauses where they get too big or if // there is a where-clause at all. if generics.params.is_empty() { - return Some(ident.to_owned()); + return Ok(ident.to_owned()); } let params = generics.params.iter(); @@ -2881,7 +2999,7 @@ fn rewrite_where_clause_rfc_style( span_end: Option, span_end_before_where: BytePos, where_clause_option: WhereClauseOption, -) -> Option { +) -> RewriteResult { let (where_keyword, allow_single_line) = rewrite_where_keyword( context, predicates, @@ -2895,8 +3013,9 @@ fn rewrite_where_clause_rfc_style( let clause_shape = shape .block() .with_max_width(context.config) - .block_left(context.config.tab_spaces())? - .sub_width(1)?; + .block_left(context.config.tab_spaces()) + .and_then(|s| s.sub_width(1)) + .max_width_error(shape.width, where_span)?; let force_single_line = context.config.where_single_line() && predicates.len() == 1 && !where_clause_option.veto_single_line; @@ -2921,7 +3040,7 @@ fn rewrite_where_clause_rfc_style( clause_shape.indent.to_string_with_newline(context.config) }; - Some(format!("{where_keyword}{clause_sep}{preds_str}")) + Ok(format!("{where_keyword}{clause_sep}{preds_str}")) } /// Rewrite `where` and comment around it. @@ -2932,12 +3051,13 @@ fn rewrite_where_keyword( shape: Shape, span_end_before_where: BytePos, where_clause_option: WhereClauseOption, -) -> Option<(String, bool)> { +) -> Result<(String, bool), RewriteError> { let block_shape = shape.block().with_max_width(context.config); // 1 = `,` let clause_shape = block_shape - .block_left(context.config.tab_spaces())? - .sub_width(1)?; + .block_left(context.config.tab_spaces()) + .and_then(|s| s.sub_width(1)) + .max_width_error(block_shape.width, where_span)?; let comment_separator = |comment: &str, shape: Shape| { if comment.is_empty() { @@ -2968,7 +3088,7 @@ fn rewrite_where_keyword( && comment_before.is_empty() && comment_after.is_empty(); - Some((result, allow_single_line)) + Ok((result, allow_single_line)) } /// Rewrite bounds on a where clause. @@ -2980,7 +3100,7 @@ fn rewrite_bounds_on_where_clause( span_end: Option, where_clause_option: WhereClauseOption, force_single_line: bool, -) -> Option { +) -> RewriteResult { let span_start = predicates[0].span().lo(); // If we don't have the start of the next span, then use the end of the // predicates, but that means we miss comments. @@ -2994,7 +3114,7 @@ fn rewrite_bounds_on_where_clause( ",", |pred| pred.span().lo(), |pred| pred.span().hi(), - |pred| pred.rewrite(context, shape), + |pred| pred.rewrite_result(context, shape), span_start, span_end, false, @@ -3013,7 +3133,7 @@ fn rewrite_bounds_on_where_clause( DefinitiveListTactic::Vertical }; - let preserve_newline = context.config.version() == Version::One; + let preserve_newline = context.config.style_edition() <= StyleEdition::Edition2021; let fmt = ListFormatting::new(shape, context.config) .tactic(shape_tactic) @@ -3033,9 +3153,9 @@ fn rewrite_where_clause( span_end: Option, span_end_before_where: BytePos, where_clause_option: WhereClauseOption, -) -> Option { +) -> RewriteResult { if predicates.is_empty() { - return Some(String::new()); + return Ok(String::new()); } if context.config.indent_style() == IndentStyle::Block { @@ -3075,7 +3195,7 @@ fn rewrite_where_clause( ",", |pred| pred.span().lo(), |pred| pred.span().hi(), - |pred| pred.rewrite(context, Shape::legacy(budget, offset)), + |pred| pred.rewrite_result(context, Shape::legacy(budget, offset)), span_start, span_end, false, @@ -3113,13 +3233,13 @@ fn rewrite_where_clause( || preds_str.contains('\n') || shape.indent.width() + " where ".len() + preds_str.len() + end_length > shape.width { - Some(format!( + Ok(format!( "\n{}where {}", (shape.indent + extra_indent).to_string(context.config), preds_str )) } else { - Some(format!(" where {preds_str}")) + Ok(format!(" where {preds_str}")) } } @@ -3140,14 +3260,14 @@ fn rewrite_comments_before_after_where( span_before_where: Span, span_after_where: Span, shape: Shape, -) -> Option<(String, String)> { +) -> Result<(String, String), RewriteError> { let before_comment = rewrite_missing_comment(span_before_where, shape, context)?; let after_comment = rewrite_missing_comment( span_after_where, shape.block_indent(context.config.tab_spaces()), context, )?; - Some((before_comment, after_comment)) + Ok((before_comment, after_comment)) } fn format_header( @@ -3169,7 +3289,7 @@ fn format_header( .opt_span_before(mk_sp(vis.span.lo(), ident.span.hi()), item_name.trim()) { let missing_span = mk_sp(after_vis, before_item_name); - if let Some(result_with_comment) = combine_strs_with_missing_comments( + if let Ok(result_with_comment) = combine_strs_with_missing_comments( context, &result, item_name, @@ -3203,7 +3323,7 @@ fn format_generics( used_width: usize, ) -> Option { let shape = Shape::legacy(context.budget(used_width + offset.width()), offset); - let mut result = rewrite_generics(context, "", generics, shape)?; + let mut result = rewrite_generics(context, "", generics, shape).ok()?; // If the generics are not parameterized then generics.span.hi() == 0, // so we use span.lo(), which is the position after `struct Foo`. @@ -3229,7 +3349,8 @@ fn format_generics( Some(span.hi()), span_end_before_where, option, - )?; + ) + .ok()?; result.push_str(&where_clause_str); ( brace_pos == BracePos::ForceSameLine || brace_style == BraceStyle::PreferSameLine, @@ -3253,7 +3374,8 @@ fn format_generics( ), shape, context, - ), + ) + .ok(), ) }; // add missing comments @@ -3295,7 +3417,11 @@ fn format_generics( impl Rewrite for ast::ForeignItem { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { - let attrs_str = self.attrs.rewrite(context, shape)?; + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { + let attrs_str = self.attrs.rewrite_result(context, shape)?; // Drop semicolon or it will be interpreted as comment. // FIXME: this may be a faulty span from libsyntax. let span = mk_sp(self.span.lo(), self.span.hi() - BytePos(1)); @@ -3328,7 +3454,7 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option defaultness, Some(&inner_attrs), ); - Some(visitor.buffer.to_owned()) + Ok(visitor.buffer.to_owned()) } else { rewrite_fn_base( context, @@ -3360,7 +3486,9 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option prefix, &static_foreign_item.ty, &RhsAssignKind::Ty, - shape.sub_width(1)?, + shape + .sub_width(1) + .max_width_error(shape.width, static_foreign_item.ty.span)?, ) .map(|s| s + ";") } @@ -3421,6 +3549,7 @@ fn rewrite_attrs( shape, allow_extend, ) + .ok() } /// Rewrite an inline mod. diff --git a/src/tools/rustfmt/src/lib.rs b/src/tools/rustfmt/src/lib.rs index 4263a49fd72..08cda6913b9 100644 --- a/src/tools/rustfmt/src/lib.rs +++ b/src/tools/rustfmt/src/lib.rs @@ -5,9 +5,6 @@ #![allow(clippy::match_like_matches_macro)] #![allow(unreachable_pub)] -// #[macro_use] -// extern crate tracing; - // N.B. these crates are loaded from the sysroot, so they need extern crate. extern crate rustc_ast; extern crate rustc_ast_pretty; @@ -48,8 +45,8 @@ use crate::utils::indent_next_line; pub use crate::config::{ - load_config, CliOptions, Color, Config, Edition, EmitMode, FileLines, FileName, NewlineStyle, - Range, Verbosity, + CliOptions, Color, Config, Edition, EmitMode, FileLines, FileName, NewlineStyle, Range, + StyleEdition, Verbosity, Version, load_config, }; pub use crate::format_report_formatter::{FormatReportFormatter, FormatReportFormatterBuilder}; @@ -94,6 +91,7 @@ macro_rules! static_regex { pub(crate) mod rustfmt_diff; mod shape; mod skip; +mod sort; pub(crate) mod source_file; pub(crate) mod source_map; mod spanned; diff --git a/src/tools/rustfmt/src/lists.rs b/src/tools/rustfmt/src/lists.rs index 41afef279e9..415144a8abc 100644 --- a/src/tools/rustfmt/src/lists.rs +++ b/src/tools/rustfmt/src/lists.rs @@ -5,10 +5,10 @@ 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::{Config, IndentStyle}; -use crate::rewrite::RewriteContext; +use crate::rewrite::{RewriteContext, RewriteError, RewriteResult}; use crate::shape::{Indent, Shape}; use crate::utils::{ 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, // Item should include attributes and doc comments. None indicates a failed // rewrite. - pub(crate) item: Option, + pub(crate) item: RewriteResult, pub(crate) post_comment: Option, // Whether there is extra whitespace before this item. pub(crate) new_lines: bool, } impl ListItem { - pub(crate) fn empty() -> ListItem { + pub(crate) fn from_item(item: RewriteResult) -> ListItem { ListItem { pre_comment: None, pre_comment_style: ListItemCommentStyle::None, - item: None, + item: item, post_comment: None, new_lines: false, } @@ -185,7 +185,7 @@ pub(crate) fn from_str>(s: S) -> ListItem { ListItem { pre_comment: None, pre_comment_style: ListItemCommentStyle::None, - item: Some(s.into()), + item: Ok(s.into()), post_comment: None, new_lines: false, } @@ -197,7 +197,11 @@ fn empty(s: &Option) -> bool { !matches!(*s, Some(ref s) if !s.is_empty()) } - !(empty(&self.pre_comment) && empty(&self.item) && empty(&self.post_comment)) + 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 @@ pub(crate) fn definitive_tactic( } // Format a list of commented items into a string. -pub(crate) fn write_list(items: I, formatting: &ListFormatting<'_>) -> Option +pub(crate) fn write_list(items: I, formatting: &ListFormatting<'_>) -> RewriteResult where I: IntoIterator + Clone, T: AsRef, @@ -281,7 +285,7 @@ pub(crate) fn write_list(items: I, formatting: &ListFormatting<'_>) -> Opt let indent_str = &formatting.shape.indent.to_string(formatting.config); while let Some((i, item)) = iter.next() { let item = item.as_ref(); - let inner_item = item.item.as_ref()?; + let inner_item = item.item.as_ref().or_else(|err| Err(err.clone()))?; let first = i == 0; let last = iter.peek().is_none(); let mut separate = match sep_place { @@ -516,7 +520,7 @@ pub(crate) fn write_list(items: I, formatting: &ListFormatting<'_>) -> Opt prev_item_is_nested_import = inner_item.contains("::"); } - Some(result) + Ok(result) } fn max_width_of_item_with_post_comment( @@ -741,7 +745,7 @@ impl<'a, T, I, F1, F2, F3> Iterator for ListItems<'a, I, F1, F2, F3> I: Iterator, F1: Fn(&T) -> BytePos, F2: Fn(&T) -> BytePos, - F3: Fn(&T) -> Option, + F3: Fn(&T) -> RewriteResult, { type Item = ListItem; @@ -775,8 +779,9 @@ fn next(&mut self) -> Option { ListItem { pre_comment, pre_comment_style, + // leave_last is set to true only for rewrite_items item: if self.inner.peek().is_none() && self.leave_last { - None + Err(RewriteError::SkipFormatting) } else { (self.get_item_string)(&item) }, @@ -805,7 +810,7 @@ pub(crate) fn itemize_list<'a, T, I, F1, F2, F3>( I: Iterator, F1: Fn(&T) -> BytePos, F2: Fn(&T) -> BytePos, - F3: Fn(&T) -> Option, + F3: Fn(&T) -> RewriteResult, { ListItems { snippet_provider, diff --git a/src/tools/rustfmt/src/macros.rs b/src/tools/rustfmt/src/macros.rs index 524fc666fae..5a35e115d8f 100644 --- a/src/tools/rustfmt/src/macros.rs +++ b/src/tools/rustfmt/src/macros.rs @@ -10,35 +10,37 @@ // and those with brackets will be formatted as array literals. 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::tokenstream::{RefTokenTreeCursor, TokenStream, TokenTree}; use rustc_ast::{ast, ptr}; use rustc_ast_pretty::pprust; use rustc_span::{ + BytePos, DUMMY_SP, Span, Symbol, symbol::{self, kw}, - BytePos, Span, Symbol, DUMMY_SP, }; use tracing::debug; 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::Version; -use crate::expr::{rewrite_array, rewrite_assign_rhs, RhsAssignKind}; -use crate::lists::{itemize_list, write_list, ListFormatting}; +use crate::expr::{RhsAssignKind, rewrite_array, rewrite_assign_rhs}; +use crate::lists::{ListFormatting, itemize_list, write_list}; use crate::overflow; use crate::parse::macros::lazy_static::parse_lazy_static; -use crate::parse::macros::{parse_expr, parse_macro_args, ParsedMacroArgs}; -use crate::rewrite::{Rewrite, RewriteContext}; +use crate::parse::macros::{ParsedMacroArgs, parse_expr, parse_macro_args}; +use crate::rewrite::{ + MacroErrorKind, Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult, +}; use crate::shape::{Indent, Shape}; use crate::source_map::SpanUtils; use crate::spanned::Spanned; use crate::utils::{ - filtered_str_fits, format_visibility, indent_next_line, is_empty_line, mk_sp, - remove_trailing_white_spaces, rewrite_ident, trim_left_preserve_layout, NodeIdExt, + NodeIdExt, filtered_str_fits, format_visibility, indent_next_line, is_empty_line, mk_sp, + remove_trailing_white_spaces, rewrite_ident, trim_left_preserve_layout, }; use crate::visitor::FmtVisitor; @@ -72,22 +74,30 @@ pub(crate) fn is_item(&self) -> bool { impl Rewrite for ast::Item { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { let mut visitor = crate::visitor::FmtVisitor::from_context(context); visitor.block_indent = shape.indent; visitor.last_pos = self.span().lo(); visitor.visit_item(self); - Some(visitor.buffer.to_owned()) + Ok(visitor.buffer.to_owned()) } } impl Rewrite for MacroArg { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { match *self { - MacroArg::Expr(ref expr) => expr.rewrite(context, shape), - MacroArg::Ty(ref ty) => ty.rewrite(context, shape), - MacroArg::Pat(ref pat) => pat.rewrite(context, shape), - MacroArg::Item(ref item) => item.rewrite(context, shape), - MacroArg::Keyword(ident, _) => Some(ident.name.to_string()), + MacroArg::Expr(ref expr) => expr.rewrite_result(context, shape), + MacroArg::Ty(ref ty) => ty.rewrite_result(context, shape), + MacroArg::Pat(ref pat) => pat.rewrite_result(context, shape), + MacroArg::Item(ref item) => item.rewrite_result(context, shape), + 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. +// 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( context: &RewriteContext<'_>, indent: Indent, position: MacroPosition, span: Span, -) -> Option { +) -> RewriteResult { // Mark this as a failure however we format it context.macro_rewrite_failure.replace(true); @@ -134,7 +146,8 @@ fn return_macro_parse_failure_fallback( }) .unwrap_or(false); if is_like_block_indent_style { - return trim_left_preserve_layout(context.snippet(span), indent, context.config); + return trim_left_preserve_layout(context.snippet(span), indent, context.config) + .macro_error(MacroErrorKind::Unknown, span); } context.skipped_range.borrow_mut().push(( @@ -147,7 +160,7 @@ fn return_macro_parse_failure_fallback( if position == MacroPosition::Item { snippet.push(';'); } - Some(snippet) + Ok(snippet) } pub(crate) fn rewrite_macro( @@ -156,13 +169,13 @@ pub(crate) fn rewrite_macro( context: &RewriteContext<'_>, shape: Shape, position: MacroPosition, -) -> Option { +) -> RewriteResult { let should_skip = context .skip_context .macros .skip(context.snippet(mac.path.span)); if should_skip { - None + Err(RewriteError::SkipFormatting) } else { let guard = context.enter_macro(); let result = catch_unwind(AssertUnwindSafe(|| { @@ -176,9 +189,16 @@ pub(crate) fn rewrite_macro( ) })); match result { - Err(..) | Ok(None) => { + Err(..) => { 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, } @@ -192,11 +212,11 @@ fn rewrite_macro_inner( shape: Shape, position: MacroPosition, is_nested_macro: bool, -) -> Option { +) -> RewriteResult { if context.config.use_try_shorthand() { if let Some(expr) = convert_try_mac(mac, context) { 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 { return match style { Delimiter::Parenthesis if position == MacroPosition::Item => { - Some(format!("{macro_name}();")) + Ok(format!("{macro_name}();")) } - Delimiter::Bracket if position == MacroPosition::Item => { - Some(format!("{macro_name}[];")) - } - Delimiter::Parenthesis => Some(format!("{macro_name}()")), - Delimiter::Bracket => Some(format!("{macro_name}[]")), - Delimiter::Brace => Some(format!("{macro_name} {{}}")), + Delimiter::Bracket if position == MacroPosition::Item => Ok(format!("{macro_name}[];")), + Delimiter::Parenthesis => Ok(format!("{macro_name}()")), + Delimiter::Bracket => Ok(format!("{macro_name}[]")), + Delimiter::Brace => Ok(format!("{macro_name} {{}}")), _ => unreachable!(), }; } // Format well-known macros which cannot be parsed as a valid AST. if macro_name == "lazy_static!" && !has_comment { - if let success @ Some(..) = format_lazy_static(context, shape, ts.clone()) { - return success; + match format_lazy_static(context, shape, ts.clone(), mac.span()) { + 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 => { // Handle special case: `vec!(expr; expr)` 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 { // Format macro invocation as function call, preserve the trailing // comma because not all macros support them. @@ -293,7 +319,7 @@ fn rewrite_macro_inner( Delimiter::Bracket => { // Handle special case: `vec![expr; expr]` 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 { // If we are rewriting `vec!` macro or other special macros, // 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 => { @@ -332,8 +358,8 @@ fn rewrite_macro_inner( // anything in between the braces (for now). let snippet = context.snippet(mac.span()).trim_start_matches(|c| c != '{'); match trim_left_preserve_layout(snippet, shape.indent, context.config) { - Some(macro_body) => Some(format!("{macro_name} {macro_body}")), - None => Some(format!("{macro_name} {snippet}")), + Some(macro_body) => Ok(format!("{macro_name} {macro_body}")), + None => Ok(format!("{macro_name} {snippet}")), } } _ => unreachable!(), @@ -346,28 +372,32 @@ fn handle_vec_semi( arg_vec: Vec, macro_name: String, delim_token: Delimiter, -) -> Option { + span: Span, +) -> RewriteResult { let (left, right) = match delim_token { Delimiter::Parenthesis => ("(", ")"), Delimiter::Bracket => ("[", "]"), _ => 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!()` + `; ` let total_overhead = 8; let nested_shape = mac_shape.block_indent(context.config.tab_spaces()); - let lhs = arg_vec[0].rewrite(context, nested_shape)?; - let rhs = arg_vec[1].rewrite(context, nested_shape)?; + let lhs = arg_vec[0].rewrite_result(context, nested_shape)?; + let rhs = arg_vec[1].rewrite_result(context, nested_shape)?; if !lhs.contains('\n') && !rhs.contains('\n') && lhs.len() + rhs.len() + total_overhead <= shape.width { // macro_name(lhs; rhs) or macro_name[lhs; rhs] - Some(format!("{macro_name}{left}{lhs}; {rhs}{right}")) + Ok(format!("{macro_name}{left}{lhs}; {rhs}{right}")) } else { // macro_name(\nlhs;\nrhs\n) or macro_name[\nlhs;\nrhs\n] - Some(format!( + Ok(format!( "{}{}{}{};{}{}{}{}", macro_name, left, @@ -385,7 +415,7 @@ fn rewrite_empty_macro_def_body( context: &RewriteContext<'_>, span: Span, shape: Shape, -) -> Option { +) -> RewriteResult { // Create an empty, dummy `ast::Block` representing an empty macro body let block = ast::Block { stmts: vec![].into(), @@ -395,7 +425,7 @@ fn rewrite_empty_macro_def_body( tokens: None, could_be_bare_literal: false, }; - block.rewrite(context, shape) + block.rewrite_result(context, shape) } pub(crate) fn rewrite_macro_def( @@ -406,8 +436,8 @@ pub(crate) fn rewrite_macro_def( ident: symbol::Ident, vis: &ast::Visibility, span: Span, -) -> Option { - let snippet = Some(remove_trailing_white_spaces(context.snippet(span))); +) -> RewriteResult { + let snippet = Ok(remove_trailing_white_spaces(context.snippet(span))); if snippet.as_ref().map_or(true, |s| s.ends_with(';')) { return snippet; } @@ -442,7 +472,7 @@ pub(crate) fn rewrite_macro_def( let lo = context.snippet_provider.span_before(span, "{"); result += " "; result += &rewrite_empty_macro_def_body(context, span.with_lo(lo), shape)?; - return Some(result); + return Ok(result); } let branch_items = itemize_list( @@ -453,13 +483,14 @@ pub(crate) fn rewrite_macro_def( |branch| branch.span.lo(), |branch| branch.span.hi(), |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 // original body - None if context.macro_rewrite_failure.get() => { - Some(context.snippet(branch.body).trim().to_string()) + // TODO(ding-young) report rewrite error even if we return Ok with original snippet + 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, "{"), span.hi(), @@ -478,8 +509,8 @@ pub(crate) fn rewrite_macro_def( } match write_list(&branch_items, &fmt) { - Some(ref s) => result += s, - None => return snippet, + Ok(ref s) => result += s, + Err(_) => return snippet, } if multi_branch_style { @@ -487,7 +518,7 @@ pub(crate) fn rewrite_macro_def( result += "}"; } - Some(result) + Ok(result) } fn register_metavariable( @@ -639,12 +670,13 @@ fn rewrite( context: &RewriteContext<'_>, shape: Shape, use_multiple_lines: bool, - ) -> Option { - let rewrite_delimited_inner = |delim_tok, args| -> Option<(String, String, String)> { + ) -> RewriteResult { + type DelimitedArgsRewrite = Result<(String, String, String), RewriteError>; + let rewrite_delimited_inner = |delim_tok, args| -> DelimitedArgsRewrite { let inner = wrap_macro_args(context, args, shape)?; let (lhs, rhs) = delim_token_to_str(context, delim_tok, shape, false, inner.is_empty()); if lhs.len() + inner.len() + rhs.len() <= shape.width { - return Some((lhs, inner, rhs)); + return Ok((lhs, inner, rhs)); } let (lhs, rhs) = delim_token_to_str(context, delim_tok, shape, true, false); @@ -652,27 +684,27 @@ fn rewrite( .block_indent(context.config.tab_spaces()) .with_max_width(context.config); let inner = wrap_macro_args(context, args, nested_shape)?; - Some((lhs, inner, rhs)) + Ok((lhs, inner, rhs)) }; 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) => { let (lhs, inner, rhs) = rewrite_delimited_inner(delim_tok, args)?; let another = another .as_ref() - .and_then(|a| a.rewrite(context, shape, use_multiple_lines)) + .and_then(|a| a.rewrite(context, shape, use_multiple_lines).ok()) .unwrap_or_else(|| "".to_owned()); 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) => { rewrite_delimited_inner(delim_tok, args) .map(|(lhs, inner, rhs)| format!("{}{}{}", lhs, inner, rhs)) } - MacroArgKind::Separator(ref sep, ref prefix) => Some(format!("{prefix}{sep} ")), - MacroArgKind::Other(ref inner, ref prefix) => Some(format!("{prefix}{inner}")), + MacroArgKind::Separator(ref sep, ref prefix) => Ok(format!("{prefix}{sep} ")), + MacroArgKind::Other(ref inner, ref prefix) => Ok(format!("{prefix}{inner}")), } } } @@ -688,7 +720,7 @@ fn rewrite( context: &RewriteContext<'_>, shape: Shape, use_multiple_lines: bool, - ) -> Option { + ) -> RewriteResult { self.kind.rewrite(context, shape, use_multiple_lines) } } @@ -966,9 +998,9 @@ fn wrap_macro_args( context: &RewriteContext<'_>, args: &[ParsedMacroArg], shape: Shape, -) -> Option { +) -> RewriteResult { 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( @@ -976,7 +1008,7 @@ fn wrap_macro_args_inner( args: &[ParsedMacroArg], shape: Shape, use_multiple_lines: bool, -) -> Option { +) -> RewriteResult { let mut result = String::with_capacity(128); let mut iter = args.iter().peekable(); 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 { - None + Err(RewriteError::Unknown) } 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 // meaning of some tokens is a bit different here from usual Rust, e.g., `*` // and `(`/`)` have special meaning. -// -// We always try and format on one line. -// FIXME: Use multi-line when every thing does not fit on one line. fn format_macro_args( context: &RewriteContext<'_>, token_stream: TokenStream, shape: Shape, -) -> Option { +) -> RewriteResult { + let span = span_for_token_stream(&token_stream); if !context.config.format_macro_matchers() { - let span = span_for_token_stream(&token_stream); - return Some(match span { + return Ok(match span { Some(span) => context.snippet(span).to_owned(), 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) } @@ -1132,9 +1163,9 @@ pub(crate) fn convert_try_mac( pub(crate) fn macro_style(mac: &ast::MacCall, context: &RewriteContext<'_>) -> Delimiter { let snippet = context.snippet(mac.span()); - let paren_pos = snippet.find_uncommented("(").unwrap_or(usize::max_value()); - let bracket_pos = snippet.find_uncommented("[").unwrap_or(usize::max_value()); - let brace_pos = snippet.find_uncommented("{").unwrap_or(usize::max_value()); + let paren_pos = snippet.find_uncommented("(").unwrap_or(usize::MAX); + let bracket_pos = snippet.find_uncommented("[").unwrap_or(usize::MAX); + let brace_pos = snippet.find_uncommented("{").unwrap_or(usize::MAX); if paren_pos < bracket_pos && paren_pos < brace_pos { Delimiter::Parenthesis @@ -1242,23 +1273,31 @@ fn rewrite( context: &RewriteContext<'_>, shape: Shape, multi_branch_style: bool, - ) -> Option { + ) -> RewriteResult { // Only attempt to format function-like macros. if self.args_paren_kind != Delimiter::Parenthesis { // 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 has_block_body = old_body.starts_with('{'); let mut prefix_width = 5; // 5 = " => {" - if context.config.version() == Version::Two { + if context.config.style_edition() >= StyleEdition::Edition2024 { if has_block_body { prefix_width = 6; // 6 = " => {{" } } - let mut result = - format_macro_args(context, self.args.clone(), shape.sub_width(prefix_width)?)?; + let mut result = format_macro_args( + context, + self.args.clone(), + shape + .sub_width(prefix_width) + .max_width_error(shape.width, self.span)?, + )?; if multi_branch_style { result += " =>"; @@ -1267,7 +1306,7 @@ fn rewrite( if !context.config.format_macro_bodies() { result += " "; 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 @@ -1276,7 +1315,8 @@ fn rewrite( // `$$`). We'll try and format like an AST node, but we'll substitute // 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(); config.set().show_parse_errors(false); @@ -1299,13 +1339,21 @@ fn rewrite( config.set().max_width(new_width); match crate::format_code_block(&body_str, &config, true) { 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) { - return None; + return Err(RewriteError::ExceedsMaxWidth { + configured_width: shape.width, + span: self.span, + }); } // Indent the body since it is in a block. @@ -1331,7 +1379,10 @@ fn rewrite( for (old, new) in &substs { if old_body.contains(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); } @@ -1346,7 +1397,7 @@ fn rewrite( result += "}"; - Some(result) + Ok(result) } } @@ -1366,7 +1417,8 @@ fn format_lazy_static( context: &RewriteContext<'_>, shape: Shape, ts: TokenStream, -) -> Option { + span: Span, +) -> RewriteResult { let mut result = String::with_capacity(1024); let nested_shape = shape .block_indent(context.config.tab_spaces()) @@ -1375,7 +1427,8 @@ fn format_lazy_static( result.push_str("lazy_static! {"); result.push_str(&nested_shape.indent.to_string_with_newline(context.config)); - let parsed_elems = parse_lazy_static(context, ts)?; + let parsed_elems = + parse_lazy_static(context, ts).macro_error(MacroErrorKind::ParseFailure, span)?; let last = parsed_elems.len() - 1; for (i, (vis, id, ty, expr)) in parsed_elems.iter().enumerate() { // Rewrite as a static item. @@ -1385,14 +1438,16 @@ fn format_lazy_static( "{}static ref {}: {} =", vis, id, - ty.rewrite(context, nested_shape)? + ty.rewrite_result(context, nested_shape)? )); result.push_str(&rewrite_assign_rhs( context, stmt, &*expr, &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(';'); if i != last { @@ -1403,7 +1458,7 @@ fn format_lazy_static( result.push_str(&shape.indent.to_string_with_newline(context.config)); result.push('}'); - Some(result) + Ok(result) } fn rewrite_macro_with_items( @@ -1415,12 +1470,12 @@ fn rewrite_macro_with_items( original_style: Delimiter, position: MacroPosition, span: Span, -) -> Option { +) -> RewriteResult { let style_to_delims = |style| match style { - Delimiter::Parenthesis => Some(("(", ")")), - Delimiter::Bracket => Some(("[", "]")), - Delimiter::Brace => Some((" {", "}")), - _ => None, + Delimiter::Parenthesis => Ok(("(", ")")), + Delimiter::Bracket => Ok(("[", "]")), + Delimiter::Brace => Ok((" {", "}")), + _ => Err(RewriteError::Unknown), }; let (opener, closer) = style_to_delims(style)?; @@ -1442,7 +1497,7 @@ fn rewrite_macro_with_items( for item in items { let item = match item { MacroArg::Item(item) => item, - _ => return None, + _ => return Err(RewriteError::Unknown), }; 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(closer); result.push_str(trailing_semicolon); - Some(result) + Ok(result) } diff --git a/src/tools/rustfmt/src/matches.rs b/src/tools/rustfmt/src/matches.rs index 30bf6271b2e..8f62648e576 100644 --- a/src/tools/rustfmt/src/matches.rs +++ b/src/tools/rustfmt/src/matches.rs @@ -2,19 +2,19 @@ use std::iter::repeat; -use rustc_ast::{ast, ptr, MatchKind}; +use rustc_ast::{MatchKind, ast, ptr}; use rustc_span::{BytePos, Span}; 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::{Config, ControlBraceStyle, IndentStyle, MatchArmLeadingPipe, Version}; +use crate::config::{Config, ControlBraceStyle, IndentStyle, MatchArmLeadingPipe, StyleEdition}; use crate::expr::{ - format_expr, is_empty_block, is_simple_block, is_unsafe_block, prefer_next_line, rewrite_cond, - ExprType, RhsTactics, + ExprType, RhsTactics, format_expr, is_empty_block, is_simple_block, is_unsafe_block, + prefer_next_line, rewrite_cond, }; -use crate::lists::{itemize_list, write_list, ListFormatting}; -use crate::rewrite::{Rewrite, RewriteContext}; +use crate::lists::{ListFormatting, itemize_list, write_list}; +use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult}; use crate::shape::Shape; use crate::source_map::SpanUtils; use crate::spanned::Spanned; @@ -56,6 +56,10 @@ fn span(&self) -> Span { impl<'a> Rewrite for ArmWrapper<'a> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { rewrite_match_arm( context, self.arm, @@ -74,7 +78,7 @@ pub(crate) fn rewrite_match( span: Span, attrs: &[ast::Attribute], match_kind: MatchKind, -) -> Option { +) -> RewriteResult { // Do not take the rhs overhead from the upper expressions into account // when rewriting match condition. let cond_shape = Shape { @@ -83,10 +87,14 @@ pub(crate) fn rewrite_match( }; // 6 = `match ` let cond_shape = match context.config.indent_style() { - IndentStyle::Visual => cond_shape.shrink_left(6)?, - IndentStyle::Block => cond_shape.offset_left(6)?, + IndentStyle::Visual => cond_shape + .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 block_sep = match context.config.control_brace_style() { ControlBraceStyle::AlwaysNextLine => alt_block_sep, @@ -105,12 +113,13 @@ pub(crate) fn rewrite_match( let inner_attrs_str = if inner_attrs.is_empty() { String::new() } else { - let shape = match context.config.version() { - Version::One => shape, - _ => shape.block_indent(context.config.tab_spaces()), + let shape = if context.config.style_edition() <= StyleEdition::Edition2021 { + shape + } else { + shape.block_indent(context.config.tab_spaces()) }; inner_attrs - .rewrite(context, shape) + .rewrite_result(context, shape) .map(|s| format!("{}{}\n", nested_indent_str, s))? }; @@ -130,16 +139,16 @@ pub(crate) fn rewrite_match( if arms.is_empty() { let snippet = context.snippet(mk_sp(open_brace_pos, span.hi() - BytePos(1))); if snippet.trim().is_empty() { - Some(format!("match {cond_str} {{}}")) + Ok(format!("match {cond_str} {{}}")) } else { // Empty match with comments or inner attributes? We are not going to bother, sorry ;) - Some(context.snippet(span).to_owned()) + Ok(context.snippet(span).to_owned()) } } else { let span_after_cond = mk_sp(cond.span.hi(), span.hi()); match match_kind { - MatchKind::Prefix => Some(format!( + MatchKind::Prefix => Ok(format!( "match {}{}{{\n{}{}{}\n{}}}", cond_str, block_sep, @@ -148,7 +157,7 @@ pub(crate) fn rewrite_match( rewrite_match_arms(context, arms, shape, span_after_cond, open_brace_pos)?, shape.indent.to_string(context.config), )), - MatchKind::Postfix => Some(format!( + MatchKind::Postfix => Ok(format!( "{}.match{}{{\n{}{}{}\n{}}}", cond_str, block_sep, @@ -198,7 +207,7 @@ fn rewrite_match_arms( shape: Shape, span: Span, open_brace_pos: BytePos, -) -> Option { +) -> RewriteResult { let arm_shape = shape .block_indent(context.config.tab_spaces()) .with_max_width(context.config); @@ -218,7 +227,7 @@ fn rewrite_match_arms( "|", |arm| arm.span().lo(), |arm| arm.span().hi(), - |arm| arm.rewrite(context, arm_shape), + |arm| arm.rewrite_result(context, arm_shape), open_brace_pos, span.hi(), false, @@ -238,19 +247,19 @@ fn rewrite_match_arm( shape: Shape, is_last: bool, has_leading_pipe: bool, -) -> Option { +) -> RewriteResult { let (missing_span, attrs_str) = if !arm.attrs.is_empty() { 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. - return Some(format!( + return Ok(format!( "{}{}", context.snippet(arm.span()), arm_comma(context.config, body, is_last), )); } let missing_span = mk_sp(arm.attrs[arm.attrs.len() - 1].span.hi(), arm.pat.span.lo()); - (missing_span, arm.attrs.rewrite(context, shape)?) + (missing_span, arm.attrs.rewrite_result(context, shape)?) } else { (mk_sp(arm.span().lo(), arm.span().lo()), String::new()) }; @@ -264,19 +273,25 @@ fn rewrite_match_arm( }; // 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)) => { // Some block with a label ` => 'label: {` // 7 = ` => : {` let label_len = label.ident.as_str().len(); - shape.sub_width(7 + label_len)?.offset_left(pipe_offset)? + shape + .sub_width(7 + label_len) + .and_then(|s| s.offset_left(pipe_offset)) + .max_width_error(shape.width, arm.span)? } _ => { // 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 let block_like_pat = trimmed_last_line_width(&pats_str) <= context.config.tab_spaces(); @@ -298,10 +313,13 @@ fn rewrite_match_arm( 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( context, - arm.body.as_ref()?, + arm.body.as_ref().unknown_error()?, &lhs_str, shape, guard_str.contains('\n'), @@ -382,7 +400,7 @@ fn rewrite_match_body( has_guard: bool, arrow_span: Span, is_last: bool, -) -> Option { +) -> RewriteResult { let (extend, body) = flatten_arm_body( context, 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 { @@ -420,7 +438,7 @@ fn rewrite_match_body( let arrow_snippet = context.snippet(arrow_span).trim(); // search for the arrow starting from the end of the snippet since there may be a match // expression within the guard - let arrow_index = if context.config.version() == Version::One { + let arrow_index = if context.config.style_edition() <= StyleEdition::Edition2021 { arrow_snippet.rfind("=>").unwrap() } else { arrow_snippet.find_last_uncommented("=>").unwrap() @@ -447,7 +465,7 @@ fn rewrite_match_body( result.push_str(&nested_indent_str); result.push_str(body_str); result.push_str(comma); - return Some(result); + return Ok(result); } let indent_str = shape.indent.to_string_with_newline(context.config); @@ -458,7 +476,7 @@ fn rewrite_match_body( } else { "" }; - let semicolon = if context.config.version() == Version::One { + let semicolon = if context.config.style_edition() <= StyleEdition::Edition2021 { "" } else { if semicolon_for_expr(context, body) { @@ -490,7 +508,7 @@ fn rewrite_match_body( result.push_str(&block_sep); result.push_str(body_str); result.push_str(&body_suffix); - Some(result) + Ok(result) }; // 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) .and_then(|shape| shape.sub_width(comma.len())); let orig_body = if forbid_same_line || !arrow_comment.is_empty() { - None + Err(RewriteError::Unknown) } else if let Some(body_shape) = orig_body_shape { let rewrite = nop_block_collapse( format_expr(body, ExprType::Statement, context, body_shape), @@ -507,7 +525,7 @@ fn rewrite_match_body( ); match rewrite { - Some(ref body_str) + Ok(ref body_str) if is_block || (!body_str.contains('\n') && unicode_str_width(body_str) <= body_shape.width) => @@ -517,7 +535,7 @@ fn rewrite_match_body( _ => rewrite, } } else { - None + Err(RewriteError::Unknown) }; 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, ); 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) => { 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) } - (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) } - (None, Some(ref next_line_str)) => combine_next_line_body(next_line_str), - (None, None) => None, - (Some(ref orig_str), _) => combine_orig_body(orig_str), + (Err(_), Ok(ref next_line_str)) => combine_next_line_body(next_line_str), + // When both orig_body and next_line_body result in errors, we currently propagate the + // 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). pattern_width: usize, multiline_pattern: bool, -) -> Option { +) -> RewriteResult { if let Some(ref guard) = *guard { // First try to fit the guard string on the same line as the pattern. // 4 = ` if `, 5 = ` => {` @@ -563,9 +584,9 @@ fn rewrite_guard( .and_then(|s| s.sub_width(5)); if !multiline_pattern { if let Some(cond_shape) = cond_shape { - if let Some(cond_str) = guard.rewrite(context, cond_shape) { + if let Ok(cond_str) = guard.rewrite_result(context, cond_shape) { 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 = ` => {` let cond_shape = Shape::indented(shape.indent.block_indent(context.config), context.config) .offset_left(3) - .and_then(|s| s.sub_width(5)); - if let Some(cond_shape) = cond_shape { - if let Some(cond_str) = guard.rewrite(context, cond_shape) { - return Some(format!( - "{}if {}", - cond_shape.indent.to_string_with_newline(context.config), - cond_str - )); - } - } - - None + .and_then(|s| s.sub_width(5)) + .max_width_error(shape.width, guard.span)?; + let cond_str = guard.rewrite_result(context, cond_shape)?; + Ok(format!( + "{}if {}", + cond_shape.indent.to_string_with_newline(context.config), + cond_str + )) } else { - Some(String::new()) + Ok(String::new()) } } -fn nop_block_collapse(block_str: Option, budget: usize) -> Option { +fn nop_block_collapse(block_str: RewriteResult, budget: usize) -> RewriteResult { debug!("nop_block_collapse {:?} {}", block_str, budget); block_str.map(|block_str| { if block_str.starts_with('{') diff --git a/src/tools/rustfmt/src/missed_spans.rs b/src/tools/rustfmt/src/missed_spans.rs index 56adb2e5e69..384de1ce9ae 100644 --- a/src/tools/rustfmt/src/missed_spans.rs +++ b/src/tools/rustfmt/src/missed_spans.rs @@ -1,10 +1,10 @@ use rustc_span::{BytePos, Pos, Span}; use tracing::debug; -use crate::comment::{is_last_comment_block, rewrite_comment, CodeCharKind, CommentCodeSlices}; -use crate::config::file_lines::FileLines; +use crate::comment::{CodeCharKind, CommentCodeSlices, is_last_comment_block, rewrite_comment}; 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::shape::{Indent, Shape}; use crate::source_map::LineRangeUtils; @@ -247,7 +247,9 @@ fn process_comment( let indent_str = self.block_indent.to_string(self.config); self.push_str(&indent_str); self.block_indent - } else if self.config.version() == Version::Two && !snippet.starts_with('\n') { + } else if self.config.style_edition() >= StyleEdition::Edition2024 + && !snippet.starts_with('\n') + { // The comment appears on the same line as the previous formatted code. // Assuming that comment is logically associated with that code, we want to keep it on // the same level and avoid mixing it with possible other comment. @@ -283,13 +285,13 @@ fn process_comment( let other_lines = &subslice[offset + 1..]; let comment_str = rewrite_comment(other_lines, false, comment_shape, self.config) - .unwrap_or_else(|| String::from(other_lines)); + .unwrap_or_else(|_| String::from(other_lines)); self.push_str(&comment_str); } } } else { let comment_str = rewrite_comment(subslice, false, comment_shape, self.config) - .unwrap_or_else(|| String::from(subslice)); + .unwrap_or_else(|_| String::from(subslice)); self.push_str(&comment_str); } diff --git a/src/tools/rustfmt/src/modules.rs b/src/tools/rustfmt/src/modules.rs index 0590f28ee05..493b04f16c6 100644 --- a/src/tools/rustfmt/src/modules.rs +++ b/src/tools/rustfmt/src/modules.rs @@ -4,8 +4,8 @@ use rustc_ast::ast; use rustc_ast::visit::Visitor; -use rustc_span::symbol::{self, sym, Symbol}; use rustc_span::Span; +use rustc_span::symbol::{self, Symbol, sym}; use thin_vec::ThinVec; use thiserror::Error; diff --git a/src/tools/rustfmt/src/overflow.rs b/src/tools/rustfmt/src/overflow.rs index cdb735be8a4..fdc8e23e72b 100644 --- a/src/tools/rustfmt/src/overflow.rs +++ b/src/tools/rustfmt/src/overflow.rs @@ -9,22 +9,22 @@ use tracing::debug; use crate::closures; -use crate::config::Version; -use crate::config::{lists::*, Config}; +use crate::config::StyleEdition; +use crate::config::{Config, lists::*}; use crate::expr::{ can_be_overflowed_expr, is_every_expr_simple, is_method_call, is_nested_call, is_simple_expr, rewrite_cond, }; use crate::lists::{ - definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator, + ListFormatting, ListItem, Separator, definitive_tactic, itemize_list, write_list, }; use crate::macros::MacroArg; -use crate::patterns::{can_be_overflowed_pat, TuplePatField}; -use crate::rewrite::{Rewrite, RewriteContext}; +use crate::patterns::{TuplePatField, can_be_overflowed_pat}; +use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult}; use crate::shape::Shape; use crate::source_map::SpanUtils; 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}; /// 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 { 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> { @@ -201,8 +205,12 @@ fn special_cases(&self, config: &Config) -> impl Iterator SPECIAL_CASE_ATTR, _ => &[], }; - let additional_cases = match (self, config.version()) { - (OverflowableItem::MacroArg(..), Version::Two) => SPECIAL_CASE_MACROS_V2, + let additional_cases = match self { + OverflowableItem::MacroArg(..) + if config.style_edition() >= StyleEdition::Edition2024 => + { + SPECIAL_CASE_MACROS_V2 + } _ => &[], }; base_cases.iter().chain(additional_cases) @@ -278,7 +286,7 @@ pub(crate) fn rewrite_with_parens<'a, T: 'a + IntoOverflowableItem<'a>>( span: Span, item_max_width: usize, force_separator_tactic: Option, -) -> Option { +) -> RewriteResult { Context::new( context, items, @@ -300,7 +308,7 @@ pub(crate) fn rewrite_with_angle_brackets<'a, T: 'a + IntoOverflowableItem<'a>>( items: impl Iterator, shape: Shape, span: Span, -) -> Option { +) -> RewriteResult { Context::new( context, items, @@ -324,7 +332,7 @@ pub(crate) fn rewrite_with_square_brackets<'a, T: 'a + IntoOverflowableItem<'a>> span: Span, force_separator_tactic: Option, delim_token: Option, -) -> Option { +) -> RewriteResult { let (lhs, rhs) = match delim_token { Some(Delimiter::Parenthesis) => ("(", ")"), Some(Delimiter::Brace) => ("{", "}"), @@ -428,7 +436,7 @@ fn rewrite_last_item_with_overflow( if closures::args_have_many_closure(&self.items) { None } else { - closures::rewrite_last_closure(self.context, expr, shape) + closures::rewrite_last_closure(self.context, expr, shape).ok() } } @@ -457,7 +465,7 @@ fn rewrite_last_item_with_overflow( if let Some(rewrite) = rewrite { // splitn(2, *).next().unwrap() is always safe. - let rewrite_first_line = Some(rewrite.splitn(2, '\n').next().unwrap().to_owned()); + let rewrite_first_line = Ok(rewrite.splitn(2, '\n').next().unwrap().to_owned()); last_list_item.item = rewrite_first_line; Some(rewrite) } else { @@ -495,7 +503,7 @@ fn try_overflow_last_item(&self, list_items: &mut Vec) -> DefinitiveLi Some(OverflowableItem::MacroArg(MacroArg::Expr(expr))) if !combine_arg_with_callee && 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); } @@ -545,22 +553,23 @@ fn try_overflow_last_item(&self, list_items: &mut Vec) -> DefinitiveLi .and_then(|last_item| last_item.rewrite(self.context, self.nested_shape)); let no_newline = rw.as_ref().map_or(false, |s| !s.contains('\n')); if no_newline { - list_items[self.items.len() - 1].item = rw; + list_items[self.items.len() - 1].item = rw.unknown_error(); } else { - list_items[self.items.len() - 1].item = Some(overflowed.to_owned()); + list_items[self.items.len() - 1].item = Ok(overflowed.to_owned()); } } 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(..)) => { - list_items[self.items.len() - 1].item = placeholder; + list_items[self.items.len() - 1].item = placeholder.unknown_error(); } _ if !self.items.is_empty() => { list_items[self.items.len() - 1].item = self .items .last() - .and_then(|last_item| last_item.rewrite(self.context, self.nested_shape)); + .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 // everything fits in a single line. @@ -613,7 +622,7 @@ fn try_overflow_last_item(&self, list_items: &mut Vec) -> DefinitiveLi tactic } - fn rewrite_items(&self) -> Option<(bool, String)> { + fn rewrite_items(&self) -> Result<(bool, String), RewriteError> { let span = self.items_span(); debug!("items: {:?}", self.items); @@ -624,7 +633,7 @@ fn rewrite_items(&self) -> Option<(bool, String)> { ",", |item| item.span().lo(), |item| item.span().hi(), - |item| item.rewrite(self.context, self.nested_shape), + |item| item.rewrite_result(self.context, self.nested_shape), span.lo(), span.hi(), true, @@ -689,7 +698,8 @@ fn wrap_items(&self, items_str: &str, shape: Shape, is_extendable: bool) -> Stri ); result.push_str(self.ident); 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) } else { // 2 = `()` @@ -711,7 +721,7 @@ fn wrap_items(&self, items_str: &str, shape: Shape, is_extendable: bool) -> Stri result } - fn rewrite(&self, shape: Shape) -> Option { + fn rewrite(&self, shape: Shape) -> RewriteResult { let (extendable, items_str) = self.rewrite_items()?; // If we are using visual indent style and failed to format, retry with block indent. @@ -725,7 +735,7 @@ fn rewrite(&self, shape: Shape) -> Option { return result; } - Some(self.wrap_items(&items_str, shape, extendable)) + Ok(self.wrap_items(&items_str, shape, extendable)) } } diff --git a/src/tools/rustfmt/src/pairs.rs b/src/tools/rustfmt/src/pairs.rs index bfc2ffed383..9c51298416b 100644 --- a/src/tools/rustfmt/src/pairs.rs +++ b/src/tools/rustfmt/src/pairs.rs @@ -1,9 +1,11 @@ use rustc_ast::ast; +use rustc_span::Span; -use crate::config::lists::*; 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::spanned::Spanned; use crate::utils::{ 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, shape: Shape, context: &RewriteContext<'_>, -) -> Option { - expr.flatten(context, shape).and_then(|list| { - if list.let_chain_count() > 0 && !list.can_rewrite_let_chain_single_line() { - rewrite_pairs_multiline(&list, shape, context) - } else { - // First we try formatting on one line. - rewrite_pairs_one_line(&list, shape, context) - .or_else(|| rewrite_pairs_multiline(&list, shape, context)) - } - }) +) -> RewriteResult { + expr.flatten(context, shape) + .unknown_error() + .and_then(|list| { + if list.let_chain_count() > 0 && !list.can_rewrite_let_chain_single_line() { + rewrite_pairs_multiline(&list, shape, context) + } else { + // 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 @@ -65,7 +70,7 @@ fn rewrite_pairs_one_line( let base_shape = shape.block(); 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 { return None; } @@ -104,19 +109,20 @@ fn rewrite_pairs_multiline( list: &PairList<'_, '_, T>, shape: Shape, context: &RewriteContext<'_>, -) -> Option { +) -> RewriteResult { let rhs_offset = shape.rhs_overhead(context.config); let nested_shape = (match context.config.indent_style() { IndentStyle::Visual => shape.visual_indent(0), IndentStyle::Block => shape.block_indent(context.config.tab_spaces()), }) .with_max_width(context.config) - .sub_width(rhs_offset)?; + .sub_width(rhs_offset) + .max_width_error(shape.width, list.span)?; let indent_str = nested_shape.indent.to_string_with_newline(context.config); let mut result = String::new(); - result.push_str(list.list[0].1.as_ref()?); + result.push_str(list.list[0].1.as_ref().map_err(|err| err.clone())?); 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 @@ -132,7 +138,7 @@ fn rewrite_pairs_multiline( if let Some(line_shape) = shape.offset_left(s.len() + 2 + trimmed_last_line_width(&result)) { - if let Some(rewrite) = e.rewrite(context, line_shape) { + if let Ok(rewrite) = e.rewrite_result(context, line_shape) { result.push(' '); result.push_str(s); result.push(' '); @@ -155,9 +161,9 @@ fn rewrite_pairs_multiline( } } - 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. @@ -168,10 +174,10 @@ pub(crate) fn rewrite_pair( context: &RewriteContext<'_>, shape: Shape, separator_place: SeparatorPlace, -) -> Option +) -> RewriteResult where - LHS: Rewrite, - RHS: Rewrite, + LHS: Rewrite + Spanned, + RHS: Rewrite + Spanned, { let tab_spaces = context.config.tab_spaces(); let lhs_overhead = match separator_place { @@ -183,15 +189,17 @@ pub(crate) fn rewrite_pair( ..shape }; let lhs_result = lhs - .rewrite(context, lhs_shape) + .rewrite_result(context, lhs_shape) .map(|lhs_str| format!("{}{}", pp.prefix, lhs_str))?; // Try to put both lhs and rhs on the same line. let rhs_orig_result = shape .offset_left(last_line_width(&lhs_result) + pp.infix.len()) .and_then(|s| s.sub_width(pp.suffix.len())) - .and_then(|rhs_shape| rhs.rewrite(context, rhs_shape)); - if let Some(ref rhs_result) = rhs_orig_result { + .max_width_error(shape.width, rhs.span()) + .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 // the rhs looks like block expression, we put the rhs on the same // line with the lhs even if the rhs is multi-lined. @@ -207,7 +215,7 @@ pub(crate) fn rewrite_pair( + first_line_width(rhs_result) + pp.suffix.len(); if one_line_width <= shape.width { - return Some(format!( + return Ok(format!( "{}{}{}{}", lhs_result, pp.infix, rhs_result, pp.suffix )); @@ -219,13 +227,15 @@ pub(crate) fn rewrite_pair( // Re-evaluate the rhs because we have more space now: let mut rhs_shape = match context.config.indent_style() { IndentStyle::Visual => shape - .sub_width(pp.suffix.len() + pp.prefix.len())? + .sub_width(pp.suffix.len() + pp.prefix.len()) + .max_width_error(shape.width, rhs.span())? .visual_indent(pp.prefix.len()), IndentStyle::Block => { // Try to calculate the initial constraint on the right hand side. let rhs_overhead = shape.rhs_overhead(context.config); Shape::indented(shape.indent.block_indent(context.config), context.config) - .sub_width(rhs_overhead)? + .sub_width(rhs_overhead) + .max_width_error(shape.width, rhs.span())? } }; let infix = match separator_place { @@ -233,15 +243,17 @@ pub(crate) fn rewrite_pair( SeparatorPlace::Front => pp.infix.trim_start(), }; 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 infix_with_sep = match separator_place { SeparatorPlace::Back => format!("{infix}{indent_str}"), SeparatorPlace::Front => format!("{indent_str}{infix}"), }; - Some(format!( + Ok(format!( "{}{}{}{}", lhs_result, infix_with_sep, rhs_result, pp.suffix )) @@ -255,8 +267,9 @@ fn flatten(&self, _: &RewriteContext<'_>, _: Shape) -> Option { - list: Vec<(&'b T, Option)>, + list: Vec<(&'b T, RewriteResult)>, separators: Vec<&'a str>, + span: Span, } fn is_ident(expr: &ast::Expr) -> bool { @@ -303,7 +316,7 @@ fn flatten( let default_rewrite = |node: &ast::Expr, sep: usize, is_first: bool| { if is_first { - return node.rewrite(context, shape); + return node.rewrite_result(context, shape); } let nested_overhead = sep + 1; let rhs_offset = shape.rhs_overhead(context.config); @@ -312,12 +325,17 @@ fn flatten( IndentStyle::Block => shape.block_indent(context.config.tab_spaces()), }) .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() { - SeparatorPlace::Back => nested_shape.sub_width(nested_overhead)?, - SeparatorPlace::Front => nested_shape.offset_left(nested_overhead)?, + SeparatorPlace::Back => nested_shape + .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, @@ -326,6 +344,7 @@ fn flatten( let mut list = vec![]; let mut separators = vec![]; let mut node = self; + let span = self.span(); loop { match node.kind { ast::ExprKind::Binary(op, ref lhs, _) if op.node == top_op => { @@ -352,7 +371,11 @@ fn flatten( } assert_eq!(list.len() - 1, separators.len()); - Some(PairList { list, separators }) + Some(PairList { + list, + separators, + span, + }) } } diff --git a/src/tools/rustfmt/src/parse/macros/asm.rs b/src/tools/rustfmt/src/parse/macros/asm.rs index 6373d0251f8..5ee083da539 100644 --- a/src/tools/rustfmt/src/parse/macros/asm.rs +++ b/src/tools/rustfmt/src/parse/macros/asm.rs @@ -1,5 +1,5 @@ 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; diff --git a/src/tools/rustfmt/src/parse/macros/cfg_if.rs b/src/tools/rustfmt/src/parse/macros/cfg_if.rs index b91d203d531..ec771f96a3f 100644 --- a/src/tools/rustfmt/src/parse/macros/cfg_if.rs +++ b/src/tools/rustfmt/src/parse/macros/cfg_if.rs @@ -1,4 +1,4 @@ -use std::panic::{catch_unwind, AssertUnwindSafe}; +use std::panic::{AssertUnwindSafe, catch_unwind}; use rustc_ast::ast; use rustc_ast::token::{Delimiter, TokenKind}; diff --git a/src/tools/rustfmt/src/parse/macros/mod.rs b/src/tools/rustfmt/src/parse/macros/mod.rs index 8d5f7f90958..7271e73db8d 100644 --- a/src/tools/rustfmt/src/parse/macros/mod.rs +++ b/src/tools/rustfmt/src/parse/macros/mod.rs @@ -1,11 +1,11 @@ use rustc_ast::token::{Delimiter, NonterminalKind, NtExprKind::*, NtPatKind::*, TokenKind}; use rustc_ast::tokenstream::TokenStream; use rustc_ast::{ast, ptr}; -use rustc_parse::parser::{ForceCollect, Parser, Recovery}; use rustc_parse::MACRO_ARGUMENTS; +use rustc_parse::parser::{ForceCollect, Parser, Recovery}; use rustc_session::parse::ParseSess; -use rustc_span::symbol::{self, kw}; use rustc_span::Symbol; +use rustc_span::symbol::{self, kw}; use crate::macros::MacroArg; use crate::rewrite::RewriteContext; diff --git a/src/tools/rustfmt/src/parse/parser.rs b/src/tools/rustfmt/src/parse/parser.rs index 6051241309d..28b4c2b612f 100644 --- a/src/tools/rustfmt/src/parse/parser.rs +++ b/src/tools/rustfmt/src/parse/parser.rs @@ -1,4 +1,4 @@ -use std::panic::{catch_unwind, AssertUnwindSafe}; +use std::panic::{AssertUnwindSafe, catch_unwind}; use std::path::{Path, PathBuf}; use rustc_ast::token::TokenKind; @@ -6,11 +6,11 @@ use rustc_errors::Diag; 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_span::{sym, Span}; +use rustc_span::{Span, sym}; use thin_vec::ThinVec; -use crate::parse::session::ParseSess; use crate::Input; +use crate::parse::session::ParseSess; pub(crate) type DirectoryOwnership = rustc_expand::module::DirOwnership; pub(crate) type ModulePathSuccess = rustc_expand::module::ModulePathSuccess; diff --git a/src/tools/rustfmt/src/parse/session.rs b/src/tools/rustfmt/src/parse/session.rs index 05cf467167c..9f27a05939e 100644 --- a/src/tools/rustfmt/src/parse/session.rs +++ b/src/tools/rustfmt/src/parse/session.rs @@ -2,13 +2,14 @@ use std::sync::atomic::{AtomicBool, Ordering}; 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::{ColorConfig, Diag, DiagCtxt, DiagInner, Level as DiagnosticLevel}; use rustc_session::parse::ParseSess as RawParseSess; use rustc_span::{ + BytePos, Span, source_map::{FilePathMapping, SourceMap}, - symbol, BytePos, Span, + symbol, }; use crate::config::file_lines::LineRange; @@ -394,7 +395,9 @@ fn build_emitter( } 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] diff --git a/src/tools/rustfmt/src/patterns.rs b/src/tools/rustfmt/src/patterns.rs index d8cb26a20f1..6fe2d4a8520 100644 --- a/src/tools/rustfmt/src/patterns.rs +++ b/src/tools/rustfmt/src/patterns.rs @@ -2,22 +2,22 @@ use rustc_ast::ptr; 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::Version; use crate::expr::{can_be_overflowed_expr, rewrite_unary_prefix, wrap_struct_field}; use crate::lists::{ - definitive_tactic, itemize_list, shape_for_tactic, struct_lit_formatting, struct_lit_shape, - struct_lit_tactic, write_list, ListFormatting, ListItem, Separator, + ListFormatting, ListItem, Separator, definitive_tactic, itemize_list, shape_for_tactic, + 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::pairs::{rewrite_pair, PairParts}; -use crate::rewrite::{Rewrite, RewriteContext}; +use crate::pairs::{PairParts, rewrite_pair}; +use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult}; use crate::shape::Shape; use crate::source_map::SpanUtils; 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}; /// 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>); +pub(crate) struct RangeOperand<'a> { + operand: &'a Option>, + pub(crate) span: Span, +} impl<'a> Rewrite for RangeOperand<'a> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { - match &self.0 { - None => Some("".to_owned()), - Some(ref exp) => exp.rewrite(context, shape), + self.rewrite_result(context, shape).ok() + } + + 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 { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { match self.kind { PatKind::Or(ref pats) => { let pat_strs = pats .iter() - .map(|p| p.rewrite(context, shape)) - .collect::>>()?; + .map(|p| p.rewrite_result(context, shape)) + .collect::, RewriteError>>()?; let use_mixed_layout = pats .iter() @@ -115,14 +126,21 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option let sub_pat = match *sub_pat { Some(ref p) => { // 2 - `@ `. - let width = shape.width.checked_sub( - mut_prefix.len() + ref_kw.len() + mut_infix.len() + id_str.len() + 2, - )?; + let width = shape + .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, "@"); combine_strs_with_missing_comments( context, "@", - &p.rewrite(context, Shape::legacy(width, shape.indent))?, + &p.rewrite_result(context, Shape::legacy(width, shape.indent))?, mk_sp(lo, p.span.lo()), shape, true, @@ -207,19 +225,25 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option } PatKind::Wild => { if 1 <= shape.width { - Some("_".to_owned()) + Ok("_".to_owned()) } else { - None + Err(RewriteError::ExceedsMaxWidth { + configured_width: 1, + span: self.span, + }) } } PatKind::Rest => { if 1 <= shape.width { - Some("..".to_owned()) + Ok("..".to_owned()) } 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) => { let infix = match end_kind.node { RangeEnd::Included(RangeSyntax::DotDotDot) => "...", @@ -239,9 +263,17 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option } else { 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( - &RangeOperand(lhs), - &RangeOperand(rhs), + &RangeOperand { + operand: lhs, + span: lspan, + }, + &RangeOperand { + operand: rhs, + span: rspan, + }, PairParts::infix(&infix), context, shape, @@ -260,19 +292,21 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option let path_str = rewrite_path(context, PathContext::Expr, q_self, path, shape)?; rewrite_tuple_pat(pat_vec, Some(path_str), self.span, context, shape) } - PatKind::Lit(ref expr) => expr.rewrite(context, shape), - PatKind::Slice(ref slice_pat) if context.config.version() == Version::One => { + PatKind::Lit(ref expr) => expr.rewrite_result(context, shape), + PatKind::Slice(ref slice_pat) + if context.config.style_edition() <= StyleEdition::Edition2021 => + { let rw: Vec = slice_pat .iter() .map(|p| { - if let Some(rw) = p.rewrite(context, shape) { + if let Ok(rw) = p.rewrite_result(context, shape) { rw } else { context.snippet(p.span).to_string() } }) .collect(); - Some(format!("[{}]", rw.join(", "))) + Ok(format!("[{}]", rw.join(", "))) } PatKind::Slice(ref slice_pat) => overflow::rewrite_with_square_brackets( context, @@ -296,10 +330,16 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option rewrite_macro(mac, None, context, shape, MacroPosition::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)), - PatKind::Err(_) => None, - PatKind::Deref(_) => None, + PatKind::Err(_) => Err(RewriteError::Unknown), + PatKind::Deref(_) => Err(RewriteError::Unknown), } } } @@ -312,20 +352,21 @@ fn rewrite_struct_pat( span: Span, context: &RewriteContext<'_>, shape: Shape, -) -> Option { +) -> RewriteResult { // 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)?; if fields.is_empty() && !ellipsis { - return Some(format!("{path_str} {{}}")); + return Ok(format!("{path_str} {{}}")); } let (ellipsis_str, terminator) = if ellipsis { (", ..", "..") } else { ("", "}") }; // 3 = ` { `, 2 = ` }`. let (h_shape, v_shape) = - struct_lit_shape(shape, context, path_str.len() + 3, ellipsis_str.len() + 2)?; + struct_lit_shape(shape, context, path_str.len() + 3, ellipsis_str.len() + 2) + .max_width_error(shape.width, span)?; let items = itemize_list( context.snippet_provider, @@ -340,7 +381,7 @@ fn rewrite_struct_pat( } }, |f| f.span.hi(), - |f| f.rewrite(context, v_shape), + |f| f.rewrite_result(context, v_shape), context.snippet_provider.span_after(span, "{"), span.hi(), false, @@ -379,11 +420,15 @@ fn rewrite_struct_pat( // ast::Pat doesn't have attrs so use &[] let fields_str = wrap_struct_field(context, &[], &fields_str, shape, v_shape, one_line_width)?; - Some(format!("{path_str} {{{fields_str}}}")) + Ok(format!("{path_str} {{{fields_str}}}")) } impl Rewrite for PatField { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + 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() { last.span.hi() } else { @@ -393,10 +438,10 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option let attrs_str = if self.attrs.is_empty() { String::from("") } 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 { combine_strs_with_missing_comments( context, @@ -417,7 +462,7 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option "{}:\n{}{}", id_str, 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( @@ -440,9 +485,13 @@ pub(crate) enum TuplePatField<'a> { impl<'a> Rewrite for TuplePatField<'a> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { match *self { - TuplePatField::Pat(p) => p.rewrite(context, shape), - TuplePatField::Dotdot(_) => Some("..".to_string()), + TuplePatField::Pat(p) => p.rewrite_result(context, shape), + TuplePatField::Dotdot(_) => Ok("..".to_string()), } } } @@ -492,9 +541,9 @@ fn rewrite_tuple_pat( span: Span, context: &RewriteContext<'_>, shape: Shape, -) -> Option { +) -> RewriteResult { 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(); @@ -548,7 +597,7 @@ fn count_wildcard_suffix_len( ",", |item| item.span().lo(), |item| item.span().hi(), - |item| item.rewrite(context, shape), + |item| item.rewrite_result(context, shape), context.snippet_provider.span_after(span, "("), span.hi() - BytePos(1), false, @@ -558,7 +607,7 @@ fn count_wildcard_suffix_len( for item in items .iter() .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; diff --git a/src/tools/rustfmt/src/reorder.rs b/src/tools/rustfmt/src/reorder.rs index fdbed939af5..8a31e0ac816 100644 --- a/src/tools/rustfmt/src/reorder.rs +++ b/src/tools/rustfmt/src/reorder.rs @@ -9,13 +9,13 @@ use std::cmp::Ordering; use rustc_ast::{ast, attr}; -use rustc_span::{symbol::sym, Span}; +use rustc_span::{Span, symbol::sym}; 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::lists::{itemize_list, write_list, ListFormatting, ListItem}; -use crate::rewrite::RewriteContext; +use crate::lists::{ListFormatting, ListItem, itemize_list, write_list}; +use crate::rewrite::{RewriteContext, RewriteErrorExt}; use crate::shape::Shape; use crate::source_map::LineRangeUtils; use crate::spanned::Spanned; @@ -59,7 +59,7 @@ fn wrap_reorderable_items( let fmt = ListFormatting::new(shape, context.config) .separator("") .align_comments(false); - write_list(list_items, &fmt) + write_list(list_items, &fmt).ok() } fn rewrite_reorderable_item( @@ -99,7 +99,7 @@ fn rewrite_reorderable_or_regroupable_items( ";", |item| item.span().lo(), |item| item.span().hi(), - |_item| Some("".to_owned()), + |_item| Ok("".to_owned()), span.lo(), span.hi(), false, @@ -131,9 +131,16 @@ fn rewrite_reorderable_or_regroupable_items( .map(|use_group| { let item_vec: Vec<_> = use_group .into_iter() - .map(|use_tree| ListItem { - item: use_tree.rewrite_top_level(context, nested_shape), - ..use_tree.list_item.unwrap_or_else(ListItem::empty) + .map(|use_tree| { + let item = use_tree.rewrite_top_level(context, nested_shape); + if let Some(list_item) = use_tree.list_item { + ListItem { + item: item, + ..list_item + } + } else { + ListItem::from_item(item) + } }) .collect(); 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().hi(), - |item| rewrite_reorderable_item(context, item, shape), + |item| rewrite_reorderable_item(context, item, shape).unknown_error(), span.lo(), span.hi(), false, diff --git a/src/tools/rustfmt/src/rewrite.rs b/src/tools/rustfmt/src/rewrite.rs index e2498a3500a..83020709797 100644 --- a/src/tools/rustfmt/src/rewrite.rs +++ b/src/tools/rustfmt/src/rewrite.rs @@ -5,17 +5,23 @@ use rustc_ast::ptr; use rustc_span::Span; +use thiserror::Error; +use crate::FormatReport; use crate::config::{Config, IndentStyle}; use crate::parse::session::ParseSess; use crate::shape::Shape; use crate::skip::SkipContext; use crate::visitor::SnippetProvider; -use crate::FormatReport; +pub(crate) type RewriteResult = Result; pub(crate) trait Rewrite { /// Rewrite self into shape. fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option; + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { + self.rewrite(context, shape).unknown_error() + } } impl Rewrite for ptr::P { @@ -24,6 +30,66 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option } } +#[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 { + fn max_width_error(self, width: usize, span: Span) -> Result; + fn macro_error(self, kind: MacroErrorKind, span: Span) -> Result; + fn unknown_error(self) -> Result; +} + +impl RewriteErrorExt for Option { + fn max_width_error(self, width: usize, span: Span) -> Result { + self.ok_or_else(|| RewriteError::ExceedsMaxWidth { + configured_width: width, + span: span, + }) + } + + fn macro_error(self, kind: MacroErrorKind, span: Span) -> Result { + self.ok_or_else(|| RewriteError::MacroFailure { + kind: kind, + span: span, + }) + } + + fn unknown_error(self) -> Result { + self.ok_or_else(|| RewriteError::Unknown) + } +} + #[derive(Clone)] pub(crate) struct RewriteContext<'a> { pub(crate) psess: &'a ParseSess, diff --git a/src/tools/rustfmt/src/rustfmt_diff.rs b/src/tools/rustfmt/src/rustfmt_diff.rs index c9883452185..4624683fa05 100644 --- a/src/tools/rustfmt/src/rustfmt_diff.rs +++ b/src/tools/rustfmt/src/rustfmt_diff.rs @@ -282,7 +282,7 @@ pub(crate) fn print_diff(diff: Vec, get_section_title: F, config: & #[cfg(test)] mod test { use super::DiffLine::*; - use super::{make_diff, Mismatch}; + use super::{Mismatch, make_diff}; use super::{ModifiedChunk, ModifiedLines}; #[test] diff --git a/src/tools/rustfmt/src/sort.rs b/src/tools/rustfmt/src/sort.rs new file mode 100644 index 00000000000..670f664a119 --- /dev/null +++ b/src/tools/rustfmt/src/sort.rs @@ -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> { + 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::().ok()?; + + Some(VersionChunk::Number { + value, + zeros, + source, + }) + } + + fn parse_str_chunk( + &mut self, + mut chars: std::str::CharIndices<'a>, + ) -> Option> { + 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 { + 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) + } +} diff --git a/src/tools/rustfmt/src/source_file.rs b/src/tools/rustfmt/src/source_file.rs index 5eea8021b32..73f8ecb5529 100644 --- a/src/tools/rustfmt/src/source_file.rs +++ b/src/tools/rustfmt/src/source_file.rs @@ -2,10 +2,10 @@ use std::io::{self, Write}; use std::path::Path; +use crate::NewlineStyle; use crate::config::FileName; use crate::emitter::{self, Emitter}; use crate::parse::session::ParseSess; -use crate::NewlineStyle; #[cfg(test)] use crate::config::Config; diff --git a/src/tools/rustfmt/src/spanned.rs b/src/tools/rustfmt/src/spanned.rs index 1ee691b4ade..b4424e476ee 100644 --- a/src/tools/rustfmt/src/spanned.rs +++ b/src/tools/rustfmt/src/spanned.rs @@ -1,9 +1,10 @@ use std::cmp::max; use rustc_ast::{ast, ptr}; -use rustc_span::{source_map, Span}; +use rustc_span::{Span, source_map}; use crate::macros::MacroArg; +use crate::patterns::RangeOperand; use crate::utils::{mk_sp, outer_attributes}; /// Spanned returns a span including attributes, if available. @@ -212,3 +213,9 @@ fn span(&self) -> Span { } } } + +impl<'a> Spanned for RangeOperand<'a> { + fn span(&self) -> Span { + self.span + } +} diff --git a/src/tools/rustfmt/src/stmt.rs b/src/tools/rustfmt/src/stmt.rs index 73a9cce416c..2788159018d 100644 --- a/src/tools/rustfmt/src/stmt.rs +++ b/src/tools/rustfmt/src/stmt.rs @@ -2,9 +2,9 @@ use rustc_span::Span; use crate::comment::recover_comment_removed; -use crate::config::Version; -use crate::expr::{format_expr, is_simple_block, ExprType}; -use crate::rewrite::{Rewrite, RewriteContext}; +use crate::config::StyleEdition; +use crate::expr::{ExprType, format_expr, is_simple_block}; +use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult}; use crate::shape::Shape; use crate::source_map::LineRangeUtils; use crate::spanned::Spanned; @@ -90,11 +90,20 @@ fn is_last_expr(&self) -> bool { impl<'a> Rewrite for Stmt<'a> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { - let expr_type = if context.config.version() == Version::Two && self.is_last_expr() { - ExprType::SubExpression - } else { - ExprType::Statement - }; + self.rewrite_result(context, shape).ok() + } + + 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( context, shape, @@ -111,11 +120,11 @@ fn format_stmt( stmt: &ast::Stmt, expr_type: ExprType, is_last_expr: bool, -) -> Option { - skip_out_of_file_lines_range!(context, stmt.span()); +) -> RewriteResult { + skip_out_of_file_lines_range_err!(context, stmt.span()); 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) => { 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) } - 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)) } diff --git a/src/tools/rustfmt/src/string.rs b/src/tools/rustfmt/src/string.rs index cb666fff695..3b971188cd5 100644 --- a/src/tools/rustfmt/src/string.rs +++ b/src/tools/rustfmt/src/string.rs @@ -84,7 +84,7 @@ pub(crate) fn rewrite_string<'a>( stripped_str .len() .checked_next_power_of_two() - .unwrap_or(usize::max_value()), + .unwrap_or(usize::MAX), ); result.push_str(fmt.opener); @@ -375,7 +375,7 @@ fn graphemes_width(graphemes: &[&str]) -> usize { #[cfg(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::shape::{Indent, Shape}; use unicode_segmentation::UnicodeSegmentation; diff --git a/src/tools/rustfmt/src/test/configuration_snippet.rs b/src/tools/rustfmt/src/test/configuration_snippet.rs index e4a390ada66..5b93703de0b 100644 --- a/src/tools/rustfmt/src/test/configuration_snippet.rs +++ b/src/tools/rustfmt/src/test/configuration_snippet.rs @@ -4,9 +4,9 @@ use std::iter::Enumerate; 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::rustfmt_diff::{make_diff, Mismatch}; +use crate::rustfmt_diff::{Mismatch, make_diff}; use crate::{Input, Session}; const CONFIGURATIONS_FILE_NAME: &str = "Configurations.md"; diff --git a/src/tools/rustfmt/src/test/mod.rs b/src/tools/rustfmt/src/test/mod.rs index 286e8b8760a..d62da08fff8 100644 --- a/src/tools/rustfmt/src/test/mod.rs +++ b/src/tools/rustfmt/src/test/mod.rs @@ -6,14 +6,17 @@ use std::mem; use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; -use std::str::Chars; +use std::str::{Chars, FromStr}; use std::thread; use crate::config::{Color, Config, EmitMode, FileName, NewlineStyle}; use crate::formatting::{ReportedErrors, SourceFile}; -use crate::rustfmt_diff::{make_diff, print_diff, DiffLine, Mismatch, ModifiedChunk, OutputWriter}; +use crate::rustfmt_diff::{DiffLine, Mismatch, ModifiedChunk, OutputWriter, make_diff, print_diff}; 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 tracing::{debug, warn}; @@ -102,10 +105,9 @@ fn is_file_skip(path: &Path) -> bool { fn get_test_files(path: &Path, recursive: bool) -> Vec { let mut files = vec![]; if path.is_dir() { - for entry in fs::read_dir(path).expect(&format!( - "couldn't read directory {}", - path.to_str().unwrap() - )) { + for entry in + fs::read_dir(path).expect(&format!("couldn't read directory {}", path.display())) + { let entry = entry.expect("couldn't get `DirEntry`"); let path = entry.path(); if path.is_dir() && recursive { @@ -119,10 +121,7 @@ fn get_test_files(path: &Path, recursive: bool) -> Vec { } fn verify_config_used(path: &Path, config_name: &str) { - for entry in fs::read_dir(path).expect(&format!( - "couldn't read {} directory", - path.to_str().unwrap() - )) { + for entry in fs::read_dir(path).expect(&format!("couldn't read {} directory", path.display())) { let entry = entry.expect("couldn't get directory entry"); let path = entry.path(); if path.extension().map_or(false, |f| f == "rs") { @@ -711,13 +710,24 @@ fn print_mismatches String>( fn read_config(filename: &Path) -> Config { 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 // that. Otherwise, if there are no significant comments at all, look for a config file with // the same name as the test file. let mut config = if !sig_comments.is_empty() { - get_config(sig_comments.get("config").map(Path::new)) + get_config( + sig_comments.get("config").map(Path::new), + edition, + style_edition, + version, + ) } 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 { @@ -748,13 +758,31 @@ enum IdempotentCheckError { Parse, } +fn get_editions_from_comments( + comments: &HashMap, +) -> (Option, Option, Option) { + ( + 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( filename: &PathBuf, opt_config: &Option, ) -> Result { let sig_comments = read_significant_comments(filename); let config = if let Some(ref config_file_path) = opt_config { - Config::from_toml_path(config_file_path).expect("`rustfmt.toml` not found") + 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 { 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 // file doesn't exist, just return the default config. Otherwise, the file must be read // successfully. -fn get_config(config_file: Option<&Path>) -> Config { +fn get_config( + config_file: Option<&Path>, + edition: Option, + style_edition: Option, + version: Option, +) -> Config { 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) => { let mut full_path = PathBuf::from("tests/config/"); full_path.push(file_name); if !full_path.exists() { - return Default::default(); + return Config::default_for_possible_style_edition(style_edition, edition, version); }; full_path } @@ -797,7 +830,14 @@ fn get_config(config_file: Option<&Path>) -> Config { .read_to_string(&mut def_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. diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs index 7730aa467ce..07b483b2b37 100644 --- a/src/tools/rustfmt/src/types.rs +++ b/src/tools/rustfmt/src/types.rs @@ -2,23 +2,23 @@ use rustc_ast::ast::{self, FnRetTy, Mutability, Term}; use rustc_ast::ptr; -use rustc_span::{symbol::kw, BytePos, Pos, Span}; +use rustc_span::{BytePos, Pos, Span, symbol::kw}; use tracing::debug; use crate::comment::{combine_strs_with_missing_comments, contains_comment}; use crate::config::lists::*; -use crate::config::{IndentStyle, TypeDensity, Version}; +use crate::config::{IndentStyle, StyleEdition, TypeDensity}; use crate::expr::{ - format_expr, rewrite_assign_rhs, rewrite_call, rewrite_tuple, rewrite_unary_prefix, ExprType, - RhsAssignKind, + ExprType, RhsAssignKind, format_expr, rewrite_assign_rhs, rewrite_call, rewrite_tuple, + rewrite_unary_prefix, }; 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::pairs::{rewrite_pair, PairParts}; -use crate::rewrite::{Rewrite, RewriteContext}; +use crate::pairs::{PairParts, rewrite_pair}; +use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult}; use crate::shape::Shape; use crate::source_map::SpanUtils; use crate::spanned::Spanned; @@ -41,7 +41,7 @@ pub(crate) fn rewrite_path( qself: &Option>, path: &ast::Path, shape: Shape, -) -> Option { +) -> RewriteResult { 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 @@ -57,7 +57,7 @@ pub(crate) fn rewrite_path( if let Some(qself) = qself { result.push('<'); - let fmt_ty = qself.ty.rewrite(context, shape)?; + let fmt_ty = qself.ty.rewrite_result(context, shape)?; result.push_str(&fmt_ty); if skip_count > 0 { @@ -67,7 +67,7 @@ pub(crate) fn rewrite_path( } // 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( PathContext::Type, @@ -103,7 +103,7 @@ fn rewrite_path_segments<'a, I>( span_hi: BytePos, context: &RewriteContext<'_>, shape: Shape, -) -> Option +) -> RewriteResult where I: Iterator, { @@ -122,7 +122,9 @@ fn rewrite_path_segments<'a, I>( } 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( path_context, segment, @@ -135,7 +137,7 @@ fn rewrite_path_segments<'a, I>( buffer.push_str(&segment_string); } - Some(buffer) + Ok(buffer) } #[derive(Debug)] @@ -169,19 +171,27 @@ fn span(&self) -> Span { impl<'a> Rewrite for SegmentParam<'a> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { match *self { - SegmentParam::Const(const_) => const_.rewrite(context, shape), - SegmentParam::LifeTime(lt) => lt.rewrite(context, shape), - SegmentParam::Type(ty) => ty.rewrite(context, shape), - SegmentParam::Binding(atc) => atc.rewrite(context, shape), + SegmentParam::Const(const_) => const_.rewrite_result(context, shape), + SegmentParam::LifeTime(lt) => lt.rewrite_result(context, shape), + SegmentParam::Type(ty) => ty.rewrite_result(context, shape), + SegmentParam::Binding(atc) => atc.rewrite_result(context, shape), } } } impl Rewrite for ast::PreciseCapturingArg { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { match self { - ast::PreciseCapturingArg::Lifetime(lt) => lt.rewrite(context, shape), + ast::PreciseCapturingArg::Lifetime(lt) => lt.rewrite_result(context, shape), ast::PreciseCapturingArg::Arg(p, _) => { rewrite_path(context, PathContext::Type, &None, p, shape) } @@ -191,13 +201,20 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option impl Rewrite for ast::AssocItemConstraint { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { use ast::AssocItemConstraintKind::{Bound, Equality}; let mut result = String::with_capacity(128); result.push_str(rewrite_ident(context, self.ident)); if let Some(ref gen_args) = self.gen_args { - let budget = shape.width.checked_sub(result.len())?; + let budget = shape + .width + .checked_sub(result.len()) + .max_width_error(shape.width, self.span)?; let shape = Shape::legacy(budget, shape.indent + result.len()); let gen_str = rewrite_generic_args(gen_args, context, shape, gen_args.span())?; result.push_str(&gen_str); @@ -210,23 +227,30 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option }; 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 rewrite = self.kind.rewrite(context, shape)?; + let rewrite = self.kind.rewrite_result(context, shape)?; result.push_str(&rewrite); - Some(result) + Ok(result) } } impl Rewrite for ast::AssocItemConstraintKind { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { match self { ast::AssocItemConstraintKind::Equality { term } => match term { - Term::Ty(ty) => ty.rewrite(context, shape), - Term::Const(c) => c.rewrite(context, shape), + Term::Ty(ty) => ty.rewrite_result(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, context: &RewriteContext<'_>, shape: Shape, -) -> Option { +) -> RewriteResult { let mut result = String::with_capacity(128); result.push_str(rewrite_ident(context, segment.ident)); let ident_len = result.len(); let shape = if context.use_block_indent() { - shape.offset_left(ident_len)? + shape.offset_left(ident_len) } 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 { 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) } - Some(result) + Ok(result) } fn format_function_type<'a, I>( @@ -298,7 +323,7 @@ fn format_function_type<'a, I>( span: Span, context: &RewriteContext<'_>, shape: Shape, -) -> Option +) -> RewriteResult where I: ExactSizeIterator, ::Item: Deref, @@ -308,12 +333,12 @@ fn format_function_type<'a, I>( let ty_shape = match context.config.indent_style() { // 4 = " -> " - IndentStyle::Block => shape.offset_left(4)?, - IndentStyle::Visual => shape.block_left(4)?, + IndentStyle::Block => shape.offset_left(4).max_width_error(shape.width, span)?, + IndentStyle::Visual => shape.block_left(4).max_width_error(shape.width, span)?, }; let output = match *output { FnRetTy::Ty(ref ty) => { - let type_str = ty.rewrite(context, ty_shape)?; + let type_str = ty.rewrite_result(context, ty_shape)?; format!(" -> {type_str}") } FnRetTy::Default(..) => String::new(), @@ -326,7 +351,10 @@ fn format_function_type<'a, I>( ) } else { // 2 for () - let budget = shape.width.checked_sub(2)?; + let budget = shape + .width + .checked_sub(2) + .max_width_error(shape.width, span)?; // 1 for ( let offset = shape.indent + 1; Shape::legacy(budget, offset) @@ -339,7 +367,8 @@ fn format_function_type<'a, I>( let list_hi = context.snippet_provider.span_before(span, ")"); let comment = context .snippet_provider - .span_to_snippet(mk_sp(list_lo, list_hi))? + .span_to_snippet(mk_sp(list_lo, list_hi)) + .unknown_error()? .trim(); let comment = if comment.starts_with("//") { format!( @@ -360,7 +389,7 @@ fn format_function_type<'a, I>( ",", |arg| arg.span().lo(), |arg| arg.span().hi(), - |arg| arg.rewrite(context, list_shape), + |arg| arg.rewrite_result(context, list_shape), list_lo, span.hi(), false, @@ -396,9 +425,9 @@ fn format_function_type<'a, I>( ) }; if output.is_empty() || last_line_width(&args) + first_line_width(&output) <= shape.width { - Some(format!("{args}{output}")) + Ok(format!("{args}{output}")) } else { - Some(format!( + Ok(format!( "{}\n{}{}", args, 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 { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { // FIXME: dead spans? let result = match *self { ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate { @@ -437,7 +470,7 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option 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 lhs = if let Some(binder_str) = rewrite_bound_params(context, shape, bound_generic_params) @@ -452,28 +485,34 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate { ref lifetime, ref bounds, - .. - }) => rewrite_bounded_lifetime(lifetime, bounds, context, shape)?, + span, + }) => rewrite_bounded_lifetime(lifetime, bounds, span, context, shape)?, ast::WherePredicate::EqPredicate(ast::WhereEqPredicate { ref lhs_ty, ref rhs_ty, .. }) => { - let lhs_ty_str = lhs_ty.rewrite(context, shape).map(|lhs| lhs + " =")?; + 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)? } }; - Some(result) + Ok(result) } } impl Rewrite for ast::GenericArg { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { match *self { - ast::GenericArg::Lifetime(ref lt) => lt.rewrite(context, shape), - ast::GenericArg::Type(ref ty) => ty.rewrite(context, shape), - ast::GenericArg::Const(ref const_) => const_.rewrite(context, shape), + ast::GenericArg::Lifetime(ref lt) => lt.rewrite_result(context, shape), + ast::GenericArg::Type(ref ty) => ty.rewrite_result(context, shape), + ast::GenericArg::Const(ref const_) => const_.rewrite_result(context, shape), } } } @@ -483,11 +522,11 @@ fn rewrite_generic_args( context: &RewriteContext<'_>, shape: Shape, span: Span, -) -> Option { +) -> RewriteResult { match gen_args { ast::GenericArgs::AngleBracketed(ref data) => { if data.args.is_empty() { - Some("".to_owned()) + Ok("".to_owned()) } else { let args = data .args @@ -513,47 +552,63 @@ fn rewrite_generic_args( context, shape, ), - ast::GenericArgs::ParenthesizedElided(..) => Some("(..)".to_owned()), + ast::GenericArgs::ParenthesizedElided(..) => Ok("(..)".to_owned()), } } fn rewrite_bounded_lifetime( lt: &ast::Lifetime, bounds: &[ast::GenericBound], + span: Span, context: &RewriteContext<'_>, shape: Shape, -) -> Option { - let result = lt.rewrite(context, shape)?; +) -> RewriteResult { + let result = lt.rewrite_result(context, shape)?; if bounds.is_empty() { - Some(result) + Ok(result) } else { let colon = type_bound_colon(context); let overhead = last_line_width(&result) + colon.len(); + let shape = shape + .sub_width(overhead) + .max_width_error(shape.width, span)?; let result = format!( "{}{}{}", result, 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 { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { format_expr(&self.value, ExprType::SubExpression, context, shape) } } impl Rewrite for ast::Lifetime { - fn rewrite(&self, context: &RewriteContext<'_>, _: Shape) -> Option { - Some(context.snippet(self.ident.span).to_owned()) + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + 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 { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { match *self { ast::GenericBound::Trait( ref poly_trait_ref, @@ -574,24 +629,30 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option asyncness.push(' '); } 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 - .rewrite(context, shape) + .rewrite_result(context, shape) .map(|s| format!("{constness}{asyncness}{polarity}{s}")) .map(|s| if has_paren { format!("({})", s) } else { s }) } ast::GenericBound::Use(ref args, 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 { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { if self.is_empty() { - return Some(String::new()); + return Ok(String::new()); } join_bounds(context, shape, self, true) @@ -600,8 +661,15 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option impl Rewrite for ast::GenericParam { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + 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. - 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 mut param = String::with_capacity(128); @@ -615,15 +683,19 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option param.push_str("const "); param.push_str(rewrite_ident(context, self.ident)); param.push_str(": "); - param.push_str(&ty.rewrite(context, shape)?); + param.push_str(&ty.rewrite_result(context, shape)?); if let Some(default) = default { let eq_str = match context.config.type_punctuation_density() { TypeDensity::Compressed => "=", TypeDensity::Wide => " = ", }; param.push_str(eq_str); - let budget = shape.width.checked_sub(param.len())?; - let rewrite = default.rewrite(context, Shape::legacy(budget, shape.indent))?; + let budget = shape + .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); } kw_span.lo() @@ -634,7 +706,7 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option if !self.bounds.is_empty() { 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 { default: Some(ref def), @@ -645,9 +717,12 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option TypeDensity::Wide => " = ", }; 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 = - 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); } @@ -673,44 +748,67 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option result.push_str(¶m); } - Some(result) + Ok(result) } } impl Rewrite for ast::PolyTraitRef { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + 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) { // 6 is "for<> ".len() let extra_offset = lifetime_str.len() + 6; - let path_str = self - .trait_ref - .rewrite(context, shape.offset_left(extra_offset)?)?; + let shape = 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 { - self.trait_ref.rewrite(context, shape) + self.trait_ref.rewrite_result(context, shape) } } } impl Rewrite for ast::TraitRef { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { rewrite_path(context, PathContext::Type, &None, &self.path, shape) } } impl Rewrite for ast::Ty { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { match self.kind { ast::TyKind::TraitObject(ref bounds, tobj_syntax) => { // we have to consider 'dyn' keyword is used or not!!! let (shape, prefix) = match tobj_syntax { - ast::TraitObjectSyntax::Dyn => (shape.offset_left(4)?, "dyn "), - ast::TraitObjectSyntax::DynStar => (shape.offset_left(5)?, "dyn* "), + ast::TraitObjectSyntax::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, ""), }; - 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. if context.inside_macro() && bounds.len() == 1 @@ -719,7 +817,7 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { res.push('+'); } - Some(format!("{prefix}{res}")) + Ok(format!("{prefix}{res}")) } ast::TyKind::Ptr(ref mt) => { let prefix = match mt.mutbl { @@ -738,8 +836,11 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option let mut cmnt_lo = ref_hi; if let Some(ref lifetime) = *lifetime { - let lt_budget = shape.width.checked_sub(2 + mut_len)?; - let lt_str = lifetime.rewrite( + let lt_budget = shape + .width + .checked_sub(2 + mut_len) + .max_width_error(shape.width, self.span())?; + let lt_str = lifetime.rewrite_result( context, Shape::legacy(lt_budget, shape.indent + 2 + mut_len), )?; @@ -783,39 +884,46 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option result = combine_strs_with_missing_comments( context, result.trim_end(), - &mt.ty.rewrite(context, shape)?, + &mt.ty.rewrite_result(context, shape)?, before_ty_span, shape, true, )?; } else { let used_width = last_line_width(&result); - let budget = shape.width.checked_sub(used_width)?; - let ty_str = mt - .ty - .rewrite(context, Shape::legacy(budget, shape.indent + used_width))?; + let budget = shape + .width + .checked_sub(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); } - Some(result) + Ok(result) } // FIXME: we drop any comments here, even though it's a silly place to put // comments. ast::TyKind::Paren(ref ty) => { - if context.config.version() == Version::One + if context.config.style_edition() <= StyleEdition::Edition2021 || 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 - .rewrite(context, Shape::legacy(budget, shape.indent + 1)) + .rewrite_result(context, Shape::legacy(budget, shape.indent + 1)) .map(|ty_str| format!("({})", ty_str)); } // 2 = () if let Some(sh) = shape.sub_width(2) { - if let Some(ref s) = ty.rewrite(context, sh) { + if let Ok(ref s) = ty.rewrite_result(context, sh) { if !s.contains('\n') { - return Some(format!("({s})")); + return Ok(format!("({s})")); } } } @@ -824,8 +932,8 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option let shape = shape .block_indent(context.config.tab_spaces()) .with_max_width(context.config); - let rw = ty.rewrite(context, shape)?; - Some(format!( + let rw = ty.rewrite_result(context, shape)?; + Ok(format!( "({}{}{})", shape.to_string_with_newline(context.config), rw, @@ -833,15 +941,18 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option )) } ast::TyKind::Slice(ref ty) => { - let budget = shape.width.checked_sub(4)?; - ty.rewrite(context, Shape::legacy(budget, shape.indent + 1)) + let budget = shape + .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)) } ast::TyKind::Tup(ref items) => { rewrite_tuple(context, items.iter(), self.span, shape, items.len() == 1) } - ast::TyKind::AnonStruct(..) => Some(context.snippet(self.span).to_owned()), - ast::TyKind::AnonUnion(..) => Some(context.snippet(self.span).to_owned()), + ast::TyKind::AnonStruct(..) => Ok(context.snippet(self.span).to_owned()), + ast::TyKind::AnonUnion(..) => Ok(context.snippet(self.span).to_owned()), ast::TyKind::Path(ref q_self, ref path) => { rewrite_path(context, PathContext::Type, q_self, path, shape) } @@ -855,24 +966,27 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option ), ast::TyKind::Infer => { if shape.width >= 1 { - Some("_".to_owned()) + Ok("_".to_owned()) } 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::Never => Some(String::from("!")), + ast::TyKind::Never => Ok(String::from("!")), ast::TyKind::MacCall(ref mac) => { 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) => { // Empty trait is not a parser error. if it.is_empty() { - return Some("impl".to_owned()); + return Ok("impl".to_owned()); } - let rw = if context.config.version() == Version::One { - it.rewrite(context, shape) + let rw = if context.config.style_edition() <= StyleEdition::Edition2021 { + it.rewrite_result(context, shape) } else { join_bounds(context, shape, it, false) }; @@ -881,8 +995,8 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option format!("impl{}{}", space, it_str) }) } - ast::TyKind::CVarArgs => Some("...".to_owned()), - ast::TyKind::Dummy | ast::TyKind::Err(_) => Some(context.snippet(self.span).to_owned()), + ast::TyKind::CVarArgs => Ok("...".to_owned()), + ast::TyKind::Dummy | ast::TyKind::Err(_) => Ok(context.snippet(self.span).to_owned()), ast::TyKind::Typeof(ref anon_const) => rewrite_call( context, "typeof", @@ -891,9 +1005,9 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option shape, ), ast::TyKind::Pat(ref ty, ref pat) => { - let ty = ty.rewrite(context, shape)?; - let pat = pat.rewrite(context, shape)?; - Some(format!("{ty} is {pat}")) + let ty = ty.rewrite_result(context, shape)?; + let pat = pat.rewrite_result(context, shape)?; + Ok(format!("{ty} is {pat}")) } } } @@ -904,7 +1018,7 @@ fn rewrite_bare_fn( span: Span, context: &RewriteContext<'_>, shape: Shape, -) -> Option { +) -> RewriteResult { debug!("rewrite_bare_fn {:#?}", shape); let mut result = String::with_capacity(128); @@ -928,9 +1042,14 @@ fn rewrite_bare_fn( result.push_str("fn"); 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 { - 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( @@ -944,7 +1063,7 @@ fn rewrite_bare_fn( result.push_str(&rewrite); - Some(result) + Ok(result) } fn is_generic_bounds_in_order(generic_bounds: &[ast::GenericBound]) -> bool { @@ -968,7 +1087,7 @@ fn join_bounds( shape: Shape, items: &[ast::GenericBound], need_indent: bool, -) -> Option { +) -> RewriteResult { join_bounds_inner(context, shape, items, need_indent, false) } @@ -978,7 +1097,7 @@ fn join_bounds_inner( items: &[ast::GenericBound], need_indent: bool, force_newline: bool, -) -> Option { +) -> RewriteResult { debug_assert!(!items.is_empty()); 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 bound_str = item.rewrite(context, shape)?; + let bound_str = item.rewrite_result(context, shape)?; (is_bound_extendable(&bound_str, item), bound_str) } else { - let bound_str = &item.rewrite(context, shape)?; + let bound_str = &item.rewrite_result(context, shape)?; match leading_span { Some(ls) if has_leading_comment => ( is_bound_extendable(bound_str, item), @@ -1100,7 +1219,7 @@ fn join_bounds_inner( true, ) .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; // or the single item is of type `Trait`, // and any of the internal arrays contains more than one item; - let retry_with_force_newline = match context.config.version() { - Version::One => { + let retry_with_force_newline = match context.config.style_edition() { + style_edition @ _ if style_edition <= StyleEdition::Edition2021 => { !force_newline && items.len() > 1 && (result.0.contains('\n') || result.0.len() > shape.width) } - Version::Two if force_newline => false, - Version::Two if (!result.0.contains('\n') && result.0.len() <= shape.width) => false, - Version::Two if items.len() > 1 => true, - Version::Two => is_item_with_multi_items_array(&items[0]), + _ if force_newline => false, + _ if (!result.0.contains('\n') && result.0.len() <= shape.width) => false, + _ if items.len() > 1 => true, + _ => is_item_with_multi_items_array(&items[0]), }; if retry_with_force_newline { join_bounds_inner(context, shape, items, need_indent, true) } else { - Some(result.0) + Ok(result.0) } } diff --git a/src/tools/rustfmt/src/utils.rs b/src/tools/rustfmt/src/utils.rs index fd59aedadfe..d1cfc6acc49 100644 --- a/src/tools/rustfmt/src/utils.rs +++ b/src/tools/rustfmt/src/utils.rs @@ -6,11 +6,11 @@ }; use rustc_ast::ptr; 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 crate::comment::{filter_normal_code, CharClasses, FullCodeCharKind, LineClasses}; -use crate::config::{Config, Version}; +use crate::comment::{CharClasses, FullCodeCharKind, LineClasses, filter_normal_code}; +use crate::config::{Config, StyleEdition}; use crate::rewrite::RewriteContext; 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) => { 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 let new_veto_trim_value = (kind == FullCodeCharKind::InString - || (config.version() == Version::Two + || (config.style_edition() >= StyleEdition::Edition2024 && kind == FullCodeCharKind::InStringCommented)) && !line.ends_with('\\'); 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. match kind { FullCodeCharKind::InStringCommented | FullCodeCharKind::EndStringCommented - if config.version() == Version::Two => + if config.style_edition() >= StyleEdition::Edition2024 => { 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 // to be adjusted for the code surrounding the code block. config.format_strings() && line.ends_with('\\') - } else if config.version() == Version::Two { + } else if config.style_edition() >= StyleEdition::Edition2024 { !kind.is_commented_string() } else { true diff --git a/src/tools/rustfmt/src/vertical.rs b/src/tools/rustfmt/src/vertical.rs index a06bc995aa5..1ec239c3821 100644 --- a/src/tools/rustfmt/src/vertical.rs +++ b/src/tools/rustfmt/src/vertical.rs @@ -11,9 +11,9 @@ use crate::expr::rewrite_field; use crate::items::{rewrite_struct_field, rewrite_struct_field_prefix}; use crate::lists::{ - definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator, + 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::source_map::SpanUtils; use crate::spanned::Spanned; @@ -24,13 +24,13 @@ pub(crate) trait AlignedItem { fn skip(&self) -> bool; fn get_span(&self) -> Span; - fn rewrite_prefix(&self, context: &RewriteContext<'_>, shape: Shape) -> Option; + fn rewrite_prefix(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult; fn rewrite_aligned_item( &self, context: &RewriteContext<'_>, shape: Shape, prefix_max_width: usize, - ) -> Option; + ) -> RewriteResult; } impl AlignedItem for ast::FieldDef { @@ -42,24 +42,23 @@ fn get_span(&self) -> Span { self.span() } - fn rewrite_prefix(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { - let attrs_str = self.attrs.rewrite(context, shape)?; + fn rewrite_prefix(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { + let attrs_str = self.attrs.rewrite_result(context, shape)?; let missing_span = if self.attrs.is_empty() { mk_sp(self.span.lo(), self.span.lo()) } else { mk_sp(self.attrs.last().unwrap().span.hi(), self.span.lo()) }; let attrs_extendable = self.ident.is_none() && is_attributes_extendable(&attrs_str); - rewrite_struct_field_prefix(context, self).and_then(|field_str| { - combine_strs_with_missing_comments( - context, - &attrs_str, - &field_str, - missing_span, - shape, - attrs_extendable, - ) - }) + let field_str = rewrite_struct_field_prefix(context, self)?; + combine_strs_with_missing_comments( + context, + &attrs_str, + &field_str, + missing_span, + shape, + attrs_extendable, + ) } fn rewrite_aligned_item( @@ -67,7 +66,7 @@ fn rewrite_aligned_item( context: &RewriteContext<'_>, shape: Shape, prefix_max_width: usize, - ) -> Option { + ) -> RewriteResult { rewrite_struct_field(context, self, shape, prefix_max_width) } } @@ -81,8 +80,8 @@ fn get_span(&self) -> Span { self.span() } - fn rewrite_prefix(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { - let attrs_str = self.attrs.rewrite(context, shape)?; + fn rewrite_prefix(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { + let attrs_str = self.attrs.rewrite_result(context, shape)?; let name = rewrite_ident(context, self.ident); let missing_span = if self.attrs.is_empty() { mk_sp(self.span.lo(), self.span.lo()) @@ -104,7 +103,7 @@ fn rewrite_aligned_item( context: &RewriteContext<'_>, shape: Shape, prefix_max_width: usize, - ) -> Option { + ) -> RewriteResult { rewrite_field(context, self, shape, prefix_max_width) } } @@ -199,7 +198,7 @@ fn struct_field_prefix_max_min_width( .rewrite_prefix(context, shape) .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)) }) .unwrap_or((0, 0)) @@ -246,12 +245,12 @@ fn rewrite_aligned_items_inner( if tactic == DefinitiveListTactic::Horizontal { // since the items fits on a line, there is no need to align them let do_rewrite = - |field: &T| -> Option { field.rewrite_aligned_item(context, item_shape, 0) }; + |field: &T| -> RewriteResult { field.rewrite_aligned_item(context, item_shape, 0) }; fields .iter() .zip(items.iter_mut()) .for_each(|(field, list_item): (&T, &mut ListItem)| { - if list_item.item.is_some() { + if list_item.item.is_ok() { list_item.item = do_rewrite(field); } }); @@ -267,7 +266,7 @@ fn rewrite_aligned_items_inner( .tactic(tactic) .trailing_separator(separator_tactic) .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. diff --git a/src/tools/rustfmt/src/visitor.rs b/src/tools/rustfmt/src/visitor.rs index ac68fc5fecf..8102fe7ad8f 100644 --- a/src/tools/rustfmt/src/visitor.rs +++ b/src/tools/rustfmt/src/visitor.rs @@ -3,24 +3,23 @@ use rustc_ast::{ast, token::Delimiter, visit}; use rustc_data_structures::sync::Lrc; -use rustc_span::{symbol, BytePos, Pos, Span}; +use rustc_span::{BytePos, Pos, Span, symbol}; use tracing::debug; use crate::attr::*; -use crate::comment::{contains_comment, rewrite_comment, CodeCharKind, CommentCodeSlices}; -use crate::config::Version; -use crate::config::{BraceStyle, Config, MacroSelector}; +use crate::comment::{CodeCharKind, CommentCodeSlices, contains_comment, rewrite_comment}; +use crate::config::{BraceStyle, Config, MacroSelector, StyleEdition}; use crate::coverage::transform_missing_snippet; use crate::items::{ - format_impl, format_trait, format_trait_alias, is_mod_decl, is_use_item, rewrite_extern_crate, - rewrite_type_alias, FnBraceStyle, FnSig, ItemVisitorKind, StaticParts, StructParts, + FnBraceStyle, FnSig, ItemVisitorKind, StaticParts, StructParts, format_impl, format_trait, + 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::parse::session::ParseSess; use crate::rewrite::{Rewrite, RewriteContext}; 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::spanned::Spanned; use crate::stmt::Stmt; @@ -291,7 +290,9 @@ fn close_block(&mut self, span: Span, unindent_comment: bool) { let mut comment_shape = 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(" "); // put the first line of the comment on the same line as the // block's last line @@ -312,8 +313,8 @@ fn close_block(&mut self, span: Span, unindent_comment: bool) { let comment_str = rewrite_comment(other_lines, false, comment_shape, config); match comment_str { - Some(ref s) => self.push_str(s), - None => self.push_str(other_lines), + Ok(ref s) => self.push_str(s), + Err(_) => self.push_str(other_lines), } } } @@ -342,8 +343,8 @@ fn close_block(&mut self, span: Span, unindent_comment: bool) { let comment_str = rewrite_comment(&sub_slice, false, comment_shape, config); match comment_str { - Some(ref s) => self.push_str(s), - None => self.push_str(&sub_slice), + Ok(ref s) => self.push_str(s), + Err(_) => self.push_str(&sub_slice), } } } @@ -561,9 +562,11 @@ pub(crate) fn visit_item(&mut self, item: &ast::Item) { ) } else { let indent = self.block_indent; - let rewrite = self.rewrite_required_fn( - indent, item.ident, sig, &item.vis, generics, item.span, - ); + let rewrite = self + .rewrite_required_fn( + indent, item.ident, sig, &item.vis, generics, item.span, + ) + .ok(); self.push_rewrite(item.span, rewrite); } } @@ -584,7 +587,8 @@ pub(crate) fn visit_item(&mut self, item: &ast::Item) { item.ident, &item.vis, item.span, - ); + ) + .ok(); self.push_rewrite(item.span, rewrite); } ast::ItemKind::Delegation(..) | ast::ItemKind::DelegationMac(..) => { @@ -609,7 +613,8 @@ fn visit_ty_alias_kind( self.block_indent, visitor_kind, span, - ); + ) + .ok(); self.push_rewrite(span, rewrite); } @@ -655,8 +660,9 @@ fn visit_assoc_item(&mut self, visitor_kind: &ItemVisitorKind<'_>) { ); } else { let indent = self.block_indent; - let rewrite = - self.rewrite_required_fn(indent, ai.ident, sig, &ai.vis, generics, ai.span); + let rewrite = self + .rewrite_required_fn(indent, ai.ident, sig, &ai.vis, generics, ai.span) + .ok(); self.push_rewrite(ai.span, rewrite); } } @@ -683,7 +689,7 @@ fn visit_mac(&mut self, mac: &ast::MacCall, ident: Option, pos: M // 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 // the trailing semicolon. This determines the correct span to ensure scenarios // with whitespace between the delimiters and trailing semi (i.e. `foo!(abc) ;`) diff --git a/src/tools/rustfmt/tests/config/issue-5801-v2.toml b/src/tools/rustfmt/tests/config/issue-5801-v2.toml index 948f4fb66bf..98e89d37916 100644 --- a/src/tools/rustfmt/tests/config/issue-5801-v2.toml +++ b/src/tools/rustfmt/tests/config/issue-5801-v2.toml @@ -1,3 +1,3 @@ max_width = 120 version = "Two" -attr_fn_like_width = 120 \ No newline at end of file +attr_fn_like_width = 120 diff --git a/src/tools/rustfmt/tests/config/issue-6302.toml b/src/tools/rustfmt/tests/config/issue-6302.toml new file mode 100644 index 00000000000..8148b37b1f6 --- /dev/null +++ b/src/tools/rustfmt/tests/config/issue-6302.toml @@ -0,0 +1 @@ +edition = 2019 diff --git a/src/tools/rustfmt/tests/config/style-edition/just-style-edition/rustfmt.toml b/src/tools/rustfmt/tests/config/style-edition/just-style-edition/rustfmt.toml new file mode 100644 index 00000000000..3501136812c --- /dev/null +++ b/src/tools/rustfmt/tests/config/style-edition/just-style-edition/rustfmt.toml @@ -0,0 +1 @@ +style_edition = "2024" diff --git a/src/tools/rustfmt/tests/config/style-edition/just-version/rustfmt.toml b/src/tools/rustfmt/tests/config/style-edition/just-version/rustfmt.toml new file mode 100644 index 00000000000..1082fd88889 --- /dev/null +++ b/src/tools/rustfmt/tests/config/style-edition/just-version/rustfmt.toml @@ -0,0 +1 @@ +version = "Two" diff --git a/src/tools/rustfmt/tests/config/style-edition/overrides/rustfmt.toml b/src/tools/rustfmt/tests/config/style-edition/overrides/rustfmt.toml new file mode 100644 index 00000000000..24205692b1f --- /dev/null +++ b/src/tools/rustfmt/tests/config/style-edition/overrides/rustfmt.toml @@ -0,0 +1,2 @@ +style_edition = "2024" +overflow_delimited_expr = false diff --git a/src/tools/rustfmt/tests/config/style-edition/style-edition-and-edition/rustfmt.toml b/src/tools/rustfmt/tests/config/style-edition/style-edition-and-edition/rustfmt.toml new file mode 100644 index 00000000000..92844e03ab6 --- /dev/null +++ b/src/tools/rustfmt/tests/config/style-edition/style-edition-and-edition/rustfmt.toml @@ -0,0 +1,2 @@ +style_edition = "2021" +edition = "2024" diff --git a/src/tools/rustfmt/tests/config/style-edition/version-edition/rustfmt.toml b/src/tools/rustfmt/tests/config/style-edition/version-edition/rustfmt.toml new file mode 100644 index 00000000000..16ea9a13f36 --- /dev/null +++ b/src/tools/rustfmt/tests/config/style-edition/version-edition/rustfmt.toml @@ -0,0 +1,2 @@ +version = "Two" +edition = "2018" diff --git a/src/tools/rustfmt/tests/config/style-edition/version-style-edition-and-edition/rustfmt.toml b/src/tools/rustfmt/tests/config/style-edition/version-style-edition-and-edition/rustfmt.toml new file mode 100644 index 00000000000..187ba13cfb6 --- /dev/null +++ b/src/tools/rustfmt/tests/config/style-edition/version-style-edition-and-edition/rustfmt.toml @@ -0,0 +1,3 @@ +version = "Two" +edition = "2018" +style_edition = "2021" diff --git a/src/tools/rustfmt/tests/config/style-edition/version-style-edition/rustfmt.toml b/src/tools/rustfmt/tests/config/style-edition/version-style-edition/rustfmt.toml new file mode 100644 index 00000000000..c894bd981bc --- /dev/null +++ b/src/tools/rustfmt/tests/config/style-edition/version-style-edition/rustfmt.toml @@ -0,0 +1,2 @@ +version = "Two" +style_edition = "2021" diff --git a/src/tools/rustfmt/tests/rustfmt/main.rs b/src/tools/rustfmt/tests/rustfmt/main.rs index 58cf0e5e4db..a9f58b9328e 100644 --- a/src/tools/rustfmt/tests/rustfmt/main.rs +++ b/src/tools/rustfmt/tests/rustfmt/main.rs @@ -5,7 +5,7 @@ use std::path::Path; 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. 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); 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)); +} diff --git a/src/tools/rustfmt/tests/source/arrow_in_comments/arrow_in_single_comment.rs b/src/tools/rustfmt/tests/source/arrow_in_comments/arrow_in_single_comment.rs index fb0576a4822..38e8967e7e6 100644 --- a/src/tools/rustfmt/tests/source/arrow_in_comments/arrow_in_single_comment.rs +++ b/src/tools/rustfmt/tests/source/arrow_in_comments/arrow_in_single_comment.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn main() { match a { _ => diff --git a/src/tools/rustfmt/tests/source/arrow_in_comments/multiple_arrows.rs b/src/tools/rustfmt/tests/source/arrow_in_comments/multiple_arrows.rs index fc696b309f2..98ef919450f 100644 --- a/src/tools/rustfmt/tests/source/arrow_in_comments/multiple_arrows.rs +++ b/src/tools/rustfmt/tests/source/arrow_in_comments/multiple_arrows.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn main() { match a { _ => // comment with => diff --git a/src/tools/rustfmt/tests/source/configs/indent_style/block_trailing_comma_call/one.rs b/src/tools/rustfmt/tests/source/configs/indent_style/block_trailing_comma_call/one.rs index 6d48ea742fc..66e2f538415 100644 --- a/src/tools/rustfmt/tests/source/configs/indent_style/block_trailing_comma_call/one.rs +++ b/src/tools/rustfmt/tests/source/configs/indent_style/block_trailing_comma_call/one.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 // rustfmt-error_on_line_overflow: false // rustfmt-indent_style: Block diff --git a/src/tools/rustfmt/tests/source/configs/indent_style/block_trailing_comma_call/two.rs b/src/tools/rustfmt/tests/source/configs/indent_style/block_trailing_comma_call/two.rs index 7a62d722c6e..51aa6376e23 100644 --- a/src/tools/rustfmt/tests/source/configs/indent_style/block_trailing_comma_call/two.rs +++ b/src/tools/rustfmt/tests/source/configs/indent_style/block_trailing_comma_call/two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 // rustfmt-error_on_line_overflow: false // rustfmt-indent_style: Block diff --git a/src/tools/rustfmt/tests/source/configs/style_edition/overflow_delim_expr_2015.rs b/src/tools/rustfmt/tests/source/configs/style_edition/overflow_delim_expr_2015.rs new file mode 100644 index 00000000000..5cb4a870fc1 --- /dev/null +++ b/src/tools/rustfmt/tests/source/configs/style_edition/overflow_delim_expr_2015.rs @@ -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(); +} diff --git a/src/tools/rustfmt/tests/source/configs/style_edition/overflow_delim_expr_2024.rs b/src/tools/rustfmt/tests/source/configs/style_edition/overflow_delim_expr_2024.rs new file mode 100644 index 00000000000..66c95e71aa9 --- /dev/null +++ b/src/tools/rustfmt/tests/source/configs/style_edition/overflow_delim_expr_2024.rs @@ -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(); +} diff --git a/src/tools/rustfmt/tests/source/fn-single-line/version_one.rs b/src/tools/rustfmt/tests/source/fn-single-line/version_one.rs index 469ab621567..85957e3fdcc 100644 --- a/src/tools/rustfmt/tests/source/fn-single-line/version_one.rs +++ b/src/tools/rustfmt/tests/source/fn-single-line/version_one.rs @@ -1,5 +1,5 @@ // rustfmt-fn_single_line: true -// rustfmt-version: One +// rustfmt-style_edition: 2015 // Test single-line functions. fn foo_expr() { diff --git a/src/tools/rustfmt/tests/source/fn-single-line/version_two.rs b/src/tools/rustfmt/tests/source/fn-single-line/version_two.rs index bf381ff1065..f119e581217 100644 --- a/src/tools/rustfmt/tests/source/fn-single-line/version_two.rs +++ b/src/tools/rustfmt/tests/source/fn-single-line/version_two.rs @@ -1,5 +1,5 @@ // rustfmt-fn_single_line: true -// rustfmt-version: Two +// rustfmt-style_edition: 2024 // Test single-line functions. fn foo_expr() { diff --git a/src/tools/rustfmt/tests/source/issue-2179/one.rs b/src/tools/rustfmt/tests/source/issue-2179/one.rs index d23947931ff..8bbd56f0521 100644 --- a/src/tools/rustfmt/tests/source/issue-2179/one.rs +++ b/src/tools/rustfmt/tests/source/issue-2179/one.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 // rustfmt-error_on_line_overflow: false fn issue_2179() { diff --git a/src/tools/rustfmt/tests/source/issue-2179/two.rs b/src/tools/rustfmt/tests/source/issue-2179/two.rs index f4cc9cc488b..631b0f3c86e 100644 --- a/src/tools/rustfmt/tests/source/issue-2179/two.rs +++ b/src/tools/rustfmt/tests/source/issue-2179/two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 // rustfmt-error_on_line_overflow: false fn issue_2179() { diff --git a/src/tools/rustfmt/tests/source/issue-3213/version_one.rs b/src/tools/rustfmt/tests/source/issue-3213/version_one.rs index f9f4cab55e3..ed7d5145150 100644 --- a/src/tools/rustfmt/tests/source/issue-3213/version_one.rs +++ b/src/tools/rustfmt/tests/source/issue-3213/version_one.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 fn foo() { match 0 { diff --git a/src/tools/rustfmt/tests/source/issue-3213/version_two.rs b/src/tools/rustfmt/tests/source/issue-3213/version_two.rs index 0f068c19d74..c6d04aced8d 100644 --- a/src/tools/rustfmt/tests/source/issue-3213/version_two.rs +++ b/src/tools/rustfmt/tests/source/issue-3213/version_two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn foo() { match 0 { diff --git a/src/tools/rustfmt/tests/source/issue-3227/two.rs b/src/tools/rustfmt/tests/source/issue-3227/two.rs index c1572c00d57..50c0ad47dc1 100644 --- a/src/tools/rustfmt/tests/source/issue-3227/two.rs +++ b/src/tools/rustfmt/tests/source/issue-3227/two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn main() { thread::spawn(|| { diff --git a/src/tools/rustfmt/tests/source/issue-3270/one.rs b/src/tools/rustfmt/tests/source/issue-3270/one.rs index 3c2e27e2293..64861176b91 100644 --- a/src/tools/rustfmt/tests/source/issue-3270/one.rs +++ b/src/tools/rustfmt/tests/source/issue-3270/one.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 pub fn main() { /* let s = String::from( diff --git a/src/tools/rustfmt/tests/source/issue-3270/two.rs b/src/tools/rustfmt/tests/source/issue-3270/two.rs index 0eb756471e7..1342cf03c39 100644 --- a/src/tools/rustfmt/tests/source/issue-3270/two.rs +++ b/src/tools/rustfmt/tests/source/issue-3270/two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 pub fn main() { /* let s = String::from( diff --git a/src/tools/rustfmt/tests/source/issue-3272/v1.rs b/src/tools/rustfmt/tests/source/issue-3272/v1.rs index f4c1b7c992b..56dc048bf8a 100644 --- a/src/tools/rustfmt/tests/source/issue-3272/v1.rs +++ b/src/tools/rustfmt/tests/source/issue-3272/v1.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 fn main() { assert!(HAYSTACK diff --git a/src/tools/rustfmt/tests/source/issue-3272/v2.rs b/src/tools/rustfmt/tests/source/issue-3272/v2.rs index 0148368edc8..f3adbe37c76 100644 --- a/src/tools/rustfmt/tests/source/issue-3272/v2.rs +++ b/src/tools/rustfmt/tests/source/issue-3272/v2.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn main() { assert!(HAYSTACK diff --git a/src/tools/rustfmt/tests/source/issue-3278/version_one.rs b/src/tools/rustfmt/tests/source/issue-3278/version_one.rs index 580679fbae3..718a32b4c7e 100644 --- a/src/tools/rustfmt/tests/source/issue-3278/version_one.rs +++ b/src/tools/rustfmt/tests/source/issue-3278/version_one.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 pub fn parse_conditional<'a, I: 'a>( ) -> impl Parser + 'a diff --git a/src/tools/rustfmt/tests/source/issue-3278/version_two.rs b/src/tools/rustfmt/tests/source/issue-3278/version_two.rs index c17b1742d39..eb605e509f9 100644 --- a/src/tools/rustfmt/tests/source/issue-3278/version_two.rs +++ b/src/tools/rustfmt/tests/source/issue-3278/version_two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 pub fn parse_conditional<'a, I: 'a>() -> impl Parser + 'a diff --git a/src/tools/rustfmt/tests/source/issue-3295/two.rs b/src/tools/rustfmt/tests/source/issue-3295/two.rs index 0eaf022249b..ae3d2ec28c0 100644 --- a/src/tools/rustfmt/tests/source/issue-3295/two.rs +++ b/src/tools/rustfmt/tests/source/issue-3295/two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 pub enum TestEnum { a, b, diff --git a/src/tools/rustfmt/tests/source/issue-3302.rs b/src/tools/rustfmt/tests/source/issue-3302.rs index c037584fd71..5e0862cb399 100644 --- a/src/tools/rustfmt/tests/source/issue-3302.rs +++ b/src/tools/rustfmt/tests/source/issue-3302.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 macro_rules! moo1 { () => { diff --git a/src/tools/rustfmt/tests/source/issue-3701/one.rs b/src/tools/rustfmt/tests/source/issue-3701/one.rs index a7f0bd3aa17..4e8518b6f18 100644 --- a/src/tools/rustfmt/tests/source/issue-3701/one.rs +++ b/src/tools/rustfmt/tests/source/issue-3701/one.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 fn build_sorted_static_get_entry_names( mut entries: Vec<(u8, &'static str)>, diff --git a/src/tools/rustfmt/tests/source/issue-3701/two.rs b/src/tools/rustfmt/tests/source/issue-3701/two.rs index 8e15c58b8b2..d7cb790a754 100644 --- a/src/tools/rustfmt/tests/source/issue-3701/two.rs +++ b/src/tools/rustfmt/tests/source/issue-3701/two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn build_sorted_static_get_entry_names( mut entries: Vec<(u8, &'static str)>, diff --git a/src/tools/rustfmt/tests/source/issue-3805.rs b/src/tools/rustfmt/tests/source/issue-3805.rs index a0289b57974..aadc4a9dddc 100644 --- a/src/tools/rustfmt/tests/source/issue-3805.rs +++ b/src/tools/rustfmt/tests/source/issue-3805.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 // rustfmt-format_macro_matchers: true // From original issue example - Line length 101 diff --git a/src/tools/rustfmt/tests/source/issue-3840/version-two_hard-tabs.rs b/src/tools/rustfmt/tests/source/issue-3840/version-two_hard-tabs.rs index 7b505fda87c..8d009eabdec 100644 --- a/src/tools/rustfmt/tests/source/issue-3840/version-two_hard-tabs.rs +++ b/src/tools/rustfmt/tests/source/issue-3840/version-two_hard-tabs.rs @@ -1,5 +1,5 @@ // rustfmt-hard_tabs: true -// rustfmt-version: Two +// rustfmt-style_edition: 2024 impl + FromEvent, A: Widget2, B: Widget2, C: for<'a> CtxFamily<'a>> Widget2 for WidgetEventLifter { diff --git a/src/tools/rustfmt/tests/source/issue-3840/version-two_soft-tabs.rs b/src/tools/rustfmt/tests/source/issue-3840/version-two_soft-tabs.rs index 39c8ef31292..9e283d3be0a 100644 --- a/src/tools/rustfmt/tests/source/issue-3840/version-two_soft-tabs.rs +++ b/src/tools/rustfmt/tests/source/issue-3840/version-two_soft-tabs.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 impl + FromEvent, A: Widget2, B: Widget2, C: for<'a> CtxFamily<'a>> Widget2 for WidgetEventLifter { diff --git a/src/tools/rustfmt/tests/source/issue-4381/style_edition_2015.rs b/src/tools/rustfmt/tests/source/issue-4381/style_edition_2015.rs new file mode 100644 index 00000000000..bb4c9feae1d --- /dev/null +++ b/src/tools/rustfmt/tests/source/issue-4381/style_edition_2015.rs @@ -0,0 +1,3 @@ +// rustfmt-style_edition: 2015 + +use std::num::{NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64}; diff --git a/src/tools/rustfmt/tests/source/issue-4530.rs b/src/tools/rustfmt/tests/source/issue-4530.rs index 9d2882abb3c..6b92122f0c0 100644 --- a/src/tools/rustfmt/tests/source/issue-4530.rs +++ b/src/tools/rustfmt/tests/source/issue-4530.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn main() { let [aaaaaaaaaaaaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, cccccccccccccccccccccccccc, ddddddddddddddddddddddddd] = panic!(); } diff --git a/src/tools/rustfmt/tests/source/issue-4689/one.rs b/src/tools/rustfmt/tests/source/issue-4689/one.rs index d048eb10fb1..bff090a3525 100644 --- a/src/tools/rustfmt/tests/source/issue-4689/one.rs +++ b/src/tools/rustfmt/tests/source/issue-4689/one.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 // Based on the issue description pub trait PrettyPrinter<'tcx>: diff --git a/src/tools/rustfmt/tests/source/issue-4689/two.rs b/src/tools/rustfmt/tests/source/issue-4689/two.rs index ea7feda825d..217535c046e 100644 --- a/src/tools/rustfmt/tests/source/issue-4689/two.rs +++ b/src/tools/rustfmt/tests/source/issue-4689/two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 // Based on the issue description pub trait PrettyPrinter<'tcx>: diff --git a/src/tools/rustfmt/tests/source/issue-5586.rs b/src/tools/rustfmt/tests/source/issue-5586.rs index 9cf6c1d58dd..061ad4bdaa4 100644 --- a/src/tools/rustfmt/tests/source/issue-5586.rs +++ b/src/tools/rustfmt/tests/source/issue-5586.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn main() { // sample 1 { diff --git a/src/tools/rustfmt/tests/source/issue-5655/one.rs b/src/tools/rustfmt/tests/source/issue-5655/one.rs index 1758ec56f8b..62df2655c29 100644 --- a/src/tools/rustfmt/tests/source/issue-5655/one.rs +++ b/src/tools/rustfmt/tests/source/issue-5655/one.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 fn foo(_: T) where diff --git a/src/tools/rustfmt/tests/source/issue-5655/two.rs b/src/tools/rustfmt/tests/source/issue-5655/two.rs index e37ebbea8af..bfe1d3813bb 100644 --- a/src/tools/rustfmt/tests/source/issue-5655/two.rs +++ b/src/tools/rustfmt/tests/source/issue-5655/two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn foo(_: T) where diff --git a/src/tools/rustfmt/tests/source/issue-5801/comment_unexpectedly_wraps_before_max_width.rs b/src/tools/rustfmt/tests/source/issue-5801/comment_unexpectedly_wraps_before_max_width.rs index 5847afd9560..e61d34604a1 100644 --- a/src/tools/rustfmt/tests/source/issue-5801/comment_unexpectedly_wraps_before_max_width.rs +++ b/src/tools/rustfmt/tests/source/issue-5801/comment_unexpectedly_wraps_before_max_width.rs @@ -1,7 +1,7 @@ // rustfmt-comment_width: 120 // rustfmt-wrap_comments: true // rustfmt-max_width: 120 -// rustfmt-version: One +// rustfmt-style_edition: 2015 /// This function is 120 columns wide and is left alone. This comment is 120 columns wide and the formatter is also fine fn my_super_cool_function_name(my_very_cool_argument_name: String, my_other_very_cool_argument_name: String) -> String { diff --git a/src/tools/rustfmt/tests/source/issue-5987/two.rs b/src/tools/rustfmt/tests/source/issue-5987/two.rs index e20026b5565..98ed35c4f9a 100644 --- a/src/tools/rustfmt/tests/source/issue-5987/two.rs +++ b/src/tools/rustfmt/tests/source/issue-5987/two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn main() { trace!( diff --git a/src/tools/rustfmt/tests/source/issue-6147/case_rustfmt_v1.rs b/src/tools/rustfmt/tests/source/issue-6147/case_rustfmt_v1.rs index 2ac2e0361c3..bcae86aaff2 100644 --- a/src/tools/rustfmt/tests/source/issue-6147/case_rustfmt_v1.rs +++ b/src/tools/rustfmt/tests/source/issue-6147/case_rustfmt_v1.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 pub fn main() { let a = Some(12); diff --git a/src/tools/rustfmt/tests/source/issue-6147/case_rustfmt_v2.rs b/src/tools/rustfmt/tests/source/issue-6147/case_rustfmt_v2.rs index c1bf1ad4bf8..da612b213fc 100644 --- a/src/tools/rustfmt/tests/source/issue-6147/case_rustfmt_v2.rs +++ b/src/tools/rustfmt/tests/source/issue-6147/case_rustfmt_v2.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 pub fn main() { let a = Some(12); diff --git a/src/tools/rustfmt/tests/source/issue_5027.rs b/src/tools/rustfmt/tests/source/issue_5027.rs index 67beeb23b71..a47d6df6f0f 100644 --- a/src/tools/rustfmt/tests/source/issue_5027.rs +++ b/src/tools/rustfmt/tests/source/issue_5027.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 pub type Iter<'a, D> = impl DoubleEndedIterator)>+ ExactSizeIterator+ 'a; diff --git a/src/tools/rustfmt/tests/source/let_else_v2.rs b/src/tools/rustfmt/tests/source/let_else_v2.rs index a420fbcf95b..23be32d629a 100644 --- a/src/tools/rustfmt/tests/source/let_else_v2.rs +++ b/src/tools/rustfmt/tests/source/let_else_v2.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 // rustfmt-single_line_let_else_max_width: 100 fn issue5901() { diff --git a/src/tools/rustfmt/tests/source/long-fn-1/version_one.rs b/src/tools/rustfmt/tests/source/long-fn-1/version_one.rs index d6832c2af09..60083024810 100644 --- a/src/tools/rustfmt/tests/source/long-fn-1/version_one.rs +++ b/src/tools/rustfmt/tests/source/long-fn-1/version_one.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 // Tests that a function which is almost short enough, but not quite, gets // formatted correctly. diff --git a/src/tools/rustfmt/tests/source/long-fn-1/version_two.rs b/src/tools/rustfmt/tests/source/long-fn-1/version_two.rs index f402a26e8b6..bce2c551c6a 100644 --- a/src/tools/rustfmt/tests/source/long-fn-1/version_two.rs +++ b/src/tools/rustfmt/tests/source/long-fn-1/version_two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 // Tests that a function which is almost short enough, but not quite, gets // formatted correctly. diff --git a/src/tools/rustfmt/tests/source/one_line_if_v1.rs b/src/tools/rustfmt/tests/source/one_line_if_v1.rs index d3dcbe6787a..bf7bc75fb88 100644 --- a/src/tools/rustfmt/tests/source/one_line_if_v1.rs +++ b/src/tools/rustfmt/tests/source/one_line_if_v1.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 fn plain_if(x: bool) -> u8 { if x { diff --git a/src/tools/rustfmt/tests/source/one_line_if_v2.rs b/src/tools/rustfmt/tests/source/one_line_if_v2.rs index 40c834959f9..f3c974b12db 100644 --- a/src/tools/rustfmt/tests/source/one_line_if_v2.rs +++ b/src/tools/rustfmt/tests/source/one_line_if_v2.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn plain_if(x: bool) -> u8 { if x { diff --git a/src/tools/rustfmt/tests/source/single-line-macro/v1.rs b/src/tools/rustfmt/tests/source/single-line-macro/v1.rs index a3aa631ed4a..fea146d8d33 100644 --- a/src/tools/rustfmt/tests/source/single-line-macro/v1.rs +++ b/src/tools/rustfmt/tests/source/single-line-macro/v1.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 // #2652 // Preserve trailing comma inside macro, even if it looks an array. diff --git a/src/tools/rustfmt/tests/source/single-line-macro/v2.rs b/src/tools/rustfmt/tests/source/single-line-macro/v2.rs index 51a665f7560..d9fba64ac70 100644 --- a/src/tools/rustfmt/tests/source/single-line-macro/v2.rs +++ b/src/tools/rustfmt/tests/source/single-line-macro/v2.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 // #2652 // Preserve trailing comma inside macro, even if it looks an array. diff --git a/src/tools/rustfmt/tests/source/trailing_comments/hard_tabs.rs b/src/tools/rustfmt/tests/source/trailing_comments/hard_tabs.rs index 88249aa5fb9..c6d1b3f0ab7 100644 --- a/src/tools/rustfmt/tests/source/trailing_comments/hard_tabs.rs +++ b/src/tools/rustfmt/tests/source/trailing_comments/hard_tabs.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 // rustfmt-wrap_comments: true // rustfmt-hard_tabs: true diff --git a/src/tools/rustfmt/tests/source/trailing_comments/soft_tabs.rs b/src/tools/rustfmt/tests/source/trailing_comments/soft_tabs.rs index 7845f713b8a..431875989f3 100644 --- a/src/tools/rustfmt/tests/source/trailing_comments/soft_tabs.rs +++ b/src/tools/rustfmt/tests/source/trailing_comments/soft_tabs.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 // rustfmt-wrap_comments: true pub const IFF_MULTICAST: ::c_int = 0x0000000800; // Supports multicast diff --git a/src/tools/rustfmt/tests/source/tuple_v2.rs b/src/tools/rustfmt/tests/source/tuple_v2.rs index 9223033832b..6dc18e00d7e 100644 --- a/src/tools/rustfmt/tests/source/tuple_v2.rs +++ b/src/tools/rustfmt/tests/source/tuple_v2.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn issue_4355() { let _ = ((1,),).0 .0; diff --git a/src/tools/rustfmt/tests/source/type-alias-where-clauses-with-comments.rs b/src/tools/rustfmt/tests/source/type-alias-where-clauses-with-comments.rs new file mode 100644 index 00000000000..722ebac47ac --- /dev/null +++ b/src/tools/rustfmt/tests/source/type-alias-where-clauses-with-comments.rs @@ -0,0 +1,31 @@ +type Foo // comment1 + // interlinear1 +where // comment2 + // interlinear2 +A: B, // comment3 +C: D, // comment4 + // interlinear3 += E; // comment5 + +type Foo // comment6 + // interlinear4 +where// comment7 + // interlinear5 +A: B, // comment8 +C: D, // comment9 + // interlinear6 += E // comment10 + // interlinear7 +where // comment11 + // interlinear8 +F: G, // comment12 +H: I; // comment13 + +type Foo // comment14 + // interlinear9 += E // comment15 + // interlinear10 +where // comment16 + // interlinear11 +F: G,// comment17 +H: I;// comment18 diff --git a/src/tools/rustfmt/tests/source/type-alias-where-clauses.rs b/src/tools/rustfmt/tests/source/type-alias-where-clauses.rs new file mode 100644 index 00000000000..ad998caf308 --- /dev/null +++ b/src/tools/rustfmt/tests/source/type-alias-where-clauses.rs @@ -0,0 +1,20 @@ + type Foo + where + A: B, + C: D, + = E; + + type Foo + where + A: B, + C: D, + = E + where + F: G, + H: I; + + type Foo + = E + where + F: G, + H: I; \ No newline at end of file diff --git a/src/tools/rustfmt/tests/target/arrow_in_comments/arrow_in_single_comment.rs b/src/tools/rustfmt/tests/target/arrow_in_comments/arrow_in_single_comment.rs index deffdbeeaaf..d638a6b4ef0 100644 --- a/src/tools/rustfmt/tests/target/arrow_in_comments/arrow_in_single_comment.rs +++ b/src/tools/rustfmt/tests/target/arrow_in_comments/arrow_in_single_comment.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn main() { match a { _ => diff --git a/src/tools/rustfmt/tests/target/arrow_in_comments/multiple_arrows.rs b/src/tools/rustfmt/tests/target/arrow_in_comments/multiple_arrows.rs index b0443411816..f6f4a60eec3 100644 --- a/src/tools/rustfmt/tests/target/arrow_in_comments/multiple_arrows.rs +++ b/src/tools/rustfmt/tests/target/arrow_in_comments/multiple_arrows.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn main() { match a { _ => diff --git a/src/tools/rustfmt/tests/target/configs/indent_style/block_trailing_comma_call/one.rs b/src/tools/rustfmt/tests/target/configs/indent_style/block_trailing_comma_call/one.rs index 6b9489bef55..204dce6d655 100644 --- a/src/tools/rustfmt/tests/target/configs/indent_style/block_trailing_comma_call/one.rs +++ b/src/tools/rustfmt/tests/target/configs/indent_style/block_trailing_comma_call/one.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 // rustfmt-error_on_line_overflow: false // rustfmt-indent_style: Block diff --git a/src/tools/rustfmt/tests/target/configs/indent_style/block_trailing_comma_call/two.rs b/src/tools/rustfmt/tests/target/configs/indent_style/block_trailing_comma_call/two.rs index 4f4292e5f48..887e8328ccc 100644 --- a/src/tools/rustfmt/tests/target/configs/indent_style/block_trailing_comma_call/two.rs +++ b/src/tools/rustfmt/tests/target/configs/indent_style/block_trailing_comma_call/two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 // rustfmt-error_on_line_overflow: false // rustfmt-indent_style: Block diff --git a/src/tools/rustfmt/tests/target/configs/style_edition/overflow_delim_expr_2015.rs b/src/tools/rustfmt/tests/target/configs/style_edition/overflow_delim_expr_2015.rs new file mode 100644 index 00000000000..05d4b8b6d33 --- /dev/null +++ b/src/tools/rustfmt/tests/target/configs/style_edition/overflow_delim_expr_2015.rs @@ -0,0 +1,135 @@ +// 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(); +} diff --git a/src/tools/rustfmt/tests/target/configs/style_edition/overflow_delim_expr_2024.rs b/src/tools/rustfmt/tests/target/configs/style_edition/overflow_delim_expr_2024.rs new file mode 100644 index 00000000000..ecd2e8ca797 --- /dev/null +++ b/src/tools/rustfmt/tests/target/configs/style_edition/overflow_delim_expr_2024.rs @@ -0,0 +1,120 @@ +// 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(); +} diff --git a/src/tools/rustfmt/tests/target/configs/version/mapped.rs b/src/tools/rustfmt/tests/target/configs/version/mapped.rs new file mode 100644 index 00000000000..296dc559a93 --- /dev/null +++ b/src/tools/rustfmt/tests/target/configs/version/mapped.rs @@ -0,0 +1,9 @@ +// rustfmt-version: Two +fn main() { + let [ + aaaaaaaaaaaaaaaaaaaaaaaaaa, + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, + cccccccccccccccccccccccccc, + ddddddddddddddddddddddddd, + ] = panic!(); +} diff --git a/src/tools/rustfmt/tests/target/fn-single-line/version_one.rs b/src/tools/rustfmt/tests/target/fn-single-line/version_one.rs index 013b2cd7216..5b704474d6d 100644 --- a/src/tools/rustfmt/tests/target/fn-single-line/version_one.rs +++ b/src/tools/rustfmt/tests/target/fn-single-line/version_one.rs @@ -1,5 +1,5 @@ // rustfmt-fn_single_line: true -// rustfmt-version: One +// rustfmt-style_edition: 2015 // Test single-line functions. fn foo_expr() { 1 } diff --git a/src/tools/rustfmt/tests/target/fn-single-line/version_two.rs b/src/tools/rustfmt/tests/target/fn-single-line/version_two.rs index b8053d4c2f5..aedd1d481b6 100644 --- a/src/tools/rustfmt/tests/target/fn-single-line/version_two.rs +++ b/src/tools/rustfmt/tests/target/fn-single-line/version_two.rs @@ -1,5 +1,5 @@ // rustfmt-fn_single_line: true -// rustfmt-version: Two +// rustfmt-style_edition: 2024 // Test single-line functions. fn foo_expr() { 1 } diff --git a/src/tools/rustfmt/tests/target/issue-2179/one.rs b/src/tools/rustfmt/tests/target/issue-2179/one.rs index 3f98acc8dcd..6593624e589 100644 --- a/src/tools/rustfmt/tests/target/issue-2179/one.rs +++ b/src/tools/rustfmt/tests/target/issue-2179/one.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 // rustfmt-error_on_line_overflow: false fn issue_2179() { diff --git a/src/tools/rustfmt/tests/target/issue-2179/two.rs b/src/tools/rustfmt/tests/target/issue-2179/two.rs index 96531509ea2..088518705fe 100644 --- a/src/tools/rustfmt/tests/target/issue-2179/two.rs +++ b/src/tools/rustfmt/tests/target/issue-2179/two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 // rustfmt-error_on_line_overflow: false fn issue_2179() { diff --git a/src/tools/rustfmt/tests/target/issue-3132.rs b/src/tools/rustfmt/tests/target/issue-3132.rs index 4dffe0ab836..4d61baa7507 100644 --- a/src/tools/rustfmt/tests/target/issue-3132.rs +++ b/src/tools/rustfmt/tests/target/issue-3132.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn test() { /* diff --git a/src/tools/rustfmt/tests/target/issue-3213/version_one.rs b/src/tools/rustfmt/tests/target/issue-3213/version_one.rs index 307903b128b..a20915df761 100644 --- a/src/tools/rustfmt/tests/target/issue-3213/version_one.rs +++ b/src/tools/rustfmt/tests/target/issue-3213/version_one.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 fn foo() { match 0 { diff --git a/src/tools/rustfmt/tests/target/issue-3213/version_two.rs b/src/tools/rustfmt/tests/target/issue-3213/version_two.rs index de93d04ba95..fb609f6a67a 100644 --- a/src/tools/rustfmt/tests/target/issue-3213/version_two.rs +++ b/src/tools/rustfmt/tests/target/issue-3213/version_two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn foo() { match 0 { diff --git a/src/tools/rustfmt/tests/target/issue-3227/one.rs b/src/tools/rustfmt/tests/target/issue-3227/one.rs index fcc8331000d..2922bfdfef7 100644 --- a/src/tools/rustfmt/tests/target/issue-3227/one.rs +++ b/src/tools/rustfmt/tests/target/issue-3227/one.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 fn main() { thread::spawn(|| { diff --git a/src/tools/rustfmt/tests/target/issue-3227/two.rs b/src/tools/rustfmt/tests/target/issue-3227/two.rs index 374ab54305d..ae7eee47194 100644 --- a/src/tools/rustfmt/tests/target/issue-3227/two.rs +++ b/src/tools/rustfmt/tests/target/issue-3227/two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn main() { thread::spawn(|| { diff --git a/src/tools/rustfmt/tests/target/issue-3270/one.rs b/src/tools/rustfmt/tests/target/issue-3270/one.rs index 78de9473243..a31a0ff5742 100644 --- a/src/tools/rustfmt/tests/target/issue-3270/one.rs +++ b/src/tools/rustfmt/tests/target/issue-3270/one.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 pub fn main() { /* let s = String::from( diff --git a/src/tools/rustfmt/tests/target/issue-3270/two.rs b/src/tools/rustfmt/tests/target/issue-3270/two.rs index e48b5921329..8e26f8ac23d 100644 --- a/src/tools/rustfmt/tests/target/issue-3270/two.rs +++ b/src/tools/rustfmt/tests/target/issue-3270/two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 pub fn main() { /* let s = String::from( diff --git a/src/tools/rustfmt/tests/target/issue-3270/wrap.rs b/src/tools/rustfmt/tests/target/issue-3270/wrap.rs index 7435c5f0866..967bfb5534c 100644 --- a/src/tools/rustfmt/tests/target/issue-3270/wrap.rs +++ b/src/tools/rustfmt/tests/target/issue-3270/wrap.rs @@ -1,5 +1,5 @@ // rustfmt-wrap_comments: true -// rustfmt-version: Two +// rustfmt-style_edition: 2024 // check that a line below max_width does not get over the limit when wrapping // it in a block comment diff --git a/src/tools/rustfmt/tests/target/issue-3272/v1.rs b/src/tools/rustfmt/tests/target/issue-3272/v1.rs index aab201027d5..7cca7ea7896 100644 --- a/src/tools/rustfmt/tests/target/issue-3272/v1.rs +++ b/src/tools/rustfmt/tests/target/issue-3272/v1.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 fn main() { assert!(HAYSTACK diff --git a/src/tools/rustfmt/tests/target/issue-3272/v2.rs b/src/tools/rustfmt/tests/target/issue-3272/v2.rs index a42a2fccd5b..ca7718c5a61 100644 --- a/src/tools/rustfmt/tests/target/issue-3272/v2.rs +++ b/src/tools/rustfmt/tests/target/issue-3272/v2.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn main() { assert!( diff --git a/src/tools/rustfmt/tests/target/issue-3278/version_one.rs b/src/tools/rustfmt/tests/target/issue-3278/version_one.rs index 580679fbae3..718a32b4c7e 100644 --- a/src/tools/rustfmt/tests/target/issue-3278/version_one.rs +++ b/src/tools/rustfmt/tests/target/issue-3278/version_one.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 pub fn parse_conditional<'a, I: 'a>( ) -> impl Parser + 'a diff --git a/src/tools/rustfmt/tests/target/issue-3278/version_two.rs b/src/tools/rustfmt/tests/target/issue-3278/version_two.rs index c17b1742d39..eb605e509f9 100644 --- a/src/tools/rustfmt/tests/target/issue-3278/version_two.rs +++ b/src/tools/rustfmt/tests/target/issue-3278/version_two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 pub fn parse_conditional<'a, I: 'a>() -> impl Parser + 'a diff --git a/src/tools/rustfmt/tests/target/issue-3295/two.rs b/src/tools/rustfmt/tests/target/issue-3295/two.rs index 3e669a0bb75..4f685172cfa 100644 --- a/src/tools/rustfmt/tests/target/issue-3295/two.rs +++ b/src/tools/rustfmt/tests/target/issue-3295/two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 pub enum TestEnum { a, b, diff --git a/src/tools/rustfmt/tests/target/issue-3302.rs b/src/tools/rustfmt/tests/target/issue-3302.rs index 146cb983819..504bfd987f5 100644 --- a/src/tools/rustfmt/tests/target/issue-3302.rs +++ b/src/tools/rustfmt/tests/target/issue-3302.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 macro_rules! moo1 { () => { diff --git a/src/tools/rustfmt/tests/target/issue-3614/version_one.rs b/src/tools/rustfmt/tests/target/issue-3614/version_one.rs index 8ab28304732..4bd972aa87a 100644 --- a/src/tools/rustfmt/tests/target/issue-3614/version_one.rs +++ b/src/tools/rustfmt/tests/target/issue-3614/version_one.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 fn main() { let toto = || { diff --git a/src/tools/rustfmt/tests/target/issue-3614/version_two.rs b/src/tools/rustfmt/tests/target/issue-3614/version_two.rs index 5d6f8e7a313..7b9534caa02 100644 --- a/src/tools/rustfmt/tests/target/issue-3614/version_two.rs +++ b/src/tools/rustfmt/tests/target/issue-3614/version_two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn main() { let toto = || { diff --git a/src/tools/rustfmt/tests/target/issue-3701/one.rs b/src/tools/rustfmt/tests/target/issue-3701/one.rs index 9d1ef9eed9a..b154724d341 100644 --- a/src/tools/rustfmt/tests/target/issue-3701/one.rs +++ b/src/tools/rustfmt/tests/target/issue-3701/one.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 fn build_sorted_static_get_entry_names( mut entries: Vec<(u8, &'static str)>, diff --git a/src/tools/rustfmt/tests/target/issue-3701/two.rs b/src/tools/rustfmt/tests/target/issue-3701/two.rs index 62ffc9d823d..995763ef291 100644 --- a/src/tools/rustfmt/tests/target/issue-3701/two.rs +++ b/src/tools/rustfmt/tests/target/issue-3701/two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn build_sorted_static_get_entry_names( mut entries: Vec<(u8, &'static str)>, diff --git a/src/tools/rustfmt/tests/target/issue-3805.rs b/src/tools/rustfmt/tests/target/issue-3805.rs index a247a43fe6d..a1eda832d76 100644 --- a/src/tools/rustfmt/tests/target/issue-3805.rs +++ b/src/tools/rustfmt/tests/target/issue-3805.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 // rustfmt-format_macro_matchers: true // From original issue example - Line length 101 diff --git a/src/tools/rustfmt/tests/target/issue-3840/version-two_hard-tabs.rs b/src/tools/rustfmt/tests/target/issue-3840/version-two_hard-tabs.rs index 084db3d1465..78a2f921735 100644 --- a/src/tools/rustfmt/tests/target/issue-3840/version-two_hard-tabs.rs +++ b/src/tools/rustfmt/tests/target/issue-3840/version-two_hard-tabs.rs @@ -1,5 +1,5 @@ // rustfmt-hard_tabs: true -// rustfmt-version: Two +// rustfmt-style_edition: 2024 impl< Target: FromEvent + FromEvent, diff --git a/src/tools/rustfmt/tests/target/issue-3840/version-two_soft-tabs.rs b/src/tools/rustfmt/tests/target/issue-3840/version-two_soft-tabs.rs index bc59b0baa56..c76dd4dafbb 100644 --- a/src/tools/rustfmt/tests/target/issue-3840/version-two_soft-tabs.rs +++ b/src/tools/rustfmt/tests/target/issue-3840/version-two_soft-tabs.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 impl< Target: FromEvent + FromEvent, diff --git a/src/tools/rustfmt/tests/target/issue-3882.rs b/src/tools/rustfmt/tests/target/issue-3882.rs index 5eb442af974..8e617635b7c 100644 --- a/src/tools/rustfmt/tests/target/issue-3882.rs +++ b/src/tools/rustfmt/tests/target/issue-3882.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn bar(_t: T, // bar ) { } diff --git a/src/tools/rustfmt/tests/target/issue-4381/style_edition_2015.rs b/src/tools/rustfmt/tests/target/issue-4381/style_edition_2015.rs new file mode 100644 index 00000000000..1fc1613083e --- /dev/null +++ b/src/tools/rustfmt/tests/target/issue-4381/style_edition_2015.rs @@ -0,0 +1,3 @@ +// rustfmt-style_edition: 2015 + +use std::num::{NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8}; diff --git a/src/tools/rustfmt/tests/target/issue-4381/style_edition_2024.rs b/src/tools/rustfmt/tests/target/issue-4381/style_edition_2024.rs new file mode 100644 index 00000000000..df85636ad57 --- /dev/null +++ b/src/tools/rustfmt/tests/target/issue-4381/style_edition_2024.rs @@ -0,0 +1,3 @@ +// rustfmt-style_edition: 2024 + +use std::num::{NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64}; diff --git a/src/tools/rustfmt/tests/target/issue-4530.rs b/src/tools/rustfmt/tests/target/issue-4530.rs index 296dc559a93..9e9fbf95c2d 100644 --- a/src/tools/rustfmt/tests/target/issue-4530.rs +++ b/src/tools/rustfmt/tests/target/issue-4530.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn main() { let [ aaaaaaaaaaaaaaaaaaaaaaaaaa, diff --git a/src/tools/rustfmt/tests/target/issue-4689/one.rs b/src/tools/rustfmt/tests/target/issue-4689/one.rs index 7735e34f3b5..fb523f38501 100644 --- a/src/tools/rustfmt/tests/target/issue-4689/one.rs +++ b/src/tools/rustfmt/tests/target/issue-4689/one.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 // Based on the issue description pub trait PrettyPrinter<'tcx>: diff --git a/src/tools/rustfmt/tests/target/issue-4689/two.rs b/src/tools/rustfmt/tests/target/issue-4689/two.rs index e3b5cd22810..30303490129 100644 --- a/src/tools/rustfmt/tests/target/issue-4689/two.rs +++ b/src/tools/rustfmt/tests/target/issue-4689/two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 // Based on the issue description pub trait PrettyPrinter<'tcx>: diff --git a/src/tools/rustfmt/tests/target/issue-5586.rs b/src/tools/rustfmt/tests/target/issue-5586.rs index 7033ae975b3..afe13d1fb1c 100644 --- a/src/tools/rustfmt/tests/target/issue-5586.rs +++ b/src/tools/rustfmt/tests/target/issue-5586.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn main() { // sample 1 { diff --git a/src/tools/rustfmt/tests/target/issue-5655/one.rs b/src/tools/rustfmt/tests/target/issue-5655/one.rs index 1758ec56f8b..62df2655c29 100644 --- a/src/tools/rustfmt/tests/target/issue-5655/one.rs +++ b/src/tools/rustfmt/tests/target/issue-5655/one.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 fn foo(_: T) where diff --git a/src/tools/rustfmt/tests/target/issue-5655/two.rs b/src/tools/rustfmt/tests/target/issue-5655/two.rs index 14fbc3d1321..4a7eb4f4bc2 100644 --- a/src/tools/rustfmt/tests/target/issue-5655/two.rs +++ b/src/tools/rustfmt/tests/target/issue-5655/two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn foo(_: T) where diff --git a/src/tools/rustfmt/tests/target/issue-5801/comment_does_not_wrap_within_max_width.rs b/src/tools/rustfmt/tests/target/issue-5801/comment_does_not_wrap_within_max_width.rs index 9f294751108..461b5873a68 100644 --- a/src/tools/rustfmt/tests/target/issue-5801/comment_does_not_wrap_within_max_width.rs +++ b/src/tools/rustfmt/tests/target/issue-5801/comment_does_not_wrap_within_max_width.rs @@ -1,7 +1,7 @@ // rustfmt-comment_width: 120 // rustfmt-wrap_comments: true // rustfmt-max_width: 120 -// rustfmt-version: Two +// rustfmt-style_edition: 2024 /// This function is 120 columns wide and is left alone. This comment is 120 columns wide and the formatter is also fine fn my_super_cool_function_name(my_very_cool_argument_name: String, my_other_very_cool_argument_name: String) -> String { diff --git a/src/tools/rustfmt/tests/target/issue-5801/comment_unexpectedly_wraps_before_max_width.rs b/src/tools/rustfmt/tests/target/issue-5801/comment_unexpectedly_wraps_before_max_width.rs index dd839dd4548..58bb8a1c2e3 100644 --- a/src/tools/rustfmt/tests/target/issue-5801/comment_unexpectedly_wraps_before_max_width.rs +++ b/src/tools/rustfmt/tests/target/issue-5801/comment_unexpectedly_wraps_before_max_width.rs @@ -1,7 +1,7 @@ // rustfmt-comment_width: 120 // rustfmt-wrap_comments: true // rustfmt-max_width: 120 -// rustfmt-version: One +// rustfmt-style_edition: 2015 /// This function is 120 columns wide and is left alone. This comment is 120 columns wide and the formatter is also fine fn my_super_cool_function_name(my_very_cool_argument_name: String, my_other_very_cool_argument_name: String) -> String { diff --git a/src/tools/rustfmt/tests/target/issue-5987/one.rs b/src/tools/rustfmt/tests/target/issue-5987/one.rs index 3c995ed28ba..17327feead5 100644 --- a/src/tools/rustfmt/tests/target/issue-5987/one.rs +++ b/src/tools/rustfmt/tests/target/issue-5987/one.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 fn main() { trace!( diff --git a/src/tools/rustfmt/tests/target/issue-5987/two.rs b/src/tools/rustfmt/tests/target/issue-5987/two.rs index 8fd92fc179e..41ecb13e3db 100644 --- a/src/tools/rustfmt/tests/target/issue-5987/two.rs +++ b/src/tools/rustfmt/tests/target/issue-5987/two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn main() { trace!( diff --git a/src/tools/rustfmt/tests/target/issue-6109.rs b/src/tools/rustfmt/tests/target/issue-6109.rs new file mode 100644 index 00000000000..7d2bbf75691 --- /dev/null +++ b/src/tools/rustfmt/tests/target/issue-6109.rs @@ -0,0 +1,7 @@ +/// Some doc comment with code snippet: +///``` +/// '\u{1F} +/// ``` +pub struct Code {} + +fn main() {} diff --git a/src/tools/rustfmt/tests/target/issue-6147/case_rustfmt_v1.rs b/src/tools/rustfmt/tests/target/issue-6147/case_rustfmt_v1.rs index 75800012c63..d8e67655e4c 100644 --- a/src/tools/rustfmt/tests/target/issue-6147/case_rustfmt_v1.rs +++ b/src/tools/rustfmt/tests/target/issue-6147/case_rustfmt_v1.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 pub fn main() { let a = Some(12); diff --git a/src/tools/rustfmt/tests/target/issue-6147/case_rustfmt_v2.rs b/src/tools/rustfmt/tests/target/issue-6147/case_rustfmt_v2.rs index 5e4220e7306..9d0b12d686b 100644 --- a/src/tools/rustfmt/tests/target/issue-6147/case_rustfmt_v2.rs +++ b/src/tools/rustfmt/tests/target/issue-6147/case_rustfmt_v2.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 pub fn main() { let a = Some(12); diff --git a/src/tools/rustfmt/tests/target/issue_5027.rs b/src/tools/rustfmt/tests/target/issue_5027.rs index 26d771720b6..1de3e0d4f15 100644 --- a/src/tools/rustfmt/tests/target/issue_5027.rs +++ b/src/tools/rustfmt/tests/target/issue_5027.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 pub type Iter<'a, D> = impl DoubleEndedIterator)> + ExactSizeIterator diff --git a/src/tools/rustfmt/tests/target/let_else_v2.rs b/src/tools/rustfmt/tests/target/let_else_v2.rs index b25ac1609d8..6e886299cb0 100644 --- a/src/tools/rustfmt/tests/target/let_else_v2.rs +++ b/src/tools/rustfmt/tests/target/let_else_v2.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 // rustfmt-single_line_let_else_max_width: 100 fn issue5901() { diff --git a/src/tools/rustfmt/tests/target/long-fn-1/version_one.rs b/src/tools/rustfmt/tests/target/long-fn-1/version_one.rs index 05f69953c26..60f672c1837 100644 --- a/src/tools/rustfmt/tests/target/long-fn-1/version_one.rs +++ b/src/tools/rustfmt/tests/target/long-fn-1/version_one.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 // Tests that a function which is almost short enough, but not quite, gets // formatted correctly. diff --git a/src/tools/rustfmt/tests/target/long-fn-1/version_two.rs b/src/tools/rustfmt/tests/target/long-fn-1/version_two.rs index 32794bccde2..f6007398bcc 100644 --- a/src/tools/rustfmt/tests/target/long-fn-1/version_two.rs +++ b/src/tools/rustfmt/tests/target/long-fn-1/version_two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 // Tests that a function which is almost short enough, but not quite, gets // formatted correctly. diff --git a/src/tools/rustfmt/tests/target/one_line_if_v1.rs b/src/tools/rustfmt/tests/target/one_line_if_v1.rs index b3c6c4cbeb2..5160ea0264c 100644 --- a/src/tools/rustfmt/tests/target/one_line_if_v1.rs +++ b/src/tools/rustfmt/tests/target/one_line_if_v1.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 fn plain_if(x: bool) -> u8 { if x { diff --git a/src/tools/rustfmt/tests/target/one_line_if_v2.rs b/src/tools/rustfmt/tests/target/one_line_if_v2.rs index 81ca4c8b8b4..a9610ec9749 100644 --- a/src/tools/rustfmt/tests/target/one_line_if_v2.rs +++ b/src/tools/rustfmt/tests/target/one_line_if_v2.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn plain_if(x: bool) -> u8 { if x { 0 } else { 1 } diff --git a/src/tools/rustfmt/tests/target/single-line-macro/v1.rs b/src/tools/rustfmt/tests/target/single-line-macro/v1.rs index a3aa631ed4a..fea146d8d33 100644 --- a/src/tools/rustfmt/tests/target/single-line-macro/v1.rs +++ b/src/tools/rustfmt/tests/target/single-line-macro/v1.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 // #2652 // Preserve trailing comma inside macro, even if it looks an array. diff --git a/src/tools/rustfmt/tests/target/single-line-macro/v2.rs b/src/tools/rustfmt/tests/target/single-line-macro/v2.rs index 9c6bcf33ad5..6fcacb70ba3 100644 --- a/src/tools/rustfmt/tests/target/single-line-macro/v2.rs +++ b/src/tools/rustfmt/tests/target/single-line-macro/v2.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 // #2652 // Preserve trailing comma inside macro, even if it looks an array. diff --git a/src/tools/rustfmt/tests/target/style_edition/default.rs b/src/tools/rustfmt/tests/target/style_edition/default.rs new file mode 100644 index 00000000000..17442df6c49 --- /dev/null +++ b/src/tools/rustfmt/tests/target/style_edition/default.rs @@ -0,0 +1,10 @@ +fn build_sorted_static_get_entry_names( + mut entries: Vec<(u8, &'static str)>, +) -> (impl Fn( + AlphabeticalTraversal, + Box>, +) -> BoxFuture<'static, Result, Status>> + + Send + + Sync + + 'static) { +} diff --git a/src/tools/rustfmt/tests/target/style_edition/follows_edition.rs b/src/tools/rustfmt/tests/target/style_edition/follows_edition.rs new file mode 100644 index 00000000000..c36a993d842 --- /dev/null +++ b/src/tools/rustfmt/tests/target/style_edition/follows_edition.rs @@ -0,0 +1,14 @@ +// rustfmt-edition: 2024 + +fn build_sorted_static_get_entry_names( + mut entries: Vec<(u8, &'static str)>, +) -> ( + impl Fn( + AlphabeticalTraversal, + Box>, + ) -> BoxFuture<'static, Result, Status>> + + Send + + Sync + + 'static +) { +} diff --git a/src/tools/rustfmt/tests/target/style_edition/overrides_edition_when_set.rs b/src/tools/rustfmt/tests/target/style_edition/overrides_edition_when_set.rs new file mode 100644 index 00000000000..6d0eaac8970 --- /dev/null +++ b/src/tools/rustfmt/tests/target/style_edition/overrides_edition_when_set.rs @@ -0,0 +1,14 @@ +// rustfmt-edition: 2018 +// rustfmt-style_edition: 2024 +fn build_sorted_static_get_entry_names( + mut entries: Vec<(u8, &'static str)>, +) -> ( + impl Fn( + AlphabeticalTraversal, + Box>, + ) -> BoxFuture<'static, Result, Status>> + + Send + + Sync + + 'static +) { +} diff --git a/src/tools/rustfmt/tests/target/trailing_comments/hard_tabs.rs b/src/tools/rustfmt/tests/target/trailing_comments/hard_tabs.rs index 35e72f1affd..e7009ac00c0 100644 --- a/src/tools/rustfmt/tests/target/trailing_comments/hard_tabs.rs +++ b/src/tools/rustfmt/tests/target/trailing_comments/hard_tabs.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 // rustfmt-wrap_comments: true // rustfmt-hard_tabs: true diff --git a/src/tools/rustfmt/tests/target/trailing_comments/soft_tabs.rs b/src/tools/rustfmt/tests/target/trailing_comments/soft_tabs.rs index eba943042ad..34cfed1a229 100644 --- a/src/tools/rustfmt/tests/target/trailing_comments/soft_tabs.rs +++ b/src/tools/rustfmt/tests/target/trailing_comments/soft_tabs.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 // rustfmt-wrap_comments: true pub const IFF_MULTICAST: ::c_int = 0x0000000800; // Supports multicast diff --git a/src/tools/rustfmt/tests/target/tuple_v2.rs b/src/tools/rustfmt/tests/target/tuple_v2.rs index ba653291c2f..fa508332be5 100644 --- a/src/tools/rustfmt/tests/target/tuple_v2.rs +++ b/src/tools/rustfmt/tests/target/tuple_v2.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn issue_4355() { let _ = ((1,),).0.0; diff --git a/src/tools/rustfmt/tests/target/type-alias-where-clauses-with-comments.rs b/src/tools/rustfmt/tests/target/type-alias-where-clauses-with-comments.rs new file mode 100644 index 00000000000..25c38916376 --- /dev/null +++ b/src/tools/rustfmt/tests/target/type-alias-where-clauses-with-comments.rs @@ -0,0 +1,39 @@ +type Foo +// comment1 +// interlinear1 +where + // comment2 + // interlinear2 + A: B, // comment3 + C: D, // comment4 +// interlinear3 += E; // comment5 + +type Foo +// comment6 +// interlinear4 +where + // comment7 + // interlinear5 + A: B, // comment8 + C: D, // comment9 +// interlinear6 += E +// comment10 +// interlinear7 +where + // comment11 + // interlinear8 + F: G, // comment12 + H: I; // comment13 + +type Foo // comment14 + // interlinear9 + = E +// comment15 +// interlinear10 +where + // comment16 + // interlinear11 + F: G, // comment17 + H: I; // comment18 diff --git a/src/tools/rustfmt/tests/target/type-alias-where-clauses.rs b/src/tools/rustfmt/tests/target/type-alias-where-clauses.rs new file mode 100644 index 00000000000..96d2d63bbb8 --- /dev/null +++ b/src/tools/rustfmt/tests/target/type-alias-where-clauses.rs @@ -0,0 +1,20 @@ +type Foo +where + A: B, + C: D, += E; + +type Foo +where + A: B, + C: D, += E +where + F: G, + H: I; + +type Foo + = E +where + F: G, + H: I;