mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 00:03:43 +00:00
Merge commit 'b23b69900eab1260be510b2bd8922f4b6de6cf1e' into sync-from-rustfmt
This commit is contained in:
commit
eeda9dd070
@ -4630,7 +4630,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustfmt-nightly"
|
||||
version = "1.7.1"
|
||||
version = "1.8.0"
|
||||
dependencies = [
|
||||
"annotate-snippets 0.9.2",
|
||||
"anyhow",
|
||||
|
@ -21,7 +21,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: install rustup
|
||||
run: |
|
||||
|
@ -64,7 +64,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Run build
|
||||
- name: install rustup
|
||||
|
@ -26,7 +26,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Run build
|
||||
- name: install rustup
|
||||
|
3
src/tools/rustfmt/.github/workflows/mac.yml
vendored
3
src/tools/rustfmt/.github/workflows/mac.yml
vendored
@ -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
|
||||
|
@ -11,7 +11,7 @@ jobs:
|
||||
name: rustdoc check
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: install rustup
|
||||
run: |
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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`
|
||||
|
@ -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
|
||||
|
@ -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<String> {
|
||||
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<String> {
|
||||
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()
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
58
src/tools/rustfmt/check_diff/src/lib.rs
Normal file
58
src/tools/rustfmt/check_diff/src/lib.rs
Normal file
@ -0,0 +1,58 @@
|
||||
use std::env;
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
use tracing::info;
|
||||
|
||||
pub enum GitError {
|
||||
FailedClone { stdout: Vec<u8>, stderr: Vec<u8> },
|
||||
IO(std::io::Error),
|
||||
}
|
||||
|
||||
impl From<io::Error> for GitError {
|
||||
fn from(error: io::Error) -> Self {
|
||||
GitError::IO(error)
|
||||
}
|
||||
}
|
||||
|
||||
/// Clone a git repository
|
||||
///
|
||||
/// Parameters:
|
||||
/// url: git clone url
|
||||
/// dest: directory where the repo should be cloned
|
||||
pub fn clone_git_repo(url: &str, dest: &Path) -> Result<(), GitError> {
|
||||
let git_cmd = Command::new("git")
|
||||
.env("GIT_TERMINAL_PROMPT", "0")
|
||||
.args([
|
||||
"clone",
|
||||
"--quiet",
|
||||
url,
|
||||
"--depth",
|
||||
"1",
|
||||
dest.to_str().unwrap(),
|
||||
])
|
||||
.output()?;
|
||||
|
||||
// if the git command does not return successfully,
|
||||
// any command on the repo will fail. So fail fast.
|
||||
if !git_cmd.status.success() {
|
||||
let error = GitError::FailedClone {
|
||||
stdout: git_cmd.stdout,
|
||||
stderr: git_cmd.stderr,
|
||||
};
|
||||
return Err(error);
|
||||
}
|
||||
|
||||
info!("Successfully clone repository.");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
pub fn change_directory_to_path(dest: &Path) -> io::Result<()> {
|
||||
let dest_path = Path::new(&dest);
|
||||
env::set_current_dir(&dest_path)?;
|
||||
info!(
|
||||
"Current directory: {}",
|
||||
env::current_dir().unwrap().display()
|
||||
);
|
||||
return Ok(());
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
12
src/tools/rustfmt/check_diff/tests/bash_commands.rs
Normal file
12
src/tools/rustfmt/check_diff/tests/bash_commands.rs
Normal file
@ -0,0 +1,12 @@
|
||||
use check_diff::change_directory_to_path;
|
||||
use std::env;
|
||||
use tempfile::Builder;
|
||||
|
||||
#[test]
|
||||
fn cd_test() {
|
||||
// Creates an empty directory in the current working directory
|
||||
let dir = Builder::new().tempdir_in("").unwrap();
|
||||
let dest_path = dir.path();
|
||||
change_directory_to_path(dest_path).unwrap();
|
||||
assert_eq!(env::current_dir().unwrap(), dest_path);
|
||||
}
|
16
src/tools/rustfmt/check_diff/tests/git.rs
Normal file
16
src/tools/rustfmt/check_diff/tests/git.rs
Normal file
@ -0,0 +1,16 @@
|
||||
use check_diff::clone_git_repo;
|
||||
|
||||
use tempfile::Builder;
|
||||
|
||||
#[test]
|
||||
fn clone_repo_test() {
|
||||
// Creates an empty directory in the current working directory
|
||||
let dir = Builder::new().tempdir_in("").unwrap();
|
||||
let sample_repo = "https://github.com/rust-lang/rustfmt.git";
|
||||
let dest_path = dir.path();
|
||||
let result = clone_git_repo(sample_repo, dest_path);
|
||||
assert!(result.is_ok());
|
||||
// check whether a .git folder exists after cloning the repo
|
||||
let git_repo = dest_path.join(".git");
|
||||
assert!(git_repo.exists());
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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"] }
|
||||
|
@ -1,5 +1,5 @@
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{quote, ToTokens};
|
||||
use quote::{ToTokens, quote};
|
||||
|
||||
pub fn fold_quote<F, I, T>(input: impl Iterator<Item = I>, f: F) -> TokenStream
|
||||
where
|
||||
|
@ -1,3 +1,3 @@
|
||||
[toolchain]
|
||||
channel = "nightly-2024-06-13"
|
||||
channel = "nightly-2024-09-10"
|
||||
components = ["llvm-tools", "rustc-dev"]
|
||||
|
@ -1,3 +1,4 @@
|
||||
error_on_line_overflow = true
|
||||
error_on_unformatted = true
|
||||
version = "Two"
|
||||
style_edition = "2024"
|
||||
overflow_delimited_expr = false
|
||||
|
@ -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<String>)> {
|
||||
) -> Result<(usize, Option<String>), 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::<Vec<_>>()
|
||||
.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<String> {
|
||||
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<String> {
|
||||
Some(match self.kind {
|
||||
self.rewrite_result(context, shape).ok()
|
||||
}
|
||||
|
||||
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
|
||||
Ok(match self.kind {
|
||||
ast::MetaItemKind::Word => {
|
||||
rewrite_path(context, PathContext::Type, &None, &self.path, shape)?
|
||||
}
|
||||
@ -287,7 +297,7 @@ impl Rewrite for ast::MetaItem {
|
||||
&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 @@ impl Rewrite for ast::MetaItem {
|
||||
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 @@ impl Rewrite for ast::MetaItem {
|
||||
// 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 @@ impl Rewrite for ast::MetaItem {
|
||||
|
||||
impl Rewrite for ast::Attribute {
|
||||
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
|
||||
self.rewrite_result(context, shape).ok()
|
||||
}
|
||||
|
||||
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
|
||||
let snippet = context.snippet(self.span);
|
||||
if self.is_doc_comment() {
|
||||
rewrite_doc_comment(snippet, shape.comment(context.config), context.config)
|
||||
@ -328,7 +344,7 @@ impl Rewrite for ast::Attribute {
|
||||
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 @@ impl Rewrite for ast::Attribute {
|
||||
}
|
||||
|
||||
// 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 @@ impl Rewrite for ast::Attribute {
|
||||
},
|
||||
))
|
||||
} else {
|
||||
Some(snippet.to_owned())
|
||||
Ok(snippet.to_owned())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -375,8 +393,12 @@ impl Rewrite for ast::Attribute {
|
||||
|
||||
impl Rewrite for [ast::Attribute] {
|
||||
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
|
||||
self.rewrite_result(context, shape).ok()
|
||||
}
|
||||
|
||||
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
|
||||
if self.is_empty() {
|
||||
return Some(String::new());
|
||||
return Ok(String::new());
|
||||
}
|
||||
|
||||
// The current remaining attributes.
|
||||
@ -392,7 +414,7 @@ impl Rewrite for [ast::Attribute] {
|
||||
// merging derives into a single attribute.
|
||||
loop {
|
||||
if attrs.is_empty() {
|
||||
return Some(result);
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
// Handle doc comments.
|
||||
@ -431,7 +453,7 @@ impl Rewrite for [ast::Attribute] {
|
||||
// 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 @@ impl Rewrite for [ast::Attribute] {
|
||||
// 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
|
||||
|
@ -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 tracing_subscriber::EnvFilter;
|
||||
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<i32> {
|
||||
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) => {}
|
||||
Some(emit_mode) => {
|
||||
return Err(OperationError::StdinBadEmit(emit_mode).into());
|
||||
}
|
||||
}
|
||||
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().verbose(Verbosity::Quiet);
|
||||
|
||||
// parse 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 @@ are included as out of line modules from `src/lib.rs`."
|
||||
}
|
||||
|
||||
fn print_version() {
|
||||
let version_info = format!(
|
||||
"{}-{}",
|
||||
option_env!("CARGO_PKG_VERSION").unwrap_or("unknown"),
|
||||
include_str!(concat!(env!("OUT_DIR"), "/commit-info.txt"))
|
||||
);
|
||||
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<Operation, OperationError> {
|
||||
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<Edition>,
|
||||
style_edition: Option<StyleEdition>,
|
||||
color: Option<Color>,
|
||||
file_lines: FileLines, // Default is all lines in all files.
|
||||
unstable_features: bool,
|
||||
@ -545,6 +568,10 @@ impl GetOptsOptions {
|
||||
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 @@ impl GetOptsOptions {
|
||||
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 @@ impl GetOptsOptions {
|
||||
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);
|
||||
}
|
||||
|
||||
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 @@ impl CliOptions for GetOptsOptions {
|
||||
fn config_path(&self) -> Option<&Path> {
|
||||
self.config_path.as_deref()
|
||||
}
|
||||
|
||||
fn edition(&self) -> Option<Edition> {
|
||||
self.inline_config
|
||||
.get("edition")
|
||||
.map_or(self.edition, |e| Edition::from_str(e).ok())
|
||||
}
|
||||
|
||||
fn style_edition(&self) -> Option<StyleEdition> {
|
||||
self.inline_config
|
||||
.get("style_edition")
|
||||
.map_or(self.style_edition, |se| StyleEdition::from_str(se).ok())
|
||||
}
|
||||
|
||||
fn version(&self) -> Option<Version> {
|
||||
self.inline_config
|
||||
.get("version")
|
||||
.map(|version| Version::from_str(version).ok())
|
||||
.flatten()
|
||||
}
|
||||
}
|
||||
|
||||
fn edition_from_edition_str(edition_str: &str) -> Result<Edition> {
|
||||
@ -702,6 +764,16 @@ fn edition_from_edition_str(edition_str: &str) -> Result<Edition> {
|
||||
}
|
||||
}
|
||||
|
||||
fn style_edition_from_style_edition_str(edition_str: &str) -> Result<StyleEdition> {
|
||||
match edition_str {
|
||||
"2015" => Ok(StyleEdition::Edition2015),
|
||||
"2018" => Ok(StyleEdition::Edition2018),
|
||||
"2021" => Ok(StyleEdition::Edition2021),
|
||||
"2024" => Ok(StyleEdition::Edition2024),
|
||||
_ => Err(format_err!("Invalid value for `--style-edition`")),
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_mode_from_emit_str(emit_str: &str) -> Result<EmitMode> {
|
||||
match emit_str {
|
||||
"files" => Ok(EmitMode::Files),
|
||||
@ -712,3 +784,185 @@ fn emit_mode_from_emit_str(emit_str: &str) -> Result<EmitMode> {
|
||||
_ => Err(format_err!("Invalid value for `--emit`")),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(dead_code)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use rustfmt_config_proc_macro::nightly_only_test;
|
||||
|
||||
fn get_config<O: CliOptions>(path: Option<&Path>, options: Option<O>) -> Config {
|
||||
load_config(path, options).unwrap().0
|
||||
}
|
||||
|
||||
#[nightly_only_test]
|
||||
#[test]
|
||||
fn flag_sets_style_edition_override_correctly() {
|
||||
let mut options = GetOptsOptions::default();
|
||||
options.style_edition = Some(StyleEdition::Edition2024);
|
||||
let config = get_config(None, Some(options));
|
||||
assert_eq!(config.style_edition(), StyleEdition::Edition2024);
|
||||
}
|
||||
|
||||
#[nightly_only_test]
|
||||
#[test]
|
||||
fn edition_sets_style_edition_override_correctly() {
|
||||
let mut options = GetOptsOptions::default();
|
||||
options.edition = Some(Edition::Edition2024);
|
||||
let config = get_config(None, Some(options));
|
||||
assert_eq!(config.style_edition(), StyleEdition::Edition2024);
|
||||
}
|
||||
|
||||
#[nightly_only_test]
|
||||
#[test]
|
||||
fn version_sets_style_edition_override_correctly() {
|
||||
let mut options = GetOptsOptions::default();
|
||||
options.inline_config = HashMap::from([("version".to_owned(), "Two".to_owned())]);
|
||||
let config = get_config(None, Some(options));
|
||||
assert_eq!(config.style_edition(), StyleEdition::Edition2024);
|
||||
assert_eq!(config.overflow_delimited_expr(), true);
|
||||
}
|
||||
|
||||
#[nightly_only_test]
|
||||
#[test]
|
||||
fn version_config_file_sets_style_edition_override_correctly() {
|
||||
let options = GetOptsOptions::default();
|
||||
let config_file = Some(Path::new("tests/config/style-edition/just-version"));
|
||||
let config = get_config(config_file, Some(options));
|
||||
assert_eq!(config.style_edition(), StyleEdition::Edition2024);
|
||||
assert_eq!(config.overflow_delimited_expr(), true);
|
||||
}
|
||||
|
||||
#[nightly_only_test]
|
||||
#[test]
|
||||
fn style_edition_flag_has_correct_precedence_over_edition() {
|
||||
let mut options = GetOptsOptions::default();
|
||||
options.style_edition = Some(StyleEdition::Edition2021);
|
||||
options.edition = Some(Edition::Edition2024);
|
||||
let config = get_config(None, Some(options));
|
||||
assert_eq!(config.style_edition(), StyleEdition::Edition2021);
|
||||
}
|
||||
|
||||
#[nightly_only_test]
|
||||
#[test]
|
||||
fn style_edition_flag_has_correct_precedence_over_version() {
|
||||
let mut options = GetOptsOptions::default();
|
||||
options.style_edition = Some(StyleEdition::Edition2018);
|
||||
options.inline_config = HashMap::from([("version".to_owned(), "Two".to_owned())]);
|
||||
let config = get_config(None, Some(options));
|
||||
assert_eq!(config.style_edition(), StyleEdition::Edition2018);
|
||||
}
|
||||
|
||||
#[nightly_only_test]
|
||||
#[test]
|
||||
fn style_edition_flag_has_correct_precedence_over_edition_version() {
|
||||
let mut options = GetOptsOptions::default();
|
||||
options.style_edition = Some(StyleEdition::Edition2021);
|
||||
options.edition = Some(Edition::Edition2018);
|
||||
options.inline_config = HashMap::from([("version".to_owned(), "Two".to_owned())]);
|
||||
let config = get_config(None, Some(options));
|
||||
assert_eq!(config.style_edition(), StyleEdition::Edition2021);
|
||||
}
|
||||
|
||||
#[nightly_only_test]
|
||||
#[test]
|
||||
fn style_edition_inline_has_correct_precedence_over_edition_version() {
|
||||
let mut options = GetOptsOptions::default();
|
||||
options.edition = Some(Edition::Edition2018);
|
||||
options.inline_config = HashMap::from([
|
||||
("version".to_owned(), "One".to_owned()),
|
||||
("style_edition".to_owned(), "2024".to_owned()),
|
||||
]);
|
||||
let config = get_config(None, Some(options));
|
||||
assert_eq!(config.style_edition(), StyleEdition::Edition2024);
|
||||
assert_eq!(config.overflow_delimited_expr(), true);
|
||||
}
|
||||
|
||||
#[nightly_only_test]
|
||||
#[test]
|
||||
fn style_edition_config_file_trumps_edition_flag_version_inline() {
|
||||
let mut options = GetOptsOptions::default();
|
||||
let config_file = Some(Path::new("tests/config/style-edition/just-style-edition"));
|
||||
options.edition = Some(Edition::Edition2018);
|
||||
options.inline_config = HashMap::from([("version".to_owned(), "One".to_owned())]);
|
||||
let config = get_config(config_file, Some(options));
|
||||
assert_eq!(config.style_edition(), StyleEdition::Edition2024);
|
||||
}
|
||||
|
||||
#[nightly_only_test]
|
||||
#[test]
|
||||
fn style_edition_config_file_trumps_edition_config_and_version_inline() {
|
||||
let mut options = GetOptsOptions::default();
|
||||
let config_file = Some(Path::new(
|
||||
"tests/config/style-edition/style-edition-and-edition",
|
||||
));
|
||||
options.inline_config = HashMap::from([("version".to_owned(), "Two".to_owned())]);
|
||||
let config = get_config(config_file, Some(options));
|
||||
assert_eq!(config.style_edition(), StyleEdition::Edition2021);
|
||||
assert_eq!(config.edition(), Edition::Edition2024);
|
||||
}
|
||||
|
||||
#[nightly_only_test]
|
||||
#[test]
|
||||
fn version_config_trumps_edition_config_and_flag() {
|
||||
let mut options = GetOptsOptions::default();
|
||||
let config_file = Some(Path::new("tests/config/style-edition/version-edition"));
|
||||
options.edition = Some(Edition::Edition2018);
|
||||
let config = get_config(config_file, Some(options));
|
||||
assert_eq!(config.style_edition(), StyleEdition::Edition2024);
|
||||
}
|
||||
|
||||
#[nightly_only_test]
|
||||
#[test]
|
||||
fn style_edition_config_file_trumps_version_config() {
|
||||
let options = GetOptsOptions::default();
|
||||
let config_file = Some(Path::new(
|
||||
"tests/config/style-edition/version-style-edition",
|
||||
));
|
||||
let config = get_config(config_file, Some(options));
|
||||
assert_eq!(config.style_edition(), StyleEdition::Edition2021);
|
||||
}
|
||||
|
||||
#[nightly_only_test]
|
||||
#[test]
|
||||
fn style_edition_config_file_trumps_edition_version_config() {
|
||||
let options = GetOptsOptions::default();
|
||||
let config_file = Some(Path::new(
|
||||
"tests/config/style-edition/version-style-edition-and-edition",
|
||||
));
|
||||
let config = get_config(config_file, Some(options));
|
||||
assert_eq!(config.style_edition(), StyleEdition::Edition2021);
|
||||
}
|
||||
|
||||
#[nightly_only_test]
|
||||
#[test]
|
||||
fn correct_defaults_for_style_edition_loaded() {
|
||||
let mut options = GetOptsOptions::default();
|
||||
options.style_edition = Some(StyleEdition::Edition2024);
|
||||
let config = get_config(None, Some(options));
|
||||
assert_eq!(config.style_edition(), StyleEdition::Edition2024);
|
||||
assert_eq!(config.overflow_delimited_expr(), true);
|
||||
}
|
||||
|
||||
#[nightly_only_test]
|
||||
#[test]
|
||||
fn style_edition_defaults_overridden_from_config() {
|
||||
let options = GetOptsOptions::default();
|
||||
let config_file = Some(Path::new("tests/config/style-edition/overrides"));
|
||||
let config = get_config(config_file, Some(options));
|
||||
assert_eq!(config.style_edition(), StyleEdition::Edition2024);
|
||||
assert_eq!(config.overflow_delimited_expr(), false);
|
||||
}
|
||||
|
||||
#[nightly_only_test]
|
||||
#[test]
|
||||
fn style_edition_defaults_overridden_from_cli() {
|
||||
let mut options = GetOptsOptions::default();
|
||||
let config_file = Some(Path::new("tests/config/style-edition/just-style-edition"));
|
||||
options.inline_config =
|
||||
HashMap::from([("overflow_delimited_expr".to_owned(), "false".to_owned())]);
|
||||
let config = get_config(config_file, Some(options));
|
||||
assert_eq!(config.style_edition(), StyleEdition::Edition2024);
|
||||
assert_eq!(config.overflow_delimited_expr(), false);
|
||||
}
|
||||
}
|
||||
|
@ -59,15 +59,15 @@ use std::borrow::Cow;
|
||||
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 @@ use thin_vec::ThinVec;
|
||||
/// 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<String> {
|
||||
// TODO(ding-young): Currently returning None when the given span is out of the range
|
||||
// covered by the snippet provider. If this is a common cause for internal
|
||||
// rewrite failure, add a new enum variant and return RewriteError instead of None
|
||||
context.snippet_provider.span_to_snippet(span).map(|s| {
|
||||
s.lines()
|
||||
.map(|l| l.trim_end())
|
||||
@ -93,12 +96,16 @@ fn format_chain_item(
|
||||
context: &RewriteContext<'_>,
|
||||
rewrite_shape: Shape,
|
||||
allow_overflow: bool,
|
||||
) -> Option<String> {
|
||||
) -> 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<String> {
|
||||
) -> 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 @@ impl ChainItemKind {
|
||||
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 @@ impl ChainItemKind {
|
||||
|
||||
impl Rewrite for ChainItem {
|
||||
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
|
||||
let shape = shape.sub_width(self.tries)?;
|
||||
self.rewrite_result(context, shape).ok()
|
||||
}
|
||||
|
||||
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
|
||||
let shape = shape
|
||||
.sub_width(self.tries)
|
||||
.max_width_error(shape.width, self.span)?;
|
||||
let rewrite = match self.kind {
|
||||
ChainItemKind::Parent {
|
||||
ref expr,
|
||||
@ -278,14 +291,14 @@ impl Rewrite for ChainItem {
|
||||
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 @@ impl Rewrite for ChainItem {
|
||||
rewrite_comment(comment, false, shape, context.config)?
|
||||
}
|
||||
};
|
||||
Some(format!("{rewrite}{}", "?".repeat(self.tries)))
|
||||
Ok(format!("{rewrite}{}", "?".repeat(self.tries)))
|
||||
}
|
||||
}
|
||||
|
||||
@ -327,14 +340,14 @@ impl ChainItem {
|
||||
span: Span,
|
||||
context: &RewriteContext<'_>,
|
||||
shape: Shape,
|
||||
) -> Option<String> {
|
||||
) -> RewriteResult {
|
||||
let type_str = if types.is_empty() {
|
||||
String::new()
|
||||
} else {
|
||||
let type_list = types
|
||||
.iter()
|
||||
.map(|ty| ty.rewrite(context, shape))
|
||||
.collect::<Option<Vec<_>>>()?;
|
||||
.map(|ty| ty.rewrite_result(context, shape))
|
||||
.collect::<Result<Vec<_>, RewriteError>>()?;
|
||||
|
||||
format!("::<{}>", type_list.join(", "))
|
||||
};
|
||||
@ -519,6 +532,10 @@ impl Chain {
|
||||
|
||||
impl Rewrite for Chain {
|
||||
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
|
||||
self.rewrite_result(context, shape).ok()
|
||||
}
|
||||
|
||||
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
|
||||
debug!("rewrite chain {:?} {:?}", self, shape);
|
||||
|
||||
let mut formatter = match context.config.indent_style() {
|
||||
@ -532,17 +549,25 @@ impl Rewrite for Chain {
|
||||
|
||||
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 @@ trait ChainFormatter {
|
||||
parent: &ChainItem,
|
||||
context: &RewriteContext<'_>,
|
||||
shape: Shape,
|
||||
) -> Option<()>;
|
||||
) -> Result<(), RewriteError>;
|
||||
fn child_shape(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<Shape>;
|
||||
fn format_children(&mut self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<()>;
|
||||
fn format_children(
|
||||
&mut self,
|
||||
context: &RewriteContext<'_>,
|
||||
child_shape: Shape,
|
||||
) -> Result<(), RewriteError>;
|
||||
fn format_last_child(
|
||||
&mut self,
|
||||
context: &RewriteContext<'_>,
|
||||
shape: Shape,
|
||||
child_shape: Shape,
|
||||
) -> Option<()>;
|
||||
fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<String>;
|
||||
) -> 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<String>;
|
||||
}
|
||||
@ -616,12 +645,16 @@ impl<'a> ChainFormatterShared<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn format_children(&mut self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<()> {
|
||||
fn format_children(
|
||||
&mut self,
|
||||
context: &RewriteContext<'_>,
|
||||
child_shape: Shape,
|
||||
) -> Result<(), RewriteError> {
|
||||
for item in &self.children[..self.children.len() - 1] {
|
||||
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 @@ impl<'a> ChainFormatterShared<'a> {
|
||||
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 @@ impl<'a> ChainFormatterShared<'a> {
|
||||
&& 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 @@ impl<'a> ChainFormatterShared<'a> {
|
||||
};
|
||||
|
||||
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 @@ impl<'a> ChainFormatterShared<'a> {
|
||||
// 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 @@ impl<'a> ChainFormatterShared<'a> {
|
||||
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<String> {
|
||||
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 @@ impl<'a> ChainFormatterShared<'a> {
|
||||
result.push_str(rewrite);
|
||||
}
|
||||
|
||||
Some(result)
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
@ -806,8 +852,8 @@ impl<'a> ChainFormatter for ChainFormatterBlock<'a> {
|
||||
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 @@ impl<'a> ChainFormatter for ChainFormatterBlock<'a> {
|
||||
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 @@ impl<'a> ChainFormatter for ChainFormatterBlock<'a> {
|
||||
}
|
||||
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<Shape> {
|
||||
@ -840,7 +888,11 @@ impl<'a> ChainFormatter for ChainFormatterBlock<'a> {
|
||||
Some(get_block_child_shape(block_end, context, shape))
|
||||
}
|
||||
|
||||
fn format_children(&mut self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<()> {
|
||||
fn format_children(
|
||||
&mut self,
|
||||
context: &RewriteContext<'_>,
|
||||
child_shape: Shape,
|
||||
) -> Result<(), RewriteError> {
|
||||
self.shared.format_children(context, child_shape)
|
||||
}
|
||||
|
||||
@ -849,12 +901,12 @@ impl<'a> ChainFormatter for ChainFormatterBlock<'a> {
|
||||
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<String> {
|
||||
fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> RewriteResult {
|
||||
self.shared.join_rewrites(context, child_shape)
|
||||
}
|
||||
|
||||
@ -885,9 +937,9 @@ impl<'a> ChainFormatter for ChainFormatterVisual<'a> {
|
||||
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 @@ impl<'a> ChainFormatter for ChainFormatterVisual<'a> {
|
||||
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 @@ impl<'a> ChainFormatter for ChainFormatterVisual<'a> {
|
||||
}
|
||||
|
||||
self.shared.rewrites.push(root_rewrite);
|
||||
Some(())
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn child_shape(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<Shape> {
|
||||
@ -932,7 +985,11 @@ impl<'a> ChainFormatter for ChainFormatterVisual<'a> {
|
||||
)
|
||||
}
|
||||
|
||||
fn format_children(&mut self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<()> {
|
||||
fn format_children(
|
||||
&mut self,
|
||||
context: &RewriteContext<'_>,
|
||||
child_shape: Shape,
|
||||
) -> Result<(), RewriteError> {
|
||||
self.shared.format_children(context, child_shape)
|
||||
}
|
||||
|
||||
@ -941,12 +998,12 @@ impl<'a> ChainFormatter for ChainFormatterVisual<'a> {
|
||||
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<String> {
|
||||
fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> RewriteResult {
|
||||
self.shared.join_rewrites(context, child_shape)
|
||||
}
|
||||
|
||||
|
@ -4,17 +4,17 @@ use thin_vec::thin_vec;
|
||||
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<String> {
|
||||
) -> 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<String> {
|
||||
) -> 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<String> {
|
||||
) -> 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<String> {
|
||||
) -> RewriteResult {
|
||||
fn allow_multi_line(expr: &ast::Expr) -> bool {
|
||||
match expr.kind {
|
||||
ast::ExprKind::Match(..)
|
||||
@ -217,12 +219,12 @@ fn rewrite_closure_expr(
|
||||
// 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<String> {
|
||||
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<String> {
|
||||
) -> 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,
|
||||
}
|
||||
}
|
||||
|
@ -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<String> {
|
||||
) -> 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<String> {
|
||||
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<String> {
|
||||
) -> 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<String> {
|
||||
) -> 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 identify_comment(
|
||||
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 identify_comment(
|
||||
)?
|
||||
};
|
||||
if rest.is_empty() {
|
||||
Some(rewritten_first_group)
|
||||
Ok(rewritten_first_group)
|
||||
} else {
|
||||
identify_comment(
|
||||
rest.trim_start(),
|
||||
@ -534,10 +534,11 @@ impl ItemizedBlock {
|
||||
|
||||
/// Returns the block as a string, with each line trimmed at the start.
|
||||
fn trimmed_block_as_string(&self) -> String {
|
||||
self.lines
|
||||
.iter()
|
||||
.map(|line| format!("{} ", line.trim_start()))
|
||||
.collect::<String>()
|
||||
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<String> {
|
||||
) -> 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<String> {
|
||||
) -> 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<String> {
|
||||
) -> 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 @@ impl<'a> Iterator for CommentCodeSlices<'a> {
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
) -> 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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 @@ macro_rules! create_config {
|
||||
// - 1: true if the option was manually initialized
|
||||
// - 2: the option value
|
||||
// - 3: true if the option is unstable
|
||||
$($i: (Cell<bool>, bool, $ty, bool)),+
|
||||
// - 4: true if the option was set manually from a CLI flag
|
||||
// FIXME: 4 is probably unnecessary and duplicative
|
||||
// https://github.com/rust-lang/rustfmt/issues/6252
|
||||
$($i: (Cell<bool>, bool, <$ty as StyleEditionDefault>::ConfigType, bool, bool)),+
|
||||
}
|
||||
|
||||
// Just like the Config struct but with each property wrapped
|
||||
@ -100,7 +103,7 @@ macro_rules! create_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 @@ macro_rules! create_config {
|
||||
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 @@ macro_rules! create_config {
|
||||
"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 @@ macro_rules! create_config {
|
||||
)+
|
||||
}
|
||||
|
||||
// Query each option, returns true if the user set the option via a CLI flag,
|
||||
// false if a default was used.
|
||||
#[allow(unreachable_pub)]
|
||||
pub struct CliConfigWasSet<'a>(&'a Config);
|
||||
|
||||
impl<'a> CliConfigWasSet<'a> {
|
||||
$(
|
||||
#[allow(unreachable_pub)]
|
||||
pub fn $i(&self) -> bool {
|
||||
(self.0).$i.4
|
||||
}
|
||||
)+
|
||||
}
|
||||
|
||||
impl Config {
|
||||
$(
|
||||
#[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 @@ macro_rules! create_config {
|
||||
self.set_merge_imports();
|
||||
self.set_fn_args_layout();
|
||||
self.set_hide_parse_errors();
|
||||
self.set_version();
|
||||
self
|
||||
}
|
||||
|
||||
@ -212,7 +288,9 @@ macro_rules! create_config {
|
||||
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 @@ macro_rules! create_config {
|
||||
match key {
|
||||
$(
|
||||
stringify!($i) => {
|
||||
let option_value = val.parse::<$ty>()
|
||||
.expect(&format!("Failed to parse override for {} (\"{}\") as a {}",
|
||||
let value = val.parse::<<$ty as StyleEditionDefault>::ConfigType>()
|
||||
.expect(
|
||||
&format!(
|
||||
"Failed to parse override for {} (\"{}\") as a {}",
|
||||
stringify!($i),
|
||||
val,
|
||||
stringify!($ty)));
|
||||
stringify!(<$ty as StyleEditionDefault>::ConfigType)
|
||||
)
|
||||
);
|
||||
|
||||
// Users are currently allowed to set unstable
|
||||
// options/variants via the `--config` options override.
|
||||
@ -261,7 +343,7 @@ macro_rules! create_config {
|
||||
// For now, do not validate whether the option or value is stable,
|
||||
// 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 @@ macro_rules! create_config {
|
||||
"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 @@ macro_rules! create_config {
|
||||
|
||||
#[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 @@ macro_rules! create_config {
|
||||
}
|
||||
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 @@ macro_rules! create_config {
|
||||
}
|
||||
}
|
||||
|
||||
fn set_version(&mut self) {
|
||||
if !self.was_set().version() {
|
||||
return;
|
||||
}
|
||||
|
||||
eprintln!(
|
||||
"Warning: the `version` option is deprecated. \
|
||||
Use `style_edition` instead."
|
||||
);
|
||||
|
||||
if self.was_set().style_edition() || self.was_set_cli().style_edition() {
|
||||
eprintln!(
|
||||
"Warning: the deprecated `version` option was \
|
||||
used in conjunction with the `style_edition` \
|
||||
option which takes precedence. \
|
||||
The value of the `version` option will be ignored."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unreachable_pub)]
|
||||
/// 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 @@ macro_rules! create_config {
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
@ -7,7 +7,7 @@ use std::{cmp, fmt, iter, str};
|
||||
|
||||
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 @@ impl From<rustc_span::FileName> for 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, "<stdin>"),
|
||||
}
|
||||
}
|
||||
@ -201,7 +201,7 @@ impl 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()
|
||||
}
|
||||
|
||||
|
@ -10,9 +10,7 @@ use crate::config::config_type::ConfigType;
|
||||
#[allow(unreachable_pub)]
|
||||
pub use crate::config::file_lines::{FileLines, FileName, Range};
|
||||
#[allow(unreachable_pub)]
|
||||
pub use crate::config::lists::*;
|
||||
#[allow(unreachable_pub)]
|
||||
pub use crate::config::macro_names::{MacroSelector, MacroSelectors};
|
||||
pub use crate::config::macro_names::MacroSelector;
|
||||
#[allow(unreachable_pub)]
|
||||
pub use crate::config::options::*;
|
||||
|
||||
@ -34,161 +32,169 @@ pub(crate) mod style_edition;
|
||||
// `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 @@ impl PartialConfig {
|
||||
|
||||
::toml::to_string(&cloned).map_err(ToTomlError)
|
||||
}
|
||||
|
||||
pub(super) fn to_parsed_config(
|
||||
self,
|
||||
style_edition_override: Option<StyleEdition>,
|
||||
edition_override: Option<Edition>,
|
||||
version_override: Option<Version>,
|
||||
dir: &Path,
|
||||
) -> Config {
|
||||
Config::default_for_possible_style_edition(
|
||||
style_edition_override.or(self.style_edition),
|
||||
edition_override.or(self.edition),
|
||||
version_override.or(self.version),
|
||||
)
|
||||
.fill_from_parsed_config(self, dir)
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn default_for_possible_style_edition(
|
||||
style_edition: Option<StyleEdition>,
|
||||
edition: Option<Edition>,
|
||||
version: Option<Version>,
|
||||
) -> Config {
|
||||
// Ensures the configuration defaults associated with Style Editions
|
||||
// follow the precedence set in
|
||||
// https://rust-lang.github.io/rfcs/3338-style-evolution.html
|
||||
// 'version' is a legacy alias for 'style_edition' that we'll support
|
||||
// for some period of time
|
||||
// FIXME(calebcartwright) - remove 'version' at some point
|
||||
match (style_edition, version, edition) {
|
||||
(Some(se), _, _) => Self::default_with_style_edition(se),
|
||||
(None, Some(Version::Two), _) => {
|
||||
Self::default_with_style_edition(StyleEdition::Edition2024)
|
||||
}
|
||||
(None, Some(Version::One), _) => {
|
||||
Self::default_with_style_edition(StyleEdition::Edition2015)
|
||||
}
|
||||
(None, None, Some(e)) => Self::default_with_style_edition(e.into()),
|
||||
(None, None, None) => Config::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn version_meets_requirement(&self) -> bool {
|
||||
if self.was_set().required_version() {
|
||||
let version = env!("CARGO_PKG_VERSION");
|
||||
@ -237,11 +282,16 @@ impl Config {
|
||||
///
|
||||
/// Returns a `Config` if the config could be read and parsed from
|
||||
/// the file, otherwise errors.
|
||||
pub(super) fn from_toml_path(file_path: &Path) -> Result<Config, Error> {
|
||||
pub(super) fn from_toml_path(
|
||||
file_path: &Path,
|
||||
edition: Option<Edition>,
|
||||
style_edition: Option<StyleEdition>,
|
||||
version: Option<Version>,
|
||||
) -> Result<Config, Error> {
|
||||
let mut file = File::open(&file_path)?;
|
||||
let mut 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 @@ impl Config {
|
||||
///
|
||||
/// Returns the `Config` to use, and the path of the project file if there was
|
||||
/// one.
|
||||
pub(super) fn from_resolved_toml_path(dir: &Path) -> Result<(Config, Option<PathBuf>), Error> {
|
||||
pub(super) fn from_resolved_toml_path(
|
||||
dir: &Path,
|
||||
edition: Option<Edition>,
|
||||
style_edition: Option<StyleEdition>,
|
||||
version: Option<Version>,
|
||||
) -> Result<(Config, Option<PathBuf>), Error> {
|
||||
/// Try to find a project file in the given directory and its parents.
|
||||
/// 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<Option<PathBuf>, Error> {
|
||||
let mut current = if dir.is_relative() {
|
||||
@ -299,12 +354,27 @@ impl Config {
|
||||
}
|
||||
|
||||
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<Config, String> {
|
||||
#[allow(dead_code)]
|
||||
pub(super) fn from_toml(toml: &str, file_path: &Path) -> Result<Config, String> {
|
||||
Self::from_toml_for_style_edition(toml, file_path, None, None, None)
|
||||
}
|
||||
|
||||
pub(crate) fn from_toml_for_style_edition(
|
||||
toml: &str,
|
||||
file_path: &Path,
|
||||
edition: Option<Edition>,
|
||||
style_edition: Option<StyleEdition>,
|
||||
version: Option<Version>,
|
||||
) -> Result<Config, String> {
|
||||
let parsed: ::toml::Value = toml
|
||||
.parse()
|
||||
.map_err(|e| format!("Could not parse TOML: {}", e))?;
|
||||
@ -318,18 +388,25 @@ impl Config {
|
||||
err.push_str(msg)
|
||||
}
|
||||
}
|
||||
match parsed.try_into() {
|
||||
|
||||
match parsed.try_into::<PartialConfig>() {
|
||||
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<O: CliOptions>(
|
||||
file_path: Option<&Path>,
|
||||
options: Option<O>,
|
||||
) -> Result<(Config, Option<PathBuf>), Error> {
|
||||
let over_ride = match options {
|
||||
Some(ref opts) => config_path(opts)?,
|
||||
None => None,
|
||||
let (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<O: CliOptions>(
|
||||
})
|
||||
}
|
||||
|
||||
// Check for the presence of known config file names (`rustfmt.toml, `.rustfmt.toml`) in `dir`
|
||||
// Check for the presence of known config file names (`rustfmt.toml`, `.rustfmt.toml`) in `dir`
|
||||
//
|
||||
// Return the path if a config file exists, empty if no file exists, and Error for IO errors
|
||||
fn get_toml_path(dir: &Path) -> Result<Option<PathBuf>, Error> {
|
||||
@ -372,7 +458,7 @@ fn get_toml_path(dir: &Path) -> Result<Option<PathBuf>, Error> {
|
||||
match fs::metadata(&config_file) {
|
||||
// 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<Option<PathBuf>, 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 @@ mod test {
|
||||
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 @@ mod test {
|
||||
|
||||
#[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 @@ match_block_trailing_comma = false
|
||||
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 @@ make_backup = false
|
||||
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 @@ make_backup = false
|
||||
#[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 @@ make_backup = false
|
||||
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 @@ make_backup = false
|
||||
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 @@ make_backup = false
|
||||
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 @@ make_backup = false
|
||||
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 @@ make_backup = false
|
||||
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 @@ make_backup = false
|
||||
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 @@ make_backup = false
|
||||
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 @@ make_backup = false
|
||||
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 @@ make_backup = false
|
||||
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 @@ make_backup = false
|
||||
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 @@ make_backup = false
|
||||
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 @@ make_backup = false
|
||||
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 @@ make_backup = false
|
||||
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 @@ make_backup = false
|
||||
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 @@ make_backup = false
|
||||
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 @@ make_backup = false
|
||||
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 @@ make_backup = false
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -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::de::{SeqAccess, Visitor};
|
||||
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 @@ impl FromStr for IgnoreList {
|
||||
/// 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<Edition>;
|
||||
fn style_edition(&self) -> Option<StyleEdition>;
|
||||
fn version(&self) -> Option<Version>;
|
||||
}
|
||||
|
||||
/// The edition of the syntax and semantics of code (RFC 2052).
|
||||
@ -454,6 +462,17 @@ impl From<Edition> for rustc_span::edition::Edition {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Edition> for StyleEdition {
|
||||
fn from(edition: Edition) -> Self {
|
||||
match edition {
|
||||
Edition::Edition2015 => StyleEdition::Edition2015,
|
||||
Edition::Edition2018 => StyleEdition::Edition2018,
|
||||
Edition::Edition2021 => StyleEdition::Edition2021,
|
||||
Edition::Edition2024 => StyleEdition::Edition2024,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Edition {
|
||||
fn partial_cmp(&self, other: &Edition) -> Option<std::cmp::Ordering> {
|
||||
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<StyleEdition> for rustc_span::edition::Edition {
|
||||
fn from(edition: StyleEdition) -> Self {
|
||||
match edition {
|
||||
StyleEdition::Edition2015 => Self::Edition2015,
|
||||
StyleEdition::Edition2018 => Self::Edition2018,
|
||||
StyleEdition::Edition2021 => Self::Edition2021,
|
||||
StyleEdition::Edition2024 => Self::Edition2024,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for StyleEdition {
|
||||
fn partial_cmp(&self, other: &StyleEdition) -> Option<std::cmp::Ordering> {
|
||||
rustc_span::edition::Edition::partial_cmp(&(*self).into(), &(*other).into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Defines unit structs to implement `StyleEditionDefault` for.
|
||||
#[macro_export]
|
||||
macro_rules! config_option_with_style_edition_default {
|
||||
($name:ident, $config_ty:ty, _ => $default:expr) => {
|
||||
#[allow(unreachable_pub)]
|
||||
pub struct $name;
|
||||
$crate::style_edition_default!($name, $config_ty, _ => $default);
|
||||
};
|
||||
($name:ident, $config_ty:ty, Edition2024 => $default_2024:expr, _ => $default_2015:expr) => {
|
||||
pub struct $name;
|
||||
$crate::style_edition_default!(
|
||||
$name,
|
||||
$config_ty,
|
||||
Edition2024 => $default_2024,
|
||||
_ => $default_2015
|
||||
);
|
||||
};
|
||||
(
|
||||
$($name:ident, $config_ty:ty, $(Edition2024 => $default_2024:expr,)? _ => $default:expr);*
|
||||
$(;)*
|
||||
) => {
|
||||
$(
|
||||
config_option_with_style_edition_default!(
|
||||
$name, $config_ty, $(Edition2024 => $default_2024,)? _ => $default
|
||||
);
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
// TODO(ytmimi) Some of the configuration values have a `Config` suffix, while others don't.
|
||||
// I chose to add a `Config` suffix in cases where a type for the config option was already
|
||||
// defined. For example, `NewlineStyle` and `NewlineStyleConfig`. There was some discussion
|
||||
// about using the `Config` suffix more consistently.
|
||||
config_option_with_style_edition_default!(
|
||||
// Fundamental stuff
|
||||
MaxWidth, usize, _ => 100;
|
||||
HardTabs, bool, _ => false;
|
||||
TabSpaces, usize, _ => 4;
|
||||
NewlineStyleConfig, NewlineStyle, _ => NewlineStyle::Auto;
|
||||
IndentStyleConfig, IndentStyle, _ => IndentStyle::Block;
|
||||
|
||||
// Width Heuristics
|
||||
UseSmallHeuristics, Heuristics, _ => Heuristics::Default;
|
||||
WidthHeuristicsConfig, WidthHeuristics, _ => WidthHeuristics::scaled(100);
|
||||
FnCallWidth, usize, _ => 60;
|
||||
AttrFnLikeWidth, usize, _ => 70;
|
||||
StructLitWidth, usize, _ => 18;
|
||||
StructVariantWidth, usize, _ => 35;
|
||||
ArrayWidth, usize, _ => 60;
|
||||
ChainWidth, usize, _ => 60;
|
||||
SingleLineIfElseMaxWidth, usize, _ => 50;
|
||||
SingleLineLetElseMaxWidth, usize, _ => 50;
|
||||
|
||||
// Comments. macros, and strings
|
||||
WrapComments, bool, _ => false;
|
||||
FormatCodeInDocComments, bool, _ => false;
|
||||
DocCommentCodeBlockWidth, usize, _ => 100;
|
||||
CommentWidth, usize, _ => 80;
|
||||
NormalizeComments, bool, _ => false;
|
||||
NormalizeDocAttributes, bool, _ => false;
|
||||
FormatStrings, bool, _ => false;
|
||||
FormatMacroMatchers, bool, _ => false;
|
||||
FormatMacroBodies, bool, _ => true;
|
||||
SkipMacroInvocations, MacroSelectors, _ => MacroSelectors::default();
|
||||
HexLiteralCaseConfig, HexLiteralCase, _ => HexLiteralCase::Preserve;
|
||||
|
||||
// Single line expressions and items
|
||||
EmptyItemSingleLine, bool, _ => true;
|
||||
StructLitSingleLine, bool, _ => true;
|
||||
FnSingleLine, bool, _ => false;
|
||||
WhereSingleLine, bool, _ => false;
|
||||
|
||||
// Imports
|
||||
ImportsIndent, IndentStyle, _ => IndentStyle::Block;
|
||||
ImportsLayout, ListTactic, _ => ListTactic::Mixed;
|
||||
ImportsGranularityConfig, ImportGranularity, _ => ImportGranularity::Preserve;
|
||||
GroupImportsTacticConfig, GroupImportsTactic, _ => GroupImportsTactic::Preserve;
|
||||
MergeImports, bool, _ => false;
|
||||
|
||||
// Ordering
|
||||
ReorderImports, bool, _ => true;
|
||||
ReorderModules, bool, _ => true;
|
||||
ReorderImplItems, bool, _ => false;
|
||||
|
||||
// Spaces around punctuation
|
||||
TypePunctuationDensity, TypeDensity, _ => TypeDensity::Wide;
|
||||
SpaceBeforeColon, bool, _ => false;
|
||||
SpaceAfterColon, bool, _ => true;
|
||||
SpacesAroundRanges, bool, _ => false;
|
||||
BinopSeparator, SeparatorPlace, _ => SeparatorPlace::Front;
|
||||
|
||||
// Misc.
|
||||
RemoveNestedParens, bool, _ => true;
|
||||
CombineControlExpr, bool, _ => true;
|
||||
ShortArrayElementWidthThreshold, usize, _ => 10;
|
||||
OverflowDelimitedExpr, bool, Edition2024 => true, _ => false;
|
||||
StructFieldAlignThreshold, usize, _ => 0;
|
||||
EnumDiscrimAlignThreshold, usize, _ => 0;
|
||||
MatchArmBlocks, bool, _ => true;
|
||||
MatchArmLeadingPipeConfig, MatchArmLeadingPipe, _ => MatchArmLeadingPipe::Never;
|
||||
ForceMultilineBlocks, bool, _ => false;
|
||||
FnArgsLayout, Density, _ => Density::Tall;
|
||||
FnParamsLayout, Density, _ => Density::Tall;
|
||||
BraceStyleConfig, BraceStyle, _ => BraceStyle::SameLineWhere;
|
||||
ControlBraceStyleConfig, ControlBraceStyle, _ => ControlBraceStyle::AlwaysSameLine;
|
||||
TrailingSemicolon, bool, _ => true;
|
||||
TrailingComma, SeparatorTactic, _ => SeparatorTactic::Vertical;
|
||||
MatchBlockTrailingComma, bool, _ => false;
|
||||
BlankLinesUpperBound, usize, _ => 1;
|
||||
BlankLinesLowerBound, usize, _ => 0;
|
||||
EditionConfig, Edition, _ => Edition::Edition2015;
|
||||
StyleEditionConfig, StyleEdition,
|
||||
Edition2024 => StyleEdition::Edition2024, _ => StyleEdition::Edition2015;
|
||||
VersionConfig, Version, Edition2024 => Version::Two, _ => Version::One;
|
||||
InlineAttributeWidth, usize, _ => 0;
|
||||
FormatGeneratedFiles, bool, _ => true;
|
||||
GeneratedMarkerLineSearchLimit, usize, _ => 5;
|
||||
|
||||
// Options that can change the source code beyond whitespace/blocks (somewhat linty things)
|
||||
MergeDerives, bool, _ => true;
|
||||
UseTryShorthand, bool, _ => false;
|
||||
UseFieldInitShorthand, bool, _ => false;
|
||||
ForceExplicitAbi, bool, _ => true;
|
||||
CondenseWildcardSuffixes, bool, _ => false;
|
||||
|
||||
// Control options (changes the operation of rustfmt, rather than the formatting)
|
||||
ColorConfig, Color, _ => Color::Auto;
|
||||
RequiredVersion, String, _ => env!("CARGO_PKG_VERSION").to_owned();
|
||||
UnstableFeatures, bool, _ => false;
|
||||
DisableAllFormatting, bool, _ => false;
|
||||
SkipChildren, bool, _ => false;
|
||||
HideParseErrors, bool, _ => false;
|
||||
ShowParseErrors, bool, _ => true;
|
||||
ErrorOnLineOverflow, bool, _ => false;
|
||||
ErrorOnUnformatted, bool, _ => false;
|
||||
Ignore, IgnoreList, _ => IgnoreList::default();
|
||||
|
||||
// Not user-facing
|
||||
Verbose, Verbosity, _ => Verbosity::Normal;
|
||||
FileLinesConfig, FileLines, _ => FileLines::all();
|
||||
EmitModeConfig, EmitMode, _ => EmitMode::Files;
|
||||
MakeBackup, bool, _ => false;
|
||||
PrintMisformattedFileNames, bool, _ => false;
|
||||
);
|
||||
|
@ -2,7 +2,7 @@ use crate::config::StyleEdition;
|
||||
|
||||
/// 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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -17,7 +17,7 @@ use crate::parse::parser::{DirectoryOwnership, Parser, ParserError};
|
||||
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;
|
||||
|
@ -13,7 +13,9 @@ use rustfmt_nightly as rustfmt;
|
||||
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 @@ impl CliOptions for NullOptions {
|
||||
fn config_path(&self) -> Option<&Path> {
|
||||
unreachable!();
|
||||
}
|
||||
fn edition(&self) -> Option<rustfmt_nightly::Edition> {
|
||||
unreachable!();
|
||||
}
|
||||
fn style_edition(&self) -> Option<rustfmt_nightly::StyleEdition> {
|
||||
unreachable!();
|
||||
}
|
||||
fn version(&self) -> Option<Version> {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
fn uncommitted_files() -> Vec<String> {
|
||||
|
@ -41,8 +41,11 @@ mod test {
|
||||
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 @@ mod test {
|
||||
|
||||
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();
|
||||
|
@ -8,19 +8,20 @@ use itertools::Itertools;
|
||||
|
||||
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 @@ impl<'a> FmtVisitor<'a> {
|
||||
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 @@ impl UseSegment {
|
||||
};
|
||||
UseSegment {
|
||||
kind,
|
||||
version: self.version,
|
||||
style_edition: self.style_edition,
|
||||
}
|
||||
}
|
||||
|
||||
@ -197,7 +199,7 @@ impl UseSegment {
|
||||
|
||||
Some(UseSegment {
|
||||
kind,
|
||||
version: context.config.version(),
|
||||
style_edition: context.config.style_edition(),
|
||||
})
|
||||
}
|
||||
|
||||
@ -331,12 +333,17 @@ impl UseTree {
|
||||
&self,
|
||||
context: &RewriteContext<'_>,
|
||||
shape: Shape,
|
||||
) -> Option<String> {
|
||||
) -> 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 @@ impl UseTree {
|
||||
})?;
|
||||
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 @@ impl UseTree {
|
||||
allow_extend,
|
||||
)
|
||||
}
|
||||
_ => Some(use_str),
|
||||
_ => Ok(use_str),
|
||||
}
|
||||
}
|
||||
|
||||
@ -444,18 +451,21 @@ impl UseTree {
|
||||
}
|
||||
}
|
||||
|
||||
let version = context.config.version();
|
||||
let style_edition = context.config.style_edition();
|
||||
|
||||
match a.kind {
|
||||
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 @@ impl UseTree {
|
||||
",",
|
||||
|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 @@ impl UseTree {
|
||||
// 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 @@ impl UseTree {
|
||||
})
|
||||
.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 @@ impl UseTree {
|
||||
_ => 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 @@ impl UseTree {
|
||||
list.sort();
|
||||
last = UseSegment {
|
||||
kind: UseSegmentKind::List(list),
|
||||
version: last.version,
|
||||
style_edition: last.style_edition,
|
||||
};
|
||||
}
|
||||
|
||||
@ -732,9 +751,12 @@ impl 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 @@ impl Ord for UseSegment {
|
||||
| (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,11 +933,15 @@ impl Ord for UseSegment {
|
||||
},
|
||||
(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())
|
||||
};
|
||||
|
||||
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;
|
||||
@ -920,7 +955,9 @@ impl Ord for UseSegment {
|
||||
if !is_upper_snake_case(ia) && is_upper_snake_case(ib) {
|
||||
return Ordering::Less;
|
||||
}
|
||||
let ident_ord = ia.cmp(ib);
|
||||
ia.cmp(ib)
|
||||
};
|
||||
|
||||
if ident_ord != Ordering::Equal {
|
||||
return ident_ord;
|
||||
}
|
||||
@ -928,9 +965,8 @@ impl Ord for UseSegment {
|
||||
(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<String> {
|
||||
) -> 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<String> {
|
||||
Some(match self.kind {
|
||||
self.rewrite_result(context, shape).ok()
|
||||
}
|
||||
|
||||
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
|
||||
Ok(match self.kind {
|
||||
UseSegmentKind::Ident(ref ident, Some(ref rename)) => {
|
||||
format!("{ident} as {rename}")
|
||||
}
|
||||
@ -1066,31 +1109,42 @@ impl Rewrite for UseSegment {
|
||||
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(
|
||||
UseSegmentKind::List(ref use_tree_list) => {
|
||||
rewrite_nested_use_tree(
|
||||
context,
|
||||
use_tree_list,
|
||||
// 1 = "{" and "}"
|
||||
shape.offset_left(1)?.sub_width(1)?,
|
||||
)?,
|
||||
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<String> {
|
||||
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<String> {
|
||||
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 @@ mod test {
|
||||
|
||||
struct Parser<'a> {
|
||||
input: Peekable<Chars<'a>>,
|
||||
version: Version,
|
||||
style_edition: StyleEdition,
|
||||
}
|
||||
|
||||
impl<'a> Parser<'a> {
|
||||
@ -1132,7 +1186,7 @@ mod test {
|
||||
buf: &mut String,
|
||||
alias_buf: &mut Option<String>,
|
||||
) {
|
||||
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 @@ mod test {
|
||||
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 @@ mod test {
|
||||
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 @@ mod test {
|
||||
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 @@ mod test {
|
||||
let kind = UseSegmentKind::Glob;
|
||||
result.push(UseSegment {
|
||||
kind,
|
||||
version: self.version,
|
||||
style_edition: self.style_edition,
|
||||
});
|
||||
}
|
||||
':' => {
|
||||
@ -1249,7 +1315,7 @@ mod test {
|
||||
|
||||
let mut parser = Parser {
|
||||
input: s.chars().peekable(),
|
||||
version: Version::One,
|
||||
style_edition: StyleEdition::Edition2015,
|
||||
};
|
||||
parser.parse_in_list()
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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::shape::Indent;
|
||||
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 @@ mod rewrite;
|
||||
pub(crate) mod rustfmt_diff;
|
||||
mod shape;
|
||||
mod skip;
|
||||
mod sort;
|
||||
pub(crate) mod source_file;
|
||||
pub(crate) mod source_map;
|
||||
mod spanned;
|
||||
|
@ -5,10 +5,10 @@ use std::iter::Peekable;
|
||||
|
||||
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<String>,
|
||||
pub(crate) item: RewriteResult,
|
||||
pub(crate) post_comment: Option<String>,
|
||||
// Whether there is extra whitespace before this item.
|
||||
pub(crate) new_lines: bool,
|
||||
}
|
||||
|
||||
impl ListItem {
|
||||
pub(crate) fn empty() -> ListItem {
|
||||
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 @@ impl 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 @@ impl ListItem {
|
||||
!matches!(*s, Some(ref s) if !s.is_empty())
|
||||
}
|
||||
|
||||
!(empty(&self.pre_comment) && empty(&self.item) && empty(&self.post_comment))
|
||||
fn empty_result(s: &RewriteResult) -> bool {
|
||||
!matches!(*s, Ok(ref s) if !s.is_empty())
|
||||
}
|
||||
|
||||
!(empty(&self.pre_comment) && empty_result(&self.item) && empty(&self.post_comment))
|
||||
}
|
||||
}
|
||||
|
||||
@ -257,7 +261,7 @@ where
|
||||
}
|
||||
|
||||
// Format a list of commented items into a string.
|
||||
pub(crate) fn write_list<I, T>(items: I, formatting: &ListFormatting<'_>) -> Option<String>
|
||||
pub(crate) fn write_list<I, T>(items: I, formatting: &ListFormatting<'_>) -> RewriteResult
|
||||
where
|
||||
I: IntoIterator<Item = T> + Clone,
|
||||
T: AsRef<ListItem>,
|
||||
@ -281,7 +285,7 @@ where
|
||||
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 @@ where
|
||||
prev_item_is_nested_import = inner_item.contains("::");
|
||||
}
|
||||
|
||||
Some(result)
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn max_width_of_item_with_post_comment<I, T>(
|
||||
@ -741,7 +745,7 @@ where
|
||||
I: Iterator<Item = T>,
|
||||
F1: Fn(&T) -> BytePos,
|
||||
F2: Fn(&T) -> BytePos,
|
||||
F3: Fn(&T) -> Option<String>,
|
||||
F3: Fn(&T) -> RewriteResult,
|
||||
{
|
||||
type Item = ListItem;
|
||||
|
||||
@ -775,8 +779,9 @@ where
|
||||
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 @@ where
|
||||
I: Iterator<Item = T>,
|
||||
F1: Fn(&T) -> BytePos,
|
||||
F2: Fn(&T) -> BytePos,
|
||||
F3: Fn(&T) -> Option<String>,
|
||||
F3: Fn(&T) -> RewriteResult,
|
||||
{
|
||||
ListItems {
|
||||
snippet_provider,
|
||||
|
@ -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 @@ impl MacroArg {
|
||||
|
||||
impl Rewrite for ast::Item {
|
||||
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
|
||||
self.rewrite_result(context, shape).ok()
|
||||
}
|
||||
|
||||
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
|
||||
let mut visitor = crate::visitor::FmtVisitor::from_context(context);
|
||||
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<String> {
|
||||
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<String> {
|
||||
) -> 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<String> {
|
||||
) -> 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<String> {
|
||||
) -> 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<MacroArg>,
|
||||
macro_name: String,
|
||||
delim_token: Delimiter,
|
||||
) -> Option<String> {
|
||||
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<String> {
|
||||
) -> 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<String> {
|
||||
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 @@ impl MacroArgKind {
|
||||
context: &RewriteContext<'_>,
|
||||
shape: Shape,
|
||||
use_multiple_lines: bool,
|
||||
) -> Option<String> {
|
||||
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 @@ impl MacroArgKind {
|
||||
.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 @@ impl ParsedMacroArg {
|
||||
context: &RewriteContext<'_>,
|
||||
shape: Shape,
|
||||
use_multiple_lines: bool,
|
||||
) -> Option<String> {
|
||||
) -> RewriteResult {
|
||||
self.kind.rewrite(context, shape, use_multiple_lines)
|
||||
}
|
||||
}
|
||||
@ -966,9 +998,9 @@ fn wrap_macro_args(
|
||||
context: &RewriteContext<'_>,
|
||||
args: &[ParsedMacroArg],
|
||||
shape: Shape,
|
||||
) -> Option<String> {
|
||||
) -> 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<String> {
|
||||
) -> 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<String> {
|
||||
if !context.config.format_macro_matchers() {
|
||||
) -> RewriteResult {
|
||||
let span = span_for_token_stream(&token_stream);
|
||||
return Some(match span {
|
||||
if !context.config.format_macro_matchers() {
|
||||
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 @@ impl MacroBranch {
|
||||
context: &RewriteContext<'_>,
|
||||
shape: Shape,
|
||||
multi_branch_style: bool,
|
||||
) -> Option<String> {
|
||||
) -> 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 @@ impl MacroBranch {
|
||||
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 @@ impl MacroBranch {
|
||||
// `$$`). 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 @@ impl MacroBranch {
|
||||
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 @@ impl MacroBranch {
|
||||
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 @@ impl MacroBranch {
|
||||
|
||||
result += "}";
|
||||
|
||||
Some(result)
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1366,7 +1417,8 @@ fn format_lazy_static(
|
||||
context: &RewriteContext<'_>,
|
||||
shape: Shape,
|
||||
ts: TokenStream,
|
||||
) -> Option<String> {
|
||||
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<String> {
|
||||
) -> 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)
|
||||
}
|
||||
|
@ -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 @@ impl<'a> Spanned for ArmWrapper<'a> {
|
||||
|
||||
impl<'a> Rewrite for ArmWrapper<'a> {
|
||||
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
|
||||
self.rewrite_result(context, shape).ok()
|
||||
}
|
||||
|
||||
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
|
||||
rewrite_match_arm(
|
||||
context,
|
||||
self.arm,
|
||||
@ -74,7 +78,7 @@ pub(crate) fn rewrite_match(
|
||||
span: Span,
|
||||
attrs: &[ast::Attribute],
|
||||
match_kind: MatchKind,
|
||||
) -> Option<String> {
|
||||
) -> 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<String> {
|
||||
) -> 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<String> {
|
||||
) -> 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<String> {
|
||||
) -> 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<String> {
|
||||
) -> 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!(
|
||||
.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
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
))
|
||||
} else {
|
||||
Some(String::new())
|
||||
Ok(String::new())
|
||||
}
|
||||
}
|
||||
|
||||
fn nop_block_collapse(block_str: Option<String>, budget: usize) -> Option<String> {
|
||||
fn nop_block_collapse(block_str: RewriteResult, budget: usize) -> RewriteResult {
|
||||
debug!("nop_block_collapse {:?} {}", block_str, budget);
|
||||
block_str.map(|block_str| {
|
||||
if block_str.starts_with('{')
|
||||
|
@ -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 @@ impl<'a> FmtVisitor<'a> {
|
||||
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 @@ impl<'a> FmtVisitor<'a> {
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -4,8 +4,8 @@ use std::path::{Path, PathBuf};
|
||||
|
||||
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;
|
||||
|
||||
|
@ -9,22 +9,22 @@ use rustc_span::Span;
|
||||
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<String> {
|
||||
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 @@ impl<'a> OverflowableItem<'a> {
|
||||
OverflowableItem::NestedMetaItem(..) => 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<SeparatorTactic>,
|
||||
) -> Option<String> {
|
||||
) -> RewriteResult {
|
||||
Context::new(
|
||||
context,
|
||||
items,
|
||||
@ -300,7 +308,7 @@ pub(crate) fn rewrite_with_angle_brackets<'a, T: 'a + IntoOverflowableItem<'a>>(
|
||||
items: impl Iterator<Item = &'a T>,
|
||||
shape: Shape,
|
||||
span: Span,
|
||||
) -> Option<String> {
|
||||
) -> 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<SeparatorTactic>,
|
||||
delim_token: Option<Delimiter>,
|
||||
) -> Option<String> {
|
||||
) -> RewriteResult {
|
||||
let (lhs, rhs) = match delim_token {
|
||||
Some(Delimiter::Parenthesis) => ("(", ")"),
|
||||
Some(Delimiter::Brace) => ("{", "}"),
|
||||
@ -428,7 +436,7 @@ impl<'a> Context<'a> {
|
||||
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 @@ impl<'a> Context<'a> {
|
||||
|
||||
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 @@ impl<'a> Context<'a> {
|
||||
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 @@ impl<'a> Context<'a> {
|
||||
.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 @@ impl<'a> Context<'a> {
|
||||
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 @@ impl<'a> Context<'a> {
|
||||
",",
|
||||
|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 @@ impl<'a> Context<'a> {
|
||||
);
|
||||
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 @@ impl<'a> Context<'a> {
|
||||
result
|
||||
}
|
||||
|
||||
fn rewrite(&self, shape: Shape) -> Option<String> {
|
||||
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 @@ impl<'a> Context<'a> {
|
||||
return result;
|
||||
}
|
||||
|
||||
Some(self.wrap_items(&items_str, shape, extendable))
|
||||
Ok(self.wrap_items(&items_str, shape, extendable))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,14 +42,17 @@ pub(crate) fn rewrite_all_pairs(
|
||||
expr: &ast::Expr,
|
||||
shape: Shape,
|
||||
context: &RewriteContext<'_>,
|
||||
) -> Option<String> {
|
||||
expr.flatten(context, shape).and_then(|list| {
|
||||
) -> 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)
|
||||
.or_else(|| rewrite_pairs_multiline(&list, shape, context))
|
||||
.unknown_error()
|
||||
.or_else(|_| rewrite_pairs_multiline(&list, shape, context))
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -65,7 +70,7 @@ fn rewrite_pairs_one_line<T: Rewrite>(
|
||||
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<T: Rewrite>(
|
||||
list: &PairList<'_, '_, T>,
|
||||
shape: Shape,
|
||||
context: &RewriteContext<'_>,
|
||||
) -> Option<String> {
|
||||
) -> 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<T: Rewrite>(
|
||||
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<T: Rewrite>(
|
||||
}
|
||||
}
|
||||
|
||||
result.push_str(default_rw.as_ref()?);
|
||||
result.push_str(default_rw.as_ref().map_err(|err| err.clone())?);
|
||||
}
|
||||
Some(result)
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
// Rewrites a single pair.
|
||||
@ -168,10 +174,10 @@ pub(crate) fn rewrite_pair<LHS, RHS>(
|
||||
context: &RewriteContext<'_>,
|
||||
shape: Shape,
|
||||
separator_place: SeparatorPlace,
|
||||
) -> Option<String>
|
||||
) -> 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 @@ where
|
||||
..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 @@ where
|
||||
+ 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 @@ where
|
||||
// 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 @@ where
|
||||
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 @@ trait FlattenPair: Rewrite + Sized {
|
||||
}
|
||||
|
||||
struct PairList<'a, 'b, T: Rewrite> {
|
||||
list: Vec<(&'b T, Option<String>)>,
|
||||
list: Vec<(&'b T, RewriteResult)>,
|
||||
separators: Vec<&'a str>,
|
||||
span: Span,
|
||||
}
|
||||
|
||||
fn is_ident(expr: &ast::Expr) -> bool {
|
||||
@ -303,7 +316,7 @@ impl FlattenPair for ast::Expr {
|
||||
|
||||
let default_rewrite = |node: &ast::Expr, sep: usize, is_first: bool| {
|
||||
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 @@ impl FlattenPair for ast::Expr {
|
||||
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 @@ impl FlattenPair for ast::Expr {
|
||||
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 @@ impl FlattenPair for ast::Expr {
|
||||
}
|
||||
|
||||
assert_eq!(list.len() - 1, separators.len());
|
||||
Some(PairList { list, separators })
|
||||
Some(PairList {
|
||||
list,
|
||||
separators,
|
||||
span,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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};
|
||||
|
@ -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;
|
||||
|
@ -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_ast::{ast, attr, ptr};
|
||||
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;
|
||||
|
@ -2,13 +2,14 @@ use std::path::Path;
|
||||
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 @@ mod tests {
|
||||
}
|
||||
|
||||
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]
|
||||
|
@ -2,22 +2,22 @@ use rustc_ast::ast::{self, BindingMode, ByRef, Pat, PatField, PatKind, RangeEnd,
|
||||
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<ptr::P<ast::Expr>>);
|
||||
pub(crate) struct RangeOperand<'a> {
|
||||
operand: &'a Option<ptr::P<ast::Expr>>,
|
||||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
impl<'a> Rewrite for RangeOperand<'a> {
|
||||
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
|
||||
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<String> {
|
||||
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::<Option<Vec<_>>>()?;
|
||||
.map(|p| p.rewrite_result(context, shape))
|
||||
.collect::<Result<Vec<_>, RewriteError>>()?;
|
||||
|
||||
let use_mixed_layout = pats
|
||||
.iter()
|
||||
@ -115,14 +126,21 @@ impl Rewrite for Pat {
|
||||
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 @@ impl Rewrite for Pat {
|
||||
}
|
||||
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 @@ impl Rewrite for Pat {
|
||||
} 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 @@ impl Rewrite for Pat {
|
||||
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<String> = 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 @@ impl Rewrite for Pat {
|
||||
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<String> {
|
||||
) -> 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<String> {
|
||||
self.rewrite_result(context, shape).ok()
|
||||
}
|
||||
|
||||
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
|
||||
let hi_pos = if let Some(last) = self.attrs.last() {
|
||||
last.span.hi()
|
||||
} else {
|
||||
@ -393,10 +438,10 @@ impl Rewrite for PatField {
|
||||
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 @@ impl Rewrite for PatField {
|
||||
"{}:\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<String> {
|
||||
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<String> {
|
||||
) -> 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;
|
||||
|
||||
|
@ -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,
|
||||
|
@ -5,17 +5,23 @@ use std::rc::Rc;
|
||||
|
||||
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<String, RewriteError>;
|
||||
pub(crate) trait Rewrite {
|
||||
/// Rewrite self into shape.
|
||||
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String>;
|
||||
|
||||
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
|
||||
self.rewrite(context, shape).unknown_error()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Rewrite> Rewrite for ptr::P<T> {
|
||||
@ -24,6 +30,66 @@ impl<T: Rewrite> Rewrite for ptr::P<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub(crate) enum MacroErrorKind {
|
||||
ParseFailure,
|
||||
ReplaceMacroVariable,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for MacroErrorKind {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
MacroErrorKind::ParseFailure => write!(f, "(parse failure)"),
|
||||
MacroErrorKind::ReplaceMacroVariable => write!(f, "(replacing macro variables with $)"),
|
||||
MacroErrorKind::Unknown => write!(f, ""),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Error, Debug)]
|
||||
pub(crate) enum RewriteError {
|
||||
#[error("Formatting was skipped due to skip attribute or out of file range.")]
|
||||
SkipFormatting,
|
||||
|
||||
#[error("It exceeds the required width of {configured_width} for the span: {span:?}")]
|
||||
ExceedsMaxWidth { configured_width: usize, span: Span },
|
||||
|
||||
#[error("Failed to format given macro{} at: {span:?}", kind)]
|
||||
MacroFailure { kind: MacroErrorKind, span: Span },
|
||||
|
||||
/// Format failure that does not fit to above categories.
|
||||
#[error("An unknown error occurred during formatting.")]
|
||||
Unknown,
|
||||
}
|
||||
|
||||
/// Extension trait used to conveniently convert to RewriteError
|
||||
pub(crate) trait RewriteErrorExt<T> {
|
||||
fn max_width_error(self, width: usize, span: Span) -> Result<T, RewriteError>;
|
||||
fn macro_error(self, kind: MacroErrorKind, span: Span) -> Result<T, RewriteError>;
|
||||
fn unknown_error(self) -> Result<T, RewriteError>;
|
||||
}
|
||||
|
||||
impl<T> RewriteErrorExt<T> for Option<T> {
|
||||
fn max_width_error(self, width: usize, span: Span) -> Result<T, RewriteError> {
|
||||
self.ok_or_else(|| RewriteError::ExceedsMaxWidth {
|
||||
configured_width: width,
|
||||
span: span,
|
||||
})
|
||||
}
|
||||
|
||||
fn macro_error(self, kind: MacroErrorKind, span: Span) -> Result<T, RewriteError> {
|
||||
self.ok_or_else(|| RewriteError::MacroFailure {
|
||||
kind: kind,
|
||||
span: span,
|
||||
})
|
||||
}
|
||||
|
||||
fn unknown_error(self) -> Result<T, RewriteError> {
|
||||
self.ok_or_else(|| RewriteError::Unknown)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct RewriteContext<'a> {
|
||||
pub(crate) psess: &'a ParseSess,
|
||||
|
@ -282,7 +282,7 @@ where
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::DiffLine::*;
|
||||
use super::{make_diff, Mismatch};
|
||||
use super::{Mismatch, make_diff};
|
||||
use super::{ModifiedChunk, ModifiedLines};
|
||||
|
||||
#[test]
|
||||
|
368
src/tools/rustfmt/src/sort.rs
Normal file
368
src/tools/rustfmt/src/sort.rs
Normal file
@ -0,0 +1,368 @@
|
||||
use itertools::EitherOrBoth;
|
||||
use itertools::Itertools;
|
||||
|
||||
/// Iterator which breaks an identifier into various [VersionChunk]s.
|
||||
struct VersionChunkIter<'a> {
|
||||
ident: &'a str,
|
||||
start: usize,
|
||||
}
|
||||
|
||||
impl<'a> VersionChunkIter<'a> {
|
||||
pub(crate) fn new(ident: &'a str) -> Self {
|
||||
Self { ident, start: 0 }
|
||||
}
|
||||
|
||||
fn parse_numeric_chunk(
|
||||
&mut self,
|
||||
mut chars: std::str::CharIndices<'a>,
|
||||
) -> Option<VersionChunk<'a>> {
|
||||
let mut end = self.start;
|
||||
let mut is_end_of_chunk = false;
|
||||
|
||||
while let Some((idx, c)) = chars.next() {
|
||||
end = self.start + idx;
|
||||
|
||||
if c.is_ascii_digit() {
|
||||
continue;
|
||||
}
|
||||
|
||||
is_end_of_chunk = true;
|
||||
break;
|
||||
}
|
||||
|
||||
let source = if is_end_of_chunk {
|
||||
let value = &self.ident[self.start..end];
|
||||
self.start = end;
|
||||
value
|
||||
} else {
|
||||
let value = &self.ident[self.start..];
|
||||
self.start = self.ident.len();
|
||||
value
|
||||
};
|
||||
|
||||
let zeros = source.chars().take_while(|c| *c == '0').count();
|
||||
let value = source.parse::<usize>().ok()?;
|
||||
|
||||
Some(VersionChunk::Number {
|
||||
value,
|
||||
zeros,
|
||||
source,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_str_chunk(
|
||||
&mut self,
|
||||
mut chars: std::str::CharIndices<'a>,
|
||||
) -> Option<VersionChunk<'a>> {
|
||||
let mut end = self.start;
|
||||
let mut is_end_of_chunk = false;
|
||||
|
||||
while let Some((idx, c)) = chars.next() {
|
||||
end = self.start + idx;
|
||||
|
||||
if c == '_' {
|
||||
is_end_of_chunk = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if !c.is_numeric() {
|
||||
continue;
|
||||
}
|
||||
|
||||
is_end_of_chunk = true;
|
||||
break;
|
||||
}
|
||||
|
||||
let source = if is_end_of_chunk {
|
||||
let value = &self.ident[self.start..end];
|
||||
self.start = end;
|
||||
value
|
||||
} else {
|
||||
let value = &self.ident[self.start..];
|
||||
self.start = self.ident.len();
|
||||
value
|
||||
};
|
||||
|
||||
Some(VersionChunk::Str(source))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for VersionChunkIter<'a> {
|
||||
type Item = VersionChunk<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let mut chars = self.ident[self.start..].char_indices();
|
||||
let (_, next) = chars.next()?;
|
||||
|
||||
if next == '_' {
|
||||
self.start = self.start + next.len_utf8();
|
||||
return Some(VersionChunk::Underscore);
|
||||
}
|
||||
|
||||
if next.is_ascii_digit() {
|
||||
return self.parse_numeric_chunk(chars);
|
||||
}
|
||||
|
||||
self.parse_str_chunk(chars)
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a chunk in the version-sort algorithm
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
enum VersionChunk<'a> {
|
||||
/// A single `_` in an identifier. Underscores are sorted before all other characters.
|
||||
Underscore,
|
||||
/// A &str chunk in the version sort.
|
||||
Str(&'a str),
|
||||
/// A numeric chunk in the version sort. Keeps track of the numeric value and leading zeros.
|
||||
Number {
|
||||
value: usize,
|
||||
zeros: usize,
|
||||
source: &'a str,
|
||||
},
|
||||
}
|
||||
|
||||
/// Determine which side of the version-sort comparison had more leading zeros.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
enum MoreLeadingZeros {
|
||||
Left,
|
||||
Right,
|
||||
Equal,
|
||||
}
|
||||
|
||||
/// Compare two identifiers based on the version sorting algorithm described in [the style guide]
|
||||
///
|
||||
/// [the style guide]: https://doc.rust-lang.org/nightly/style-guide/#sorting
|
||||
pub(crate) fn version_sort(a: &str, b: &str) -> std::cmp::Ordering {
|
||||
let iter_a = VersionChunkIter::new(a);
|
||||
let iter_b = VersionChunkIter::new(b);
|
||||
let mut more_leading_zeros = MoreLeadingZeros::Equal;
|
||||
|
||||
for either_or_both in iter_a.zip_longest(iter_b) {
|
||||
match either_or_both {
|
||||
EitherOrBoth::Left(_) => return std::cmp::Ordering::Greater,
|
||||
EitherOrBoth::Right(_) => return std::cmp::Ordering::Less,
|
||||
EitherOrBoth::Both(a, b) => match (a, b) {
|
||||
(VersionChunk::Underscore, VersionChunk::Underscore) => {
|
||||
continue;
|
||||
}
|
||||
(VersionChunk::Underscore, _) => return std::cmp::Ordering::Less,
|
||||
(_, VersionChunk::Underscore) => return std::cmp::Ordering::Greater,
|
||||
(VersionChunk::Str(ca), VersionChunk::Str(cb))
|
||||
| (VersionChunk::Str(ca), VersionChunk::Number { source: cb, .. })
|
||||
| (VersionChunk::Number { source: ca, .. }, VersionChunk::Str(cb)) => {
|
||||
match ca.cmp(&cb) {
|
||||
std::cmp::Ordering::Equal => {
|
||||
continue;
|
||||
}
|
||||
order @ _ => return order,
|
||||
}
|
||||
}
|
||||
(
|
||||
VersionChunk::Number {
|
||||
value: va,
|
||||
zeros: lza,
|
||||
..
|
||||
},
|
||||
VersionChunk::Number {
|
||||
value: vb,
|
||||
zeros: lzb,
|
||||
..
|
||||
},
|
||||
) => match va.cmp(&vb) {
|
||||
std::cmp::Ordering::Equal => {
|
||||
if lza == lzb {
|
||||
continue;
|
||||
}
|
||||
|
||||
if more_leading_zeros == MoreLeadingZeros::Equal && lza > lzb {
|
||||
more_leading_zeros = MoreLeadingZeros::Left;
|
||||
} else if more_leading_zeros == MoreLeadingZeros::Equal && lza < lzb {
|
||||
more_leading_zeros = MoreLeadingZeros::Right;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
order @ _ => return order,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
match more_leading_zeros {
|
||||
MoreLeadingZeros::Equal => std::cmp::Ordering::Equal,
|
||||
MoreLeadingZeros::Left => std::cmp::Ordering::Less,
|
||||
MoreLeadingZeros::Right => std::cmp::Ordering::Greater,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_chunks() {
|
||||
let mut iter = VersionChunkIter::new("x86_128");
|
||||
assert_eq!(iter.next(), Some(VersionChunk::Str("x")));
|
||||
assert_eq!(
|
||||
iter.next(),
|
||||
Some(VersionChunk::Number {
|
||||
value: 86,
|
||||
zeros: 0,
|
||||
source: "86"
|
||||
})
|
||||
);
|
||||
assert_eq!(iter.next(), Some(VersionChunk::Underscore));
|
||||
assert_eq!(
|
||||
iter.next(),
|
||||
Some(VersionChunk::Number {
|
||||
value: 128,
|
||||
zeros: 0,
|
||||
source: "128"
|
||||
})
|
||||
);
|
||||
assert_eq!(iter.next(), None);
|
||||
|
||||
let mut iter = VersionChunkIter::new("w005s09t");
|
||||
assert_eq!(iter.next(), Some(VersionChunk::Str("w")));
|
||||
assert_eq!(
|
||||
iter.next(),
|
||||
Some(VersionChunk::Number {
|
||||
value: 5,
|
||||
zeros: 2,
|
||||
source: "005"
|
||||
})
|
||||
);
|
||||
assert_eq!(iter.next(), Some(VersionChunk::Str("s")));
|
||||
assert_eq!(
|
||||
iter.next(),
|
||||
Some(VersionChunk::Number {
|
||||
value: 9,
|
||||
zeros: 1,
|
||||
source: "09"
|
||||
})
|
||||
);
|
||||
assert_eq!(iter.next(), Some(VersionChunk::Str("t")));
|
||||
assert_eq!(iter.next(), None);
|
||||
|
||||
let mut iter = VersionChunkIter::new("ZY_WX");
|
||||
assert_eq!(iter.next(), Some(VersionChunk::Str("ZY")));
|
||||
assert_eq!(iter.next(), Some(VersionChunk::Underscore));
|
||||
assert_eq!(iter.next(), Some(VersionChunk::Str("WX")));
|
||||
|
||||
let mut iter = VersionChunkIter::new("_v1");
|
||||
assert_eq!(iter.next(), Some(VersionChunk::Underscore));
|
||||
assert_eq!(iter.next(), Some(VersionChunk::Str("v")));
|
||||
assert_eq!(
|
||||
iter.next(),
|
||||
Some(VersionChunk::Number {
|
||||
value: 1,
|
||||
zeros: 0,
|
||||
source: "1"
|
||||
})
|
||||
);
|
||||
|
||||
let mut iter = VersionChunkIter::new("_1v");
|
||||
assert_eq!(iter.next(), Some(VersionChunk::Underscore));
|
||||
assert_eq!(
|
||||
iter.next(),
|
||||
Some(VersionChunk::Number {
|
||||
value: 1,
|
||||
zeros: 0,
|
||||
source: "1"
|
||||
})
|
||||
);
|
||||
assert_eq!(iter.next(), Some(VersionChunk::Str("v")));
|
||||
|
||||
let mut iter = VersionChunkIter::new("v009");
|
||||
assert_eq!(iter.next(), Some(VersionChunk::Str("v")));
|
||||
assert_eq!(
|
||||
iter.next(),
|
||||
Some(VersionChunk::Number {
|
||||
value: 9,
|
||||
zeros: 2,
|
||||
source: "009"
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_version_sort() {
|
||||
let mut input = vec!["", "b", "a"];
|
||||
let expected = vec!["", "a", "b"];
|
||||
input.sort_by(|a, b| version_sort(a, b));
|
||||
assert_eq!(input, expected);
|
||||
|
||||
let mut input = vec!["x7x", "xxx"];
|
||||
let expected = vec!["x7x", "xxx"];
|
||||
input.sort_by(|a, b| version_sort(a, b));
|
||||
assert_eq!(input, expected);
|
||||
|
||||
let mut input = vec!["applesauce", "apple"];
|
||||
let expected = vec!["apple", "applesauce"];
|
||||
input.sort_by(|a, b| version_sort(a, b));
|
||||
assert_eq!(input, expected);
|
||||
|
||||
let mut input = vec!["aaaaa", "aaa_a"];
|
||||
let expected = vec!["aaa_a", "aaaaa"];
|
||||
input.sort_by(|a, b| version_sort(a, b));
|
||||
assert_eq!(input, expected);
|
||||
|
||||
let mut input = vec!["AAAAA", "AAA1A", "BBBBB", "BB_BB", "C3CCC"];
|
||||
let expected = vec!["AAA1A", "AAAAA", "BB_BB", "BBBBB", "C3CCC"];
|
||||
input.sort_by(|a, b| version_sort(a, b));
|
||||
assert_eq!(input, expected);
|
||||
|
||||
let mut input = vec!["1_000_000", "1_010_001"];
|
||||
let expected = vec!["1_000_000", "1_010_001"];
|
||||
input.sort_by(|a, b| version_sort(a, b));
|
||||
assert_eq!(input, expected);
|
||||
|
||||
let mut input = vec![
|
||||
"5", "50", "500", "5_000", "5_005", "5_050", "5_500", "50_000", "50_005", "50_050",
|
||||
"50_500",
|
||||
];
|
||||
let expected = vec![
|
||||
"5", "5_000", "5_005", "5_050", "5_500", "50", "50_000", "50_005", "50_050", "50_500",
|
||||
"500",
|
||||
];
|
||||
input.sort_by(|a, b| version_sort(a, b));
|
||||
assert_eq!(input, expected);
|
||||
|
||||
let mut input = vec!["X86_64", "x86_64", "X86_128", "x86_128"];
|
||||
let expected = vec!["X86_64", "X86_128", "x86_64", "x86_128"];
|
||||
input.sort_by(|a, b| version_sort(a, b));
|
||||
assert_eq!(input, expected);
|
||||
|
||||
let mut input = vec!["__", "_"];
|
||||
let expected = vec!["_", "__"];
|
||||
input.sort_by(|a, b| version_sort(a, b));
|
||||
assert_eq!(input, expected);
|
||||
|
||||
let mut input = vec!["foo_", "foo"];
|
||||
let expected = vec!["foo", "foo_"];
|
||||
input.sort_by(|a, b| version_sort(a, b));
|
||||
assert_eq!(input, expected);
|
||||
|
||||
let mut input = vec!["A", "AA", "B", "a", "aA", "aa", "b"];
|
||||
let expected = vec!["A", "AA", "B", "a", "aA", "aa", "b"];
|
||||
input.sort_by(|a, b| version_sort(a, b));
|
||||
assert_eq!(input, expected);
|
||||
|
||||
let mut input = vec![
|
||||
"x86_128", "usize", "uz", "v000", "v00", "v0", "v0s", "v00t", "v0u", "v001", "v01",
|
||||
"v1", "v009", "x87", "zyxw", "_ZYXW", "_abcd", "A2", "ABCD", "Z_YXW", "ZY_XW", "ZY_XW",
|
||||
"ZYXW", "v09", "v9", "v010", "v10", "w005s09t", "w5s009t", "x64", "x86", "x86_32",
|
||||
"ua", "x86_64", "ZYXW_", "a1", "abcd", "u_zzz", "u8", "u16", "u32", "u64", "u128",
|
||||
"u256",
|
||||
];
|
||||
let expected = vec![
|
||||
"_ZYXW", "_abcd", "A2", "ABCD", "Z_YXW", "ZY_XW", "ZY_XW", "ZYXW", "ZYXW_", "a1",
|
||||
"abcd", "u_zzz", "u8", "u16", "u32", "u64", "u128", "u256", "ua", "usize", "uz",
|
||||
"v000", "v00", "v0", "v0s", "v00t", "v0u", "v001", "v01", "v1", "v009", "v09", "v9",
|
||||
"v010", "v10", "w005s09t", "w5s009t", "x64", "x86", "x86_32", "x86_64", "x86_128",
|
||||
"x87", "zyxw",
|
||||
];
|
||||
input.sort_by(|a, b| version_sort(a, b));
|
||||
assert_eq!(input, expected)
|
||||
}
|
||||
}
|
@ -2,10 +2,10 @@ use std::fs;
|
||||
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;
|
||||
|
@ -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 @@ impl Spanned for ast::PreciseCapturingArg {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Spanned for RangeOperand<'a> {
|
||||
fn span(&self) -> Span {
|
||||
self.span
|
||||
}
|
||||
}
|
||||
|
@ -2,9 +2,9 @@ use rustc_ast::ast;
|
||||
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,7 +90,16 @@ impl<'a> Stmt<'a> {
|
||||
|
||||
impl<'a> Rewrite for Stmt<'a> {
|
||||
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
|
||||
let expr_type = if context.config.version() == Version::Two && self.is_last_expr() {
|
||||
self.rewrite_result(context, shape).ok()
|
||||
}
|
||||
|
||||
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
|
||||
@ -111,11 +120,11 @@ fn format_stmt(
|
||||
stmt: &ast::Stmt,
|
||||
expr_type: ExprType,
|
||||
is_last_expr: bool,
|
||||
) -> Option<String> {
|
||||
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,
|
||||
};
|
||||
result.and_then(|res| recover_comment_removed(res, stmt.span(), context))
|
||||
ast::StmtKind::MacCall(..) | ast::StmtKind::Item(..) | ast::StmtKind::Empty => {
|
||||
Err(RewriteError::Unknown)
|
||||
}
|
||||
};
|
||||
result.map(|res| recover_comment_removed(res, stmt.span(), context))
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -4,9 +4,9 @@ use std::io::{BufRead, BufReader, Write};
|
||||
use std::iter::Enumerate;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use super::{print_mismatches, write_message, DIFF_CONTEXT_SIZE};
|
||||
use 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";
|
||||
|
@ -6,14 +6,17 @@ use std::iter::Peekable;
|
||||
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<PathBuf> {
|
||||
let mut files = vec![];
|
||||
if path.is_dir() {
|
||||
for entry in fs::read_dir(path).expect(&format!(
|
||||
"couldn't read directory {}",
|
||||
path.to_str().unwrap()
|
||||
)) {
|
||||
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<PathBuf> {
|
||||
}
|
||||
|
||||
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<T: Fn(u32) -> 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<String, String>,
|
||||
) -> (Option<Edition>, Option<StyleEdition>, Option<Version>) {
|
||||
(
|
||||
comments
|
||||
.get("edition")
|
||||
.map(|e| Edition::from_str(e).expect(&format!("invalid edition value: '{}'", e))),
|
||||
comments.get("style_edition").map(|se| {
|
||||
StyleEdition::from_str(se).expect(&format!("invalid style_edition value: '{}'", se))
|
||||
}),
|
||||
comments
|
||||
.get("version")
|
||||
.map(|v| Version::from_str(v).expect(&format!("invalid version value: '{}'", v))),
|
||||
)
|
||||
}
|
||||
|
||||
fn idempotent_check(
|
||||
filename: &PathBuf,
|
||||
opt_config: &Option<PathBuf>,
|
||||
) -> Result<FormatReport, IdempotentCheckError> {
|
||||
let sig_comments = read_significant_comments(filename);
|
||||
let config = if let Some(ref config_file_path) = opt_config {
|
||||
Config::from_toml_path(config_file_path).expect("`rustfmt.toml` not found")
|
||||
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<Edition>,
|
||||
style_edition: Option<StyleEdition>,
|
||||
version: Option<Version>,
|
||||
) -> 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.
|
||||
|
@ -2,23 +2,23 @@ use std::ops::Deref;
|
||||
|
||||
use rustc_ast::ast::{self, FnRetTy, Mutability, Term};
|
||||
use rustc_ast::ptr;
|
||||
use rustc_span::{symbol::kw, BytePos, Pos, Span};
|
||||
use 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<ptr::P<ast::QSelf>>,
|
||||
path: &ast::Path,
|
||||
shape: Shape,
|
||||
) -> Option<String> {
|
||||
) -> 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<String>
|
||||
) -> RewriteResult
|
||||
where
|
||||
I: Iterator<Item = &'a ast::PathSegment>,
|
||||
{
|
||||
@ -122,7 +122,9 @@ where
|
||||
}
|
||||
|
||||
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 @@ where
|
||||
buffer.push_str(&segment_string);
|
||||
}
|
||||
|
||||
Some(buffer)
|
||||
Ok(buffer)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -169,19 +171,27 @@ impl<'a> Spanned for SegmentParam<'a> {
|
||||
|
||||
impl<'a> Rewrite for SegmentParam<'a> {
|
||||
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
|
||||
self.rewrite_result(context, shape).ok()
|
||||
}
|
||||
|
||||
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
|
||||
match *self {
|
||||
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<String> {
|
||||
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 @@ impl Rewrite for ast::PreciseCapturingArg {
|
||||
|
||||
impl Rewrite for ast::AssocItemConstraint {
|
||||
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
|
||||
self.rewrite_result(context, shape).ok()
|
||||
}
|
||||
|
||||
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
|
||||
use ast::AssocItemConstraintKind::{Bound, Equality};
|
||||
|
||||
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 @@ impl Rewrite for ast::AssocItemConstraint {
|
||||
};
|
||||
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<String> {
|
||||
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<String> {
|
||||
) -> 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<String>
|
||||
) -> RewriteResult
|
||||
where
|
||||
I: ExactSizeIterator,
|
||||
<I as Iterator>::Item: Deref,
|
||||
@ -308,12 +333,12 @@ where
|
||||
|
||||
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 @@ where
|
||||
)
|
||||
} 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 @@ where
|
||||
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 @@ where
|
||||
",",
|
||||
|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 @@ where
|
||||
)
|
||||
};
|
||||
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<String> {
|
||||
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 @@ impl Rewrite for ast::WherePredicate {
|
||||
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 @@ impl Rewrite for ast::WherePredicate {
|
||||
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<String> {
|
||||
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<String> {
|
||||
) -> 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<String> {
|
||||
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<String> {
|
||||
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<String> {
|
||||
Some(context.snippet(self.ident.span).to_owned())
|
||||
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
|
||||
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<String> {
|
||||
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 @@ impl Rewrite for ast::GenericBound {
|
||||
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<String> {
|
||||
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 @@ impl Rewrite for ast::GenericBounds {
|
||||
|
||||
impl Rewrite for ast::GenericParam {
|
||||
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
|
||||
self.rewrite_result(context, shape).ok()
|
||||
}
|
||||
|
||||
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
|
||||
// FIXME: If there are more than one attributes, this will force multiline.
|
||||
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 @@ impl Rewrite for ast::GenericParam {
|
||||
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 @@ impl Rewrite for ast::GenericParam {
|
||||
|
||||
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 @@ impl Rewrite for ast::GenericParam {
|
||||
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 @@ impl Rewrite for ast::GenericParam {
|
||||
result.push_str(¶m);
|
||||
}
|
||||
|
||||
Some(result)
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl Rewrite for ast::PolyTraitRef {
|
||||
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
|
||||
self.rewrite_result(context, shape).ok()
|
||||
}
|
||||
|
||||
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
|
||||
if let Some(lifetime_str) = rewrite_bound_params(context, shape, &self.bound_generic_params)
|
||||
{
|
||||
// 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<String> {
|
||||
self.rewrite_result(context, shape).ok()
|
||||
}
|
||||
|
||||
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
|
||||
rewrite_path(context, PathContext::Type, &None, &self.path, shape)
|
||||
}
|
||||
}
|
||||
|
||||
impl Rewrite for ast::Ty {
|
||||
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
|
||||
self.rewrite_result(context, shape).ok()
|
||||
}
|
||||
|
||||
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
|
||||
match self.kind {
|
||||
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 @@ impl Rewrite for ast::Ty {
|
||||
{
|
||||
res.push('+');
|
||||
}
|
||||
Some(format!("{prefix}{res}"))
|
||||
Ok(format!("{prefix}{res}"))
|
||||
}
|
||||
ast::TyKind::Ptr(ref mt) => {
|
||||
let prefix = match mt.mutbl {
|
||||
@ -738,8 +836,11 @@ impl Rewrite for ast::Ty {
|
||||
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 @@ impl Rewrite for ast::Ty {
|
||||
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 @@ impl Rewrite for ast::Ty {
|
||||
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 @@ impl Rewrite for ast::Ty {
|
||||
))
|
||||
}
|
||||
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 @@ impl Rewrite for ast::Ty {
|
||||
),
|
||||
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 @@ impl Rewrite for ast::Ty {
|
||||
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 @@ impl Rewrite for ast::Ty {
|
||||
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<String> {
|
||||
) -> 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<String> {
|
||||
) -> 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<String> {
|
||||
) -> 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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,11 +6,11 @@ use rustc_ast::ast::{
|
||||
};
|
||||
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
|
||||
|
@ -11,9 +11,9 @@ use crate::config::lists::*;
|
||||
use crate::expr::rewrite_field;
|
||||
use crate::items::{rewrite_struct_field, rewrite_struct_field_prefix};
|
||||
use crate::lists::{
|
||||
definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator,
|
||||
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 @@ use crate::utils::{
|
||||
pub(crate) trait AlignedItem {
|
||||
fn skip(&self) -> bool;
|
||||
fn get_span(&self) -> Span;
|
||||
fn rewrite_prefix(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String>;
|
||||
fn rewrite_prefix(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult;
|
||||
fn rewrite_aligned_item(
|
||||
&self,
|
||||
context: &RewriteContext<'_>,
|
||||
shape: Shape,
|
||||
prefix_max_width: usize,
|
||||
) -> Option<String>;
|
||||
) -> RewriteResult;
|
||||
}
|
||||
|
||||
impl AlignedItem for ast::FieldDef {
|
||||
@ -42,15 +42,15 @@ impl AlignedItem for ast::FieldDef {
|
||||
self.span()
|
||||
}
|
||||
|
||||
fn rewrite_prefix(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
|
||||
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| {
|
||||
let field_str = rewrite_struct_field_prefix(context, self)?;
|
||||
combine_strs_with_missing_comments(
|
||||
context,
|
||||
&attrs_str,
|
||||
@ -59,7 +59,6 @@ impl AlignedItem for ast::FieldDef {
|
||||
shape,
|
||||
attrs_extendable,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn rewrite_aligned_item(
|
||||
@ -67,7 +66,7 @@ impl AlignedItem for ast::FieldDef {
|
||||
context: &RewriteContext<'_>,
|
||||
shape: Shape,
|
||||
prefix_max_width: usize,
|
||||
) -> Option<String> {
|
||||
) -> RewriteResult {
|
||||
rewrite_struct_field(context, self, shape, prefix_max_width)
|
||||
}
|
||||
}
|
||||
@ -81,8 +80,8 @@ impl AlignedItem for ast::ExprField {
|
||||
self.span()
|
||||
}
|
||||
|
||||
fn rewrite_prefix(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
|
||||
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 @@ impl AlignedItem for ast::ExprField {
|
||||
context: &RewriteContext<'_>,
|
||||
shape: Shape,
|
||||
prefix_max_width: usize,
|
||||
) -> Option<String> {
|
||||
) -> RewriteResult {
|
||||
rewrite_field(context, self, shape, prefix_max_width)
|
||||
}
|
||||
}
|
||||
@ -199,7 +198,7 @@ fn struct_field_prefix_max_min_width<T: AlignedItem>(
|
||||
.rewrite_prefix(context, shape)
|
||||
.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<T: AlignedItem>(
|
||||
if tactic == DefinitiveListTactic::Horizontal {
|
||||
// since the items fits on a line, there is no need to align them
|
||||
let do_rewrite =
|
||||
|field: &T| -> Option<String> { field.rewrite_aligned_item(context, item_shape, 0) };
|
||||
|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<T: AlignedItem>(
|
||||
.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.
|
||||
|
@ -3,24 +3,23 @@ use std::rc::Rc;
|
||||
|
||||
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 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
|
||||
|
||||
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 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
|
||||
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 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
|
||||
|
||||
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 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
|
||||
)
|
||||
} else {
|
||||
let indent = self.block_indent;
|
||||
let rewrite = self.rewrite_required_fn(
|
||||
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 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
|
||||
item.ident,
|
||||
&item.vis,
|
||||
item.span,
|
||||
);
|
||||
)
|
||||
.ok();
|
||||
self.push_rewrite(item.span, rewrite);
|
||||
}
|
||||
ast::ItemKind::Delegation(..) | ast::ItemKind::DelegationMac(..) => {
|
||||
@ -609,7 +613,8 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
|
||||
self.block_indent,
|
||||
visitor_kind,
|
||||
span,
|
||||
);
|
||||
)
|
||||
.ok();
|
||||
self.push_rewrite(span, rewrite);
|
||||
}
|
||||
|
||||
@ -655,8 +660,9 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
|
||||
);
|
||||
} 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 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
|
||||
|
||||
// 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) ;`)
|
||||
|
1
src/tools/rustfmt/tests/config/issue-6302.toml
Normal file
1
src/tools/rustfmt/tests/config/issue-6302.toml
Normal file
@ -0,0 +1 @@
|
||||
edition = 2019
|
@ -0,0 +1 @@
|
||||
style_edition = "2024"
|
@ -0,0 +1 @@
|
||||
version = "Two"
|
@ -0,0 +1,2 @@
|
||||
style_edition = "2024"
|
||||
overflow_delimited_expr = false
|
@ -0,0 +1,2 @@
|
||||
style_edition = "2021"
|
||||
edition = "2024"
|
@ -0,0 +1,2 @@
|
||||
version = "Two"
|
||||
edition = "2018"
|
@ -0,0 +1,3 @@
|
||||
version = "Two"
|
||||
edition = "2018"
|
||||
style_edition = "2021"
|
@ -0,0 +1,2 @@
|
||||
version = "Two"
|
||||
style_edition = "2021"
|
@ -5,7 +5,7 @@ use std::fs::remove_file;
|
||||
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));
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
// rustfmt-version: Two
|
||||
// rustfmt-style_edition: 2024
|
||||
fn main() {
|
||||
match a {
|
||||
_ =>
|
||||
|
@ -1,4 +1,4 @@
|
||||
// rustfmt-version: Two
|
||||
// rustfmt-style_edition: 2024
|
||||
fn main() {
|
||||
match a {
|
||||
_ => // comment with =>
|
||||
|
@ -1,4 +1,4 @@
|
||||
// rustfmt-version: One
|
||||
// rustfmt-style_edition: 2015
|
||||
// rustfmt-error_on_line_overflow: false
|
||||
// rustfmt-indent_style: Block
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
// rustfmt-version: Two
|
||||
// rustfmt-style_edition: 2024
|
||||
// rustfmt-error_on_line_overflow: false
|
||||
// rustfmt-indent_style: Block
|
||||
|
||||
|
@ -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();
|
||||
}
|
@ -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();
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
// rustfmt-fn_single_line: true
|
||||
// rustfmt-version: One
|
||||
// rustfmt-style_edition: 2015
|
||||
// Test single-line functions.
|
||||
|
||||
fn foo_expr() {
|
||||
|
@ -1,5 +1,5 @@
|
||||
// rustfmt-fn_single_line: true
|
||||
// rustfmt-version: Two
|
||||
// rustfmt-style_edition: 2024
|
||||
// Test single-line functions.
|
||||
|
||||
fn foo_expr() {
|
||||
|
@ -1,4 +1,4 @@
|
||||
// rustfmt-version: One
|
||||
// rustfmt-style_edition: 2015
|
||||
// rustfmt-error_on_line_overflow: false
|
||||
|
||||
fn issue_2179() {
|
||||
|
@ -1,4 +1,4 @@
|
||||
// rustfmt-version: Two
|
||||
// rustfmt-style_edition: 2024
|
||||
// rustfmt-error_on_line_overflow: false
|
||||
|
||||
fn issue_2179() {
|
||||
|
@ -1,4 +1,4 @@
|
||||
// rustfmt-version: One
|
||||
// rustfmt-style_edition: 2015
|
||||
|
||||
fn foo() {
|
||||
match 0 {
|
||||
|
@ -1,4 +1,4 @@
|
||||
// rustfmt-version: Two
|
||||
// rustfmt-style_edition: 2024
|
||||
|
||||
fn foo() {
|
||||
match 0 {
|
||||
|
@ -1,4 +1,4 @@
|
||||
// rustfmt-version: Two
|
||||
// rustfmt-style_edition: 2024
|
||||
|
||||
fn main() {
|
||||
thread::spawn(|| {
|
||||
|
@ -1,4 +1,4 @@
|
||||
// rustfmt-version: One
|
||||
// rustfmt-style_edition: 2015
|
||||
|
||||
pub fn main() {
|
||||
/* let s = String::from(
|
||||
|
@ -1,4 +1,4 @@
|
||||
// rustfmt-version: Two
|
||||
// rustfmt-style_edition: 2024
|
||||
|
||||
pub fn main() {
|
||||
/* let s = String::from(
|
||||
|
@ -1,4 +1,4 @@
|
||||
// rustfmt-version: One
|
||||
// rustfmt-style_edition: 2015
|
||||
|
||||
fn main() {
|
||||
assert!(HAYSTACK
|
||||
|
@ -1,4 +1,4 @@
|
||||
// rustfmt-version: Two
|
||||
// rustfmt-style_edition: 2024
|
||||
|
||||
fn main() {
|
||||
assert!(HAYSTACK
|
||||
|
@ -1,4 +1,4 @@
|
||||
// rustfmt-version: One
|
||||
// rustfmt-style_edition: 2015
|
||||
|
||||
pub fn parse_conditional<'a, I: 'a>(
|
||||
) -> impl Parser<Input = I, Output = Expr, PartialState = ()> + 'a
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user