prettyplease-0.2.37/.cargo_vcs_info.json0000644000000001360000000000100136460ustar { "git": { "sha1": "c971184fa8c5ef5a2828196e35bd99469455b46b" }, "path_in_vcs": "" }prettyplease-0.2.37/.gitattributes000064400000000000000000000001061046102023000153260ustar 00000000000000cargo-expand/*.rs linguist-generated examples/*.rs linguist-generated prettyplease-0.2.37/.github/FUNDING.yml000064400000000000000000000000201046102023000156030ustar 00000000000000github: dtolnay prettyplease-0.2.37/.github/workflows/ci.yml000064400000000000000000000063251046102023000171570ustar 00000000000000name: CI on: push: pull_request: workflow_dispatch: schedule: [cron: "40 1 * * *"] permissions: contents: read env: RUSTFLAGS: -Dwarnings jobs: pre_ci: uses: dtolnay/.github/.github/workflows/pre_ci.yml@master test: name: Rust ${{matrix.rust}} needs: pre_ci if: needs.pre_ci.outputs.continue runs-on: ubuntu-latest strategy: fail-fast: false matrix: rust: [nightly, beta, stable, 1.62.0] timeout-minutes: 45 steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: toolchain: ${{matrix.rust}} - run: cargo check - run: cargo check --features verbatim - run: cargo test env: RUSTFLAGS: ${{env.RUSTFLAGS}} ${{matrix.rust == 'nightly' && '--cfg exhaustive' || ''}} - run: cargo test --release --test test_precedence env: RUSTFLAGS: ${{env.RUSTFLAGS}} ${{matrix.rust == 'nightly' && '--cfg exhaustive' || ''}} if: matrix.rust != '1.62.0' - uses: actions/upload-artifact@v4 if: matrix.rust == 'nightly' && always() with: name: Cargo.lock path: Cargo.lock continue-on-error: true examples: name: Examples needs: pre_ci if: needs.pre_ci.outputs.continue runs-on: ubuntu-latest timeout-minutes: 45 steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@nightly with: components: llvm-tools, rustc-dev, rustfmt - run: cargo run --manifest-path examples/update/Cargo.toml - run: git diff --exit-code - run: cargo run --manifest-path cargo-expand/update/Cargo.toml - run: git diff --exit-code fuzz: name: Fuzz needs: pre_ci if: needs.pre_ci.outputs.continue runs-on: ubuntu-latest timeout-minutes: 45 steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@nightly - uses: dtolnay/install@cargo-fuzz - run: cargo fuzz check minimal: name: Minimal versions needs: pre_ci if: needs.pre_ci.outputs.continue runs-on: ubuntu-latest timeout-minutes: 45 steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@nightly - run: cargo generate-lockfile -Z minimal-versions - run: cargo check --locked doc: name: Documentation needs: pre_ci if: needs.pre_ci.outputs.continue runs-on: ubuntu-latest timeout-minutes: 45 env: RUSTDOCFLAGS: -Dwarnings steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@nightly - uses: dtolnay/install@cargo-docs-rs - run: cargo docs-rs clippy: name: Clippy runs-on: ubuntu-latest if: github.event_name != 'pull_request' timeout-minutes: 45 steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@clippy - run: cargo clippy --features verbatim -- -Dclippy::all -Dclippy::pedantic outdated: name: Outdated runs-on: ubuntu-latest if: github.event_name != 'pull_request' timeout-minutes: 45 steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - uses: dtolnay/install@cargo-outdated - run: cargo outdated --workspace --exit-code 1 prettyplease-0.2.37/.gitignore000064400000000000000000000000251046102023000144230ustar 00000000000000/target/ /Cargo.lock prettyplease-0.2.37/Cargo.lock0000644000000024460000000000100116270ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "indoc" version = "2.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" [[package]] name = "prettyplease" version = "0.2.37" dependencies = [ "indoc", "proc-macro2", "quote", "syn", ] [[package]] name = "proc-macro2" version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] [[package]] name = "syn" version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" prettyplease-0.2.37/Cargo.toml0000644000000041040000000000100116430ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.62" name = "prettyplease" version = "0.2.37" authors = ["David Tolnay "] build = "build.rs" links = "prettyplease02" exclude = ["cargo-expand"] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "A minimal `syn` syntax tree pretty-printer" documentation = "https://docs.rs/prettyplease" readme = "README.md" keywords = ["rustfmt"] categories = ["development-tools"] license = "MIT OR Apache-2.0" repository = "https://github.com/dtolnay/prettyplease" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] rustdoc-args = [ "--generate-link-to-definition", "--extern-html-root-url=core=https://doc.rust-lang.org", "--extern-html-root-url=alloc=https://doc.rust-lang.org", "--extern-html-root-url=std=https://doc.rust-lang.org", ] [package.metadata.playground] features = ["verbatim"] [features] verbatim = ["syn/parsing"] [lib] name = "prettyplease" path = "src/lib.rs" [[test]] name = "test" path = "tests/test.rs" [[test]] name = "test_precedence" path = "tests/test_precedence.rs" [dependencies.proc-macro2] version = "1.0.80" default-features = false [dependencies.syn] version = "2.0.105" features = ["full"] default-features = false [dev-dependencies.indoc] version = "2" [dev-dependencies.proc-macro2] version = "1.0.80" default-features = false [dev-dependencies.quote] version = "1.0.35" default-features = false [dev-dependencies.syn] version = "2.0.105" features = [ "clone-impls", "extra-traits", "parsing", "printing", "visit-mut", ] default-features = false prettyplease-0.2.37/Cargo.toml.orig000064400000000000000000000025311046102023000153260ustar 00000000000000[package] name = "prettyplease" version = "0.2.37" authors = ["David Tolnay "] autoexamples = false categories = ["development-tools"] description = "A minimal `syn` syntax tree pretty-printer" documentation = "https://docs.rs/prettyplease" edition = "2021" exclude = ["cargo-expand"] keywords = ["rustfmt"] license = "MIT OR Apache-2.0" links = "prettyplease02" repository = "https://github.com/dtolnay/prettyplease" rust-version = "1.62" [features] verbatim = ["syn/parsing"] [dependencies] proc-macro2 = { version = "1.0.80", default-features = false } syn = { version = "2.0.105", default-features = false, features = ["full"] } [dev-dependencies] indoc = "2" proc-macro2 = { version = "1.0.80", default-features = false } quote = { version = "1.0.35", default-features = false } syn = { version = "2.0.105", default-features = false, features = ["clone-impls", "extra-traits", "parsing", "printing", "visit-mut"] } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] rustdoc-args = [ "--generate-link-to-definition", "--extern-html-root-url=core=https://doc.rust-lang.org", "--extern-html-root-url=alloc=https://doc.rust-lang.org", "--extern-html-root-url=std=https://doc.rust-lang.org", ] [package.metadata.playground] features = ["verbatim"] [workspace] members = ["cargo-expand/update", "examples/update"] prettyplease-0.2.37/LICENSE-APACHE000064400000000000000000000227731046102023000143750ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS prettyplease-0.2.37/LICENSE-MIT000064400000000000000000000017771046102023000141060ustar 00000000000000Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. prettyplease-0.2.37/README.md000064400000000000000000000346511046102023000137260ustar 00000000000000prettyplease::unparse ===================== [github](https://github.com/dtolnay/prettyplease) [crates.io](https://crates.io/crates/prettyplease) [docs.rs](https://docs.rs/prettyplease) [build status](https://github.com/dtolnay/prettyplease/actions?query=branch%3Amaster) A minimal `syn` syntax tree pretty-printer.
## Overview This is a pretty-printer to turn a `syn` syntax tree into a `String` of well-formatted source code. In contrast to rustfmt, this library is intended to be suitable for arbitrary generated code. Rustfmt prioritizes high-quality output that is impeccable enough that you'd be comfortable spending your career staring at its output — but that means some heavyweight algorithms, and it has a tendency to bail out on code that is hard to format (for example [rustfmt#3697], and there are dozens more issues like it). That's not necessarily a big deal for human-generated code because when code gets highly nested, the human will naturally be inclined to refactor into more easily formattable code. But for generated code, having the formatter just give up leaves it totally unreadable. [rustfmt#3697]: https://github.com/rust-lang/rustfmt/issues/3697 This library is designed using the simplest possible algorithm and data structures that can deliver about 95% of the quality of rustfmt-formatted output. In my experience testing real-world code, approximately 97-98% of output lines come out identical between rustfmt's formatting and this crate's. The rest have slightly different linebreak decisions, but still clearly follow the dominant modern Rust style. The tradeoffs made by this crate are a good fit for generated code that you will *not* spend your career staring at. For example, the output of `bindgen`, or the output of `cargo-expand`. In those cases it's more important that the whole thing be formattable without the formatter giving up, than that it be flawless.
## Feature matrix Here are a few superficial comparisons of this crate against the AST pretty-printer built into rustc, and rustfmt. The sections below go into more detail comparing the output of each of these libraries. | | prettyplease | rustc | rustfmt | |:---|:---:|:---:|:---:| | non-pathological behavior on big or generated code | 💚 | ❌ | ❌ | | idiomatic modern formatting ("locally indistinguishable from rustfmt") | 💚 | ❌ | 💚 | | throughput | 60 MB/s | 39 MB/s | 2.8 MB/s | | number of dependencies | 3 | 72 | 66 | | compile time including dependencies | 2.4 sec | 23.1 sec | 29.8 sec | | buildable using a stable Rust compiler | 💚 | ❌ | ❌ | | published to crates.io | 💚 | ❌ | ❌ | | extensively configurable output | ❌ | ❌ | 💚 | | intended to accommodate hand-maintained source code | ❌ | ❌ | 💚 |
## Comparison to rustfmt - [input.rs](https://github.com/dtolnay/prettyplease/blob/0.1.0/examples/input.rs) - [output.prettyplease.rs](https://github.com/dtolnay/prettyplease/blob/0.1.0/examples/output.prettyplease.rs) - [output.rustfmt.rs](https://github.com/dtolnay/prettyplease/blob/0.1.0/examples/output.rustfmt.rs) If you weren't told which output file is which, it would be practically impossible to tell — **except** for line 435 in the rustfmt output, which is more than 1000 characters long because rustfmt just gave up formatting that part of the file: ```rust match segments[5] { 0 => write!(f, "::{}", ipv4), 0xffff => write!(f, "::ffff:{}", ipv4), _ => unreachable!(), } } else { # [derive (Copy , Clone , Default)] struct Span { start : usize , len : usize , } let zeroes = { let mut longest = Span :: default () ; let mut current = Span :: default () ; for (i , & segment) in segments . iter () . enumerate () { if segment == 0 { if current . len == 0 { current . start = i ; } current . len += 1 ; if current . len > longest . len { longest = current ; } } else { current = Span :: default () ; } } longest } ; # [doc = " Write a colon-separated part of the address"] # [inline] fn fmt_subslice (f : & mut fmt :: Formatter < '_ > , chunk : & [u16]) -> fmt :: Result { if let Some ((first , tail)) = chunk . split_first () { write ! (f , "{:x}" , first) ? ; for segment in tail { f . write_char (':') ? ; write ! (f , "{:x}" , segment) ? ; } } Ok (()) } if zeroes . len > 1 { fmt_subslice (f , & segments [.. zeroes . start]) ? ; f . write_str ("::") ? ; fmt_subslice (f , & segments [zeroes . start + zeroes . len ..]) } else { fmt_subslice (f , & segments) } } } else { const IPV6_BUF_LEN: usize = (4 * 8) + 7; let mut buf = [0u8; IPV6_BUF_LEN]; let mut buf_slice = &mut buf[..]; ``` This is a pretty typical manifestation of rustfmt bailing out in generated code — a chunk of the input ends up on one line. The other manifestation is that you're working on some code, running rustfmt on save like a conscientious developer, but after a while notice it isn't doing anything. You introduce an intentional formatting issue, like a stray indent or semicolon, and run rustfmt to check your suspicion. Nope, it doesn't get cleaned up — rustfmt is just not formatting the part of the file you are working on. The prettyplease library is designed to have no pathological cases that force a bail out; the entire input you give it will get formatted in some "good enough" form. Separately, rustfmt can be problematic to integrate into projects. It's written using rustc's internal syntax tree, so it can't be built by a stable compiler. Its releases are not regularly published to crates.io, so in Cargo builds you'd need to depend on it as a git dependency, which precludes publishing your crate to crates.io also. You can shell out to a `rustfmt` binary, but that'll be whatever rustfmt version is installed on each developer's system (if any), which can lead to spurious diffs in checked-in generated code formatted by different versions. In contrast prettyplease is designed to be easy to pull in as a library, and compiles fast.
## Comparison to rustc_ast_pretty - [input.rs](https://github.com/dtolnay/prettyplease/blob/0.1.0/examples/input.rs) - [output.prettyplease.rs](https://github.com/dtolnay/prettyplease/blob/0.1.0/examples/output.prettyplease.rs) - [output.rustc.rs](https://github.com/dtolnay/prettyplease/blob/0.1.0/examples/output.rustc.rs) This is the pretty-printer that gets used when rustc prints source code, such as `rustc -Zunpretty=expanded`. It's used also by the standard library's `stringify!` when stringifying an interpolated macro_rules AST fragment, like an $:expr, and transitively by `dbg!` and many macros in the ecosystem. Rustc's formatting is mostly okay, but does not hew closely to the dominant contemporary style of Rust formatting. Some things wouldn't ever be written on one line, like this `match` expression, and certainly not with a comma in front of the closing brace: ```rust fn eq(&self, other: &IpAddr) -> bool { match other { IpAddr::V4(v4) => self == v4, IpAddr::V6(_) => false, } } ``` Some places use non-multiple-of-4 indentation, which is definitely not the norm: ```rust pub const fn to_ipv6_mapped(&self) -> Ipv6Addr { let [a, b, c, d] = self.octets(); Ipv6Addr{inner: c::in6_addr{s6_addr: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, a, b, c, d],},} } ``` And although there isn't an egregious example of it in the link because the input code is pretty tame, in general rustc_ast_pretty has pathological behavior on generated code. It has a tendency to use excessive horizontal indentation and rapidly run out of width: ```rust ::std::io::_print(::core::fmt::Arguments::new_v1(&[""], &match (&msg,) { _args => [::core::fmt::ArgumentV1::new(_args.0, ::core::fmt::Display::fmt)], })); ``` The snippets above are clearly different from modern rustfmt style. In contrast, prettyplease is designed to have output that is practically indistinguishable from rustfmt-formatted code.
## Example ```rust // [dependencies] // prettyplease = "0.2" // syn = { version = "2", default-features = false, features = ["full", "parsing"] } const INPUT: &str = stringify! { use crate::{ lazy::{Lazy, SyncLazy, SyncOnceCell}, panic, sync::{ atomic::{AtomicUsize, Ordering::SeqCst}, mpsc::channel, Mutex, }, thread, }; impl Into for T where U: From { fn into(self) -> U { U::from(self) } } }; fn main() { let syntax_tree = syn::parse_file(INPUT).unwrap(); let formatted = prettyplease::unparse(&syntax_tree); print!("{}", formatted); } ```
## Algorithm notes The approach and terminology used in the implementation are derived from [*Derek C. Oppen, "Pretty Printing" (1979)*][paper], on which rustc_ast_pretty is also based, and from rustc_ast_pretty's implementation written by Graydon Hoare in 2011 (and modernized over the years by dozens of volunteer maintainers). [paper]: http://i.stanford.edu/pub/cstr/reports/cs/tr/79/770/CS-TR-79-770.pdf The paper describes two language-agnostic interacting procedures `Scan()` and `Print()`. Language-specific code decomposes an input data structure into a stream of `string` and `break` tokens, and `begin` and `end` tokens for grouping. Each `begin`–`end` range may be identified as either "consistent breaking" or "inconsistent breaking". If a group is consistently breaking, then if the whole contents do not fit on the line, *every* `break` token in the group will receive a linebreak. This is appropriate, for example, for Rust struct literals, or arguments of a function call. If a group is inconsistently breaking, then the `string` tokens in the group are greedily placed on the line until out of space, and linebroken only at those `break` tokens for which the next string would not fit. For example, this is appropriate for the contents of a braced `use` statement in Rust. Scan's job is to efficiently accumulate sizing information about groups and breaks. For every `begin` token we compute the distance to the matched `end` token, and for every `break` we compute the distance to the next `break`. The algorithm uses a ringbuffer to hold tokens whose size is not yet ascertained. The maximum size of the ringbuffer is bounded by the target line length and does not grow indefinitely, regardless of deep nesting in the input stream. That's because once a group is sufficiently big, the precise size can no longer make a difference to linebreak decisions and we can effectively treat it as "infinity". Print's job is to use the sizing information to efficiently assign a "broken" or "not broken" status to every `begin` token. At that point the output is easily constructed by concatenating `string` tokens and breaking at `break` tokens contained within a broken group. Leveraging these primitives (i.e. cleverly placing the all-or-nothing consistent breaks and greedy inconsistent breaks) to yield rustfmt-compatible formatting for all of Rust's syntax tree nodes is a fun challenge. Here is a visualization of some Rust tokens fed into the pretty printing algorithm. Consistently breaking `begin`—`end` pairs are represented by `«`⁠`»`, inconsistently breaking by `‹`⁠`›`, `break` by `·`, and the rest of the non-whitespace are `string`. ```text use crate::«{· ‹ lazy::«{·‹Lazy,· SyncLazy,· SyncOnceCell›·}»,· panic,· sync::«{· ‹ atomic::«{·‹AtomicUsize,· Ordering::SeqCst›·}»,· mpsc::channel,· Mutex›,· }»,· thread›,· }»;· «‹«impl<«·T‹›,· U‹›·»>» Into<«·U·»>· for T›· where· U:‹ From<«·T·»>›,· {· « fn into(·«·self·») -> U {· ‹ U::from(«·self·»)›· » }· »}· ``` The algorithm described in the paper is not quite sufficient for producing well-formatted Rust code that is locally indistinguishable from rustfmt's style. The reason is that in the paper, the complete non-whitespace contents are assumed to be independent of linebreak decisions, with Scan and Print being only in control of the whitespace (spaces and line breaks). In Rust as idiomatically formatted by rustfmt, that is not the case. Trailing commas are one example; the punctuation is only known *after* the broken vs non-broken status of the surrounding group is known: ```rust let _ = Struct { x: 0, y: true }; let _ = Struct { x: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, y: yyyyyyyyyyyyyyyyyyyyyyyyyyyyyy, //<- trailing comma if the expression wrapped }; ``` The formatting of `match` expressions is another case; we want small arms on the same line as the pattern, and big arms wrapped in a brace. The presence of the brace punctuation, comma, and semicolon are all dependent on whether the arm fits on the line: ```rust match total_nanos.checked_add(entry.nanos as u64) { Some(n) => tmp = n, //<- small arm, inline with comma None => { total_secs = total_secs .checked_add(total_nanos / NANOS_PER_SEC as u64) .expect("overflow in iter::sum over durations"); } //<- big arm, needs brace added, and also semicolon^ } ``` The printing algorithm implementation in this crate accommodates all of these situations with conditional punctuation tokens whose selection can be deferred and populated after it's known that the group is or is not broken.
#### License Licensed under either of Apache License, Version 2.0 or MIT license at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. prettyplease-0.2.37/build.rs000064400000000000000000000012061046102023000141020ustar 00000000000000use std::env; use std::ffi::OsString; use std::process; fn main() { println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rustc-check-cfg=cfg(exhaustive)"); println!("cargo:rustc-check-cfg=cfg(prettyplease_debug)"); println!("cargo:rustc-check-cfg=cfg(prettyplease_debug_indent)"); let pkg_version = cargo_env_var("CARGO_PKG_VERSION"); println!("cargo:VERSION={}", pkg_version.to_str().unwrap()); } fn cargo_env_var(key: &str) -> OsString { env::var_os(key).unwrap_or_else(|| { eprintln!("Environment variable ${key} is not set during execution of build script"); process::exit(1); }) } prettyplease-0.2.37/examples/.tokeignore000064400000000000000000000000051046102023000164160ustar 00000000000000*.rs prettyplease-0.2.37/examples/input.rs000064400000000000000000000366271046102023000157770ustar 00000000000000use crate :: cmp :: Ordering ; use crate :: fmt :: { self , Write as FmtWrite } ; use crate :: hash ; use crate :: io :: Write as IoWrite ; use crate :: mem :: transmute ; use crate :: sys :: net :: netc as c ; use crate :: sys_common :: { AsInner , FromInner , IntoInner } ; # [derive (Copy , Clone , Eq , PartialEq , Hash , PartialOrd , Ord)] pub enum IpAddr { V4 (Ipv4Addr) , V6 (Ipv6Addr) , } # [derive (Copy)] pub struct Ipv4Addr { inner : c :: in_addr , } # [derive (Copy)] pub struct Ipv6Addr { inner : c :: in6_addr , } # [derive (Copy , PartialEq , Eq , Clone , Hash , Debug)] # [non_exhaustive] pub enum Ipv6MulticastScope { InterfaceLocal , LinkLocal , RealmLocal , AdminLocal , SiteLocal , OrganizationLocal , Global , } impl IpAddr { pub const fn is_unspecified (& self) -> bool { match self { IpAddr :: V4 (ip) => ip . is_unspecified () , IpAddr :: V6 (ip) => ip . is_unspecified () , } } pub const fn is_loopback (& self) -> bool { match self { IpAddr :: V4 (ip) => ip . is_loopback () , IpAddr :: V6 (ip) => ip . is_loopback () , } } pub const fn is_global (& self) -> bool { match self { IpAddr :: V4 (ip) => ip . is_global () , IpAddr :: V6 (ip) => ip . is_global () , } } pub const fn is_multicast (& self) -> bool { match self { IpAddr :: V4 (ip) => ip . is_multicast () , IpAddr :: V6 (ip) => ip . is_multicast () , } } pub const fn is_documentation (& self) -> bool { match self { IpAddr :: V4 (ip) => ip . is_documentation () , IpAddr :: V6 (ip) => ip . is_documentation () , } } pub const fn is_benchmarking (& self) -> bool { match self { IpAddr :: V4 (ip) => ip . is_benchmarking () , IpAddr :: V6 (ip) => ip . is_benchmarking () , } } pub const fn is_ipv4 (& self) -> bool { matches ! (self , IpAddr :: V4 (_)) } pub const fn is_ipv6 (& self) -> bool { matches ! (self , IpAddr :: V6 (_)) } pub const fn to_canonical (& self) -> IpAddr { match self { & v4 @ IpAddr :: V4 (_) => v4 , IpAddr :: V6 (v6) => v6 . to_canonical () , } } } impl Ipv4Addr { pub const fn new (a : u8 , b : u8 , c : u8 , d : u8) -> Ipv4Addr { Ipv4Addr { inner : c :: in_addr { s_addr : u32 :: from_ne_bytes ([a , b , c , d]) } } } pub const LOCALHOST : Self = Ipv4Addr :: new (127 , 0 , 0 , 1) ; # [doc (alias = "INADDR_ANY")] pub const UNSPECIFIED : Self = Ipv4Addr :: new (0 , 0 , 0 , 0) ; pub const BROADCAST : Self = Ipv4Addr :: new (255 , 255 , 255 , 255) ; pub const fn octets (& self) -> [u8 ; 4] { self . inner . s_addr . to_ne_bytes () } pub const fn is_unspecified (& self) -> bool { self . inner . s_addr == 0 } pub const fn is_loopback (& self) -> bool { self . octets () [0] == 127 } pub const fn is_private (& self) -> bool { match self . octets () { [10 , ..] => true , [172 , b , ..] if b >= 16 && b <= 31 => true , [192 , 168 , ..] => true , _ => false , } } pub const fn is_link_local (& self) -> bool { matches ! (self . octets () , [169 , 254 , ..]) } pub const fn is_global (& self) -> bool { if u32 :: from_be_bytes (self . octets ()) == 0xc0000009 || u32 :: from_be_bytes (self . octets ()) == 0xc000000a { return true ; } ! self . is_private () && ! self . is_loopback () && ! self . is_link_local () && ! self . is_broadcast () && ! self . is_documentation () && ! self . is_shared () && ! (self . octets () [0] == 192 && self . octets () [1] == 0 && self . octets () [2] == 0) && ! self . is_reserved () && ! self . is_benchmarking () && self . octets () [0] != 0 } pub const fn is_shared (& self) -> bool { self . octets () [0] == 100 && (self . octets () [1] & 0b1100_0000 == 0b0100_0000) } pub const fn is_benchmarking (& self) -> bool { self . octets () [0] == 198 && (self . octets () [1] & 0xfe) == 18 } pub const fn is_reserved (& self) -> bool { self . octets () [0] & 240 == 240 && ! self . is_broadcast () } pub const fn is_multicast (& self) -> bool { self . octets () [0] >= 224 && self . octets () [0] <= 239 } pub const fn is_broadcast (& self) -> bool { u32 :: from_be_bytes (self . octets ()) == u32 :: from_be_bytes (Self :: BROADCAST . octets ()) } pub const fn is_documentation (& self) -> bool { matches ! (self . octets () , [192 , 0 , 2 , _] | [198 , 51 , 100 , _] | [203 , 0 , 113 , _]) } pub const fn to_ipv6_compatible (& self) -> Ipv6Addr { let [a , b , c , d] = self . octets () ; Ipv6Addr { inner : c :: in6_addr { s6_addr : [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , a , b , c , d] } , } } pub const fn to_ipv6_mapped (& self) -> Ipv6Addr { let [a , b , c , d] = self . octets () ; Ipv6Addr { inner : c :: in6_addr { s6_addr : [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0xFF , 0xFF , a , b , c , d] } , } } } impl fmt :: Display for IpAddr { fn fmt (& self , fmt : & mut fmt :: Formatter < '_ >) -> fmt :: Result { match self { IpAddr :: V4 (ip) => ip . fmt (fmt) , IpAddr :: V6 (ip) => ip . fmt (fmt) , } } } impl fmt :: Debug for IpAddr { fn fmt (& self , fmt : & mut fmt :: Formatter < '_ >) -> fmt :: Result { fmt :: Display :: fmt (self , fmt) } } impl From < Ipv4Addr > for IpAddr { fn from (ipv4 : Ipv4Addr) -> IpAddr { IpAddr :: V4 (ipv4) } } impl From < Ipv6Addr > for IpAddr { fn from (ipv6 : Ipv6Addr) -> IpAddr { IpAddr :: V6 (ipv6) } } impl fmt :: Display for Ipv4Addr { fn fmt (& self , fmt : & mut fmt :: Formatter < '_ >) -> fmt :: Result { let octets = self . octets () ; if fmt . precision () . is_none () && fmt . width () . is_none () { write ! (fmt , "{}.{}.{}.{}" , octets [0] , octets [1] , octets [2] , octets [3]) } else { const IPV4_BUF_LEN : usize = 15 ; let mut buf = [0u8 ; IPV4_BUF_LEN] ; let mut buf_slice = & mut buf [..] ; write ! (buf_slice , "{}.{}.{}.{}" , octets [0] , octets [1] , octets [2] , octets [3]) . unwrap () ; let len = IPV4_BUF_LEN - buf_slice . len () ; let buf = unsafe { crate :: str :: from_utf8_unchecked (& buf [.. len]) } ; fmt . pad (buf) } } } impl fmt :: Debug for Ipv4Addr { fn fmt (& self , fmt : & mut fmt :: Formatter < '_ >) -> fmt :: Result { fmt :: Display :: fmt (self , fmt) } } impl Clone for Ipv4Addr { fn clone (& self) -> Ipv4Addr { * self } } impl PartialEq for Ipv4Addr { fn eq (& self , other : & Ipv4Addr) -> bool { self . inner . s_addr == other . inner . s_addr } } impl PartialEq < Ipv4Addr > for IpAddr { fn eq (& self , other : & Ipv4Addr) -> bool { match self { IpAddr :: V4 (v4) => v4 == other , IpAddr :: V6 (_) => false , } } } impl PartialEq < IpAddr > for Ipv4Addr { fn eq (& self , other : & IpAddr) -> bool { match other { IpAddr :: V4 (v4) => self == v4 , IpAddr :: V6 (_) => false , } } } impl Eq for Ipv4Addr { } impl hash :: Hash for Ipv4Addr { fn hash < H : hash :: Hasher > (& self , s : & mut H) { { self . inner . s_addr } . hash (s) } } impl PartialOrd for Ipv4Addr { fn partial_cmp (& self , other : & Ipv4Addr) -> Option < Ordering > { Some (self . cmp (other)) } } impl PartialOrd < Ipv4Addr > for IpAddr { fn partial_cmp (& self , other : & Ipv4Addr) -> Option < Ordering > { match self { IpAddr :: V4 (v4) => v4 . partial_cmp (other) , IpAddr :: V6 (_) => Some (Ordering :: Greater) , } } } impl PartialOrd < IpAddr > for Ipv4Addr { fn partial_cmp (& self , other : & IpAddr) -> Option < Ordering > { match other { IpAddr :: V4 (v4) => self . partial_cmp (v4) , IpAddr :: V6 (_) => Some (Ordering :: Less) , } } } impl Ord for Ipv4Addr { fn cmp (& self , other : & Ipv4Addr) -> Ordering { u32 :: from_be (self . inner . s_addr) . cmp (& u32 :: from_be (other . inner . s_addr)) } } impl IntoInner < c :: in_addr > for Ipv4Addr { fn into_inner (self) -> c :: in_addr { self . inner } } impl From < Ipv4Addr > for u32 { fn from (ip : Ipv4Addr) -> u32 { let ip = ip . octets () ; u32 :: from_be_bytes (ip) } } impl From < u32 > for Ipv4Addr { fn from (ip : u32) -> Ipv4Addr { Ipv4Addr :: from (ip . to_be_bytes ()) } } impl From < [u8 ; 4] > for Ipv4Addr { fn from (octets : [u8 ; 4]) -> Ipv4Addr { Ipv4Addr :: new (octets [0] , octets [1] , octets [2] , octets [3]) } } impl From < [u8 ; 4] > for IpAddr { fn from (octets : [u8 ; 4]) -> IpAddr { IpAddr :: V4 (Ipv4Addr :: from (octets)) } } impl Ipv6Addr { pub const fn new (a : u16 , b : u16 , c : u16 , d : u16 , e : u16 , f : u16 , g : u16 , h : u16) -> Ipv6Addr { let addr16 = [a . to_be () , b . to_be () , c . to_be () , d . to_be () , e . to_be () , f . to_be () , g . to_be () , h . to_be () ,] ; Ipv6Addr { inner : c :: in6_addr { s6_addr : unsafe { transmute :: < _ , [u8 ; 16] > (addr16) } , } , } } pub const LOCALHOST : Self = Ipv6Addr :: new (0 , 0 , 0 , 0 , 0 , 0 , 0 , 1) ; pub const UNSPECIFIED : Self = Ipv6Addr :: new (0 , 0 , 0 , 0 , 0 , 0 , 0 , 0) ; pub const fn segments (& self) -> [u16 ; 8] { let [a , b , c , d , e , f , g , h] = unsafe { transmute :: < _ , [u16 ; 8] > (self . inner . s6_addr) } ; [u16 :: from_be (a) , u16 :: from_be (b) , u16 :: from_be (c) , u16 :: from_be (d) , u16 :: from_be (e) , u16 :: from_be (f) , u16 :: from_be (g) , u16 :: from_be (h) ,] } pub const fn is_unspecified (& self) -> bool { u128 :: from_be_bytes (self . octets ()) == u128 :: from_be_bytes (Ipv6Addr :: UNSPECIFIED . octets ()) } pub const fn is_loopback (& self) -> bool { u128 :: from_be_bytes (self . octets ()) == u128 :: from_be_bytes (Ipv6Addr :: LOCALHOST . octets ()) } pub const fn is_global (& self) -> bool { match self . multicast_scope () { Some (Ipv6MulticastScope :: Global) => true , None => self . is_unicast_global () , _ => false , } } pub const fn is_unique_local (& self) -> bool { (self . segments () [0] & 0xfe00) == 0xfc00 } pub const fn is_unicast (& self) -> bool { ! self . is_multicast () } pub const fn is_unicast_link_local (& self) -> bool { (self . segments () [0] & 0xffc0) == 0xfe80 } pub const fn is_documentation (& self) -> bool { (self . segments () [0] == 0x2001) && (self . segments () [1] == 0xdb8) } pub const fn is_benchmarking (& self) -> bool { (self . segments () [0] == 0x2001) && (self . segments () [1] == 0x2) && (self . segments () [2] == 0) } pub const fn is_unicast_global (& self) -> bool { self . is_unicast () && ! self . is_loopback () && ! self . is_unicast_link_local () && ! self . is_unique_local () && ! self . is_unspecified () && ! self . is_documentation () } pub const fn multicast_scope (& self) -> Option < Ipv6MulticastScope > { if self . is_multicast () { match self . segments () [0] & 0x000f { 1 => Some (Ipv6MulticastScope :: InterfaceLocal) , 2 => Some (Ipv6MulticastScope :: LinkLocal) , 3 => Some (Ipv6MulticastScope :: RealmLocal) , 4 => Some (Ipv6MulticastScope :: AdminLocal) , 5 => Some (Ipv6MulticastScope :: SiteLocal) , 8 => Some (Ipv6MulticastScope :: OrganizationLocal) , 14 => Some (Ipv6MulticastScope :: Global) , _ => None , } } else { None } } pub const fn is_multicast (& self) -> bool { (self . segments () [0] & 0xff00) == 0xff00 } pub const fn to_ipv4_mapped (& self) -> Option < Ipv4Addr > { match self . octets () { [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0xff , 0xff , a , b , c , d] => { Some (Ipv4Addr :: new (a , b , c , d)) } _ => None , } } pub const fn to_ipv4 (& self) -> Option < Ipv4Addr > { if let [0 , 0 , 0 , 0 , 0 , 0 | 0xffff , ab , cd] = self . segments () { let [a , b] = ab . to_be_bytes () ; let [c , d] = cd . to_be_bytes () ; Some (Ipv4Addr :: new (a , b , c , d)) } else { None } } pub const fn to_canonical (& self) -> IpAddr { if let Some (mapped) = self . to_ipv4_mapped () { return IpAddr :: V4 (mapped) ; } IpAddr :: V6 (* self) } pub const fn octets (& self) -> [u8 ; 16] { self . inner . s6_addr } } impl fmt :: Display for Ipv6Addr { fn fmt (& self , f : & mut fmt :: Formatter < '_ >) -> fmt :: Result { if f . precision () . is_none () && f . width () . is_none () { let segments = self . segments () ; if self . is_unspecified () { f . write_str ("::") } else if self . is_loopback () { f . write_str ("::1") } else if let Some (ipv4) = self . to_ipv4 () { match segments [5] { 0 => write ! (f , "::{}" , ipv4) , 0xffff => write ! (f , "::ffff:{}" , ipv4) , _ => unreachable ! () , } } else { # [derive (Copy , Clone , Default)] struct Span { start : usize , len : usize , } let zeroes = { let mut longest = Span :: default () ; let mut current = Span :: default () ; for (i , & segment) in segments . iter () . enumerate () { if segment == 0 { if current . len == 0 { current . start = i ; } current . len += 1 ; if current . len > longest . len { longest = current ; } } else { current = Span :: default () ; } } longest } ; # [doc = " Write a colon-separated part of the address"] # [inline] fn fmt_subslice (f : & mut fmt :: Formatter < '_ > , chunk : & [u16]) -> fmt :: Result { if let Some ((first , tail)) = chunk . split_first () { write ! (f , "{:x}" , first) ? ; for segment in tail { f . write_char (':') ? ; write ! (f , "{:x}" , segment) ? ; } } Ok (()) } if zeroes . len > 1 { fmt_subslice (f , & segments [.. zeroes . start]) ? ; f . write_str ("::") ? ; fmt_subslice (f , & segments [zeroes . start + zeroes . len ..]) } else { fmt_subslice (f , & segments) } } } else { const IPV6_BUF_LEN : usize = (4 * 8) + 7 ; let mut buf = [0u8 ; IPV6_BUF_LEN] ; let mut buf_slice = & mut buf [..] ; write ! (buf_slice , "{}" , self) . unwrap () ; let len = IPV6_BUF_LEN - buf_slice . len () ; let buf = unsafe { crate :: str :: from_utf8_unchecked (& buf [.. len]) } ; f . pad (buf) } } } impl fmt :: Debug for Ipv6Addr { fn fmt (& self , fmt : & mut fmt :: Formatter < '_ >) -> fmt :: Result { fmt :: Display :: fmt (self , fmt) } } impl Clone for Ipv6Addr { fn clone (& self) -> Ipv6Addr { * self } } impl PartialEq for Ipv6Addr { fn eq (& self , other : & Ipv6Addr) -> bool { self . inner . s6_addr == other . inner . s6_addr } } impl PartialEq < IpAddr > for Ipv6Addr { fn eq (& self , other : & IpAddr) -> bool { match other { IpAddr :: V4 (_) => false , IpAddr :: V6 (v6) => self == v6 , } } } impl PartialEq < Ipv6Addr > for IpAddr { fn eq (& self , other : & Ipv6Addr) -> bool { match self { IpAddr :: V4 (_) => false , IpAddr :: V6 (v6) => v6 == other , } } } impl Eq for Ipv6Addr { } impl hash :: Hash for Ipv6Addr { fn hash < H : hash :: Hasher > (& self , s : & mut H) { self . inner . s6_addr . hash (s) } } impl PartialOrd for Ipv6Addr { fn partial_cmp (& self , other : & Ipv6Addr) -> Option < Ordering > { Some (self . cmp (other)) } } impl PartialOrd < Ipv6Addr > for IpAddr { fn partial_cmp (& self , other : & Ipv6Addr) -> Option < Ordering > { match self { IpAddr :: V4 (_) => Some (Ordering :: Less) , IpAddr :: V6 (v6) => v6 . partial_cmp (other) , } } } impl PartialOrd < IpAddr > for Ipv6Addr { fn partial_cmp (& self , other : & IpAddr) -> Option < Ordering > { match other { IpAddr :: V4 (_) => Some (Ordering :: Greater) , IpAddr :: V6 (v6) => self . partial_cmp (v6) , } } } impl Ord for Ipv6Addr { fn cmp (& self , other : & Ipv6Addr) -> Ordering { self . segments () . cmp (& other . segments ()) } } impl AsInner < c :: in6_addr > for Ipv6Addr { fn as_inner (& self) -> & c :: in6_addr { & self . inner } } impl FromInner < c :: in6_addr > for Ipv6Addr { fn from_inner (addr : c :: in6_addr) -> Ipv6Addr { Ipv6Addr { inner : addr } } } impl From < Ipv6Addr > for u128 { fn from (ip : Ipv6Addr) -> u128 { let ip = ip . octets () ; u128 :: from_be_bytes (ip) } } impl From < u128 > for Ipv6Addr { fn from (ip : u128) -> Ipv6Addr { Ipv6Addr :: from (ip . to_be_bytes ()) } } impl From < [u8 ; 16] > for Ipv6Addr { fn from (octets : [u8 ; 16]) -> Ipv6Addr { let inner = c :: in6_addr { s6_addr : octets } ; Ipv6Addr :: from_inner (inner) } } impl From < [u16 ; 8] > for Ipv6Addr { fn from (segments : [u16 ; 8]) -> Ipv6Addr { let [a , b , c , d , e , f , g , h] = segments ; Ipv6Addr :: new (a , b , c , d , e , f , g , h) } } impl From < [u8 ; 16] > for IpAddr { fn from (octets : [u8 ; 16]) -> IpAddr { IpAddr :: V6 (Ipv6Addr :: from (octets)) } } impl From < [u16 ; 8] > for IpAddr { fn from (segments : [u16 ; 8]) -> IpAddr { IpAddr :: V6 (Ipv6Addr :: from (segments)) } } prettyplease-0.2.37/examples/output.prettyplease.rs000064400000000000000000000440061046102023000207060ustar 00000000000000use crate::cmp::Ordering; use crate::fmt::{self, Write as FmtWrite}; use crate::hash; use crate::io::Write as IoWrite; use crate::mem::transmute; use crate::sys::net::netc as c; use crate::sys_common::{AsInner, FromInner, IntoInner}; #[derive(Copy, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)] pub enum IpAddr { V4(Ipv4Addr), V6(Ipv6Addr), } #[derive(Copy)] pub struct Ipv4Addr { inner: c::in_addr, } #[derive(Copy)] pub struct Ipv6Addr { inner: c::in6_addr, } #[derive(Copy, PartialEq, Eq, Clone, Hash, Debug)] #[non_exhaustive] pub enum Ipv6MulticastScope { InterfaceLocal, LinkLocal, RealmLocal, AdminLocal, SiteLocal, OrganizationLocal, Global, } impl IpAddr { pub const fn is_unspecified(&self) -> bool { match self { IpAddr::V4(ip) => ip.is_unspecified(), IpAddr::V6(ip) => ip.is_unspecified(), } } pub const fn is_loopback(&self) -> bool { match self { IpAddr::V4(ip) => ip.is_loopback(), IpAddr::V6(ip) => ip.is_loopback(), } } pub const fn is_global(&self) -> bool { match self { IpAddr::V4(ip) => ip.is_global(), IpAddr::V6(ip) => ip.is_global(), } } pub const fn is_multicast(&self) -> bool { match self { IpAddr::V4(ip) => ip.is_multicast(), IpAddr::V6(ip) => ip.is_multicast(), } } pub const fn is_documentation(&self) -> bool { match self { IpAddr::V4(ip) => ip.is_documentation(), IpAddr::V6(ip) => ip.is_documentation(), } } pub const fn is_benchmarking(&self) -> bool { match self { IpAddr::V4(ip) => ip.is_benchmarking(), IpAddr::V6(ip) => ip.is_benchmarking(), } } pub const fn is_ipv4(&self) -> bool { matches!(self, IpAddr::V4(_)) } pub const fn is_ipv6(&self) -> bool { matches!(self, IpAddr::V6(_)) } pub const fn to_canonical(&self) -> IpAddr { match self { &v4 @ IpAddr::V4(_) => v4, IpAddr::V6(v6) => v6.to_canonical(), } } } impl Ipv4Addr { pub const fn new(a: u8, b: u8, c: u8, d: u8) -> Ipv4Addr { Ipv4Addr { inner: c::in_addr { s_addr: u32::from_ne_bytes([a, b, c, d]), }, } } pub const LOCALHOST: Self = Ipv4Addr::new(127, 0, 0, 1); #[doc(alias = "INADDR_ANY")] pub const UNSPECIFIED: Self = Ipv4Addr::new(0, 0, 0, 0); pub const BROADCAST: Self = Ipv4Addr::new(255, 255, 255, 255); pub const fn octets(&self) -> [u8; 4] { self.inner.s_addr.to_ne_bytes() } pub const fn is_unspecified(&self) -> bool { self.inner.s_addr == 0 } pub const fn is_loopback(&self) -> bool { self.octets()[0] == 127 } pub const fn is_private(&self) -> bool { match self.octets() { [10, ..] => true, [172, b, ..] if b >= 16 && b <= 31 => true, [192, 168, ..] => true, _ => false, } } pub const fn is_link_local(&self) -> bool { matches!(self.octets(), [169, 254, ..]) } pub const fn is_global(&self) -> bool { if u32::from_be_bytes(self.octets()) == 0xc0000009 || u32::from_be_bytes(self.octets()) == 0xc000000a { return true; } !self.is_private() && !self.is_loopback() && !self.is_link_local() && !self.is_broadcast() && !self.is_documentation() && !self.is_shared() && !(self.octets()[0] == 192 && self.octets()[1] == 0 && self.octets()[2] == 0) && !self.is_reserved() && !self.is_benchmarking() && self.octets()[0] != 0 } pub const fn is_shared(&self) -> bool { self.octets()[0] == 100 && (self.octets()[1] & 0b1100_0000 == 0b0100_0000) } pub const fn is_benchmarking(&self) -> bool { self.octets()[0] == 198 && (self.octets()[1] & 0xfe) == 18 } pub const fn is_reserved(&self) -> bool { self.octets()[0] & 240 == 240 && !self.is_broadcast() } pub const fn is_multicast(&self) -> bool { self.octets()[0] >= 224 && self.octets()[0] <= 239 } pub const fn is_broadcast(&self) -> bool { u32::from_be_bytes(self.octets()) == u32::from_be_bytes(Self::BROADCAST.octets()) } pub const fn is_documentation(&self) -> bool { matches!(self.octets(), [192, 0, 2, _] | [198, 51, 100, _] | [203, 0, 113, _]) } pub const fn to_ipv6_compatible(&self) -> Ipv6Addr { let [a, b, c, d] = self.octets(); Ipv6Addr { inner: c::in6_addr { s6_addr: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, a, b, c, d], }, } } pub const fn to_ipv6_mapped(&self) -> Ipv6Addr { let [a, b, c, d] = self.octets(); Ipv6Addr { inner: c::in6_addr { s6_addr: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, a, b, c, d], }, } } } impl fmt::Display for IpAddr { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { match self { IpAddr::V4(ip) => ip.fmt(fmt), IpAddr::V6(ip) => ip.fmt(fmt), } } } impl fmt::Debug for IpAddr { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(self, fmt) } } impl From for IpAddr { fn from(ipv4: Ipv4Addr) -> IpAddr { IpAddr::V4(ipv4) } } impl From for IpAddr { fn from(ipv6: Ipv6Addr) -> IpAddr { IpAddr::V6(ipv6) } } impl fmt::Display for Ipv4Addr { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { let octets = self.octets(); if fmt.precision().is_none() && fmt.width().is_none() { write!(fmt, "{}.{}.{}.{}", octets[0], octets[1], octets[2], octets[3]) } else { const IPV4_BUF_LEN: usize = 15; let mut buf = [0u8; IPV4_BUF_LEN]; let mut buf_slice = &mut buf[..]; write!(buf_slice, "{}.{}.{}.{}", octets[0], octets[1], octets[2], octets[3]) .unwrap(); let len = IPV4_BUF_LEN - buf_slice.len(); let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) }; fmt.pad(buf) } } } impl fmt::Debug for Ipv4Addr { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(self, fmt) } } impl Clone for Ipv4Addr { fn clone(&self) -> Ipv4Addr { *self } } impl PartialEq for Ipv4Addr { fn eq(&self, other: &Ipv4Addr) -> bool { self.inner.s_addr == other.inner.s_addr } } impl PartialEq for IpAddr { fn eq(&self, other: &Ipv4Addr) -> bool { match self { IpAddr::V4(v4) => v4 == other, IpAddr::V6(_) => false, } } } impl PartialEq for Ipv4Addr { fn eq(&self, other: &IpAddr) -> bool { match other { IpAddr::V4(v4) => self == v4, IpAddr::V6(_) => false, } } } impl Eq for Ipv4Addr {} impl hash::Hash for Ipv4Addr { fn hash(&self, s: &mut H) { { self.inner.s_addr }.hash(s) } } impl PartialOrd for Ipv4Addr { fn partial_cmp(&self, other: &Ipv4Addr) -> Option { Some(self.cmp(other)) } } impl PartialOrd for IpAddr { fn partial_cmp(&self, other: &Ipv4Addr) -> Option { match self { IpAddr::V4(v4) => v4.partial_cmp(other), IpAddr::V6(_) => Some(Ordering::Greater), } } } impl PartialOrd for Ipv4Addr { fn partial_cmp(&self, other: &IpAddr) -> Option { match other { IpAddr::V4(v4) => self.partial_cmp(v4), IpAddr::V6(_) => Some(Ordering::Less), } } } impl Ord for Ipv4Addr { fn cmp(&self, other: &Ipv4Addr) -> Ordering { u32::from_be(self.inner.s_addr).cmp(&u32::from_be(other.inner.s_addr)) } } impl IntoInner for Ipv4Addr { fn into_inner(self) -> c::in_addr { self.inner } } impl From for u32 { fn from(ip: Ipv4Addr) -> u32 { let ip = ip.octets(); u32::from_be_bytes(ip) } } impl From for Ipv4Addr { fn from(ip: u32) -> Ipv4Addr { Ipv4Addr::from(ip.to_be_bytes()) } } impl From<[u8; 4]> for Ipv4Addr { fn from(octets: [u8; 4]) -> Ipv4Addr { Ipv4Addr::new(octets[0], octets[1], octets[2], octets[3]) } } impl From<[u8; 4]> for IpAddr { fn from(octets: [u8; 4]) -> IpAddr { IpAddr::V4(Ipv4Addr::from(octets)) } } impl Ipv6Addr { pub const fn new( a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16, ) -> Ipv6Addr { let addr16 = [ a.to_be(), b.to_be(), c.to_be(), d.to_be(), e.to_be(), f.to_be(), g.to_be(), h.to_be(), ]; Ipv6Addr { inner: c::in6_addr { s6_addr: unsafe { transmute::<_, [u8; 16]>(addr16) }, }, } } pub const LOCALHOST: Self = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1); pub const UNSPECIFIED: Self = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0); pub const fn segments(&self) -> [u16; 8] { let [a, b, c, d, e, f, g, h] = unsafe { transmute::<_, [u16; 8]>(self.inner.s6_addr) }; [ u16::from_be(a), u16::from_be(b), u16::from_be(c), u16::from_be(d), u16::from_be(e), u16::from_be(f), u16::from_be(g), u16::from_be(h), ] } pub const fn is_unspecified(&self) -> bool { u128::from_be_bytes(self.octets()) == u128::from_be_bytes(Ipv6Addr::UNSPECIFIED.octets()) } pub const fn is_loopback(&self) -> bool { u128::from_be_bytes(self.octets()) == u128::from_be_bytes(Ipv6Addr::LOCALHOST.octets()) } pub const fn is_global(&self) -> bool { match self.multicast_scope() { Some(Ipv6MulticastScope::Global) => true, None => self.is_unicast_global(), _ => false, } } pub const fn is_unique_local(&self) -> bool { (self.segments()[0] & 0xfe00) == 0xfc00 } pub const fn is_unicast(&self) -> bool { !self.is_multicast() } pub const fn is_unicast_link_local(&self) -> bool { (self.segments()[0] & 0xffc0) == 0xfe80 } pub const fn is_documentation(&self) -> bool { (self.segments()[0] == 0x2001) && (self.segments()[1] == 0xdb8) } pub const fn is_benchmarking(&self) -> bool { (self.segments()[0] == 0x2001) && (self.segments()[1] == 0x2) && (self.segments()[2] == 0) } pub const fn is_unicast_global(&self) -> bool { self.is_unicast() && !self.is_loopback() && !self.is_unicast_link_local() && !self.is_unique_local() && !self.is_unspecified() && !self.is_documentation() } pub const fn multicast_scope(&self) -> Option { if self.is_multicast() { match self.segments()[0] & 0x000f { 1 => Some(Ipv6MulticastScope::InterfaceLocal), 2 => Some(Ipv6MulticastScope::LinkLocal), 3 => Some(Ipv6MulticastScope::RealmLocal), 4 => Some(Ipv6MulticastScope::AdminLocal), 5 => Some(Ipv6MulticastScope::SiteLocal), 8 => Some(Ipv6MulticastScope::OrganizationLocal), 14 => Some(Ipv6MulticastScope::Global), _ => None, } } else { None } } pub const fn is_multicast(&self) -> bool { (self.segments()[0] & 0xff00) == 0xff00 } pub const fn to_ipv4_mapped(&self) -> Option { match self.octets() { [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, a, b, c, d] => { Some(Ipv4Addr::new(a, b, c, d)) } _ => None, } } pub const fn to_ipv4(&self) -> Option { if let [0, 0, 0, 0, 0, 0 | 0xffff, ab, cd] = self.segments() { let [a, b] = ab.to_be_bytes(); let [c, d] = cd.to_be_bytes(); Some(Ipv4Addr::new(a, b, c, d)) } else { None } } pub const fn to_canonical(&self) -> IpAddr { if let Some(mapped) = self.to_ipv4_mapped() { return IpAddr::V4(mapped); } IpAddr::V6(*self) } pub const fn octets(&self) -> [u8; 16] { self.inner.s6_addr } } impl fmt::Display for Ipv6Addr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if f.precision().is_none() && f.width().is_none() { let segments = self.segments(); if self.is_unspecified() { f.write_str("::") } else if self.is_loopback() { f.write_str("::1") } else if let Some(ipv4) = self.to_ipv4() { match segments[5] { 0 => write!(f, "::{}", ipv4), 0xffff => write!(f, "::ffff:{}", ipv4), _ => unreachable!(), } } else { #[derive(Copy, Clone, Default)] struct Span { start: usize, len: usize, } let zeroes = { let mut longest = Span::default(); let mut current = Span::default(); for (i, &segment) in segments.iter().enumerate() { if segment == 0 { if current.len == 0 { current.start = i; } current.len += 1; if current.len > longest.len { longest = current; } } else { current = Span::default(); } } longest }; /// Write a colon-separated part of the address #[inline] fn fmt_subslice( f: &mut fmt::Formatter<'_>, chunk: &[u16], ) -> fmt::Result { if let Some((first, tail)) = chunk.split_first() { write!(f, "{:x}", first)?; for segment in tail { f.write_char(':')?; write!(f, "{:x}", segment)?; } } Ok(()) } if zeroes.len > 1 { fmt_subslice(f, &segments[..zeroes.start])?; f.write_str("::")?; fmt_subslice(f, &segments[zeroes.start + zeroes.len..]) } else { fmt_subslice(f, &segments) } } } else { const IPV6_BUF_LEN: usize = (4 * 8) + 7; let mut buf = [0u8; IPV6_BUF_LEN]; let mut buf_slice = &mut buf[..]; write!(buf_slice, "{}", self).unwrap(); let len = IPV6_BUF_LEN - buf_slice.len(); let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) }; f.pad(buf) } } } impl fmt::Debug for Ipv6Addr { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(self, fmt) } } impl Clone for Ipv6Addr { fn clone(&self) -> Ipv6Addr { *self } } impl PartialEq for Ipv6Addr { fn eq(&self, other: &Ipv6Addr) -> bool { self.inner.s6_addr == other.inner.s6_addr } } impl PartialEq for Ipv6Addr { fn eq(&self, other: &IpAddr) -> bool { match other { IpAddr::V4(_) => false, IpAddr::V6(v6) => self == v6, } } } impl PartialEq for IpAddr { fn eq(&self, other: &Ipv6Addr) -> bool { match self { IpAddr::V4(_) => false, IpAddr::V6(v6) => v6 == other, } } } impl Eq for Ipv6Addr {} impl hash::Hash for Ipv6Addr { fn hash(&self, s: &mut H) { self.inner.s6_addr.hash(s) } } impl PartialOrd for Ipv6Addr { fn partial_cmp(&self, other: &Ipv6Addr) -> Option { Some(self.cmp(other)) } } impl PartialOrd for IpAddr { fn partial_cmp(&self, other: &Ipv6Addr) -> Option { match self { IpAddr::V4(_) => Some(Ordering::Less), IpAddr::V6(v6) => v6.partial_cmp(other), } } } impl PartialOrd for Ipv6Addr { fn partial_cmp(&self, other: &IpAddr) -> Option { match other { IpAddr::V4(_) => Some(Ordering::Greater), IpAddr::V6(v6) => self.partial_cmp(v6), } } } impl Ord for Ipv6Addr { fn cmp(&self, other: &Ipv6Addr) -> Ordering { self.segments().cmp(&other.segments()) } } impl AsInner for Ipv6Addr { fn as_inner(&self) -> &c::in6_addr { &self.inner } } impl FromInner for Ipv6Addr { fn from_inner(addr: c::in6_addr) -> Ipv6Addr { Ipv6Addr { inner: addr } } } impl From for u128 { fn from(ip: Ipv6Addr) -> u128 { let ip = ip.octets(); u128::from_be_bytes(ip) } } impl From for Ipv6Addr { fn from(ip: u128) -> Ipv6Addr { Ipv6Addr::from(ip.to_be_bytes()) } } impl From<[u8; 16]> for Ipv6Addr { fn from(octets: [u8; 16]) -> Ipv6Addr { let inner = c::in6_addr { s6_addr: octets }; Ipv6Addr::from_inner(inner) } } impl From<[u16; 8]> for Ipv6Addr { fn from(segments: [u16; 8]) -> Ipv6Addr { let [a, b, c, d, e, f, g, h] = segments; Ipv6Addr::new(a, b, c, d, e, f, g, h) } } impl From<[u8; 16]> for IpAddr { fn from(octets: [u8; 16]) -> IpAddr { IpAddr::V6(Ipv6Addr::from(octets)) } } impl From<[u16; 8]> for IpAddr { fn from(segments: [u16; 8]) -> IpAddr { IpAddr::V6(Ipv6Addr::from(segments)) } } prettyplease-0.2.37/examples/output.rustc.rs000064400000000000000000000426071046102023000173320ustar 00000000000000use crate::cmp::Ordering;use crate::fmt::{self, Write as FmtWrite}; use crate::hash; use crate::io::Write as IoWrite; use crate::mem::transmute; use crate::sys::net::netc as c; use crate::sys_common::{AsInner, FromInner, IntoInner}; #[derive(Copy, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)] pub enum IpAddr { V4(Ipv4Addr), V6(Ipv6Addr), } #[derive(Copy)] pub struct Ipv4Addr { inner: c::in_addr, } #[derive(Copy)] pub struct Ipv6Addr { inner: c::in6_addr, } #[derive(Copy, PartialEq, Eq, Clone, Hash, Debug)] #[non_exhaustive] pub enum Ipv6MulticastScope { InterfaceLocal, LinkLocal, RealmLocal, AdminLocal, SiteLocal, OrganizationLocal, Global, } impl IpAddr { pub const fn is_unspecified(&self) -> bool { match self { IpAddr::V4(ip) => ip.is_unspecified(), IpAddr::V6(ip) => ip.is_unspecified(), } } pub const fn is_loopback(&self) -> bool { match self { IpAddr::V4(ip) => ip.is_loopback(), IpAddr::V6(ip) => ip.is_loopback(), } } pub const fn is_global(&self) -> bool { match self { IpAddr::V4(ip) => ip.is_global(), IpAddr::V6(ip) => ip.is_global(), } } pub const fn is_multicast(&self) -> bool { match self { IpAddr::V4(ip) => ip.is_multicast(), IpAddr::V6(ip) => ip.is_multicast(), } } pub const fn is_documentation(&self) -> bool { match self { IpAddr::V4(ip) => ip.is_documentation(), IpAddr::V6(ip) => ip.is_documentation(), } } pub const fn is_benchmarking(&self) -> bool { match self { IpAddr::V4(ip) => ip.is_benchmarking(), IpAddr::V6(ip) => ip.is_benchmarking(), } } pub const fn is_ipv4(&self) -> bool { matches!(self, IpAddr :: V4(_)) } pub const fn is_ipv6(&self) -> bool { matches!(self, IpAddr :: V6(_)) } pub const fn to_canonical(&self) -> IpAddr { match self { &v4 @ IpAddr::V4(_) => v4, IpAddr::V6(v6) => v6.to_canonical(), } } } impl Ipv4Addr { pub const fn new(a: u8, b: u8, c: u8, d: u8) -> Ipv4Addr { Ipv4Addr { inner: c::in_addr { s_addr: u32::from_ne_bytes([a, b, c, d]) }, } } pub const LOCALHOST: Self = Ipv4Addr::new(127, 0, 0, 1); #[doc(alias = "INADDR_ANY")] pub const UNSPECIFIED: Self = Ipv4Addr::new(0, 0, 0, 0); pub const BROADCAST: Self = Ipv4Addr::new(255, 255, 255, 255); pub const fn octets(&self) -> [u8; 4] { self.inner.s_addr.to_ne_bytes() } pub const fn is_unspecified(&self) -> bool { self.inner.s_addr == 0 } pub const fn is_loopback(&self) -> bool { self.octets()[0] == 127 } pub const fn is_private(&self) -> bool { match self.octets() { [10, ..] => true, [172, b, ..] if b >= 16 && b <= 31 => true, [192, 168, ..] => true, _ => false, } } pub const fn is_link_local(&self) -> bool { matches!(self.octets(), [169, 254, ..]) } pub const fn is_global(&self) -> bool { if u32::from_be_bytes(self.octets()) == 0xc0000009 || u32::from_be_bytes(self.octets()) == 0xc000000a { return true; } !self.is_private() && !self.is_loopback() && !self.is_link_local() && !self.is_broadcast() && !self.is_documentation() && !self.is_shared() && !(self.octets()[0] == 192 && self.octets()[1] == 0 && self.octets()[2] == 0) && !self.is_reserved() && !self.is_benchmarking() && self.octets()[0] != 0 } pub const fn is_shared(&self) -> bool { self.octets()[0] == 100 && (self.octets()[1] & 0b1100_0000 == 0b0100_0000) } pub const fn is_benchmarking(&self) -> bool { self.octets()[0] == 198 && (self.octets()[1] & 0xfe) == 18 } pub const fn is_reserved(&self) -> bool { self.octets()[0] & 240 == 240 && !self.is_broadcast() } pub const fn is_multicast(&self) -> bool { self.octets()[0] >= 224 && self.octets()[0] <= 239 } pub const fn is_broadcast(&self) -> bool { u32::from_be_bytes(self.octets()) == u32::from_be_bytes(Self::BROADCAST.octets()) } pub const fn is_documentation(&self) -> bool { matches!(self.octets(), [192, 0, 2, _] | [198, 51, 100, _] | [203, 0, 113, _]) } pub const fn to_ipv6_compatible(&self) -> Ipv6Addr { let [a, b, c, d] = self.octets(); Ipv6Addr { inner: c::in6_addr { s6_addr: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, a, b, c, d], }, } } pub const fn to_ipv6_mapped(&self) -> Ipv6Addr { let [a, b, c, d] = self.octets(); Ipv6Addr { inner: c::in6_addr { s6_addr: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, a, b, c, d], }, } } } impl fmt::Display for IpAddr { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { match self { IpAddr::V4(ip) => ip.fmt(fmt), IpAddr::V6(ip) => ip.fmt(fmt), } } } impl fmt::Debug for IpAddr { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(self, fmt) } } impl From for IpAddr { fn from(ipv4: Ipv4Addr) -> IpAddr { IpAddr::V4(ipv4) } } impl From for IpAddr { fn from(ipv6: Ipv6Addr) -> IpAddr { IpAddr::V6(ipv6) } } impl fmt::Display for Ipv4Addr { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { let octets = self.octets(); if fmt.precision().is_none() && fmt.width().is_none() { write!(fmt, "{}.{}.{}.{}", octets [0], octets [1], octets [2], octets [3]) } else { const IPV4_BUF_LEN: usize = 15; let mut buf = [0u8; IPV4_BUF_LEN]; let mut buf_slice = &mut buf[..]; write!(buf_slice, "{}.{}.{}.{}", octets [0], octets [1], octets [2], octets [3]).unwrap(); let len = IPV4_BUF_LEN - buf_slice.len(); let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) }; fmt.pad(buf) } } } impl fmt::Debug for Ipv4Addr { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(self, fmt) } } impl Clone for Ipv4Addr { fn clone(&self) -> Ipv4Addr { *self } } impl PartialEq for Ipv4Addr { fn eq(&self, other: &Ipv4Addr) -> bool { self.inner.s_addr == other.inner.s_addr } } impl PartialEq for IpAddr { fn eq(&self, other: &Ipv4Addr) -> bool { match self { IpAddr::V4(v4) => v4 == other, IpAddr::V6(_) => false, } } } impl PartialEq for Ipv4Addr { fn eq(&self, other: &IpAddr) -> bool { match other { IpAddr::V4(v4) => self == v4, IpAddr::V6(_) => false, } } } impl Eq for Ipv4Addr {} impl hash::Hash for Ipv4Addr { fn hash(&self, s: &mut H) { { self.inner.s_addr }.hash(s) } } impl PartialOrd for Ipv4Addr { fn partial_cmp(&self, other: &Ipv4Addr) -> Option { Some(self.cmp(other)) } } impl PartialOrd for IpAddr { fn partial_cmp(&self, other: &Ipv4Addr) -> Option { match self { IpAddr::V4(v4) => v4.partial_cmp(other), IpAddr::V6(_) => Some(Ordering::Greater), } } } impl PartialOrd for Ipv4Addr { fn partial_cmp(&self, other: &IpAddr) -> Option { match other { IpAddr::V4(v4) => self.partial_cmp(v4), IpAddr::V6(_) => Some(Ordering::Less), } } } impl Ord for Ipv4Addr { fn cmp(&self, other: &Ipv4Addr) -> Ordering { u32::from_be(self.inner.s_addr).cmp(&u32::from_be(other.inner.s_addr)) } } impl IntoInner for Ipv4Addr { fn into_inner(self) -> c::in_addr { self.inner } } impl From for u32 { fn from(ip: Ipv4Addr) -> u32 { let ip = ip.octets(); u32::from_be_bytes(ip) } } impl From for Ipv4Addr { fn from(ip: u32) -> Ipv4Addr { Ipv4Addr::from(ip.to_be_bytes()) } } impl From<[u8; 4]> for Ipv4Addr { fn from(octets: [u8; 4]) -> Ipv4Addr { Ipv4Addr::new(octets[0], octets[1], octets[2], octets[3]) } } impl From<[u8; 4]> for IpAddr { fn from(octets: [u8; 4]) -> IpAddr { IpAddr::V4(Ipv4Addr::from(octets)) } } impl Ipv6Addr { pub const fn new(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> Ipv6Addr { let addr16 = [a.to_be(), b.to_be(), c.to_be(), d.to_be(), e.to_be(), f.to_be(), g.to_be(), h.to_be()]; Ipv6Addr { inner: c::in6_addr { s6_addr: unsafe { transmute::<_, [u8; 16]>(addr16) }, }, } } pub const LOCALHOST: Self = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1); pub const UNSPECIFIED: Self = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0); pub const fn segments(&self) -> [u16; 8] { let [a, b, c, d, e, f, g, h] = unsafe { transmute::<_, [u16; 8]>(self.inner.s6_addr) }; [u16::from_be(a), u16::from_be(b), u16::from_be(c), u16::from_be(d), u16::from_be(e), u16::from_be(f), u16::from_be(g), u16::from_be(h)] } pub const fn is_unspecified(&self) -> bool { u128::from_be_bytes(self.octets()) == u128::from_be_bytes(Ipv6Addr::UNSPECIFIED.octets()) } pub const fn is_loopback(&self) -> bool { u128::from_be_bytes(self.octets()) == u128::from_be_bytes(Ipv6Addr::LOCALHOST.octets()) } pub const fn is_global(&self) -> bool { match self.multicast_scope() { Some(Ipv6MulticastScope::Global) => true, None => self.is_unicast_global(), _ => false, } } pub const fn is_unique_local(&self) -> bool { (self.segments()[0] & 0xfe00) == 0xfc00 } pub const fn is_unicast(&self) -> bool { !self.is_multicast() } pub const fn is_unicast_link_local(&self) -> bool { (self.segments()[0] & 0xffc0) == 0xfe80 } pub const fn is_documentation(&self) -> bool { (self.segments()[0] == 0x2001) && (self.segments()[1] == 0xdb8) } pub const fn is_benchmarking(&self) -> bool { (self.segments()[0] == 0x2001) && (self.segments()[1] == 0x2) && (self.segments()[2] == 0) } pub const fn is_unicast_global(&self) -> bool { self.is_unicast() && !self.is_loopback() && !self.is_unicast_link_local() && !self.is_unique_local() && !self.is_unspecified() && !self.is_documentation() } pub const fn multicast_scope(&self) -> Option { if self.is_multicast() { match self.segments()[0] & 0x000f { 1 => Some(Ipv6MulticastScope::InterfaceLocal), 2 => Some(Ipv6MulticastScope::LinkLocal), 3 => Some(Ipv6MulticastScope::RealmLocal), 4 => Some(Ipv6MulticastScope::AdminLocal), 5 => Some(Ipv6MulticastScope::SiteLocal), 8 => Some(Ipv6MulticastScope::OrganizationLocal), 14 => Some(Ipv6MulticastScope::Global), _ => None, } } else { None } } pub const fn is_multicast(&self) -> bool { (self.segments()[0] & 0xff00) == 0xff00 } pub const fn to_ipv4_mapped(&self) -> Option { match self.octets() { [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, a, b, c, d] => { Some(Ipv4Addr::new(a, b, c, d)) } _ => None, } } pub const fn to_ipv4(&self) -> Option { if let [0, 0, 0, 0, 0, 0 | 0xffff, ab, cd] = self.segments() { let [a, b] = ab.to_be_bytes(); let [c, d] = cd.to_be_bytes(); Some(Ipv4Addr::new(a, b, c, d)) } else { None } } pub const fn to_canonical(&self) -> IpAddr { if let Some(mapped) = self.to_ipv4_mapped() { return IpAddr::V4(mapped); } IpAddr::V6(*self) } pub const fn octets(&self) -> [u8; 16] { self.inner.s6_addr } } impl fmt::Display for Ipv6Addr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if f.precision().is_none() && f.width().is_none() { let segments = self.segments(); if self.is_unspecified() { f.write_str("::") } else if self.is_loopback() { f.write_str("::1") } else if let Some(ipv4) = self.to_ipv4() { match segments[5] { 0 => write!(f, "::{}", ipv4), 0xffff => write!(f, "::ffff:{}", ipv4), _ => unreachable!(), } } else { #[derive(Copy, Clone, Default)] struct Span { start: usize, len: usize, } let zeroes = { let mut longest = Span::default(); let mut current = Span::default(); for (i, &segment) in segments.iter().enumerate() { if segment == 0 { if current.len == 0 { current.start = i; } current.len += 1; if current.len > longest.len { longest = current; } } else { current = Span::default(); } } longest }; #[doc = " Write a colon-separated part of the address"] #[inline] fn fmt_subslice(f: &mut fmt::Formatter<'_>, chunk: &[u16]) -> fmt::Result { if let Some((first, tail)) = chunk.split_first() { write!(f, "{:x}", first)?; for segment in tail { f.write_char(':')?; write!(f, "{:x}", segment)?; } } Ok(()) } if zeroes.len > 1 { fmt_subslice(f, &segments[..zeroes.start])?; f.write_str("::")?; fmt_subslice(f, &segments[zeroes.start + zeroes.len..]) } else { fmt_subslice(f, &segments) } } } else { const IPV6_BUF_LEN: usize = (4 * 8) + 7; let mut buf = [0u8; IPV6_BUF_LEN]; let mut buf_slice = &mut buf[..]; write!(buf_slice, "{}", self).unwrap(); let len = IPV6_BUF_LEN - buf_slice.len(); let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) }; f.pad(buf) } } } impl fmt::Debug for Ipv6Addr { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(self, fmt) } } impl Clone for Ipv6Addr { fn clone(&self) -> Ipv6Addr { *self } } impl PartialEq for Ipv6Addr { fn eq(&self, other: &Ipv6Addr) -> bool { self.inner.s6_addr == other.inner.s6_addr } } impl PartialEq for Ipv6Addr { fn eq(&self, other: &IpAddr) -> bool { match other { IpAddr::V4(_) => false, IpAddr::V6(v6) => self == v6, } } } impl PartialEq for IpAddr { fn eq(&self, other: &Ipv6Addr) -> bool { match self { IpAddr::V4(_) => false, IpAddr::V6(v6) => v6 == other, } } } impl Eq for Ipv6Addr {} impl hash::Hash for Ipv6Addr { fn hash(&self, s: &mut H) { self.inner.s6_addr.hash(s) } } impl PartialOrd for Ipv6Addr { fn partial_cmp(&self, other: &Ipv6Addr) -> Option { Some(self.cmp(other)) } } impl PartialOrd for IpAddr { fn partial_cmp(&self, other: &Ipv6Addr) -> Option { match self { IpAddr::V4(_) => Some(Ordering::Less), IpAddr::V6(v6) => v6.partial_cmp(other), } } } impl PartialOrd for Ipv6Addr { fn partial_cmp(&self, other: &IpAddr) -> Option { match other { IpAddr::V4(_) => Some(Ordering::Greater), IpAddr::V6(v6) => self.partial_cmp(v6), } } } impl Ord for Ipv6Addr { fn cmp(&self, other: &Ipv6Addr) -> Ordering { self.segments().cmp(&other.segments()) } } impl AsInner for Ipv6Addr { fn as_inner(&self) -> &c::in6_addr { &self.inner } } impl FromInner for Ipv6Addr { fn from_inner(addr: c::in6_addr) -> Ipv6Addr { Ipv6Addr { inner: addr } } } impl From for u128 { fn from(ip: Ipv6Addr) -> u128 { let ip = ip.octets(); u128::from_be_bytes(ip) } } impl From for Ipv6Addr { fn from(ip: u128) -> Ipv6Addr { Ipv6Addr::from(ip.to_be_bytes()) } } impl From<[u8; 16]> for Ipv6Addr { fn from(octets: [u8; 16]) -> Ipv6Addr { let inner = c::in6_addr { s6_addr: octets }; Ipv6Addr::from_inner(inner) } } impl From<[u16; 8]> for Ipv6Addr { fn from(segments: [u16; 8]) -> Ipv6Addr { let [a, b, c, d, e, f, g, h] = segments; Ipv6Addr::new(a, b, c, d, e, f, g, h) } } impl From<[u8; 16]> for IpAddr { fn from(octets: [u8; 16]) -> IpAddr { IpAddr::V6(Ipv6Addr::from(octets)) } } impl From<[u16; 8]> for IpAddr { fn from(segments: [u16; 8]) -> IpAddr { IpAddr::V6(Ipv6Addr::from(segments)) } } prettyplease-0.2.37/examples/output.rustfmt.rs000064400000000000000000000424421046102023000176730ustar 00000000000000use crate::cmp::Ordering; use crate::fmt::{self, Write as FmtWrite}; use crate::hash; use crate::io::Write as IoWrite; use crate::mem::transmute; use crate::sys::net::netc as c; use crate::sys_common::{AsInner, FromInner, IntoInner}; #[derive(Copy, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)] pub enum IpAddr { V4(Ipv4Addr), V6(Ipv6Addr), } #[derive(Copy)] pub struct Ipv4Addr { inner: c::in_addr, } #[derive(Copy)] pub struct Ipv6Addr { inner: c::in6_addr, } #[derive(Copy, PartialEq, Eq, Clone, Hash, Debug)] #[non_exhaustive] pub enum Ipv6MulticastScope { InterfaceLocal, LinkLocal, RealmLocal, AdminLocal, SiteLocal, OrganizationLocal, Global, } impl IpAddr { pub const fn is_unspecified(&self) -> bool { match self { IpAddr::V4(ip) => ip.is_unspecified(), IpAddr::V6(ip) => ip.is_unspecified(), } } pub const fn is_loopback(&self) -> bool { match self { IpAddr::V4(ip) => ip.is_loopback(), IpAddr::V6(ip) => ip.is_loopback(), } } pub const fn is_global(&self) -> bool { match self { IpAddr::V4(ip) => ip.is_global(), IpAddr::V6(ip) => ip.is_global(), } } pub const fn is_multicast(&self) -> bool { match self { IpAddr::V4(ip) => ip.is_multicast(), IpAddr::V6(ip) => ip.is_multicast(), } } pub const fn is_documentation(&self) -> bool { match self { IpAddr::V4(ip) => ip.is_documentation(), IpAddr::V6(ip) => ip.is_documentation(), } } pub const fn is_benchmarking(&self) -> bool { match self { IpAddr::V4(ip) => ip.is_benchmarking(), IpAddr::V6(ip) => ip.is_benchmarking(), } } pub const fn is_ipv4(&self) -> bool { matches!(self, IpAddr::V4(_)) } pub const fn is_ipv6(&self) -> bool { matches!(self, IpAddr::V6(_)) } pub const fn to_canonical(&self) -> IpAddr { match self { &v4 @ IpAddr::V4(_) => v4, IpAddr::V6(v6) => v6.to_canonical(), } } } impl Ipv4Addr { pub const fn new(a: u8, b: u8, c: u8, d: u8) -> Ipv4Addr { Ipv4Addr { inner: c::in_addr { s_addr: u32::from_ne_bytes([a, b, c, d]), }, } } pub const LOCALHOST: Self = Ipv4Addr::new(127, 0, 0, 1); #[doc(alias = "INADDR_ANY")] pub const UNSPECIFIED: Self = Ipv4Addr::new(0, 0, 0, 0); pub const BROADCAST: Self = Ipv4Addr::new(255, 255, 255, 255); pub const fn octets(&self) -> [u8; 4] { self.inner.s_addr.to_ne_bytes() } pub const fn is_unspecified(&self) -> bool { self.inner.s_addr == 0 } pub const fn is_loopback(&self) -> bool { self.octets()[0] == 127 } pub const fn is_private(&self) -> bool { match self.octets() { [10, ..] => true, [172, b, ..] if b >= 16 && b <= 31 => true, [192, 168, ..] => true, _ => false, } } pub const fn is_link_local(&self) -> bool { matches!(self.octets(), [169, 254, ..]) } pub const fn is_global(&self) -> bool { if u32::from_be_bytes(self.octets()) == 0xc0000009 || u32::from_be_bytes(self.octets()) == 0xc000000a { return true; } !self.is_private() && !self.is_loopback() && !self.is_link_local() && !self.is_broadcast() && !self.is_documentation() && !self.is_shared() && !(self.octets()[0] == 192 && self.octets()[1] == 0 && self.octets()[2] == 0) && !self.is_reserved() && !self.is_benchmarking() && self.octets()[0] != 0 } pub const fn is_shared(&self) -> bool { self.octets()[0] == 100 && (self.octets()[1] & 0b1100_0000 == 0b0100_0000) } pub const fn is_benchmarking(&self) -> bool { self.octets()[0] == 198 && (self.octets()[1] & 0xfe) == 18 } pub const fn is_reserved(&self) -> bool { self.octets()[0] & 240 == 240 && !self.is_broadcast() } pub const fn is_multicast(&self) -> bool { self.octets()[0] >= 224 && self.octets()[0] <= 239 } pub const fn is_broadcast(&self) -> bool { u32::from_be_bytes(self.octets()) == u32::from_be_bytes(Self::BROADCAST.octets()) } pub const fn is_documentation(&self) -> bool { matches!( self.octets(), [192, 0, 2, _] | [198, 51, 100, _] | [203, 0, 113, _] ) } pub const fn to_ipv6_compatible(&self) -> Ipv6Addr { let [a, b, c, d] = self.octets(); Ipv6Addr { inner: c::in6_addr { s6_addr: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, a, b, c, d], }, } } pub const fn to_ipv6_mapped(&self) -> Ipv6Addr { let [a, b, c, d] = self.octets(); Ipv6Addr { inner: c::in6_addr { s6_addr: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, a, b, c, d], }, } } } impl fmt::Display for IpAddr { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { match self { IpAddr::V4(ip) => ip.fmt(fmt), IpAddr::V6(ip) => ip.fmt(fmt), } } } impl fmt::Debug for IpAddr { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(self, fmt) } } impl From for IpAddr { fn from(ipv4: Ipv4Addr) -> IpAddr { IpAddr::V4(ipv4) } } impl From for IpAddr { fn from(ipv6: Ipv6Addr) -> IpAddr { IpAddr::V6(ipv6) } } impl fmt::Display for Ipv4Addr { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { let octets = self.octets(); if fmt.precision().is_none() && fmt.width().is_none() { write!( fmt, "{}.{}.{}.{}", octets[0], octets[1], octets[2], octets[3] ) } else { const IPV4_BUF_LEN: usize = 15; let mut buf = [0u8; IPV4_BUF_LEN]; let mut buf_slice = &mut buf[..]; write!( buf_slice, "{}.{}.{}.{}", octets[0], octets[1], octets[2], octets[3] ) .unwrap(); let len = IPV4_BUF_LEN - buf_slice.len(); let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) }; fmt.pad(buf) } } } impl fmt::Debug for Ipv4Addr { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(self, fmt) } } impl Clone for Ipv4Addr { fn clone(&self) -> Ipv4Addr { *self } } impl PartialEq for Ipv4Addr { fn eq(&self, other: &Ipv4Addr) -> bool { self.inner.s_addr == other.inner.s_addr } } impl PartialEq for IpAddr { fn eq(&self, other: &Ipv4Addr) -> bool { match self { IpAddr::V4(v4) => v4 == other, IpAddr::V6(_) => false, } } } impl PartialEq for Ipv4Addr { fn eq(&self, other: &IpAddr) -> bool { match other { IpAddr::V4(v4) => self == v4, IpAddr::V6(_) => false, } } } impl Eq for Ipv4Addr {} impl hash::Hash for Ipv4Addr { fn hash(&self, s: &mut H) { { self.inner.s_addr }.hash(s) } } impl PartialOrd for Ipv4Addr { fn partial_cmp(&self, other: &Ipv4Addr) -> Option { Some(self.cmp(other)) } } impl PartialOrd for IpAddr { fn partial_cmp(&self, other: &Ipv4Addr) -> Option { match self { IpAddr::V4(v4) => v4.partial_cmp(other), IpAddr::V6(_) => Some(Ordering::Greater), } } } impl PartialOrd for Ipv4Addr { fn partial_cmp(&self, other: &IpAddr) -> Option { match other { IpAddr::V4(v4) => self.partial_cmp(v4), IpAddr::V6(_) => Some(Ordering::Less), } } } impl Ord for Ipv4Addr { fn cmp(&self, other: &Ipv4Addr) -> Ordering { u32::from_be(self.inner.s_addr).cmp(&u32::from_be(other.inner.s_addr)) } } impl IntoInner for Ipv4Addr { fn into_inner(self) -> c::in_addr { self.inner } } impl From for u32 { fn from(ip: Ipv4Addr) -> u32 { let ip = ip.octets(); u32::from_be_bytes(ip) } } impl From for Ipv4Addr { fn from(ip: u32) -> Ipv4Addr { Ipv4Addr::from(ip.to_be_bytes()) } } impl From<[u8; 4]> for Ipv4Addr { fn from(octets: [u8; 4]) -> Ipv4Addr { Ipv4Addr::new(octets[0], octets[1], octets[2], octets[3]) } } impl From<[u8; 4]> for IpAddr { fn from(octets: [u8; 4]) -> IpAddr { IpAddr::V4(Ipv4Addr::from(octets)) } } impl Ipv6Addr { pub const fn new(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> Ipv6Addr { let addr16 = [ a.to_be(), b.to_be(), c.to_be(), d.to_be(), e.to_be(), f.to_be(), g.to_be(), h.to_be(), ]; Ipv6Addr { inner: c::in6_addr { s6_addr: unsafe { transmute::<_, [u8; 16]>(addr16) }, }, } } pub const LOCALHOST: Self = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1); pub const UNSPECIFIED: Self = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0); pub const fn segments(&self) -> [u16; 8] { let [a, b, c, d, e, f, g, h] = unsafe { transmute::<_, [u16; 8]>(self.inner.s6_addr) }; [ u16::from_be(a), u16::from_be(b), u16::from_be(c), u16::from_be(d), u16::from_be(e), u16::from_be(f), u16::from_be(g), u16::from_be(h), ] } pub const fn is_unspecified(&self) -> bool { u128::from_be_bytes(self.octets()) == u128::from_be_bytes(Ipv6Addr::UNSPECIFIED.octets()) } pub const fn is_loopback(&self) -> bool { u128::from_be_bytes(self.octets()) == u128::from_be_bytes(Ipv6Addr::LOCALHOST.octets()) } pub const fn is_global(&self) -> bool { match self.multicast_scope() { Some(Ipv6MulticastScope::Global) => true, None => self.is_unicast_global(), _ => false, } } pub const fn is_unique_local(&self) -> bool { (self.segments()[0] & 0xfe00) == 0xfc00 } pub const fn is_unicast(&self) -> bool { !self.is_multicast() } pub const fn is_unicast_link_local(&self) -> bool { (self.segments()[0] & 0xffc0) == 0xfe80 } pub const fn is_documentation(&self) -> bool { (self.segments()[0] == 0x2001) && (self.segments()[1] == 0xdb8) } pub const fn is_benchmarking(&self) -> bool { (self.segments()[0] == 0x2001) && (self.segments()[1] == 0x2) && (self.segments()[2] == 0) } pub const fn is_unicast_global(&self) -> bool { self.is_unicast() && !self.is_loopback() && !self.is_unicast_link_local() && !self.is_unique_local() && !self.is_unspecified() && !self.is_documentation() } pub const fn multicast_scope(&self) -> Option { if self.is_multicast() { match self.segments()[0] & 0x000f { 1 => Some(Ipv6MulticastScope::InterfaceLocal), 2 => Some(Ipv6MulticastScope::LinkLocal), 3 => Some(Ipv6MulticastScope::RealmLocal), 4 => Some(Ipv6MulticastScope::AdminLocal), 5 => Some(Ipv6MulticastScope::SiteLocal), 8 => Some(Ipv6MulticastScope::OrganizationLocal), 14 => Some(Ipv6MulticastScope::Global), _ => None, } } else { None } } pub const fn is_multicast(&self) -> bool { (self.segments()[0] & 0xff00) == 0xff00 } pub const fn to_ipv4_mapped(&self) -> Option { match self.octets() { [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, a, b, c, d] => { Some(Ipv4Addr::new(a, b, c, d)) } _ => None, } } pub const fn to_ipv4(&self) -> Option { if let [0, 0, 0, 0, 0, 0 | 0xffff, ab, cd] = self.segments() { let [a, b] = ab.to_be_bytes(); let [c, d] = cd.to_be_bytes(); Some(Ipv4Addr::new(a, b, c, d)) } else { None } } pub const fn to_canonical(&self) -> IpAddr { if let Some(mapped) = self.to_ipv4_mapped() { return IpAddr::V4(mapped); } IpAddr::V6(*self) } pub const fn octets(&self) -> [u8; 16] { self.inner.s6_addr } } impl fmt::Display for Ipv6Addr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if f.precision().is_none() && f.width().is_none() { let segments = self.segments(); if self.is_unspecified() { f.write_str("::") } else if self.is_loopback() { f.write_str("::1") } else if let Some(ipv4) = self.to_ipv4() { match segments[5] { 0 => write!(f, "::{}", ipv4), 0xffff => write!(f, "::ffff:{}", ipv4), _ => unreachable!(), } } else { # [derive (Copy , Clone , Default)] struct Span { start : usize , len : usize , } let zeroes = { let mut longest = Span :: default () ; let mut current = Span :: default () ; for (i , & segment) in segments . iter () . enumerate () { if segment == 0 { if current . len == 0 { current . start = i ; } current . len += 1 ; if current . len > longest . len { longest = current ; } } else { current = Span :: default () ; } } longest } ; # [doc = " Write a colon-separated part of the address"] # [inline] fn fmt_subslice (f : & mut fmt :: Formatter < '_ > , chunk : & [u16]) -> fmt :: Result { if let Some ((first , tail)) = chunk . split_first () { write ! (f , "{:x}" , first) ? ; for segment in tail { f . write_char (':') ? ; write ! (f , "{:x}" , segment) ? ; } } Ok (()) } if zeroes . len > 1 { fmt_subslice (f , & segments [.. zeroes . start]) ? ; f . write_str ("::") ? ; fmt_subslice (f , & segments [zeroes . start + zeroes . len ..]) } else { fmt_subslice (f , & segments) } } } else { const IPV6_BUF_LEN: usize = (4 * 8) + 7; let mut buf = [0u8; IPV6_BUF_LEN]; let mut buf_slice = &mut buf[..]; write!(buf_slice, "{}", self).unwrap(); let len = IPV6_BUF_LEN - buf_slice.len(); let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) }; f.pad(buf) } } } impl fmt::Debug for Ipv6Addr { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(self, fmt) } } impl Clone for Ipv6Addr { fn clone(&self) -> Ipv6Addr { *self } } impl PartialEq for Ipv6Addr { fn eq(&self, other: &Ipv6Addr) -> bool { self.inner.s6_addr == other.inner.s6_addr } } impl PartialEq for Ipv6Addr { fn eq(&self, other: &IpAddr) -> bool { match other { IpAddr::V4(_) => false, IpAddr::V6(v6) => self == v6, } } } impl PartialEq for IpAddr { fn eq(&self, other: &Ipv6Addr) -> bool { match self { IpAddr::V4(_) => false, IpAddr::V6(v6) => v6 == other, } } } impl Eq for Ipv6Addr {} impl hash::Hash for Ipv6Addr { fn hash(&self, s: &mut H) { self.inner.s6_addr.hash(s) } } impl PartialOrd for Ipv6Addr { fn partial_cmp(&self, other: &Ipv6Addr) -> Option { Some(self.cmp(other)) } } impl PartialOrd for IpAddr { fn partial_cmp(&self, other: &Ipv6Addr) -> Option { match self { IpAddr::V4(_) => Some(Ordering::Less), IpAddr::V6(v6) => v6.partial_cmp(other), } } } impl PartialOrd for Ipv6Addr { fn partial_cmp(&self, other: &IpAddr) -> Option { match other { IpAddr::V4(_) => Some(Ordering::Greater), IpAddr::V6(v6) => self.partial_cmp(v6), } } } impl Ord for Ipv6Addr { fn cmp(&self, other: &Ipv6Addr) -> Ordering { self.segments().cmp(&other.segments()) } } impl AsInner for Ipv6Addr { fn as_inner(&self) -> &c::in6_addr { &self.inner } } impl FromInner for Ipv6Addr { fn from_inner(addr: c::in6_addr) -> Ipv6Addr { Ipv6Addr { inner: addr } } } impl From for u128 { fn from(ip: Ipv6Addr) -> u128 { let ip = ip.octets(); u128::from_be_bytes(ip) } } impl From for Ipv6Addr { fn from(ip: u128) -> Ipv6Addr { Ipv6Addr::from(ip.to_be_bytes()) } } impl From<[u8; 16]> for Ipv6Addr { fn from(octets: [u8; 16]) -> Ipv6Addr { let inner = c::in6_addr { s6_addr: octets }; Ipv6Addr::from_inner(inner) } } impl From<[u16; 8]> for Ipv6Addr { fn from(segments: [u16; 8]) -> Ipv6Addr { let [a, b, c, d, e, f, g, h] = segments; Ipv6Addr::new(a, b, c, d, e, f, g, h) } } impl From<[u8; 16]> for IpAddr { fn from(octets: [u8; 16]) -> IpAddr { IpAddr::V6(Ipv6Addr::from(octets)) } } impl From<[u16; 8]> for IpAddr { fn from(segments: [u16; 8]) -> IpAddr { IpAddr::V6(Ipv6Addr::from(segments)) } } prettyplease-0.2.37/src/algorithm.rs000064400000000000000000000302761046102023000155710ustar 00000000000000// Adapted from https://github.com/rust-lang/rust/blob/1.57.0/compiler/rustc_ast_pretty/src/pp.rs. // See "Algorithm notes" in the crate-level rustdoc. use crate::ring::RingBuffer; use crate::{MARGIN, MIN_SPACE}; use std::borrow::Cow; use std::cmp; use std::collections::VecDeque; use std::iter; #[derive(Clone, Copy, PartialEq)] pub enum Breaks { Consistent, Inconsistent, } #[derive(Clone, Copy, Default)] pub struct BreakToken { pub offset: isize, pub blank_space: usize, pub pre_break: Option, pub post_break: &'static str, pub no_break: Option, pub if_nonempty: bool, pub never_break: bool, } #[derive(Clone, Copy)] pub struct BeginToken { pub offset: isize, pub breaks: Breaks, } #[derive(Clone)] pub enum Token { String(Cow<'static, str>), Break(BreakToken), Begin(BeginToken), End, } #[derive(Copy, Clone)] enum PrintFrame { Fits(Breaks), Broken(usize, Breaks), } pub const SIZE_INFINITY: isize = 0xffff; pub struct Printer { out: String, // Number of spaces left on line space: isize, // Ring-buffer of tokens and calculated sizes buf: RingBuffer, // Total size of tokens already printed left_total: isize, // Total size of tokens enqueued, including printed and not yet printed right_total: isize, // Holds the ring-buffer index of the Begin that started the current block, // possibly with the most recent Break after that Begin (if there is any) on // top of it. Values are pushed and popped on the back of the queue using it // like stack, and elsewhere old values are popped from the front of the // queue as they become irrelevant due to the primary ring-buffer advancing. scan_stack: VecDeque, // Stack of blocks-in-progress being flushed by print print_stack: Vec, // Level of indentation of current line indent: usize, // Buffered indentation to avoid writing trailing whitespace pending_indentation: usize, } #[derive(Clone)] struct BufEntry { token: Token, size: isize, } impl Printer { pub fn new() -> Self { Printer { out: String::new(), space: MARGIN, buf: RingBuffer::new(), left_total: 0, right_total: 0, scan_stack: VecDeque::new(), print_stack: Vec::new(), indent: 0, pending_indentation: 0, } } pub fn eof(mut self) -> String { if !self.scan_stack.is_empty() { self.check_stack(0); self.advance_left(); } self.out } pub fn scan_begin(&mut self, token: BeginToken) { if self.scan_stack.is_empty() { self.left_total = 1; self.right_total = 1; self.buf.clear(); } let right = self.buf.push(BufEntry { token: Token::Begin(token), size: -self.right_total, }); self.scan_stack.push_back(right); } pub fn scan_end(&mut self) { if self.scan_stack.is_empty() { self.print_end(); } else { if !self.buf.is_empty() { if let Token::Break(break_token) = self.buf.last().token { if self.buf.len() >= 2 { if let Token::Begin(_) = self.buf.second_last().token { self.buf.pop_last(); self.buf.pop_last(); self.scan_stack.pop_back(); self.scan_stack.pop_back(); self.right_total -= break_token.blank_space as isize; return; } } if break_token.if_nonempty { self.buf.pop_last(); self.scan_stack.pop_back(); self.right_total -= break_token.blank_space as isize; } } } let right = self.buf.push(BufEntry { token: Token::End, size: -1, }); self.scan_stack.push_back(right); } } pub fn scan_break(&mut self, token: BreakToken) { if self.scan_stack.is_empty() { self.left_total = 1; self.right_total = 1; self.buf.clear(); } else { self.check_stack(0); } let right = self.buf.push(BufEntry { token: Token::Break(token), size: -self.right_total, }); self.scan_stack.push_back(right); self.right_total += token.blank_space as isize; } pub fn scan_string(&mut self, string: Cow<'static, str>) { if self.scan_stack.is_empty() { self.print_string(string); } else { let len = string.len() as isize; self.buf.push(BufEntry { token: Token::String(string), size: len, }); self.right_total += len; self.check_stream(); } } #[track_caller] pub fn offset(&mut self, offset: isize) { match &mut self.buf.last_mut().token { Token::Break(token) => token.offset += offset, Token::Begin(_) => {} Token::String(_) | Token::End => unreachable!(), } } pub fn end_with_max_width(&mut self, max: isize) { let mut depth = 1; for &index in self.scan_stack.iter().rev() { let entry = &self.buf[index]; match entry.token { Token::Begin(_) => { depth -= 1; if depth == 0 { if entry.size < 0 { let actual_width = entry.size + self.right_total; if actual_width > max { self.buf.push(BufEntry { token: Token::String(Cow::Borrowed("")), size: SIZE_INFINITY, }); self.right_total += SIZE_INFINITY; } } break; } } Token::End => depth += 1, Token::Break(_) => {} Token::String(_) => unreachable!(), } } self.scan_end(); } pub fn ends_with(&self, ch: char) -> bool { for i in self.buf.index_range().rev() { if let Token::String(token) = &self.buf[i].token { return token.ends_with(ch); } } self.out.ends_with(ch) } fn check_stream(&mut self) { while self.right_total - self.left_total > self.space { if *self.scan_stack.front().unwrap() == self.buf.index_range().start { self.scan_stack.pop_front().unwrap(); self.buf.first_mut().size = SIZE_INFINITY; } self.advance_left(); if self.buf.is_empty() { break; } } } fn advance_left(&mut self) { while self.buf.first().size >= 0 { let left = self.buf.pop_first(); match left.token { Token::String(string) => { self.left_total += left.size; self.print_string(string); } Token::Break(token) => { self.left_total += token.blank_space as isize; self.print_break(token, left.size); } Token::Begin(token) => self.print_begin(token, left.size), Token::End => self.print_end(), } if self.buf.is_empty() { break; } } } fn check_stack(&mut self, mut depth: usize) { while let Some(&index) = self.scan_stack.back() { let entry = &mut self.buf[index]; match entry.token { Token::Begin(_) => { if depth == 0 { break; } self.scan_stack.pop_back().unwrap(); entry.size += self.right_total; depth -= 1; } Token::End => { self.scan_stack.pop_back().unwrap(); entry.size = 1; depth += 1; } Token::Break(_) => { self.scan_stack.pop_back().unwrap(); entry.size += self.right_total; if depth == 0 { break; } } Token::String(_) => unreachable!(), } } } fn get_top(&self) -> PrintFrame { const OUTER: PrintFrame = PrintFrame::Broken(0, Breaks::Inconsistent); self.print_stack.last().map_or(OUTER, PrintFrame::clone) } fn print_begin(&mut self, token: BeginToken, size: isize) { if cfg!(prettyplease_debug) { self.out.push(match token.breaks { Breaks::Consistent => '«', Breaks::Inconsistent => '‹', }); if cfg!(prettyplease_debug_indent) { self.out .extend(token.offset.to_string().chars().map(|ch| match ch { '0'..='9' => ['₀', '₁', '₂', '₃', '₄', '₅', '₆', '₇', '₈', '₉'] [(ch as u8 - b'0') as usize], '-' => '₋', _ => unreachable!(), })); } } if size > self.space { self.print_stack .push(PrintFrame::Broken(self.indent, token.breaks)); self.indent = usize::try_from(self.indent as isize + token.offset).unwrap(); } else { self.print_stack.push(PrintFrame::Fits(token.breaks)); } } fn print_end(&mut self) { let breaks = match self.print_stack.pop().unwrap() { PrintFrame::Broken(indent, breaks) => { self.indent = indent; breaks } PrintFrame::Fits(breaks) => breaks, }; if cfg!(prettyplease_debug) { self.out.push(match breaks { Breaks::Consistent => '»', Breaks::Inconsistent => '›', }); } } fn print_break(&mut self, token: BreakToken, size: isize) { let fits = token.never_break || match self.get_top() { PrintFrame::Fits(..) => true, PrintFrame::Broken(.., Breaks::Consistent) => false, PrintFrame::Broken(.., Breaks::Inconsistent) => size <= self.space, }; if fits { self.pending_indentation += token.blank_space; self.space -= token.blank_space as isize; if let Some(no_break) = token.no_break { self.out.push(no_break); self.space -= no_break.len_utf8() as isize; } if cfg!(prettyplease_debug) { self.out.push('·'); } } else { if let Some(pre_break) = token.pre_break { self.print_indent(); self.out.push(pre_break); } if cfg!(prettyplease_debug) { self.out.push('·'); } self.out.push('\n'); let indent = self.indent as isize + token.offset; self.pending_indentation = usize::try_from(indent).unwrap(); self.space = cmp::max(MARGIN - indent, MIN_SPACE); if !token.post_break.is_empty() { self.print_indent(); self.out.push_str(token.post_break); self.space -= token.post_break.len() as isize; } } } fn print_string(&mut self, string: Cow<'static, str>) { self.print_indent(); self.out.push_str(&string); self.space -= string.len() as isize; } fn print_indent(&mut self) { self.out.reserve(self.pending_indentation); self.out .extend(iter::repeat(' ').take(self.pending_indentation)); self.pending_indentation = 0; } } prettyplease-0.2.37/src/attr.rs000064400000000000000000000222071046102023000145500ustar 00000000000000use crate::algorithm::Printer; use crate::fixup::FixupContext; use crate::path::PathKind; use crate::INDENT; use proc_macro2::{Delimiter, Group, TokenStream, TokenTree}; use syn::{AttrStyle, Attribute, Expr, Lit, MacroDelimiter, Meta, MetaList, MetaNameValue}; impl Printer { pub fn outer_attrs(&mut self, attrs: &[Attribute]) { for attr in attrs { if let AttrStyle::Outer = attr.style { self.attr(attr); } } } pub fn inner_attrs(&mut self, attrs: &[Attribute]) { for attr in attrs { if let AttrStyle::Inner(_) = attr.style { self.attr(attr); } } } fn attr(&mut self, attr: &Attribute) { if let Some(mut doc) = value_of_attribute("doc", attr) { if !doc.contains('\n') && match attr.style { AttrStyle::Outer => !doc.starts_with('/'), AttrStyle::Inner(_) => true, } { trim_trailing_spaces(&mut doc); self.word(match attr.style { AttrStyle::Outer => "///", AttrStyle::Inner(_) => "//!", }); self.word(doc); self.hardbreak(); return; } else if can_be_block_comment(&doc) && match attr.style { AttrStyle::Outer => !doc.starts_with(&['*', '/'][..]), AttrStyle::Inner(_) => true, } { trim_interior_trailing_spaces(&mut doc); self.word(match attr.style { AttrStyle::Outer => "/**", AttrStyle::Inner(_) => "/*!", }); self.word(doc); self.word("*/"); self.hardbreak(); return; } } else if let Some(mut comment) = value_of_attribute("comment", attr) { if !comment.contains('\n') { trim_trailing_spaces(&mut comment); self.word("//"); self.word(comment); self.hardbreak(); return; } else if can_be_block_comment(&comment) && !comment.starts_with(&['*', '!'][..]) { trim_interior_trailing_spaces(&mut comment); self.word("/*"); self.word(comment); self.word("*/"); self.hardbreak(); return; } } self.word(match attr.style { AttrStyle::Outer => "#", AttrStyle::Inner(_) => "#!", }); self.word("["); self.meta(&attr.meta); self.word("]"); self.space(); } fn meta(&mut self, meta: &Meta) { match meta { Meta::Path(path) => self.path(path, PathKind::Simple), Meta::List(meta) => self.meta_list(meta), Meta::NameValue(meta) => self.meta_name_value(meta), } } fn meta_list(&mut self, meta: &MetaList) { self.path(&meta.path, PathKind::Simple); let delimiter = match meta.delimiter { MacroDelimiter::Paren(_) => Delimiter::Parenthesis, MacroDelimiter::Brace(_) => Delimiter::Brace, MacroDelimiter::Bracket(_) => Delimiter::Bracket, }; let group = Group::new(delimiter, meta.tokens.clone()); self.attr_tokens(TokenStream::from(TokenTree::Group(group))); } fn meta_name_value(&mut self, meta: &MetaNameValue) { self.path(&meta.path, PathKind::Simple); self.word(" = "); self.expr(&meta.value, FixupContext::NONE); } fn attr_tokens(&mut self, tokens: TokenStream) { let mut stack = Vec::new(); stack.push((tokens.into_iter().peekable(), Delimiter::None)); let mut space = Self::nbsp as fn(&mut Self); #[derive(PartialEq)] enum State { Word, Punct, TrailingComma, } use State::*; let mut state = Word; while let Some((tokens, delimiter)) = stack.last_mut() { match tokens.next() { Some(TokenTree::Ident(ident)) => { if let Word = state { space(self); } self.ident(&ident); state = Word; } Some(TokenTree::Punct(punct)) => { let ch = punct.as_char(); if let (Word, '=') = (state, ch) { self.nbsp(); } if ch == ',' && tokens.peek().is_none() { self.trailing_comma(true); state = TrailingComma; } else { self.token_punct(ch); if ch == '=' { self.nbsp(); } else if ch == ',' { space(self); } state = Punct; } } Some(TokenTree::Literal(literal)) => { if let Word = state { space(self); } self.token_literal(&literal); state = Word; } Some(TokenTree::Group(group)) => { let delimiter = group.delimiter(); let stream = group.stream(); match delimiter { Delimiter::Parenthesis => { self.word("("); self.cbox(INDENT); self.zerobreak(); state = Punct; } Delimiter::Brace => { self.word("{"); state = Punct; } Delimiter::Bracket => { self.word("["); state = Punct; } Delimiter::None => {} } stack.push((stream.into_iter().peekable(), delimiter)); space = Self::space; } None => { match delimiter { Delimiter::Parenthesis => { if state != TrailingComma { self.zerobreak(); } self.offset(-INDENT); self.end(); self.word(")"); state = Punct; } Delimiter::Brace => { self.word("}"); state = Punct; } Delimiter::Bracket => { self.word("]"); state = Punct; } Delimiter::None => {} } stack.pop(); if stack.is_empty() { space = Self::nbsp; } } } } } } fn value_of_attribute(requested: &str, attr: &Attribute) -> Option { let value = match &attr.meta { Meta::NameValue(meta) if meta.path.is_ident(requested) => &meta.value, _ => return None, }; let lit = match value { Expr::Lit(expr) if expr.attrs.is_empty() => &expr.lit, _ => return None, }; match lit { Lit::Str(string) => Some(string.value()), _ => None, } } pub fn has_outer(attrs: &[Attribute]) -> bool { for attr in attrs { if let AttrStyle::Outer = attr.style { return true; } } false } pub fn has_inner(attrs: &[Attribute]) -> bool { for attr in attrs { if let AttrStyle::Inner(_) = attr.style { return true; } } false } fn trim_trailing_spaces(doc: &mut String) { doc.truncate(doc.trim_end_matches(' ').len()); } fn trim_interior_trailing_spaces(doc: &mut String) { if !doc.contains(" \n") { return; } let mut trimmed = String::with_capacity(doc.len()); let mut lines = doc.split('\n').peekable(); while let Some(line) = lines.next() { if lines.peek().is_some() { trimmed.push_str(line.trim_end_matches(' ')); trimmed.push('\n'); } else { trimmed.push_str(line); } } *doc = trimmed; } fn can_be_block_comment(value: &str) -> bool { let mut depth = 0usize; let bytes = value.as_bytes(); let mut i = 0usize; let upper = bytes.len() - 1; while i < upper { if bytes[i] == b'/' && bytes[i + 1] == b'*' { depth += 1; i += 2; } else if bytes[i] == b'*' && bytes[i + 1] == b'/' { if depth == 0 { return false; } depth -= 1; i += 2; } else { i += 1; } } depth == 0 && !value.ends_with('/') } prettyplease-0.2.37/src/classify.rs000064400000000000000000000264161046102023000154210ustar 00000000000000use proc_macro2::{Delimiter, TokenStream, TokenTree}; use std::ops::ControlFlow; use syn::punctuated::Punctuated; use syn::{Expr, MacroDelimiter, Path, PathArguments, ReturnType, Token, Type, TypeParamBound}; pub(crate) fn requires_semi_to_be_stmt(expr: &Expr) -> bool { match expr { Expr::Macro(expr) => !matches!(expr.mac.delimiter, MacroDelimiter::Brace(_)), _ => requires_comma_to_be_match_arm(expr), } } pub(crate) fn requires_comma_to_be_match_arm(mut expr: &Expr) -> bool { loop { match expr { #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] Expr::If(_) | Expr::Match(_) | Expr::Block(_) | Expr::Unsafe(_) // both under ExprKind::Block in rustc | Expr::While(_) | Expr::Loop(_) | Expr::ForLoop(_) | Expr::TryBlock(_) | Expr::Const(_) => return false, Expr::Array(_) | Expr::Assign(_) | Expr::Async(_) | Expr::Await(_) | Expr::Binary(_) | Expr::Break(_) | Expr::Call(_) | Expr::Cast(_) | Expr::Closure(_) | Expr::Continue(_) | Expr::Field(_) | Expr::Index(_) | Expr::Infer(_) | Expr::Let(_) | Expr::Lit(_) | Expr::Macro(_) | Expr::MethodCall(_) | Expr::Paren(_) | Expr::Path(_) | Expr::Range(_) | Expr::RawAddr(_) | Expr::Reference(_) | Expr::Repeat(_) | Expr::Return(_) | Expr::Struct(_) | Expr::Try(_) | Expr::Tuple(_) | Expr::Unary(_) | Expr::Yield(_) | Expr::Verbatim(_) => return true, Expr::Group(group) => expr = &group.expr, _ => return true, } } } pub(crate) fn trailing_unparameterized_path(mut ty: &Type) -> bool { loop { match ty { #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] Type::BareFn(t) => match &t.output { ReturnType::Default => return false, ReturnType::Type(_, ret) => ty = ret, }, Type::ImplTrait(t) => match last_type_in_bounds(&t.bounds) { ControlFlow::Break(trailing_path) => return trailing_path, ControlFlow::Continue(t) => ty = t, }, Type::Path(t) => match last_type_in_path(&t.path) { ControlFlow::Break(trailing_path) => return trailing_path, ControlFlow::Continue(t) => ty = t, }, Type::Ptr(t) => ty = &t.elem, Type::Reference(t) => ty = &t.elem, Type::TraitObject(t) => match last_type_in_bounds(&t.bounds) { ControlFlow::Break(trailing_path) => return trailing_path, ControlFlow::Continue(t) => ty = t, }, Type::Array(_) | Type::Group(_) | Type::Infer(_) | Type::Macro(_) | Type::Never(_) | Type::Paren(_) | Type::Slice(_) | Type::Tuple(_) | Type::Verbatim(_) => return false, _ => return false, } } fn last_type_in_path(path: &Path) -> ControlFlow { match &path.segments.last().unwrap().arguments { PathArguments::None => ControlFlow::Break(true), PathArguments::AngleBracketed(_) => ControlFlow::Break(false), PathArguments::Parenthesized(arg) => match &arg.output { ReturnType::Default => ControlFlow::Break(false), ReturnType::Type(_, ret) => ControlFlow::Continue(ret), }, } } fn last_type_in_bounds( bounds: &Punctuated, ) -> ControlFlow { match bounds.last().unwrap() { #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] TypeParamBound::Trait(t) => last_type_in_path(&t.path), TypeParamBound::Lifetime(_) | TypeParamBound::PreciseCapture(_) | TypeParamBound::Verbatim(_) => ControlFlow::Break(false), _ => ControlFlow::Break(false), } } } /// Whether the expression's first token is the label of a loop/block. pub(crate) fn expr_leading_label(mut expr: &Expr) -> bool { loop { match expr { #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] Expr::Block(e) => return e.label.is_some(), Expr::ForLoop(e) => return e.label.is_some(), Expr::Loop(e) => return e.label.is_some(), Expr::While(e) => return e.label.is_some(), Expr::Assign(e) => expr = &e.left, Expr::Await(e) => expr = &e.base, Expr::Binary(e) => expr = &e.left, Expr::Call(e) => expr = &e.func, Expr::Cast(e) => expr = &e.expr, Expr::Field(e) => expr = &e.base, Expr::Index(e) => expr = &e.expr, Expr::MethodCall(e) => expr = &e.receiver, Expr::Range(e) => match &e.start { Some(start) => expr = start, None => return false, }, Expr::Try(e) => expr = &e.expr, Expr::Array(_) | Expr::Async(_) | Expr::Break(_) | Expr::Closure(_) | Expr::Const(_) | Expr::Continue(_) | Expr::If(_) | Expr::Infer(_) | Expr::Let(_) | Expr::Lit(_) | Expr::Macro(_) | Expr::Match(_) | Expr::Paren(_) | Expr::Path(_) | Expr::RawAddr(_) | Expr::Reference(_) | Expr::Repeat(_) | Expr::Return(_) | Expr::Struct(_) | Expr::TryBlock(_) | Expr::Tuple(_) | Expr::Unary(_) | Expr::Unsafe(_) | Expr::Verbatim(_) | Expr::Yield(_) => return false, Expr::Group(e) => { if !e.attrs.is_empty() { return false; } expr = &e.expr; } _ => return false, } } } /// Whether the expression's last token is `}`. pub(crate) fn expr_trailing_brace(mut expr: &Expr) -> bool { loop { match expr { #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] Expr::Async(_) | Expr::Block(_) | Expr::Const(_) | Expr::ForLoop(_) | Expr::If(_) | Expr::Loop(_) | Expr::Match(_) | Expr::Struct(_) | Expr::TryBlock(_) | Expr::Unsafe(_) | Expr::While(_) => return true, Expr::Assign(e) => expr = &e.right, Expr::Binary(e) => expr = &e.right, Expr::Break(e) => match &e.expr { Some(e) => expr = e, None => return false, }, Expr::Cast(e) => return type_trailing_brace(&e.ty), Expr::Closure(e) => expr = &e.body, Expr::Group(e) => expr = &e.expr, Expr::Let(e) => expr = &e.expr, Expr::Macro(e) => return matches!(e.mac.delimiter, MacroDelimiter::Brace(_)), Expr::Range(e) => match &e.end { Some(end) => expr = end, None => return false, }, Expr::RawAddr(e) => expr = &e.expr, Expr::Reference(e) => expr = &e.expr, Expr::Return(e) => match &e.expr { Some(e) => expr = e, None => return false, }, Expr::Unary(e) => expr = &e.expr, Expr::Verbatim(e) => return tokens_trailing_brace(e), Expr::Yield(e) => match &e.expr { Some(e) => expr = e, None => return false, }, Expr::Array(_) | Expr::Await(_) | Expr::Call(_) | Expr::Continue(_) | Expr::Field(_) | Expr::Index(_) | Expr::Infer(_) | Expr::Lit(_) | Expr::MethodCall(_) | Expr::Paren(_) | Expr::Path(_) | Expr::Repeat(_) | Expr::Try(_) | Expr::Tuple(_) => return false, _ => return false, } } fn type_trailing_brace(mut ty: &Type) -> bool { loop { match ty { #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] Type::BareFn(t) => match &t.output { ReturnType::Default => return false, ReturnType::Type(_, ret) => ty = ret, }, Type::ImplTrait(t) => match last_type_in_bounds(&t.bounds) { ControlFlow::Break(trailing_brace) => return trailing_brace, ControlFlow::Continue(t) => ty = t, }, Type::Macro(t) => return matches!(t.mac.delimiter, MacroDelimiter::Brace(_)), Type::Path(t) => match last_type_in_path(&t.path) { Some(t) => ty = t, None => return false, }, Type::Ptr(t) => ty = &t.elem, Type::Reference(t) => ty = &t.elem, Type::TraitObject(t) => match last_type_in_bounds(&t.bounds) { ControlFlow::Break(trailing_brace) => return trailing_brace, ControlFlow::Continue(t) => ty = t, }, Type::Verbatim(t) => return tokens_trailing_brace(t), Type::Array(_) | Type::Group(_) | Type::Infer(_) | Type::Never(_) | Type::Paren(_) | Type::Slice(_) | Type::Tuple(_) => return false, _ => return false, } } } fn last_type_in_path(path: &Path) -> Option<&Type> { match &path.segments.last().unwrap().arguments { PathArguments::None | PathArguments::AngleBracketed(_) => None, PathArguments::Parenthesized(arg) => match &arg.output { ReturnType::Default => None, ReturnType::Type(_, ret) => Some(ret), }, } } fn last_type_in_bounds( bounds: &Punctuated, ) -> ControlFlow { match bounds.last().unwrap() { #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] TypeParamBound::Trait(t) => match last_type_in_path(&t.path) { Some(t) => ControlFlow::Continue(t), None => ControlFlow::Break(false), }, TypeParamBound::Lifetime(_) | TypeParamBound::PreciseCapture(_) => { ControlFlow::Break(false) } TypeParamBound::Verbatim(t) => ControlFlow::Break(tokens_trailing_brace(t)), _ => ControlFlow::Break(false), } } fn tokens_trailing_brace(tokens: &TokenStream) -> bool { if let Some(TokenTree::Group(last)) = tokens.clone().into_iter().last() { last.delimiter() == Delimiter::Brace } else { false } } } prettyplease-0.2.37/src/convenience.rs000064400000000000000000000043661046102023000161000ustar 00000000000000use crate::algorithm::{self, BeginToken, BreakToken, Breaks, Printer}; use std::borrow::Cow; impl Printer { pub fn ibox(&mut self, indent: isize) { self.scan_begin(BeginToken { offset: indent, breaks: Breaks::Inconsistent, }); } pub fn cbox(&mut self, indent: isize) { self.scan_begin(BeginToken { offset: indent, breaks: Breaks::Consistent, }); } pub fn end(&mut self) { self.scan_end(); } pub fn word>>(&mut self, wrd: S) { let s = wrd.into(); self.scan_string(s); } fn spaces(&mut self, n: usize) { self.scan_break(BreakToken { blank_space: n, ..BreakToken::default() }); } pub fn zerobreak(&mut self) { self.spaces(0); } pub fn space(&mut self) { self.spaces(1); } pub fn nbsp(&mut self) { self.word(" "); } pub fn hardbreak(&mut self) { self.spaces(algorithm::SIZE_INFINITY as usize); } pub fn space_if_nonempty(&mut self) { self.scan_break(BreakToken { blank_space: 1, if_nonempty: true, ..BreakToken::default() }); } pub fn hardbreak_if_nonempty(&mut self) { self.scan_break(BreakToken { blank_space: algorithm::SIZE_INFINITY as usize, if_nonempty: true, ..BreakToken::default() }); } pub fn trailing_comma(&mut self, is_last: bool) { if is_last { self.scan_break(BreakToken { pre_break: Some(','), ..BreakToken::default() }); } else { self.word(","); self.space(); } } pub fn trailing_comma_or_space(&mut self, is_last: bool) { if is_last { self.scan_break(BreakToken { blank_space: 1, pre_break: Some(','), ..BreakToken::default() }); } else { self.word(","); self.space(); } } pub fn neverbreak(&mut self) { self.scan_break(BreakToken { never_break: true, ..BreakToken::default() }); } } prettyplease-0.2.37/src/data.rs000064400000000000000000000046441046102023000145140ustar 00000000000000use crate::algorithm::Printer; use crate::fixup::FixupContext; use crate::iter::IterDelimited; use crate::path::PathKind; use crate::INDENT; use syn::{Field, Fields, FieldsUnnamed, Variant, VisRestricted, Visibility}; impl Printer { pub fn variant(&mut self, variant: &Variant) { self.outer_attrs(&variant.attrs); self.ident(&variant.ident); match &variant.fields { Fields::Named(fields) => { self.nbsp(); self.word("{"); self.cbox(INDENT); self.space(); for field in fields.named.iter().delimited() { self.field(&field); self.trailing_comma_or_space(field.is_last); } self.offset(-INDENT); self.end(); self.word("}"); } Fields::Unnamed(fields) => { self.cbox(INDENT); self.fields_unnamed(fields); self.end(); } Fields::Unit => {} } if let Some((_eq_token, discriminant)) = &variant.discriminant { self.word(" = "); self.expr(discriminant, FixupContext::NONE); } } pub fn fields_unnamed(&mut self, fields: &FieldsUnnamed) { self.word("("); self.zerobreak(); for field in fields.unnamed.iter().delimited() { self.field(&field); self.trailing_comma(field.is_last); } self.offset(-INDENT); self.word(")"); } pub fn field(&mut self, field: &Field) { self.outer_attrs(&field.attrs); self.visibility(&field.vis); if let Some(ident) = &field.ident { self.ident(ident); self.word(": "); } self.ty(&field.ty); } pub fn visibility(&mut self, vis: &Visibility) { match vis { Visibility::Public(_) => self.word("pub "), Visibility::Restricted(vis) => self.vis_restricted(vis), Visibility::Inherited => {} } } fn vis_restricted(&mut self, vis: &VisRestricted) { self.word("pub("); let omit_in = vis.path.get_ident().map_or(false, |ident| { matches!(ident.to_string().as_str(), "self" | "super" | "crate") }); if !omit_in { self.word("in "); } self.path(&vis.path, PathKind::Simple); self.word(") "); } } prettyplease-0.2.37/src/expr.rs000064400000000000000000001441251046102023000145600ustar 00000000000000use crate::algorithm::{BreakToken, Printer}; use crate::attr; use crate::classify; use crate::fixup::FixupContext; use crate::iter::IterDelimited; use crate::path::PathKind; use crate::precedence::Precedence; use crate::stmt; use crate::INDENT; use proc_macro2::TokenStream; use syn::punctuated::Punctuated; use syn::{ token, Arm, Attribute, BinOp, Block, Expr, ExprArray, ExprAssign, ExprAsync, ExprAwait, ExprBinary, ExprBlock, ExprBreak, ExprCall, ExprCast, ExprClosure, ExprConst, ExprContinue, ExprField, ExprForLoop, ExprGroup, ExprIf, ExprIndex, ExprInfer, ExprLet, ExprLit, ExprLoop, ExprMacro, ExprMatch, ExprMethodCall, ExprParen, ExprPath, ExprRange, ExprRawAddr, ExprReference, ExprRepeat, ExprReturn, ExprStruct, ExprTry, ExprTryBlock, ExprTuple, ExprUnary, ExprUnsafe, ExprWhile, ExprYield, FieldValue, Index, Label, Lit, Member, PointerMutability, RangeLimits, ReturnType, Stmt, Token, UnOp, }; impl Printer { pub fn expr(&mut self, expr: &Expr, mut fixup: FixupContext) { let needs_paren = fixup.parenthesize(expr); if needs_paren { self.word("("); fixup = FixupContext::NONE; } let beginning_of_line = false; match expr { #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] Expr::Array(expr) => self.expr_array(expr), Expr::Assign(expr) => self.expr_assign(expr, fixup), Expr::Async(expr) => self.expr_async(expr), Expr::Await(expr) => self.expr_await(expr, beginning_of_line, fixup), Expr::Binary(expr) => self.expr_binary(expr, fixup), Expr::Block(expr) => self.expr_block(expr), Expr::Break(expr) => self.expr_break(expr, fixup), Expr::Call(expr) => self.expr_call(expr, beginning_of_line, fixup), Expr::Cast(expr) => self.expr_cast(expr, fixup), Expr::Closure(expr) => self.expr_closure(expr, fixup), Expr::Const(expr) => self.expr_const(expr), Expr::Continue(expr) => self.expr_continue(expr), Expr::Field(expr) => self.expr_field(expr, beginning_of_line, fixup), Expr::ForLoop(expr) => self.expr_for_loop(expr), Expr::Group(expr) => self.expr_group(expr, fixup), Expr::If(expr) => self.expr_if(expr), Expr::Index(expr) => self.expr_index(expr, beginning_of_line, fixup), Expr::Infer(expr) => self.expr_infer(expr), Expr::Let(expr) => self.expr_let(expr, fixup), Expr::Lit(expr) => self.expr_lit(expr), Expr::Loop(expr) => self.expr_loop(expr), Expr::Macro(expr) => self.expr_macro(expr), Expr::Match(expr) => self.expr_match(expr), Expr::MethodCall(expr) => self.expr_method_call(expr, beginning_of_line, fixup), Expr::Paren(expr) => self.expr_paren(expr), Expr::Path(expr) => self.expr_path(expr), Expr::Range(expr) => self.expr_range(expr, fixup), Expr::RawAddr(expr) => self.expr_raw_addr(expr, fixup), Expr::Reference(expr) => self.expr_reference(expr, fixup), Expr::Repeat(expr) => self.expr_repeat(expr), Expr::Return(expr) => self.expr_return(expr, fixup), Expr::Struct(expr) => self.expr_struct(expr), Expr::Try(expr) => self.expr_try(expr, beginning_of_line, fixup), Expr::TryBlock(expr) => self.expr_try_block(expr), Expr::Tuple(expr) => self.expr_tuple(expr), Expr::Unary(expr) => self.expr_unary(expr, fixup), Expr::Unsafe(expr) => self.expr_unsafe(expr), Expr::Verbatim(expr) => self.expr_verbatim(expr, fixup), Expr::While(expr) => self.expr_while(expr), Expr::Yield(expr) => self.expr_yield(expr, fixup), _ => unimplemented!("unknown Expr"), } if needs_paren { self.word(")"); } } pub fn expr_beginning_of_line( &mut self, expr: &Expr, mut needs_paren: bool, beginning_of_line: bool, mut fixup: FixupContext, ) { needs_paren |= fixup.parenthesize(expr); if needs_paren { self.word("("); fixup = FixupContext::NONE; } match expr { Expr::Await(expr) => self.expr_await(expr, beginning_of_line, fixup), Expr::Field(expr) => self.expr_field(expr, beginning_of_line, fixup), Expr::Index(expr) => self.expr_index(expr, beginning_of_line, fixup), Expr::MethodCall(expr) => self.expr_method_call(expr, beginning_of_line, fixup), Expr::Try(expr) => self.expr_try(expr, beginning_of_line, fixup), _ => self.expr(expr, fixup), } if needs_paren { self.word(")"); } } fn prefix_subexpr( &mut self, expr: &Expr, mut needs_paren: bool, beginning_of_line: bool, mut fixup: FixupContext, ) { needs_paren |= fixup.parenthesize(expr); if needs_paren { self.word("("); fixup = FixupContext::NONE; } match expr { Expr::Await(expr) => self.prefix_subexpr_await(expr, beginning_of_line, fixup), Expr::Call(expr) => self.prefix_subexpr_call(expr, fixup), Expr::Field(expr) => self.prefix_subexpr_field(expr, beginning_of_line, fixup), Expr::Index(expr) => self.prefix_subexpr_index(expr, beginning_of_line, fixup), Expr::MethodCall(expr) => { let unindent_call_args = false; self.prefix_subexpr_method_call(expr, beginning_of_line, unindent_call_args, fixup); } Expr::Try(expr) => self.prefix_subexpr_try(expr, beginning_of_line, fixup), _ => { self.cbox(-INDENT); self.expr(expr, fixup); self.end(); } } if needs_paren { self.word(")"); } } fn expr_condition(&mut self, expr: &Expr) { self.cbox(0); self.expr(expr, FixupContext::new_condition()); if needs_newline_if_wrap(expr) { self.space(); } else { self.nbsp(); } self.end(); } pub fn subexpr(&mut self, expr: &Expr, needs_paren: bool, mut fixup: FixupContext) { if needs_paren { self.word("("); fixup = FixupContext::NONE; } self.expr(expr, fixup); if needs_paren { self.word(")"); } } fn expr_array(&mut self, expr: &ExprArray) { self.outer_attrs(&expr.attrs); if expr.elems.is_empty() { self.word("[]"); } else if simple_array(&expr.elems) { self.cbox(INDENT); self.word("["); self.zerobreak(); self.ibox(0); for elem in expr.elems.iter().delimited() { self.expr(&elem, FixupContext::NONE); if !elem.is_last { self.word(","); self.space(); } } self.end(); self.trailing_comma(true); self.offset(-INDENT); self.word("]"); self.end(); } else { self.word("["); self.cbox(INDENT); self.zerobreak(); for elem in expr.elems.iter().delimited() { self.expr(&elem, FixupContext::NONE); self.trailing_comma(elem.is_last); } self.offset(-INDENT); self.end(); self.word("]"); } } fn expr_assign(&mut self, expr: &ExprAssign, fixup: FixupContext) { let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_operator( &expr.left, false, false, Precedence::Assign, ); let right_fixup = fixup.rightmost_subexpression_fixup(false, false, Precedence::Assign); self.outer_attrs(&expr.attrs); self.ibox(0); if !expr.attrs.is_empty() { self.word("("); } self.subexpr(&expr.left, left_prec <= Precedence::Range, left_fixup); self.word(" = "); self.neverbreak(); self.expr(&expr.right, right_fixup); if !expr.attrs.is_empty() { self.word(")"); } self.end(); } fn expr_async(&mut self, expr: &ExprAsync) { self.outer_attrs(&expr.attrs); self.word("async "); if expr.capture.is_some() { self.word("move "); } self.cbox(INDENT); self.small_block(&expr.block, &expr.attrs); self.end(); } fn expr_await(&mut self, expr: &ExprAwait, beginning_of_line: bool, fixup: FixupContext) { self.outer_attrs(&expr.attrs); self.cbox(INDENT); self.prefix_subexpr_await(expr, beginning_of_line, fixup); self.end(); } fn prefix_subexpr_await( &mut self, expr: &ExprAwait, beginning_of_line: bool, fixup: FixupContext, ) { let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_dot(&expr.base); self.prefix_subexpr( &expr.base, left_prec < Precedence::Unambiguous, beginning_of_line, left_fixup, ); if !(beginning_of_line && is_short_ident(&expr.base)) { self.scan_break(BreakToken { no_break: self.ends_with('.').then_some(' '), ..BreakToken::default() }); } self.word(".await"); } fn expr_binary(&mut self, expr: &ExprBinary, fixup: FixupContext) { let binop_prec = Precedence::of_binop(&expr.op); let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_operator( &expr.left, match &expr.op { BinOp::Sub(_) | BinOp::Mul(_) | BinOp::And(_) | BinOp::Or(_) | BinOp::BitAnd(_) | BinOp::BitOr(_) | BinOp::Shl(_) | BinOp::Lt(_) => true, _ => false, }, match &expr.op { BinOp::Shl(_) | BinOp::Lt(_) => true, _ => false, }, binop_prec, ); let left_needs_group = match binop_prec { Precedence::Assign => left_prec <= Precedence::Range, Precedence::Compare => left_prec <= binop_prec, _ => left_prec < binop_prec, }; let right_fixup = fixup.rightmost_subexpression_fixup(false, false, binop_prec); let right_needs_group = binop_prec != Precedence::Assign && right_fixup.rightmost_subexpression_precedence(&expr.right) <= binop_prec; self.outer_attrs(&expr.attrs); self.ibox(INDENT); self.ibox(-INDENT); if !expr.attrs.is_empty() { self.word("("); } self.subexpr(&expr.left, left_needs_group, left_fixup); self.end(); self.space(); self.binary_operator(&expr.op); self.nbsp(); self.subexpr(&expr.right, right_needs_group, right_fixup); if !expr.attrs.is_empty() { self.word(")"); } self.end(); } pub fn expr_block(&mut self, expr: &ExprBlock) { self.outer_attrs(&expr.attrs); if let Some(label) = &expr.label { self.label(label); } self.cbox(INDENT); self.small_block(&expr.block, &expr.attrs); self.end(); } fn expr_break(&mut self, expr: &ExprBreak, fixup: FixupContext) { self.outer_attrs(&expr.attrs); self.word("break"); if let Some(lifetime) = &expr.label { self.nbsp(); self.lifetime(lifetime); } if let Some(value) = &expr.expr { self.nbsp(); self.subexpr( value, expr.label.is_none() && classify::expr_leading_label(value), fixup.rightmost_subexpression_fixup(true, true, Precedence::Jump), ); } } fn expr_call(&mut self, expr: &ExprCall, beginning_of_line: bool, fixup: FixupContext) { let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_operator( &expr.func, true, false, Precedence::Unambiguous, ); let needs_paren = if let Expr::Field(func) = &*expr.func { matches!(func.member, Member::Named(_)) } else { left_prec < Precedence::Unambiguous }; self.outer_attrs(&expr.attrs); self.expr_beginning_of_line(&expr.func, needs_paren, beginning_of_line, left_fixup); self.word("("); self.call_args(&expr.args); self.word(")"); } fn prefix_subexpr_call(&mut self, expr: &ExprCall, fixup: FixupContext) { let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_operator( &expr.func, true, false, Precedence::Unambiguous, ); let needs_paren = if let Expr::Field(func) = &*expr.func { matches!(func.member, Member::Named(_)) } else { left_prec < Precedence::Unambiguous }; let beginning_of_line = false; self.prefix_subexpr(&expr.func, needs_paren, beginning_of_line, left_fixup); self.word("("); self.call_args(&expr.args); self.word(")"); } fn expr_cast(&mut self, expr: &ExprCast, fixup: FixupContext) { let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_operator(&expr.expr, false, false, Precedence::Cast); self.outer_attrs(&expr.attrs); self.ibox(INDENT); self.ibox(-INDENT); if !expr.attrs.is_empty() { self.word("("); } self.subexpr(&expr.expr, left_prec < Precedence::Cast, left_fixup); self.end(); self.space(); self.word("as "); self.ty(&expr.ty); if !expr.attrs.is_empty() { self.word(")"); } self.end(); } fn expr_closure(&mut self, expr: &ExprClosure, fixup: FixupContext) { self.outer_attrs(&expr.attrs); self.ibox(0); if let Some(bound_lifetimes) = &expr.lifetimes { self.bound_lifetimes(bound_lifetimes); } if expr.constness.is_some() { self.word("const "); } if expr.movability.is_some() { self.word("static "); } if expr.asyncness.is_some() { self.word("async "); } if expr.capture.is_some() { self.word("move "); } self.cbox(INDENT); self.word("|"); for pat in expr.inputs.iter().delimited() { if pat.is_first { self.zerobreak(); } self.pat(&pat); if !pat.is_last { self.word(","); self.space(); } } match &expr.output { ReturnType::Default => { self.word("|"); self.space(); self.offset(-INDENT); self.end(); self.neverbreak(); let wrap_in_brace = match &*expr.body { Expr::Match(ExprMatch { attrs, .. }) | Expr::Call(ExprCall { attrs, .. }) => { attr::has_outer(attrs) } body => !is_blocklike(body), }; if wrap_in_brace { self.cbox(INDENT); let okay_to_brace = parseable_as_stmt(&expr.body); self.scan_break(BreakToken { pre_break: Some(if okay_to_brace { '{' } else { '(' }), ..BreakToken::default() }); self.expr( &expr.body, fixup.rightmost_subexpression_fixup(false, false, Precedence::Jump), ); self.scan_break(BreakToken { offset: -INDENT, pre_break: (okay_to_brace && stmt::add_semi(&expr.body)).then_some(';'), post_break: if okay_to_brace { "}" } else { ")" }, ..BreakToken::default() }); self.end(); } else { self.expr( &expr.body, fixup.rightmost_subexpression_fixup(false, false, Precedence::Jump), ); } } ReturnType::Type(_arrow, ty) => { if !expr.inputs.is_empty() { self.trailing_comma(true); self.offset(-INDENT); } self.word("|"); self.end(); self.word(" -> "); self.ty(ty); self.nbsp(); self.neverbreak(); if matches!(&*expr.body, Expr::Block(body) if body.attrs.is_empty() && body.label.is_none()) { self.expr( &expr.body, fixup.rightmost_subexpression_fixup(false, false, Precedence::Jump), ); } else { self.cbox(INDENT); self.expr_as_small_block(&expr.body, 0); self.end(); } } } self.end(); } pub fn expr_const(&mut self, expr: &ExprConst) { self.outer_attrs(&expr.attrs); self.word("const "); self.cbox(INDENT); self.small_block(&expr.block, &expr.attrs); self.end(); } fn expr_continue(&mut self, expr: &ExprContinue) { self.outer_attrs(&expr.attrs); self.word("continue"); if let Some(lifetime) = &expr.label { self.nbsp(); self.lifetime(lifetime); } } fn expr_field(&mut self, expr: &ExprField, beginning_of_line: bool, fixup: FixupContext) { self.outer_attrs(&expr.attrs); self.cbox(INDENT); self.prefix_subexpr_field(expr, beginning_of_line, fixup); self.end(); } fn prefix_subexpr_field( &mut self, expr: &ExprField, beginning_of_line: bool, fixup: FixupContext, ) { let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_dot(&expr.base); self.prefix_subexpr( &expr.base, left_prec < Precedence::Unambiguous, beginning_of_line, left_fixup, ); if !(beginning_of_line && is_short_ident(&expr.base)) { self.scan_break(BreakToken { no_break: self.ends_with('.').then_some(' '), ..BreakToken::default() }); } self.word("."); self.member(&expr.member); } fn expr_for_loop(&mut self, expr: &ExprForLoop) { self.outer_attrs(&expr.attrs); self.ibox(0); if let Some(label) = &expr.label { self.label(label); } self.word("for "); self.pat(&expr.pat); self.word(" in "); self.neverbreak(); self.expr_condition(&expr.expr); self.word("{"); self.neverbreak(); self.cbox(INDENT); self.hardbreak_if_nonempty(); self.inner_attrs(&expr.attrs); for stmt in expr.body.stmts.iter().delimited() { self.stmt(&stmt, stmt.is_last); } self.offset(-INDENT); self.end(); self.word("}"); self.end(); } fn expr_group(&mut self, expr: &ExprGroup, fixup: FixupContext) { self.outer_attrs(&expr.attrs); self.expr(&expr.expr, fixup); } fn expr_if(&mut self, expr: &ExprIf) { self.outer_attrs(&expr.attrs); self.cbox(INDENT); self.word("if "); self.cbox(-INDENT); self.expr_condition(&expr.cond); self.end(); if let Some((_else_token, else_branch)) = &expr.else_branch { let mut else_branch = &**else_branch; self.small_block(&expr.then_branch, &[]); loop { self.word(" else "); match else_branch { Expr::If(expr) => { self.word("if "); self.cbox(-INDENT); self.expr_condition(&expr.cond); self.end(); self.small_block(&expr.then_branch, &[]); if let Some((_else_token, next)) = &expr.else_branch { else_branch = next; continue; } } Expr::Block(expr) => { self.small_block(&expr.block, &[]); } // If not one of the valid expressions to exist in an else // clause, wrap in a block. other => self.expr_as_small_block(other, INDENT), } break; } } else if expr.then_branch.stmts.is_empty() { self.word("{}"); } else { self.word("{"); self.hardbreak(); for stmt in expr.then_branch.stmts.iter().delimited() { self.stmt(&stmt, stmt.is_last); } self.offset(-INDENT); self.word("}"); } self.end(); } fn expr_index(&mut self, expr: &ExprIndex, beginning_of_line: bool, fixup: FixupContext) { let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_operator( &expr.expr, true, false, Precedence::Unambiguous, ); self.outer_attrs(&expr.attrs); self.expr_beginning_of_line( &expr.expr, left_prec < Precedence::Unambiguous, beginning_of_line, left_fixup, ); self.word("["); self.expr(&expr.index, FixupContext::NONE); self.word("]"); } fn prefix_subexpr_index( &mut self, expr: &ExprIndex, beginning_of_line: bool, fixup: FixupContext, ) { let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_operator( &expr.expr, true, false, Precedence::Unambiguous, ); self.prefix_subexpr( &expr.expr, left_prec < Precedence::Unambiguous, beginning_of_line, left_fixup, ); self.word("["); self.expr(&expr.index, FixupContext::NONE); self.word("]"); } fn expr_infer(&mut self, expr: &ExprInfer) { self.outer_attrs(&expr.attrs); self.word("_"); } fn expr_let(&mut self, expr: &ExprLet, fixup: FixupContext) { let (right_prec, right_fixup) = fixup.rightmost_subexpression(&expr.expr, Precedence::Let); self.outer_attrs(&expr.attrs); self.ibox(0); self.word("let "); self.ibox(0); self.pat(&expr.pat); self.end(); self.word(" = "); self.neverbreak(); self.ibox(0); self.subexpr(&expr.expr, right_prec < Precedence::Let, right_fixup); self.end(); self.end(); } pub fn expr_lit(&mut self, expr: &ExprLit) { self.outer_attrs(&expr.attrs); self.lit(&expr.lit); } fn expr_loop(&mut self, expr: &ExprLoop) { self.outer_attrs(&expr.attrs); if let Some(label) = &expr.label { self.label(label); } self.word("loop {"); self.cbox(INDENT); self.hardbreak_if_nonempty(); self.inner_attrs(&expr.attrs); for stmt in expr.body.stmts.iter().delimited() { self.stmt(&stmt, stmt.is_last); } self.offset(-INDENT); self.end(); self.word("}"); } pub fn expr_macro(&mut self, expr: &ExprMacro) { self.outer_attrs(&expr.attrs); let semicolon = false; self.mac(&expr.mac, None, semicolon); } fn expr_match(&mut self, expr: &ExprMatch) { self.outer_attrs(&expr.attrs); self.ibox(0); self.word("match "); self.expr_condition(&expr.expr); self.word("{"); self.neverbreak(); self.cbox(INDENT); self.hardbreak_if_nonempty(); self.inner_attrs(&expr.attrs); for arm in &expr.arms { self.arm(arm); self.hardbreak(); } self.offset(-INDENT); self.end(); self.word("}"); self.end(); } fn expr_method_call( &mut self, expr: &ExprMethodCall, beginning_of_line: bool, fixup: FixupContext, ) { self.outer_attrs(&expr.attrs); self.cbox(INDENT); let unindent_call_args = beginning_of_line && is_short_ident(&expr.receiver); self.prefix_subexpr_method_call(expr, beginning_of_line, unindent_call_args, fixup); self.end(); } fn prefix_subexpr_method_call( &mut self, expr: &ExprMethodCall, beginning_of_line: bool, unindent_call_args: bool, fixup: FixupContext, ) { let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_dot(&expr.receiver); self.prefix_subexpr( &expr.receiver, left_prec < Precedence::Unambiguous, beginning_of_line, left_fixup, ); if !(beginning_of_line && is_short_ident(&expr.receiver)) { self.scan_break(BreakToken { no_break: self.ends_with('.').then_some(' '), ..BreakToken::default() }); } self.word("."); self.ident(&expr.method); if let Some(turbofish) = &expr.turbofish { self.angle_bracketed_generic_arguments(turbofish, PathKind::Expr); } self.cbox(if unindent_call_args { -INDENT } else { 0 }); self.word("("); self.call_args(&expr.args); self.word(")"); self.end(); } fn expr_paren(&mut self, expr: &ExprParen) { self.outer_attrs(&expr.attrs); self.word("("); self.expr(&expr.expr, FixupContext::NONE); self.word(")"); } pub fn expr_path(&mut self, expr: &ExprPath) { self.outer_attrs(&expr.attrs); self.qpath(&expr.qself, &expr.path, PathKind::Expr); } pub fn expr_range(&mut self, expr: &ExprRange, fixup: FixupContext) { self.outer_attrs(&expr.attrs); if !expr.attrs.is_empty() { self.word("("); } if let Some(start) = &expr.start { let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_operator(start, true, false, Precedence::Range); self.subexpr(start, left_prec <= Precedence::Range, left_fixup); } else if self.ends_with('.') { self.nbsp(); } self.word(match expr.limits { RangeLimits::HalfOpen(_) => "..", RangeLimits::Closed(_) => "..=", }); if let Some(end) = &expr.end { let right_fixup = fixup.rightmost_subexpression_fixup(false, true, Precedence::Range); let right_prec = right_fixup.rightmost_subexpression_precedence(end); self.subexpr(end, right_prec <= Precedence::Range, right_fixup); } if !expr.attrs.is_empty() { self.word(")"); } } fn expr_raw_addr(&mut self, expr: &ExprRawAddr, fixup: FixupContext) { let (right_prec, right_fixup) = fixup.rightmost_subexpression(&expr.expr, Precedence::Prefix); self.outer_attrs(&expr.attrs); self.word("&raw "); self.pointer_mutability(&expr.mutability); self.nbsp(); self.subexpr(&expr.expr, right_prec < Precedence::Prefix, right_fixup); } fn expr_reference(&mut self, expr: &ExprReference, fixup: FixupContext) { let (right_prec, right_fixup) = fixup.rightmost_subexpression(&expr.expr, Precedence::Prefix); self.outer_attrs(&expr.attrs); self.word("&"); if expr.mutability.is_some() { self.word("mut "); } self.subexpr(&expr.expr, right_prec < Precedence::Prefix, right_fixup); } fn expr_repeat(&mut self, expr: &ExprRepeat) { self.outer_attrs(&expr.attrs); self.word("["); self.expr(&expr.expr, FixupContext::NONE); self.word("; "); self.expr(&expr.len, FixupContext::NONE); self.word("]"); } fn expr_return(&mut self, expr: &ExprReturn, fixup: FixupContext) { self.outer_attrs(&expr.attrs); self.word("return"); if let Some(value) = &expr.expr { self.nbsp(); self.expr( value, fixup.rightmost_subexpression_fixup(true, false, Precedence::Jump), ); } } fn expr_struct(&mut self, expr: &ExprStruct) { self.outer_attrs(&expr.attrs); self.cbox(INDENT); self.ibox(-INDENT); self.qpath(&expr.qself, &expr.path, PathKind::Expr); self.end(); self.word(" {"); self.space_if_nonempty(); for field_value in expr.fields.iter().delimited() { self.field_value(&field_value); self.trailing_comma_or_space(field_value.is_last && expr.rest.is_none()); } if let Some(rest) = &expr.rest { self.word(".."); self.expr(rest, FixupContext::NONE); self.space(); } self.offset(-INDENT); self.end_with_max_width(34); self.word("}"); } fn expr_try(&mut self, expr: &ExprTry, beginning_of_line: bool, fixup: FixupContext) { let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_dot(&expr.expr); self.outer_attrs(&expr.attrs); self.expr_beginning_of_line( &expr.expr, left_prec < Precedence::Unambiguous, beginning_of_line, left_fixup, ); self.word("?"); } fn prefix_subexpr_try(&mut self, expr: &ExprTry, beginning_of_line: bool, fixup: FixupContext) { let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_dot(&expr.expr); self.prefix_subexpr( &expr.expr, left_prec < Precedence::Unambiguous, beginning_of_line, left_fixup, ); self.word("?"); } fn expr_try_block(&mut self, expr: &ExprTryBlock) { self.outer_attrs(&expr.attrs); self.word("try "); self.cbox(INDENT); self.small_block(&expr.block, &expr.attrs); self.end(); } fn expr_tuple(&mut self, expr: &ExprTuple) { self.outer_attrs(&expr.attrs); self.word("("); self.cbox(INDENT); self.zerobreak(); for elem in expr.elems.iter().delimited() { self.expr(&elem, FixupContext::NONE); if expr.elems.len() == 1 { self.word(","); self.zerobreak(); } else { self.trailing_comma(elem.is_last); } } self.offset(-INDENT); self.end(); self.word(")"); } fn expr_unary(&mut self, expr: &ExprUnary, fixup: FixupContext) { let (right_prec, right_fixup) = fixup.rightmost_subexpression(&expr.expr, Precedence::Prefix); self.outer_attrs(&expr.attrs); self.unary_operator(&expr.op); self.subexpr(&expr.expr, right_prec < Precedence::Prefix, right_fixup); } fn expr_unsafe(&mut self, expr: &ExprUnsafe) { self.outer_attrs(&expr.attrs); self.word("unsafe "); self.cbox(INDENT); self.small_block(&expr.block, &expr.attrs); self.end(); } #[cfg(not(feature = "verbatim"))] fn expr_verbatim(&mut self, expr: &TokenStream, _fixup: FixupContext) { if !expr.is_empty() { unimplemented!("Expr::Verbatim `{}`", expr); } } #[cfg(feature = "verbatim")] fn expr_verbatim(&mut self, tokens: &TokenStream, fixup: FixupContext) { use syn::parse::discouraged::Speculative; use syn::parse::{Parse, ParseStream, Result}; use syn::{parenthesized, Ident}; enum ExprVerbatim { Empty, Ellipsis, Become(Become), Builtin(Builtin), } struct Become { attrs: Vec, tail_call: Expr, } struct Builtin { attrs: Vec, name: Ident, args: TokenStream, } mod kw { syn::custom_keyword!(builtin); syn::custom_keyword!(raw); } impl Parse for ExprVerbatim { fn parse(input: ParseStream) -> Result { let ahead = input.fork(); let attrs = ahead.call(Attribute::parse_outer)?; let lookahead = ahead.lookahead1(); if input.is_empty() { Ok(ExprVerbatim::Empty) } else if lookahead.peek(Token![become]) { input.advance_to(&ahead); input.parse::()?; let tail_call: Expr = input.parse()?; Ok(ExprVerbatim::Become(Become { attrs, tail_call })) } else if lookahead.peek(kw::builtin) { input.advance_to(&ahead); input.parse::()?; input.parse::()?; let name: Ident = input.parse()?; let args; parenthesized!(args in input); let args: TokenStream = args.parse()?; Ok(ExprVerbatim::Builtin(Builtin { attrs, name, args })) } else if lookahead.peek(Token![...]) { input.parse::()?; Ok(ExprVerbatim::Ellipsis) } else { Err(lookahead.error()) } } } let expr: ExprVerbatim = match syn::parse2(tokens.clone()) { Ok(expr) => expr, Err(_) => unimplemented!("Expr::Verbatim `{}`", tokens), }; match expr { ExprVerbatim::Empty => {} ExprVerbatim::Ellipsis => { self.word("..."); } ExprVerbatim::Become(expr) => { self.outer_attrs(&expr.attrs); self.word("become"); self.nbsp(); self.expr( &expr.tail_call, fixup.rightmost_subexpression_fixup(true, false, Precedence::Jump), ); } ExprVerbatim::Builtin(expr) => { self.outer_attrs(&expr.attrs); self.word("builtin # "); self.ident(&expr.name); self.word("("); if !expr.args.is_empty() { self.cbox(INDENT); self.zerobreak(); self.ibox(0); self.macro_rules_tokens(expr.args, false); self.end(); self.zerobreak(); self.offset(-INDENT); self.end(); } self.word(")"); } } } fn expr_while(&mut self, expr: &ExprWhile) { self.outer_attrs(&expr.attrs); if let Some(label) = &expr.label { self.label(label); } self.word("while "); self.expr_condition(&expr.cond); self.word("{"); self.neverbreak(); self.cbox(INDENT); self.hardbreak_if_nonempty(); self.inner_attrs(&expr.attrs); for stmt in expr.body.stmts.iter().delimited() { self.stmt(&stmt, stmt.is_last); } self.offset(-INDENT); self.end(); self.word("}"); } fn expr_yield(&mut self, expr: &ExprYield, fixup: FixupContext) { self.outer_attrs(&expr.attrs); self.word("yield"); if let Some(value) = &expr.expr { self.nbsp(); self.expr( value, fixup.rightmost_subexpression_fixup(true, false, Precedence::Jump), ); } } fn label(&mut self, label: &Label) { self.lifetime(&label.name); self.word(": "); } fn field_value(&mut self, field_value: &FieldValue) { self.outer_attrs(&field_value.attrs); self.member(&field_value.member); if field_value.colon_token.is_some() { self.word(": "); self.ibox(0); self.expr(&field_value.expr, FixupContext::NONE); self.end(); } } fn arm(&mut self, arm: &Arm) { self.outer_attrs(&arm.attrs); self.ibox(0); self.pat(&arm.pat); if let Some((_if_token, guard)) = &arm.guard { self.word(" if "); self.expr(guard, FixupContext::NONE); } self.word(" => "); let empty_block; let mut body = &*arm.body; while let Expr::Block(expr) = body { if expr.attrs.is_empty() && expr.label.is_none() { let mut stmts = expr.block.stmts.iter(); if let (Some(Stmt::Expr(inner, None)), None) = (stmts.next(), stmts.next()) { body = inner; continue; } } break; } if let Expr::Tuple(expr) = body { if expr.elems.is_empty() && expr.attrs.is_empty() { empty_block = Expr::Block(ExprBlock { attrs: Vec::new(), label: None, block: Block { brace_token: token::Brace::default(), stmts: Vec::new(), }, }); body = &empty_block; } } if let Expr::Block(body) = body { if let Some(label) = &body.label { self.label(label); } self.word("{"); self.neverbreak(); self.cbox(INDENT); self.hardbreak_if_nonempty(); self.inner_attrs(&body.attrs); for stmt in body.block.stmts.iter().delimited() { self.stmt(&stmt, stmt.is_last); } self.offset(-INDENT); self.end(); self.word("}"); } else { self.neverbreak(); self.cbox(INDENT); let okay_to_brace = parseable_as_stmt(body); self.scan_break(BreakToken { pre_break: Some(if okay_to_brace { '{' } else { '(' }), ..BreakToken::default() }); self.expr_beginning_of_line(body, false, true, FixupContext::new_match_arm()); self.scan_break(BreakToken { offset: -INDENT, pre_break: (okay_to_brace && stmt::add_semi(body)).then_some(';'), post_break: if okay_to_brace { "}" } else { ")," }, no_break: classify::requires_comma_to_be_match_arm(body).then_some(','), ..BreakToken::default() }); self.end(); } self.end(); } fn call_args(&mut self, args: &Punctuated) { let mut iter = args.iter(); match (iter.next(), iter.next()) { (Some(expr), None) if is_blocklike(expr) => { self.expr(expr, FixupContext::NONE); } _ => { self.cbox(INDENT); self.zerobreak(); for arg in args.iter().delimited() { self.expr(&arg, FixupContext::NONE); self.trailing_comma(arg.is_last); } self.offset(-INDENT); self.end(); } } } pub fn small_block(&mut self, block: &Block, attrs: &[Attribute]) { self.word("{"); if attr::has_inner(attrs) || !block.stmts.is_empty() { self.space(); self.inner_attrs(attrs); match block.stmts.as_slice() { [Stmt::Expr(expr, None)] if stmt::break_after(expr) => { self.ibox(0); self.expr_beginning_of_line(expr, false, true, FixupContext::new_stmt()); self.end(); self.space(); } _ => { for stmt in block.stmts.iter().delimited() { self.stmt(&stmt, stmt.is_last); } } } self.offset(-INDENT); } self.word("}"); } pub fn expr_as_small_block(&mut self, expr: &Expr, indent: isize) { self.word("{"); self.space(); self.ibox(indent); self.expr_beginning_of_line(expr, false, true, FixupContext::new_stmt()); self.end(); self.space(); self.offset(-INDENT); self.word("}"); } pub fn member(&mut self, member: &Member) { match member { Member::Named(ident) => self.ident(ident), Member::Unnamed(index) => self.index(index), } } fn index(&mut self, member: &Index) { self.word(member.index.to_string()); } fn binary_operator(&mut self, op: &BinOp) { self.word( match op { #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] BinOp::Add(_) => "+", BinOp::Sub(_) => "-", BinOp::Mul(_) => "*", BinOp::Div(_) => "/", BinOp::Rem(_) => "%", BinOp::And(_) => "&&", BinOp::Or(_) => "||", BinOp::BitXor(_) => "^", BinOp::BitAnd(_) => "&", BinOp::BitOr(_) => "|", BinOp::Shl(_) => "<<", BinOp::Shr(_) => ">>", BinOp::Eq(_) => "==", BinOp::Lt(_) => "<", BinOp::Le(_) => "<=", BinOp::Ne(_) => "!=", BinOp::Ge(_) => ">=", BinOp::Gt(_) => ">", BinOp::AddAssign(_) => "+=", BinOp::SubAssign(_) => "-=", BinOp::MulAssign(_) => "*=", BinOp::DivAssign(_) => "/=", BinOp::RemAssign(_) => "%=", BinOp::BitXorAssign(_) => "^=", BinOp::BitAndAssign(_) => "&=", BinOp::BitOrAssign(_) => "|=", BinOp::ShlAssign(_) => "<<=", BinOp::ShrAssign(_) => ">>=", _ => unimplemented!("unknown BinOp"), }, ); } fn unary_operator(&mut self, op: &UnOp) { self.word( match op { #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] UnOp::Deref(_) => "*", UnOp::Not(_) => "!", UnOp::Neg(_) => "-", _ => unimplemented!("unknown UnOp"), }, ); } fn pointer_mutability(&mut self, mutability: &PointerMutability) { match mutability { PointerMutability::Const(_) => self.word("const"), PointerMutability::Mut(_) => self.word("mut"), } } } fn needs_newline_if_wrap(expr: &Expr) -> bool { match expr { #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] Expr::Array(_) | Expr::Async(_) | Expr::Block(_) | Expr::Break(ExprBreak { expr: None, .. }) | Expr::Closure(_) | Expr::Const(_) | Expr::Continue(_) | Expr::ForLoop(_) | Expr::If(_) | Expr::Infer(_) | Expr::Lit(_) | Expr::Loop(_) | Expr::Macro(_) | Expr::Match(_) | Expr::Path(_) | Expr::Range(ExprRange { end: None, .. }) | Expr::Repeat(_) | Expr::Return(ExprReturn { expr: None, .. }) | Expr::Struct(_) | Expr::TryBlock(_) | Expr::Tuple(_) | Expr::Unsafe(_) | Expr::Verbatim(_) | Expr::While(_) | Expr::Yield(ExprYield { expr: None, .. }) => false, Expr::Assign(_) | Expr::Await(_) | Expr::Binary(_) | Expr::Cast(_) | Expr::Field(_) | Expr::Index(_) | Expr::MethodCall(_) => true, Expr::Break(ExprBreak { expr: Some(e), .. }) | Expr::Call(ExprCall { func: e, .. }) | Expr::Group(ExprGroup { expr: e, .. }) | Expr::Let(ExprLet { expr: e, .. }) | Expr::Paren(ExprParen { expr: e, .. }) | Expr::Range(ExprRange { end: Some(e), .. }) | Expr::RawAddr(ExprRawAddr { expr: e, .. }) | Expr::Reference(ExprReference { expr: e, .. }) | Expr::Return(ExprReturn { expr: Some(e), .. }) | Expr::Try(ExprTry { expr: e, .. }) | Expr::Unary(ExprUnary { expr: e, .. }) | Expr::Yield(ExprYield { expr: Some(e), .. }) => needs_newline_if_wrap(e), _ => false, } } fn is_short_ident(expr: &Expr) -> bool { if let Expr::Path(expr) = expr { return expr.attrs.is_empty() && expr.qself.is_none() && expr .path .get_ident() .map_or(false, |ident| ident.to_string().len() as isize <= INDENT); } false } fn is_blocklike(expr: &Expr) -> bool { match expr { #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] Expr::Array(ExprArray { attrs, .. }) | Expr::Async(ExprAsync { attrs, .. }) | Expr::Block(ExprBlock { attrs, .. }) | Expr::Closure(ExprClosure { attrs, .. }) | Expr::Const(ExprConst { attrs, .. }) | Expr::Struct(ExprStruct { attrs, .. }) | Expr::TryBlock(ExprTryBlock { attrs, .. }) | Expr::Tuple(ExprTuple { attrs, .. }) | Expr::Unsafe(ExprUnsafe { attrs, .. }) => !attr::has_outer(attrs), Expr::Assign(_) | Expr::Await(_) | Expr::Binary(_) | Expr::Break(_) | Expr::Call(_) | Expr::Cast(_) | Expr::Continue(_) | Expr::Field(_) | Expr::ForLoop(_) | Expr::If(_) | Expr::Index(_) | Expr::Infer(_) | Expr::Let(_) | Expr::Lit(_) | Expr::Loop(_) | Expr::Macro(_) | Expr::Match(_) | Expr::MethodCall(_) | Expr::Paren(_) | Expr::Path(_) | Expr::Range(_) | Expr::RawAddr(_) | Expr::Reference(_) | Expr::Repeat(_) | Expr::Return(_) | Expr::Try(_) | Expr::Unary(_) | Expr::Verbatim(_) | Expr::While(_) | Expr::Yield(_) => false, Expr::Group(e) => is_blocklike(&e.expr), _ => false, } } pub fn simple_block(expr: &Expr) -> Option<&ExprBlock> { if let Expr::Block(expr) = expr { if expr.attrs.is_empty() && expr.label.is_none() { return Some(expr); } } None } pub fn simple_array(elements: &Punctuated) -> bool { for expr in elements { if let Expr::Lit(expr) = expr { match expr.lit { #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] Lit::Byte(_) | Lit::Char(_) | Lit::Int(_) | Lit::Bool(_) => {} Lit::Str(_) | Lit::ByteStr(_) | Lit::CStr(_) | Lit::Float(_) | Lit::Verbatim(_) => { return false; } _ => return false, } } else { return false; } } true } // Expressions for which `$expr` and `{ $expr }` mean the same thing. // // This is not the case for all expressions. For example `{} | x | x` has some // bitwise OR operators while `{ {} |x| x }` has a block followed by a closure. fn parseable_as_stmt(mut expr: &Expr) -> bool { loop { match expr { #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] Expr::Array(_) | Expr::Async(_) | Expr::Block(_) | Expr::Break(_) | Expr::Closure(_) | Expr::Const(_) | Expr::Continue(_) | Expr::ForLoop(_) | Expr::If(_) | Expr::Infer(_) | Expr::Lit(_) | Expr::Loop(_) | Expr::Macro(_) | Expr::Match(_) | Expr::Paren(_) | Expr::Path(_) | Expr::RawAddr(_) | Expr::Reference(_) | Expr::Repeat(_) | Expr::Return(_) | Expr::Struct(_) | Expr::TryBlock(_) | Expr::Tuple(_) | Expr::Unary(_) | Expr::Unsafe(_) | Expr::Verbatim(_) | Expr::While(_) | Expr::Yield(_) => return true, Expr::Let(_) => return false, Expr::Assign(e) => { if !classify::requires_semi_to_be_stmt(&e.left) { return false; } expr = &e.left; } Expr::Await(e) => expr = &e.base, Expr::Binary(e) => { if !classify::requires_semi_to_be_stmt(&e.left) { return false; } expr = &e.left; } Expr::Call(e) => { if !classify::requires_semi_to_be_stmt(&e.func) { return false; } expr = &e.func; } Expr::Cast(e) => { if !classify::requires_semi_to_be_stmt(&e.expr) { return false; } expr = &e.expr; } Expr::Field(e) => expr = &e.base, Expr::Group(e) => expr = &e.expr, Expr::Index(e) => { if !classify::requires_semi_to_be_stmt(&e.expr) { return false; } expr = &e.expr; } Expr::MethodCall(e) => expr = &e.receiver, Expr::Range(e) => match &e.start { None => return true, Some(start) => { if !classify::requires_semi_to_be_stmt(start) { return false; } expr = start; } }, Expr::Try(e) => expr = &e.expr, _ => return false, } } } prettyplease-0.2.37/src/file.rs000064400000000000000000000006111046102023000145100ustar 00000000000000use crate::algorithm::Printer; use syn::File; impl Printer { pub fn file(&mut self, file: &File) { self.cbox(0); if let Some(shebang) = &file.shebang { self.word(shebang.clone()); self.hardbreak(); } self.inner_attrs(&file.attrs); for item in &file.items { self.item(item); } self.end(); } } prettyplease-0.2.37/src/fixup.rs000064400000000000000000000604411046102023000147330ustar 00000000000000use crate::classify; use crate::precedence::Precedence; use syn::{ Expr, ExprBreak, ExprRange, ExprRawAddr, ExprReference, ExprReturn, ExprUnary, ExprYield, }; #[derive(Copy, Clone)] pub struct FixupContext { previous_operator: Precedence, next_operator: Precedence, // Print expression such that it can be parsed back as a statement // consisting of the original expression. // // The effect of this is for binary operators in statement position to set // `leftmost_subexpression_in_stmt` when printing their left-hand operand. // // (match x {}) - 1; // match needs parens when LHS of binary operator // // match x {}; // not when its own statement // stmt: bool, // This is the difference between: // // (match x {}) - 1; // subexpression needs parens // // let _ = match x {} - 1; // no parens // // There are 3 distinguishable contexts in which `print_expr` might be // called with the expression `$match` as its argument, where `$match` // represents an expression of kind `ExprKind::Match`: // // - stmt=false leftmost_subexpression_in_stmt=false // // Example: `let _ = $match - 1;` // // No parentheses required. // // - stmt=false leftmost_subexpression_in_stmt=true // // Example: `$match - 1;` // // Must parenthesize `($match)`, otherwise parsing back the output as a // statement would terminate the statement after the closing brace of // the match, parsing `-1;` as a separate statement. // // - stmt=true leftmost_subexpression_in_stmt=false // // Example: `$match;` // // No parentheses required. leftmost_subexpression_in_stmt: bool, // Print expression such that it can be parsed as a match arm. // // This is almost equivalent to `stmt`, but the grammar diverges a tiny bit // between statements and match arms when it comes to braced macro calls. // Macro calls with brace delimiter terminate a statement without a // semicolon, but do not terminate a match-arm without comma. // // m! {} - 1; // two statements: a macro call followed by -1 literal // // match () { // _ => m! {} - 1, // binary subtraction operator // } // match_arm: bool, // This is almost equivalent to `leftmost_subexpression_in_stmt`, other than // for braced macro calls. // // If we have `m! {} - 1` as an expression, the leftmost subexpression // `m! {}` will need to be parenthesized in the statement case but not the // match-arm case. // // (m! {}) - 1; // subexpression needs parens // // match () { // _ => m! {} - 1, // no parens // } // leftmost_subexpression_in_match_arm: bool, // This is the difference between: // // if let _ = (Struct {}) {} // needs parens // // match () { // () if let _ = Struct {} => {} // no parens // } // condition: bool, // This is the difference between: // // if break Struct {} == (break) {} // needs parens // // if break break == Struct {} {} // no parens // rightmost_subexpression_in_condition: bool, // This is the difference between: // // if break ({ x }).field + 1 {} needs parens // // if break 1 + { x }.field {} // no parens // leftmost_subexpression_in_optional_operand: bool, // This is the difference between: // // let _ = (return) - 1; // without paren, this would return -1 // // let _ = return + 1; // no paren because '+' cannot begin expr // next_operator_can_begin_expr: bool, // This is the difference between: // // let _ = 1 + return 1; // no parens if rightmost subexpression // // let _ = 1 + (return 1) + 1; // needs parens // next_operator_can_continue_expr: bool, // This is the difference between: // // let _ = x as u8 + T; // // let _ = (x as u8) < T; // // Without parens, the latter would want to parse `u8 Self { FixupContext { stmt: true, ..FixupContext::NONE } } /// Create the initial fixup for printing an expression as the right-hand /// side of a match arm. pub fn new_match_arm() -> Self { FixupContext { match_arm: true, ..FixupContext::NONE } } /// Create the initial fixup for printing an expression as the "condition" /// of an `if` or `while`. There are a few other positions which are /// grammatically equivalent and also use this, such as the iterator /// expression in `for` and the scrutinee in `match`. pub fn new_condition() -> Self { FixupContext { condition: true, rightmost_subexpression_in_condition: true, ..FixupContext::NONE } } /// Transform this fixup into the one that should apply when printing the /// leftmost subexpression of the current expression. /// /// The leftmost subexpression is any subexpression that has the same first /// token as the current expression, but has a different last token. /// /// For example in `$a + $b` and `$a.method()`, the subexpression `$a` is a /// leftmost subexpression. /// /// Not every expression has a leftmost subexpression. For example neither /// `-$a` nor `[$a]` have one. pub fn leftmost_subexpression_with_operator( self, expr: &Expr, next_operator_can_begin_expr: bool, next_operator_can_begin_generics: bool, precedence: Precedence, ) -> (Precedence, Self) { let fixup = FixupContext { next_operator: precedence, stmt: false, leftmost_subexpression_in_stmt: self.stmt || self.leftmost_subexpression_in_stmt, match_arm: false, leftmost_subexpression_in_match_arm: self.match_arm || self.leftmost_subexpression_in_match_arm, rightmost_subexpression_in_condition: false, next_operator_can_begin_expr, next_operator_can_continue_expr: true, next_operator_can_begin_generics, ..self }; (fixup.leftmost_subexpression_precedence(expr), fixup) } /// Transform this fixup into the one that should apply when printing a /// leftmost subexpression followed by a `.` or `?` token, which confer /// different statement boundary rules compared to other leftmost /// subexpressions. pub fn leftmost_subexpression_with_dot(self, expr: &Expr) -> (Precedence, Self) { let fixup = FixupContext { next_operator: Precedence::Unambiguous, stmt: self.stmt || self.leftmost_subexpression_in_stmt, leftmost_subexpression_in_stmt: false, match_arm: self.match_arm || self.leftmost_subexpression_in_match_arm, leftmost_subexpression_in_match_arm: false, rightmost_subexpression_in_condition: false, next_operator_can_begin_expr: false, next_operator_can_continue_expr: true, next_operator_can_begin_generics: false, ..self }; (fixup.leftmost_subexpression_precedence(expr), fixup) } fn leftmost_subexpression_precedence(self, expr: &Expr) -> Precedence { if !self.next_operator_can_begin_expr || self.next_operator == Precedence::Range { if let Scan::Bailout = scan_right(expr, self, Precedence::MIN, 0, 0) { if scan_left(expr, self) { return Precedence::Unambiguous; } } } self.precedence(expr) } /// Transform this fixup into the one that should apply when printing the /// rightmost subexpression of the current expression. /// /// The rightmost subexpression is any subexpression that has a different /// first token than the current expression, but has the same last token. /// /// For example in `$a + $b` and `-$b`, the subexpression `$b` is a /// rightmost subexpression. /// /// Not every expression has a rightmost subexpression. For example neither /// `[$b]` nor `$a.f($b)` have one. pub fn rightmost_subexpression( self, expr: &Expr, precedence: Precedence, ) -> (Precedence, Self) { let fixup = self.rightmost_subexpression_fixup(false, false, precedence); (fixup.rightmost_subexpression_precedence(expr), fixup) } pub fn rightmost_subexpression_fixup( self, reset_allow_struct: bool, optional_operand: bool, precedence: Precedence, ) -> Self { FixupContext { previous_operator: precedence, stmt: false, leftmost_subexpression_in_stmt: false, match_arm: false, leftmost_subexpression_in_match_arm: false, condition: self.condition && !reset_allow_struct, leftmost_subexpression_in_optional_operand: self.condition && optional_operand, ..self } } pub fn rightmost_subexpression_precedence(self, expr: &Expr) -> Precedence { let default_prec = self.precedence(expr); if match self.previous_operator { Precedence::Assign | Precedence::Let | Precedence::Prefix => { default_prec < self.previous_operator } _ => default_prec <= self.previous_operator, } && match self.next_operator { Precedence::Range | Precedence::Or | Precedence::And => true, _ => !self.next_operator_can_begin_expr, } { if let Scan::Bailout | Scan::Fail = scan_right(expr, self, self.previous_operator, 1, 0) { if scan_left(expr, self) { return Precedence::Prefix; } } } default_prec } /// Determine whether parentheses are needed around the given expression to /// head off the early termination of a statement or condition. pub fn parenthesize(self, expr: &Expr) -> bool { (self.leftmost_subexpression_in_stmt && !classify::requires_semi_to_be_stmt(expr)) || ((self.stmt || self.leftmost_subexpression_in_stmt) && matches!(expr, Expr::Let(_))) || (self.leftmost_subexpression_in_match_arm && !classify::requires_comma_to_be_match_arm(expr)) || (self.condition && matches!(expr, Expr::Struct(_))) || (self.rightmost_subexpression_in_condition && matches!( expr, Expr::Return(ExprReturn { expr: None, .. }) | Expr::Yield(ExprYield { expr: None, .. }) )) || (self.rightmost_subexpression_in_condition && !self.condition && matches!( expr, Expr::Break(ExprBreak { expr: None, .. }) | Expr::Path(_) | Expr::Range(ExprRange { end: None, .. }) )) || (self.leftmost_subexpression_in_optional_operand && matches!(expr, Expr::Block(expr) if expr.attrs.is_empty() && expr.label.is_none())) } /// Determines the effective precedence of a subexpression. Some expressions /// have higher or lower precedence when adjacent to particular operators. fn precedence(self, expr: &Expr) -> Precedence { if self.next_operator_can_begin_expr { // Decrease precedence of value-less jumps when followed by an // operator that would otherwise get interpreted as beginning a // value for the jump. if let Expr::Break(ExprBreak { expr: None, .. }) | Expr::Return(ExprReturn { expr: None, .. }) | Expr::Yield(ExprYield { expr: None, .. }) = expr { return Precedence::Jump; } } if !self.next_operator_can_continue_expr { match expr { // Increase precedence of expressions that extend to the end of // current statement or group. Expr::Break(_) | Expr::Closure(_) | Expr::Let(_) | Expr::Return(_) | Expr::Yield(_) => { return Precedence::Prefix; } Expr::Range(e) if e.start.is_none() => return Precedence::Prefix, _ => {} } } if self.next_operator_can_begin_generics { if let Expr::Cast(cast) = expr { if classify::trailing_unparameterized_path(&cast.ty) { return Precedence::MIN; } } } Precedence::of(expr) } } #[derive(Copy, Clone, PartialEq)] enum Scan { Fail, Bailout, Consume, } fn scan_left(expr: &Expr, fixup: FixupContext) -> bool { match expr { Expr::Assign(_) => fixup.previous_operator <= Precedence::Assign, Expr::Binary(e) => match Precedence::of_binop(&e.op) { Precedence::Assign => fixup.previous_operator <= Precedence::Assign, binop_prec => fixup.previous_operator < binop_prec, }, Expr::Cast(_) => fixup.previous_operator < Precedence::Cast, Expr::Range(e) => e.start.is_none() || fixup.previous_operator < Precedence::Assign, _ => true, } } fn scan_right( expr: &Expr, fixup: FixupContext, precedence: Precedence, fail_offset: u8, bailout_offset: u8, ) -> Scan { let consume_by_precedence = if match precedence { Precedence::Assign | Precedence::Compare => precedence <= fixup.next_operator, _ => precedence < fixup.next_operator, } || fixup.next_operator == Precedence::MIN { Scan::Consume } else { Scan::Bailout }; if fixup.parenthesize(expr) { return consume_by_precedence; } match expr { #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] Expr::Assign(e) if e.attrs.is_empty() => { if match fixup.next_operator { Precedence::Unambiguous => fail_offset >= 2, _ => bailout_offset >= 1, } { return Scan::Consume; } let right_fixup = fixup.rightmost_subexpression_fixup(false, false, Precedence::Assign); let scan = scan_right( &e.right, right_fixup, Precedence::Assign, match fixup.next_operator { Precedence::Unambiguous => fail_offset, _ => 1, }, 1, ); if let Scan::Bailout | Scan::Consume = scan { Scan::Consume } else if let Precedence::Unambiguous = fixup.next_operator { Scan::Fail } else { Scan::Bailout } } Expr::Binary(e) if e.attrs.is_empty() => { if match fixup.next_operator { Precedence::Unambiguous => { fail_offset >= 2 && (consume_by_precedence == Scan::Consume || bailout_offset >= 1) } _ => bailout_offset >= 1, } { return Scan::Consume; } let binop_prec = Precedence::of_binop(&e.op); if binop_prec == Precedence::Compare && fixup.next_operator == Precedence::Compare { return Scan::Consume; } let right_fixup = fixup.rightmost_subexpression_fixup(false, false, binop_prec); let scan = scan_right( &e.right, right_fixup, binop_prec, match fixup.next_operator { Precedence::Unambiguous => fail_offset, _ => 1, }, consume_by_precedence as u8 - Scan::Bailout as u8, ); match scan { Scan::Fail => {} Scan::Bailout => return consume_by_precedence, Scan::Consume => return Scan::Consume, } let right_needs_group = binop_prec != Precedence::Assign && right_fixup.rightmost_subexpression_precedence(&e.right) <= binop_prec; if right_needs_group { consume_by_precedence } else if let (Scan::Fail, Precedence::Unambiguous) = (scan, fixup.next_operator) { Scan::Fail } else { Scan::Bailout } } Expr::RawAddr(ExprRawAddr { expr, .. }) | Expr::Reference(ExprReference { expr, .. }) | Expr::Unary(ExprUnary { expr, .. }) => { if match fixup.next_operator { Precedence::Unambiguous => { fail_offset >= 2 && (consume_by_precedence == Scan::Consume || bailout_offset >= 1) } _ => bailout_offset >= 1, } { return Scan::Consume; } let right_fixup = fixup.rightmost_subexpression_fixup(false, false, Precedence::Prefix); let scan = scan_right( expr, right_fixup, precedence, match fixup.next_operator { Precedence::Unambiguous => fail_offset, _ => 1, }, consume_by_precedence as u8 - Scan::Bailout as u8, ); match scan { Scan::Fail => {} Scan::Bailout => return consume_by_precedence, Scan::Consume => return Scan::Consume, } if right_fixup.rightmost_subexpression_precedence(expr) < Precedence::Prefix { consume_by_precedence } else if let (Scan::Fail, Precedence::Unambiguous) = (scan, fixup.next_operator) { Scan::Fail } else { Scan::Bailout } } Expr::Range(e) if e.attrs.is_empty() => match &e.end { Some(end) => { if fail_offset >= 2 { return Scan::Consume; } let right_fixup = fixup.rightmost_subexpression_fixup(false, true, Precedence::Range); let scan = scan_right( end, right_fixup, Precedence::Range, fail_offset, match fixup.next_operator { Precedence::Assign | Precedence::Range => 0, _ => 1, }, ); if match (scan, fixup.next_operator) { (Scan::Fail, _) => false, (Scan::Bailout, Precedence::Assign | Precedence::Range) => false, (Scan::Bailout | Scan::Consume, _) => true, } { return Scan::Consume; } if right_fixup.rightmost_subexpression_precedence(end) <= Precedence::Range { Scan::Consume } else { Scan::Fail } } None => { if fixup.next_operator_can_begin_expr { Scan::Consume } else { Scan::Fail } } }, Expr::Break(e) => match &e.expr { Some(value) => { if bailout_offset >= 1 || e.label.is_none() && classify::expr_leading_label(value) { return Scan::Consume; } let right_fixup = fixup.rightmost_subexpression_fixup(true, true, Precedence::Jump); match scan_right(value, right_fixup, Precedence::Jump, 1, 1) { Scan::Fail => Scan::Bailout, Scan::Bailout | Scan::Consume => Scan::Consume, } } None => match fixup.next_operator { Precedence::Assign if precedence > Precedence::Assign => Scan::Fail, _ => Scan::Consume, }, }, Expr::Return(ExprReturn { expr, .. }) | Expr::Yield(ExprYield { expr, .. }) => match expr { Some(e) => { if bailout_offset >= 1 { return Scan::Consume; } let right_fixup = fixup.rightmost_subexpression_fixup(true, false, Precedence::Jump); match scan_right(e, right_fixup, Precedence::Jump, 1, 1) { Scan::Fail => Scan::Bailout, Scan::Bailout | Scan::Consume => Scan::Consume, } } None => match fixup.next_operator { Precedence::Assign if precedence > Precedence::Assign => Scan::Fail, _ => Scan::Consume, }, }, Expr::Closure(_) => Scan::Consume, Expr::Let(e) => { if bailout_offset >= 1 { return Scan::Consume; } let right_fixup = fixup.rightmost_subexpression_fixup(false, false, Precedence::Let); let scan = scan_right( &e.expr, right_fixup, Precedence::Let, 1, if fixup.next_operator < Precedence::Let { 0 } else { 1 }, ); match scan { Scan::Fail | Scan::Bailout if fixup.next_operator < Precedence::Let => { return Scan::Bailout; } Scan::Consume => return Scan::Consume, _ => {} } if right_fixup.rightmost_subexpression_precedence(&e.expr) < Precedence::Let { Scan::Consume } else if let Scan::Fail = scan { Scan::Bailout } else { Scan::Consume } } Expr::Group(e) => scan_right(&e.expr, fixup, precedence, fail_offset, bailout_offset), Expr::Array(_) | Expr::Assign(_) | Expr::Async(_) | Expr::Await(_) | Expr::Binary(_) | Expr::Block(_) | Expr::Call(_) | Expr::Cast(_) | Expr::Const(_) | Expr::Continue(_) | Expr::Field(_) | Expr::ForLoop(_) | Expr::If(_) | Expr::Index(_) | Expr::Infer(_) | Expr::Lit(_) | Expr::Loop(_) | Expr::Macro(_) | Expr::Match(_) | Expr::MethodCall(_) | Expr::Paren(_) | Expr::Path(_) | Expr::Range(_) | Expr::Repeat(_) | Expr::Struct(_) | Expr::Try(_) | Expr::TryBlock(_) | Expr::Tuple(_) | Expr::Unsafe(_) | Expr::Verbatim(_) | Expr::While(_) => match fixup.next_operator { Precedence::Assign | Precedence::Range if precedence == Precedence::Range => Scan::Fail, _ if precedence == Precedence::Let && fixup.next_operator < Precedence::Let => { Scan::Fail } _ => consume_by_precedence, }, _ => match fixup.next_operator { Precedence::Assign | Precedence::Range if precedence == Precedence::Range => Scan::Fail, _ if precedence == Precedence::Let && fixup.next_operator < Precedence::Let => { Scan::Fail } _ => consume_by_precedence, }, } } prettyplease-0.2.37/src/generics.rs000064400000000000000000000341271046102023000154010ustar 00000000000000use crate::algorithm::Printer; use crate::iter::IterDelimited; use crate::path::PathKind; use crate::INDENT; use proc_macro2::TokenStream; use std::ptr; use syn::{ BoundLifetimes, CapturedParam, ConstParam, Expr, GenericParam, Generics, LifetimeParam, PreciseCapture, PredicateLifetime, PredicateType, TraitBound, TraitBoundModifier, TypeParam, TypeParamBound, WhereClause, WherePredicate, }; impl Printer { pub fn generics(&mut self, generics: &Generics) { if generics.params.is_empty() { return; } self.word("<"); self.cbox(0); self.zerobreak(); // Print lifetimes before types and consts, regardless of their // order in self.params. #[derive(Ord, PartialOrd, Eq, PartialEq)] enum Group { First, Second, } fn group(param: &GenericParam) -> Group { match param { GenericParam::Lifetime(_) => Group::First, GenericParam::Type(_) | GenericParam::Const(_) => Group::Second, } } let last = generics.params.iter().max_by_key(|param| group(param)); for current_group in [Group::First, Group::Second] { for param in &generics.params { if group(param) == current_group { self.generic_param(param); self.trailing_comma(ptr::eq(param, last.unwrap())); } } } self.offset(-INDENT); self.end(); self.word(">"); } fn generic_param(&mut self, generic_param: &GenericParam) { match generic_param { GenericParam::Type(type_param) => self.type_param(type_param), GenericParam::Lifetime(lifetime_param) => self.lifetime_param(lifetime_param), GenericParam::Const(const_param) => self.const_param(const_param), } } pub fn bound_lifetimes(&mut self, bound_lifetimes: &BoundLifetimes) { self.word("for<"); for param in bound_lifetimes.lifetimes.iter().delimited() { self.generic_param(¶m); if !param.is_last { self.word(", "); } } self.word("> "); } fn lifetime_param(&mut self, lifetime_param: &LifetimeParam) { self.outer_attrs(&lifetime_param.attrs); self.lifetime(&lifetime_param.lifetime); for lifetime in lifetime_param.bounds.iter().delimited() { if lifetime.is_first { self.word(": "); } else { self.word(" + "); } self.lifetime(&lifetime); } } fn type_param(&mut self, type_param: &TypeParam) { self.outer_attrs(&type_param.attrs); self.ident(&type_param.ident); self.ibox(INDENT); for type_param_bound in type_param.bounds.iter().delimited() { if type_param_bound.is_first { self.word(": "); } else { self.space(); self.word("+ "); } self.type_param_bound(&type_param_bound); } if let Some(default) = &type_param.default { self.space(); self.word("= "); self.ty(default); } self.end(); } pub fn type_param_bound(&mut self, type_param_bound: &TypeParamBound) { match type_param_bound { #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] TypeParamBound::Trait(trait_bound) => { self.trait_bound(trait_bound, TraitBoundConst::None); } TypeParamBound::Lifetime(lifetime) => self.lifetime(lifetime), TypeParamBound::PreciseCapture(precise_capture) => { self.precise_capture(precise_capture); } TypeParamBound::Verbatim(bound) => self.type_param_bound_verbatim(bound), _ => unimplemented!("unknown TypeParamBound"), } } fn trait_bound(&mut self, trait_bound: &TraitBound, constness: TraitBoundConst) { if trait_bound.paren_token.is_some() { self.word("("); } if let Some(bound_lifetimes) = &trait_bound.lifetimes { self.bound_lifetimes(bound_lifetimes); } match constness { TraitBoundConst::None => {} #[cfg(feature = "verbatim")] TraitBoundConst::Conditional => self.word("[const] "), #[cfg(feature = "verbatim")] TraitBoundConst::Unconditional => self.word("const "), } self.trait_bound_modifier(&trait_bound.modifier); for segment in trait_bound.path.segments.iter().delimited() { if !segment.is_first || trait_bound.path.leading_colon.is_some() { self.word("::"); } self.path_segment(&segment, PathKind::Type); } if trait_bound.paren_token.is_some() { self.word(")"); } } fn trait_bound_modifier(&mut self, trait_bound_modifier: &TraitBoundModifier) { match trait_bound_modifier { TraitBoundModifier::None => {} TraitBoundModifier::Maybe(_question_mark) => self.word("?"), } } #[cfg(not(feature = "verbatim"))] fn type_param_bound_verbatim(&mut self, bound: &TokenStream) { unimplemented!("TypeParamBound::Verbatim `{}`", bound); } #[cfg(feature = "verbatim")] fn type_param_bound_verbatim(&mut self, tokens: &TokenStream) { use syn::parse::{Parse, ParseStream, Result}; use syn::{ bracketed, parenthesized, token, ParenthesizedGenericArguments, Path, PathArguments, Token, }; enum TypeParamBoundVerbatim { Ellipsis, Const(TraitBound, TraitBoundConst), } impl Parse for TypeParamBoundVerbatim { fn parse(input: ParseStream) -> Result { if input.peek(Token![...]) { input.parse::()?; return Ok(TypeParamBoundVerbatim::Ellipsis); } let content; let content = if input.peek(token::Paren) { parenthesized!(content in input); &content } else { input }; let lifetimes: Option = content.parse()?; let constness = if content.peek(token::Bracket) { let conditionally_const; bracketed!(conditionally_const in content); conditionally_const.parse::()?; TraitBoundConst::Conditional } else if content.peek(Token![const]) { content.parse::()?; TraitBoundConst::Unconditional } else { TraitBoundConst::None }; let modifier: TraitBoundModifier = content.parse()?; let mut path: Path = content.parse()?; if path.segments.last().unwrap().arguments.is_empty() && (content.peek(token::Paren) || content.peek(Token![::]) && content.peek3(token::Paren)) { content.parse::>()?; let args: ParenthesizedGenericArguments = content.parse()?; let parenthesized = PathArguments::Parenthesized(args); path.segments.last_mut().unwrap().arguments = parenthesized; } Ok(TypeParamBoundVerbatim::Const( TraitBound { paren_token: None, modifier, lifetimes, path, }, constness, )) } } let bound: TypeParamBoundVerbatim = match syn::parse2(tokens.clone()) { Ok(bound) => bound, Err(_) => unimplemented!("TypeParamBound::Verbatim `{}`", tokens), }; match bound { TypeParamBoundVerbatim::Ellipsis => { self.word("..."); } TypeParamBoundVerbatim::Const(trait_bound, constness) => { self.trait_bound(&trait_bound, constness); } } } fn const_param(&mut self, const_param: &ConstParam) { self.outer_attrs(&const_param.attrs); self.word("const "); self.ident(&const_param.ident); self.word(": "); self.ty(&const_param.ty); if let Some(default) = &const_param.default { self.word(" = "); self.const_argument(default); } } pub fn where_clause_for_body(&mut self, where_clause: &Option) { let hardbreaks = true; let semi = false; self.where_clause_impl(where_clause, hardbreaks, semi); } pub fn where_clause_semi(&mut self, where_clause: &Option) { let hardbreaks = true; let semi = true; self.where_clause_impl(where_clause, hardbreaks, semi); } pub fn where_clause_oneline(&mut self, where_clause: &Option) { let hardbreaks = false; let semi = false; self.where_clause_impl(where_clause, hardbreaks, semi); } pub fn where_clause_oneline_semi(&mut self, where_clause: &Option) { let hardbreaks = false; let semi = true; self.where_clause_impl(where_clause, hardbreaks, semi); } fn where_clause_impl( &mut self, where_clause: &Option, hardbreaks: bool, semi: bool, ) { let where_clause = match where_clause { Some(where_clause) if !where_clause.predicates.is_empty() => where_clause, _ => { if semi { self.word(";"); } else { self.nbsp(); } return; } }; if hardbreaks { self.hardbreak(); self.offset(-INDENT); self.word("where"); self.hardbreak(); for predicate in where_clause.predicates.iter().delimited() { self.where_predicate(&predicate); if predicate.is_last && semi { self.word(";"); } else { self.word(","); self.hardbreak(); } } if !semi { self.offset(-INDENT); } } else { self.space(); self.offset(-INDENT); self.word("where"); self.space(); for predicate in where_clause.predicates.iter().delimited() { self.where_predicate(&predicate); if predicate.is_last && semi { self.word(";"); } else { self.trailing_comma_or_space(predicate.is_last); } } if !semi { self.offset(-INDENT); } } } fn where_predicate(&mut self, predicate: &WherePredicate) { match predicate { #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] WherePredicate::Type(predicate) => self.predicate_type(predicate), WherePredicate::Lifetime(predicate) => self.predicate_lifetime(predicate), _ => unimplemented!("unknown WherePredicate"), } } fn predicate_type(&mut self, predicate: &PredicateType) { if let Some(bound_lifetimes) = &predicate.lifetimes { self.bound_lifetimes(bound_lifetimes); } self.ty(&predicate.bounded_ty); self.word(":"); if predicate.bounds.len() == 1 { self.ibox(0); } else { self.ibox(INDENT); } for type_param_bound in predicate.bounds.iter().delimited() { if type_param_bound.is_first { self.nbsp(); } else { self.space(); self.word("+ "); } self.type_param_bound(&type_param_bound); } self.end(); } fn predicate_lifetime(&mut self, predicate: &PredicateLifetime) { self.lifetime(&predicate.lifetime); self.word(":"); self.ibox(INDENT); for lifetime in predicate.bounds.iter().delimited() { if lifetime.is_first { self.nbsp(); } else { self.space(); self.word("+ "); } self.lifetime(&lifetime); } self.end(); } fn precise_capture(&mut self, precise_capture: &PreciseCapture) { self.word("use<"); for capture in precise_capture.params.iter().delimited() { self.captured_param(&capture); if !capture.is_last { self.word(", "); } } self.word(">"); } fn captured_param(&mut self, capture: &CapturedParam) { match capture { #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] CapturedParam::Lifetime(lifetime) => self.lifetime(lifetime), CapturedParam::Ident(ident) => self.ident(ident), _ => unimplemented!("unknown CapturedParam"), } } pub fn const_argument(&mut self, expr: &Expr) { match expr { #![cfg_attr(all(test, exhaustive), allow(non_exhaustive_omitted_patterns))] Expr::Lit(expr) => self.expr_lit(expr), Expr::Path(expr) if expr.attrs.is_empty() && expr.qself.is_none() && expr.path.get_ident().is_some() => { self.expr_path(expr); } Expr::Block(expr) => self.expr_block(expr), _ => { self.cbox(INDENT); self.expr_as_small_block(expr, 0); self.end(); } } } } enum TraitBoundConst { None, #[cfg(feature = "verbatim")] Conditional, #[cfg(feature = "verbatim")] Unconditional, } prettyplease-0.2.37/src/item.rs000064400000000000000000001737311046102023000145450ustar 00000000000000use crate::algorithm::Printer; use crate::fixup::FixupContext; use crate::iter::IterDelimited; use crate::mac; use crate::path::PathKind; use crate::INDENT; use proc_macro2::TokenStream; use syn::{ Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemMacro, ForeignItemStatic, ForeignItemType, ImplItem, ImplItemConst, ImplItemFn, ImplItemMacro, ImplItemType, Item, ItemConst, ItemEnum, ItemExternCrate, ItemFn, ItemForeignMod, ItemImpl, ItemMacro, ItemMod, ItemStatic, ItemStruct, ItemTrait, ItemTraitAlias, ItemType, ItemUnion, ItemUse, Receiver, Signature, StaticMutability, TraitItem, TraitItemConst, TraitItemFn, TraitItemMacro, TraitItemType, Type, UseGlob, UseGroup, UseName, UsePath, UseRename, UseTree, Variadic, }; impl Printer { pub fn item(&mut self, item: &Item) { match item { #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] Item::Const(item) => self.item_const(item), Item::Enum(item) => self.item_enum(item), Item::ExternCrate(item) => self.item_extern_crate(item), Item::Fn(item) => self.item_fn(item), Item::ForeignMod(item) => self.item_foreign_mod(item), Item::Impl(item) => self.item_impl(item), Item::Macro(item) => self.item_macro(item), Item::Mod(item) => self.item_mod(item), Item::Static(item) => self.item_static(item), Item::Struct(item) => self.item_struct(item), Item::Trait(item) => self.item_trait(item), Item::TraitAlias(item) => self.item_trait_alias(item), Item::Type(item) => self.item_type(item), Item::Union(item) => self.item_union(item), Item::Use(item) => self.item_use(item), Item::Verbatim(item) => self.item_verbatim(item), _ => unimplemented!("unknown Item"), } } fn item_const(&mut self, item: &ItemConst) { self.outer_attrs(&item.attrs); self.cbox(0); self.visibility(&item.vis); self.word("const "); self.ident(&item.ident); self.generics(&item.generics); self.word(": "); self.ty(&item.ty); self.word(" = "); self.neverbreak(); self.expr(&item.expr, FixupContext::NONE); self.word(";"); self.end(); self.hardbreak(); } fn item_enum(&mut self, item: &ItemEnum) { self.outer_attrs(&item.attrs); self.cbox(INDENT); self.visibility(&item.vis); self.word("enum "); self.ident(&item.ident); self.generics(&item.generics); self.where_clause_for_body(&item.generics.where_clause); self.word("{"); self.hardbreak_if_nonempty(); for variant in &item.variants { self.variant(variant); self.word(","); self.hardbreak(); } self.offset(-INDENT); self.end(); self.word("}"); self.hardbreak(); } fn item_extern_crate(&mut self, item: &ItemExternCrate) { self.outer_attrs(&item.attrs); self.visibility(&item.vis); self.word("extern crate "); self.ident(&item.ident); if let Some((_as_token, rename)) = &item.rename { self.word(" as "); self.ident(rename); } self.word(";"); self.hardbreak(); } fn item_fn(&mut self, item: &ItemFn) { self.outer_attrs(&item.attrs); self.cbox(INDENT); self.visibility(&item.vis); self.signature( &item.sig, #[cfg(feature = "verbatim")] &verbatim::Safety::Disallowed, ); self.where_clause_for_body(&item.sig.generics.where_clause); self.word("{"); self.hardbreak_if_nonempty(); self.inner_attrs(&item.attrs); for stmt in item.block.stmts.iter().delimited() { self.stmt(&stmt, stmt.is_last); } self.offset(-INDENT); self.end(); self.word("}"); self.hardbreak(); } fn item_foreign_mod(&mut self, item: &ItemForeignMod) { self.outer_attrs(&item.attrs); self.cbox(INDENT); if item.unsafety.is_some() { self.word("unsafe "); } self.abi(&item.abi); self.word("{"); self.hardbreak_if_nonempty(); self.inner_attrs(&item.attrs); for foreign_item in &item.items { self.foreign_item(foreign_item); } self.offset(-INDENT); self.end(); self.word("}"); self.hardbreak(); } fn item_impl(&mut self, item: &ItemImpl) { self.outer_attrs(&item.attrs); self.cbox(INDENT); self.ibox(-INDENT); self.cbox(INDENT); if item.defaultness.is_some() { self.word("default "); } if item.unsafety.is_some() { self.word("unsafe "); } self.word("impl"); self.generics(&item.generics); self.end(); self.nbsp(); if let Some((negative_polarity, path, _for_token)) = &item.trait_ { if negative_polarity.is_some() { self.word("!"); } self.path(path, PathKind::Type); self.space(); self.word("for "); } self.ty(&item.self_ty); self.end(); self.where_clause_for_body(&item.generics.where_clause); self.word("{"); self.hardbreak_if_nonempty(); self.inner_attrs(&item.attrs); for impl_item in &item.items { self.impl_item(impl_item); } self.offset(-INDENT); self.end(); self.word("}"); self.hardbreak(); } fn item_macro(&mut self, item: &ItemMacro) { self.outer_attrs(&item.attrs); let semicolon = mac::requires_semi(&item.mac.delimiter); self.mac(&item.mac, item.ident.as_ref(), semicolon); self.hardbreak(); } fn item_mod(&mut self, item: &ItemMod) { self.outer_attrs(&item.attrs); self.cbox(INDENT); self.visibility(&item.vis); if item.unsafety.is_some() { self.word("unsafe "); } self.word("mod "); self.ident(&item.ident); if let Some((_brace, items)) = &item.content { self.word(" {"); self.hardbreak_if_nonempty(); self.inner_attrs(&item.attrs); for item in items { self.item(item); } self.offset(-INDENT); self.end(); self.word("}"); } else { self.word(";"); self.end(); } self.hardbreak(); } fn item_static(&mut self, item: &ItemStatic) { self.outer_attrs(&item.attrs); self.cbox(0); self.visibility(&item.vis); self.word("static "); self.static_mutability(&item.mutability); self.ident(&item.ident); self.word(": "); self.ty(&item.ty); self.word(" = "); self.neverbreak(); self.expr(&item.expr, FixupContext::NONE); self.word(";"); self.end(); self.hardbreak(); } fn item_struct(&mut self, item: &ItemStruct) { self.outer_attrs(&item.attrs); self.cbox(INDENT); self.visibility(&item.vis); self.word("struct "); self.ident(&item.ident); self.generics(&item.generics); match &item.fields { Fields::Named(fields) => { self.where_clause_for_body(&item.generics.where_clause); self.word("{"); self.hardbreak_if_nonempty(); for field in &fields.named { self.field(field); self.word(","); self.hardbreak(); } self.offset(-INDENT); self.end(); self.word("}"); } Fields::Unnamed(fields) => { self.fields_unnamed(fields); self.where_clause_semi(&item.generics.where_clause); self.end(); } Fields::Unit => { self.where_clause_semi(&item.generics.where_clause); self.end(); } } self.hardbreak(); } fn item_trait(&mut self, item: &ItemTrait) { self.outer_attrs(&item.attrs); self.cbox(INDENT); self.visibility(&item.vis); if item.unsafety.is_some() { self.word("unsafe "); } if item.auto_token.is_some() { self.word("auto "); } self.word("trait "); self.ident(&item.ident); self.generics(&item.generics); for supertrait in item.supertraits.iter().delimited() { if supertrait.is_first { self.word(": "); } else { self.word(" + "); } self.type_param_bound(&supertrait); } self.where_clause_for_body(&item.generics.where_clause); self.word("{"); self.hardbreak_if_nonempty(); self.inner_attrs(&item.attrs); for trait_item in &item.items { self.trait_item(trait_item); } self.offset(-INDENT); self.end(); self.word("}"); self.hardbreak(); } fn item_trait_alias(&mut self, item: &ItemTraitAlias) { self.outer_attrs(&item.attrs); self.cbox(INDENT); self.visibility(&item.vis); self.word("trait "); self.ident(&item.ident); self.generics(&item.generics); self.word(" = "); self.neverbreak(); for bound in item.bounds.iter().delimited() { if !bound.is_first { self.space(); self.word("+ "); } self.type_param_bound(&bound); } self.where_clause_semi(&item.generics.where_clause); self.end(); self.hardbreak(); } fn item_type(&mut self, item: &ItemType) { self.outer_attrs(&item.attrs); self.cbox(INDENT); self.visibility(&item.vis); self.word("type "); self.ident(&item.ident); self.generics(&item.generics); self.where_clause_oneline(&item.generics.where_clause); self.word("= "); self.neverbreak(); self.ibox(-INDENT); self.ty(&item.ty); self.end(); self.word(";"); self.end(); self.hardbreak(); } fn item_union(&mut self, item: &ItemUnion) { self.outer_attrs(&item.attrs); self.cbox(INDENT); self.visibility(&item.vis); self.word("union "); self.ident(&item.ident); self.generics(&item.generics); self.where_clause_for_body(&item.generics.where_clause); self.word("{"); self.hardbreak_if_nonempty(); for field in &item.fields.named { self.field(field); self.word(","); self.hardbreak(); } self.offset(-INDENT); self.end(); self.word("}"); self.hardbreak(); } fn item_use(&mut self, item: &ItemUse) { self.outer_attrs(&item.attrs); self.visibility(&item.vis); self.word("use "); if item.leading_colon.is_some() { self.word("::"); } self.use_tree(&item.tree); self.word(";"); self.hardbreak(); } #[cfg(not(feature = "verbatim"))] fn item_verbatim(&mut self, item: &TokenStream) { if !item.is_empty() { unimplemented!("Item::Verbatim `{}`", item); } self.hardbreak(); } #[cfg(feature = "verbatim")] fn item_verbatim(&mut self, tokens: &TokenStream) { use syn::parse::{Parse, ParseStream, Result}; use syn::punctuated::Punctuated; use syn::{ braced, parenthesized, token, Attribute, Generics, Ident, Lifetime, Token, Visibility, }; use verbatim::{ FlexibleItemConst, FlexibleItemFn, FlexibleItemStatic, FlexibleItemType, WhereClauseLocation, }; enum ItemVerbatim { Empty, Ellipsis, ConstFlexible(FlexibleItemConst), FnFlexible(FlexibleItemFn), ImplFlexible(ImplFlexible), Macro2(Macro2), StaticFlexible(FlexibleItemStatic), TypeFlexible(FlexibleItemType), UseBrace(UseBrace), } struct ImplFlexible { attrs: Vec, vis: Visibility, defaultness: bool, unsafety: bool, generics: Generics, constness: ImplConstness, negative_impl: bool, trait_: Option, self_ty: Type, items: Vec, } enum ImplConstness { None, MaybeConst, Const, } struct Macro2 { attrs: Vec, vis: Visibility, ident: Ident, args: Option, body: TokenStream, } struct UseBrace { attrs: Vec, vis: Visibility, trees: Punctuated, } struct RootUseTree { leading_colon: Option, inner: UseTree, } impl Parse for ImplConstness { fn parse(input: ParseStream) -> Result { if input.parse::>()?.is_some() { input.parse::()?; Ok(ImplConstness::MaybeConst) } else if input.parse::>()?.is_some() { Ok(ImplConstness::Const) } else { Ok(ImplConstness::None) } } } impl Parse for RootUseTree { fn parse(input: ParseStream) -> Result { Ok(RootUseTree { leading_colon: input.parse()?, inner: input.parse()?, }) } } impl Parse for ItemVerbatim { fn parse(input: ParseStream) -> Result { if input.is_empty() { return Ok(ItemVerbatim::Empty); } else if input.peek(Token![...]) { input.parse::()?; return Ok(ItemVerbatim::Ellipsis); } let mut attrs = input.call(Attribute::parse_outer)?; let vis: Visibility = input.parse()?; let lookahead = input.lookahead1(); if lookahead.peek(Token![const]) && (input.peek2(Ident) || input.peek2(Token![_])) { let defaultness = false; let flexible_item = FlexibleItemConst::parse(attrs, vis, defaultness, input)?; Ok(ItemVerbatim::ConstFlexible(flexible_item)) } else if input.peek(Token![const]) || lookahead.peek(Token![async]) || lookahead.peek(Token![unsafe]) && !input.peek2(Token![impl]) || lookahead.peek(Token![extern]) || lookahead.peek(Token![fn]) { let defaultness = false; let flexible_item = FlexibleItemFn::parse(attrs, vis, defaultness, input)?; Ok(ItemVerbatim::FnFlexible(flexible_item)) } else if lookahead.peek(Token![default]) || input.peek(Token![unsafe]) || lookahead.peek(Token![impl]) { let defaultness = input.parse::>()?.is_some(); let unsafety = input.parse::>()?.is_some(); input.parse::()?; let has_generics = input.peek(Token![<]) && (input.peek2(Token![>]) || input.peek2(Token![#]) || (input.peek2(Ident) || input.peek2(Lifetime)) && (input.peek3(Token![:]) || input.peek3(Token![,]) || input.peek3(Token![>]) || input.peek3(Token![=])) || input.peek2(Token![const])); let mut generics: Generics = if has_generics { input.parse()? } else { Generics::default() }; let constness: ImplConstness = input.parse()?; let negative_impl = !input.peek2(token::Brace) && input.parse::>()?.is_some(); let first_ty: Type = input.parse()?; let (trait_, self_ty) = if input.parse::>()?.is_some() { (Some(first_ty), input.parse()?) } else { (None, first_ty) }; generics.where_clause = input.parse()?; let content; braced!(content in input); let inner_attrs = content.call(Attribute::parse_inner)?; attrs.extend(inner_attrs); let mut items = Vec::new(); while !content.is_empty() { items.push(content.parse()?); } Ok(ItemVerbatim::ImplFlexible(ImplFlexible { attrs, vis, defaultness, unsafety, generics, constness, negative_impl, trait_, self_ty, items, })) } else if lookahead.peek(Token![macro]) { input.parse::()?; let ident: Ident = input.parse()?; let args = if input.peek(token::Paren) { let paren_content; parenthesized!(paren_content in input); Some(paren_content.parse::()?) } else { None }; let brace_content; braced!(brace_content in input); let body: TokenStream = brace_content.parse()?; Ok(ItemVerbatim::Macro2(Macro2 { attrs, vis, ident, args, body, })) } else if lookahead.peek(Token![static]) { let flexible_item = FlexibleItemStatic::parse(attrs, vis, input)?; Ok(ItemVerbatim::StaticFlexible(flexible_item)) } else if lookahead.peek(Token![type]) { let defaultness = false; let flexible_item = FlexibleItemType::parse( attrs, vis, defaultness, input, WhereClauseLocation::BeforeEq, )?; Ok(ItemVerbatim::TypeFlexible(flexible_item)) } else if lookahead.peek(Token![use]) { input.parse::()?; let content; braced!(content in input); let trees = content.parse_terminated(RootUseTree::parse, Token![,])?; input.parse::()?; Ok(ItemVerbatim::UseBrace(UseBrace { attrs, vis, trees })) } else { Err(lookahead.error()) } } } let item: ItemVerbatim = match syn::parse2(tokens.clone()) { Ok(item) => item, Err(_) => unimplemented!("Item::Verbatim `{}`", tokens), }; match item { ItemVerbatim::Empty => { self.hardbreak(); } ItemVerbatim::Ellipsis => { self.word("..."); self.hardbreak(); } ItemVerbatim::ConstFlexible(item) => { self.flexible_item_const(&item); } ItemVerbatim::FnFlexible(item) => { self.flexible_item_fn(&item); } ItemVerbatim::ImplFlexible(item) => { self.outer_attrs(&item.attrs); self.cbox(INDENT); self.ibox(-INDENT); self.cbox(INDENT); self.visibility(&item.vis); if item.defaultness { self.word("default "); } if item.unsafety { self.word("unsafe "); } self.word("impl"); self.generics(&item.generics); self.end(); self.nbsp(); match item.constness { ImplConstness::None => {} ImplConstness::MaybeConst => self.word("?const "), ImplConstness::Const => self.word("const "), } if item.negative_impl { self.word("!"); } if let Some(trait_) = &item.trait_ { self.ty(trait_); self.space(); self.word("for "); } self.ty(&item.self_ty); self.end(); self.where_clause_for_body(&item.generics.where_clause); self.word("{"); self.hardbreak_if_nonempty(); self.inner_attrs(&item.attrs); for impl_item in &item.items { self.impl_item(impl_item); } self.offset(-INDENT); self.end(); self.word("}"); self.hardbreak(); } ItemVerbatim::Macro2(item) => { self.outer_attrs(&item.attrs); self.visibility(&item.vis); self.word("macro "); self.ident(&item.ident); if let Some(args) = &item.args { self.word("("); self.cbox(INDENT); self.zerobreak(); self.ibox(0); self.macro_rules_tokens(args.clone(), true); self.end(); self.zerobreak(); self.offset(-INDENT); self.end(); self.word(")"); } self.word(" {"); if !item.body.is_empty() { self.neverbreak(); self.cbox(INDENT); self.hardbreak(); self.ibox(0); self.macro_rules_tokens(item.body.clone(), false); self.end(); self.hardbreak(); self.offset(-INDENT); self.end(); } self.word("}"); self.hardbreak(); } ItemVerbatim::StaticFlexible(item) => { self.flexible_item_static(&item); } ItemVerbatim::TypeFlexible(item) => { self.flexible_item_type(&item); } ItemVerbatim::UseBrace(item) => { self.outer_attrs(&item.attrs); self.visibility(&item.vis); self.word("use "); if item.trees.len() == 1 { self.word("::"); self.use_tree(&item.trees[0].inner); } else { self.cbox(INDENT); self.word("{"); self.zerobreak(); self.ibox(0); for use_tree in item.trees.iter().delimited() { if use_tree.leading_colon.is_some() { self.word("::"); } self.use_tree(&use_tree.inner); if !use_tree.is_last { self.word(","); let mut use_tree = &use_tree.inner; while let UseTree::Path(use_path) = use_tree { use_tree = &use_path.tree; } if let UseTree::Group(_) = use_tree { self.hardbreak(); } else { self.space(); } } } self.end(); self.trailing_comma(true); self.offset(-INDENT); self.word("}"); self.end(); } self.word(";"); self.hardbreak(); } } } fn use_tree(&mut self, use_tree: &UseTree) { match use_tree { UseTree::Path(use_path) => self.use_path(use_path), UseTree::Name(use_name) => self.use_name(use_name), UseTree::Rename(use_rename) => self.use_rename(use_rename), UseTree::Glob(use_glob) => self.use_glob(use_glob), UseTree::Group(use_group) => self.use_group(use_group), } } fn use_path(&mut self, use_path: &UsePath) { self.ident(&use_path.ident); self.word("::"); self.use_tree(&use_path.tree); } fn use_name(&mut self, use_name: &UseName) { self.ident(&use_name.ident); } fn use_rename(&mut self, use_rename: &UseRename) { self.ident(&use_rename.ident); self.word(" as "); self.ident(&use_rename.rename); } fn use_glob(&mut self, use_glob: &UseGlob) { let _ = use_glob; self.word("*"); } fn use_group(&mut self, use_group: &UseGroup) { if use_group.items.is_empty() { self.word("{}"); } else if use_group.items.len() == 1 && match &use_group.items[0] { UseTree::Name(use_name) => use_name.ident != "self", UseTree::Rename(use_rename) => use_rename.ident != "self", _ => true, } { self.use_tree(&use_group.items[0]); } else { self.cbox(INDENT); self.word("{"); self.zerobreak(); self.ibox(0); for use_tree in use_group.items.iter().delimited() { self.use_tree(&use_tree); if !use_tree.is_last { self.word(","); let mut use_tree = *use_tree; while let UseTree::Path(use_path) = use_tree { use_tree = &use_path.tree; } if let UseTree::Group(_) = use_tree { self.hardbreak(); } else { self.space(); } } } self.end(); self.trailing_comma(true); self.offset(-INDENT); self.word("}"); self.end(); } } fn foreign_item(&mut self, foreign_item: &ForeignItem) { match foreign_item { #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] ForeignItem::Fn(item) => self.foreign_item_fn(item), ForeignItem::Static(item) => self.foreign_item_static(item), ForeignItem::Type(item) => self.foreign_item_type(item), ForeignItem::Macro(item) => self.foreign_item_macro(item), ForeignItem::Verbatim(item) => self.foreign_item_verbatim(item), _ => unimplemented!("unknown ForeignItem"), } } fn foreign_item_fn(&mut self, foreign_item: &ForeignItemFn) { self.outer_attrs(&foreign_item.attrs); self.cbox(INDENT); self.visibility(&foreign_item.vis); self.signature( &foreign_item.sig, #[cfg(feature = "verbatim")] &verbatim::Safety::Disallowed, ); self.where_clause_semi(&foreign_item.sig.generics.where_clause); self.end(); self.hardbreak(); } fn foreign_item_static(&mut self, foreign_item: &ForeignItemStatic) { self.outer_attrs(&foreign_item.attrs); self.cbox(0); self.visibility(&foreign_item.vis); self.word("static "); self.static_mutability(&foreign_item.mutability); self.ident(&foreign_item.ident); self.word(": "); self.ty(&foreign_item.ty); self.word(";"); self.end(); self.hardbreak(); } fn foreign_item_type(&mut self, foreign_item: &ForeignItemType) { self.outer_attrs(&foreign_item.attrs); self.cbox(0); self.visibility(&foreign_item.vis); self.word("type "); self.ident(&foreign_item.ident); self.generics(&foreign_item.generics); self.word(";"); self.end(); self.hardbreak(); } fn foreign_item_macro(&mut self, foreign_item: &ForeignItemMacro) { self.outer_attrs(&foreign_item.attrs); let semicolon = mac::requires_semi(&foreign_item.mac.delimiter); self.mac(&foreign_item.mac, None, semicolon); self.hardbreak(); } #[cfg(not(feature = "verbatim"))] fn foreign_item_verbatim(&mut self, foreign_item: &TokenStream) { if !foreign_item.is_empty() { unimplemented!("ForeignItem::Verbatim `{}`", foreign_item); } self.hardbreak(); } #[cfg(feature = "verbatim")] fn foreign_item_verbatim(&mut self, tokens: &TokenStream) { use syn::parse::{Parse, ParseStream, Result}; use syn::{Abi, Attribute, Token, Visibility}; use verbatim::{ kw, FlexibleItemFn, FlexibleItemStatic, FlexibleItemType, WhereClauseLocation, }; enum ForeignItemVerbatim { Empty, Ellipsis, FnFlexible(FlexibleItemFn), StaticFlexible(FlexibleItemStatic), TypeFlexible(FlexibleItemType), } fn peek_signature(input: ParseStream) -> bool { let fork = input.fork(); fork.parse::>().is_ok() && fork.parse::>().is_ok() && ((fork.peek(kw::safe) && fork.parse::().is_ok()) || fork.parse::>().is_ok()) && fork.parse::>().is_ok() && fork.peek(Token![fn]) } impl Parse for ForeignItemVerbatim { fn parse(input: ParseStream) -> Result { if input.is_empty() { return Ok(ForeignItemVerbatim::Empty); } else if input.peek(Token![...]) { input.parse::()?; return Ok(ForeignItemVerbatim::Ellipsis); } let attrs = input.call(Attribute::parse_outer)?; let vis: Visibility = input.parse()?; let defaultness = false; let lookahead = input.lookahead1(); if lookahead.peek(Token![fn]) || peek_signature(input) { let flexible_item = FlexibleItemFn::parse(attrs, vis, defaultness, input)?; Ok(ForeignItemVerbatim::FnFlexible(flexible_item)) } else if lookahead.peek(Token![static]) || ((input.peek(Token![unsafe]) || input.peek(kw::safe)) && input.peek2(Token![static])) { let flexible_item = FlexibleItemStatic::parse(attrs, vis, input)?; Ok(ForeignItemVerbatim::StaticFlexible(flexible_item)) } else if lookahead.peek(Token![type]) { let flexible_item = FlexibleItemType::parse( attrs, vis, defaultness, input, WhereClauseLocation::Both, )?; Ok(ForeignItemVerbatim::TypeFlexible(flexible_item)) } else { Err(lookahead.error()) } } } let foreign_item: ForeignItemVerbatim = match syn::parse2(tokens.clone()) { Ok(foreign_item) => foreign_item, Err(_) => unimplemented!("ForeignItem::Verbatim `{}`", tokens), }; match foreign_item { ForeignItemVerbatim::Empty => { self.hardbreak(); } ForeignItemVerbatim::Ellipsis => { self.word("..."); self.hardbreak(); } ForeignItemVerbatim::FnFlexible(foreign_item) => { self.flexible_item_fn(&foreign_item); } ForeignItemVerbatim::StaticFlexible(foreign_item) => { self.flexible_item_static(&foreign_item); } ForeignItemVerbatim::TypeFlexible(foreign_item) => { self.flexible_item_type(&foreign_item); } } } fn trait_item(&mut self, trait_item: &TraitItem) { match trait_item { #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] TraitItem::Const(item) => self.trait_item_const(item), TraitItem::Fn(item) => self.trait_item_fn(item), TraitItem::Type(item) => self.trait_item_type(item), TraitItem::Macro(item) => self.trait_item_macro(item), TraitItem::Verbatim(item) => self.trait_item_verbatim(item), _ => unimplemented!("unknown TraitItem"), } } fn trait_item_const(&mut self, trait_item: &TraitItemConst) { self.outer_attrs(&trait_item.attrs); self.cbox(0); self.word("const "); self.ident(&trait_item.ident); self.generics(&trait_item.generics); self.word(": "); self.ty(&trait_item.ty); if let Some((_eq_token, default)) = &trait_item.default { self.word(" = "); self.neverbreak(); self.expr(default, FixupContext::NONE); } self.word(";"); self.end(); self.hardbreak(); } fn trait_item_fn(&mut self, trait_item: &TraitItemFn) { self.outer_attrs(&trait_item.attrs); self.cbox(INDENT); self.signature( &trait_item.sig, #[cfg(feature = "verbatim")] &verbatim::Safety::Disallowed, ); if let Some(block) = &trait_item.default { self.where_clause_for_body(&trait_item.sig.generics.where_clause); self.word("{"); self.hardbreak_if_nonempty(); self.inner_attrs(&trait_item.attrs); for stmt in block.stmts.iter().delimited() { self.stmt(&stmt, stmt.is_last); } self.offset(-INDENT); self.end(); self.word("}"); } else { self.where_clause_semi(&trait_item.sig.generics.where_clause); self.end(); } self.hardbreak(); } fn trait_item_type(&mut self, trait_item: &TraitItemType) { self.outer_attrs(&trait_item.attrs); self.cbox(INDENT); self.word("type "); self.ident(&trait_item.ident); self.generics(&trait_item.generics); for bound in trait_item.bounds.iter().delimited() { if bound.is_first { self.word(": "); } else { self.space(); self.word("+ "); } self.type_param_bound(&bound); } if let Some((_eq_token, default)) = &trait_item.default { self.word(" = "); self.neverbreak(); self.ibox(-INDENT); self.ty(default); self.end(); } self.where_clause_oneline_semi(&trait_item.generics.where_clause); self.end(); self.hardbreak(); } fn trait_item_macro(&mut self, trait_item: &TraitItemMacro) { self.outer_attrs(&trait_item.attrs); let semicolon = mac::requires_semi(&trait_item.mac.delimiter); self.mac(&trait_item.mac, None, semicolon); self.hardbreak(); } #[cfg(not(feature = "verbatim"))] fn trait_item_verbatim(&mut self, trait_item: &TokenStream) { if !trait_item.is_empty() { unimplemented!("TraitItem::Verbatim `{}`", trait_item); } self.hardbreak(); } #[cfg(feature = "verbatim")] fn trait_item_verbatim(&mut self, tokens: &TokenStream) { use syn::parse::{Parse, ParseStream, Result}; use syn::{Attribute, Ident, Token, Visibility}; use verbatim::{FlexibleItemConst, FlexibleItemType, WhereClauseLocation}; enum TraitItemVerbatim { Empty, Ellipsis, ConstFlexible(FlexibleItemConst), TypeFlexible(FlexibleItemType), PubOrDefault(PubOrDefaultTraitItem), } struct PubOrDefaultTraitItem { attrs: Vec, vis: Visibility, defaultness: bool, trait_item: TraitItem, } impl Parse for TraitItemVerbatim { fn parse(input: ParseStream) -> Result { if input.is_empty() { return Ok(TraitItemVerbatim::Empty); } else if input.peek(Token![...]) { input.parse::()?; return Ok(TraitItemVerbatim::Ellipsis); } let attrs = input.call(Attribute::parse_outer)?; let vis: Visibility = input.parse()?; let defaultness = input.parse::>()?.is_some(); let lookahead = input.lookahead1(); if lookahead.peek(Token![const]) && (input.peek2(Ident) || input.peek2(Token![_])) { let flexible_item = FlexibleItemConst::parse(attrs, vis, defaultness, input)?; Ok(TraitItemVerbatim::ConstFlexible(flexible_item)) } else if lookahead.peek(Token![type]) { let flexible_item = FlexibleItemType::parse( attrs, vis, defaultness, input, WhereClauseLocation::AfterEq, )?; Ok(TraitItemVerbatim::TypeFlexible(flexible_item)) } else if (input.peek(Token![const]) || lookahead.peek(Token![async]) || lookahead.peek(Token![unsafe]) || lookahead.peek(Token![extern]) || lookahead.peek(Token![fn])) && (!matches!(vis, Visibility::Inherited) || defaultness) { Ok(TraitItemVerbatim::PubOrDefault(PubOrDefaultTraitItem { attrs, vis, defaultness, trait_item: input.parse()?, })) } else { Err(lookahead.error()) } } } let impl_item: TraitItemVerbatim = match syn::parse2(tokens.clone()) { Ok(impl_item) => impl_item, Err(_) => unimplemented!("TraitItem::Verbatim `{}`", tokens), }; match impl_item { TraitItemVerbatim::Empty => { self.hardbreak(); } TraitItemVerbatim::Ellipsis => { self.word("..."); self.hardbreak(); } TraitItemVerbatim::ConstFlexible(trait_item) => { self.flexible_item_const(&trait_item); } TraitItemVerbatim::TypeFlexible(trait_item) => { self.flexible_item_type(&trait_item); } TraitItemVerbatim::PubOrDefault(trait_item) => { self.outer_attrs(&trait_item.attrs); self.visibility(&trait_item.vis); if trait_item.defaultness { self.word("default "); } self.trait_item(&trait_item.trait_item); } } } fn impl_item(&mut self, impl_item: &ImplItem) { match impl_item { #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] ImplItem::Const(item) => self.impl_item_const(item), ImplItem::Fn(item) => self.impl_item_fn(item), ImplItem::Type(item) => self.impl_item_type(item), ImplItem::Macro(item) => self.impl_item_macro(item), ImplItem::Verbatim(item) => self.impl_item_verbatim(item), _ => unimplemented!("unknown ImplItem"), } } fn impl_item_const(&mut self, impl_item: &ImplItemConst) { self.outer_attrs(&impl_item.attrs); self.cbox(0); self.visibility(&impl_item.vis); if impl_item.defaultness.is_some() { self.word("default "); } self.word("const "); self.ident(&impl_item.ident); self.generics(&impl_item.generics); self.word(": "); self.ty(&impl_item.ty); self.word(" = "); self.neverbreak(); self.expr(&impl_item.expr, FixupContext::NONE); self.word(";"); self.end(); self.hardbreak(); } fn impl_item_fn(&mut self, impl_item: &ImplItemFn) { self.outer_attrs(&impl_item.attrs); self.cbox(INDENT); self.visibility(&impl_item.vis); if impl_item.defaultness.is_some() { self.word("default "); } self.signature( &impl_item.sig, #[cfg(feature = "verbatim")] &verbatim::Safety::Disallowed, ); self.where_clause_for_body(&impl_item.sig.generics.where_clause); self.word("{"); self.hardbreak_if_nonempty(); self.inner_attrs(&impl_item.attrs); for stmt in impl_item.block.stmts.iter().delimited() { self.stmt(&stmt, stmt.is_last); } self.offset(-INDENT); self.end(); self.word("}"); self.hardbreak(); } fn impl_item_type(&mut self, impl_item: &ImplItemType) { self.outer_attrs(&impl_item.attrs); self.cbox(INDENT); self.visibility(&impl_item.vis); if impl_item.defaultness.is_some() { self.word("default "); } self.word("type "); self.ident(&impl_item.ident); self.generics(&impl_item.generics); self.word(" = "); self.neverbreak(); self.ibox(-INDENT); self.ty(&impl_item.ty); self.end(); self.where_clause_oneline_semi(&impl_item.generics.where_clause); self.end(); self.hardbreak(); } fn impl_item_macro(&mut self, impl_item: &ImplItemMacro) { self.outer_attrs(&impl_item.attrs); let semicolon = mac::requires_semi(&impl_item.mac.delimiter); self.mac(&impl_item.mac, None, semicolon); self.hardbreak(); } #[cfg(not(feature = "verbatim"))] fn impl_item_verbatim(&mut self, impl_item: &TokenStream) { if !impl_item.is_empty() { unimplemented!("ImplItem::Verbatim `{}`", impl_item); } self.hardbreak(); } #[cfg(feature = "verbatim")] fn impl_item_verbatim(&mut self, tokens: &TokenStream) { use syn::parse::{Parse, ParseStream, Result}; use syn::{Attribute, Ident, Token, Visibility}; use verbatim::{FlexibleItemConst, FlexibleItemFn, FlexibleItemType, WhereClauseLocation}; enum ImplItemVerbatim { Empty, Ellipsis, ConstFlexible(FlexibleItemConst), FnFlexible(FlexibleItemFn), TypeFlexible(FlexibleItemType), } impl Parse for ImplItemVerbatim { fn parse(input: ParseStream) -> Result { if input.is_empty() { return Ok(ImplItemVerbatim::Empty); } else if input.peek(Token![...]) { input.parse::()?; return Ok(ImplItemVerbatim::Ellipsis); } let attrs = input.call(Attribute::parse_outer)?; let vis: Visibility = input.parse()?; let defaultness = input.parse::>()?.is_some(); let lookahead = input.lookahead1(); if lookahead.peek(Token![const]) && (input.peek2(Ident) || input.peek2(Token![_])) { let flexible_item = FlexibleItemConst::parse(attrs, vis, defaultness, input)?; Ok(ImplItemVerbatim::ConstFlexible(flexible_item)) } else if input.peek(Token![const]) || lookahead.peek(Token![async]) || lookahead.peek(Token![unsafe]) || lookahead.peek(Token![extern]) || lookahead.peek(Token![fn]) { let flexible_item = FlexibleItemFn::parse(attrs, vis, defaultness, input)?; Ok(ImplItemVerbatim::FnFlexible(flexible_item)) } else if lookahead.peek(Token![type]) { let flexible_item = FlexibleItemType::parse( attrs, vis, defaultness, input, WhereClauseLocation::AfterEq, )?; Ok(ImplItemVerbatim::TypeFlexible(flexible_item)) } else { Err(lookahead.error()) } } } let impl_item: ImplItemVerbatim = match syn::parse2(tokens.clone()) { Ok(impl_item) => impl_item, Err(_) => unimplemented!("ImplItem::Verbatim `{}`", tokens), }; match impl_item { ImplItemVerbatim::Empty => { self.hardbreak(); } ImplItemVerbatim::Ellipsis => { self.word("..."); self.hardbreak(); } ImplItemVerbatim::ConstFlexible(impl_item) => { self.flexible_item_const(&impl_item); } ImplItemVerbatim::FnFlexible(impl_item) => { self.flexible_item_fn(&impl_item); } ImplItemVerbatim::TypeFlexible(impl_item) => { self.flexible_item_type(&impl_item); } } } fn signature( &mut self, signature: &Signature, #[cfg(feature = "verbatim")] safety: &verbatim::Safety, ) { if signature.constness.is_some() { self.word("const "); } if signature.asyncness.is_some() { self.word("async "); } #[cfg(feature = "verbatim")] { if let verbatim::Safety::Disallowed = safety { if signature.unsafety.is_some() { self.word("unsafe "); } } else { self.safety(safety); } } #[cfg(not(feature = "verbatim"))] { if signature.unsafety.is_some() { self.word("unsafe "); } } if let Some(abi) = &signature.abi { self.abi(abi); } self.word("fn "); self.ident(&signature.ident); self.generics(&signature.generics); self.word("("); self.neverbreak(); self.cbox(0); self.zerobreak(); for input in signature.inputs.iter().delimited() { self.fn_arg(&input); let is_last = input.is_last && signature.variadic.is_none(); self.trailing_comma(is_last); } if let Some(variadic) = &signature.variadic { self.variadic(variadic); self.zerobreak(); } self.offset(-INDENT); self.end(); self.word(")"); self.cbox(-INDENT); self.return_type(&signature.output); self.end(); } fn fn_arg(&mut self, fn_arg: &FnArg) { match fn_arg { FnArg::Receiver(receiver) => self.receiver(receiver), FnArg::Typed(pat_type) => self.pat_type(pat_type), } } fn receiver(&mut self, receiver: &Receiver) { self.outer_attrs(&receiver.attrs); if let Some((_ampersand, lifetime)) = &receiver.reference { self.word("&"); if let Some(lifetime) = lifetime { self.lifetime(lifetime); self.nbsp(); } } if receiver.mutability.is_some() { self.word("mut "); } self.word("self"); if receiver.colon_token.is_some() { self.word(": "); self.ty(&receiver.ty); } else { let consistent = match (&receiver.reference, &receiver.mutability, &*receiver.ty) { (Some(_), mutability, Type::Reference(ty)) => { mutability.is_some() == ty.mutability.is_some() && match &*ty.elem { Type::Path(ty) => ty.qself.is_none() && ty.path.is_ident("Self"), _ => false, } } (None, _, Type::Path(ty)) => ty.qself.is_none() && ty.path.is_ident("Self"), _ => false, }; if !consistent { self.word(": "); self.ty(&receiver.ty); } } } fn variadic(&mut self, variadic: &Variadic) { self.outer_attrs(&variadic.attrs); if let Some((pat, _colon)) = &variadic.pat { self.pat(pat); self.word(": "); } self.word("..."); } fn static_mutability(&mut self, mutability: &StaticMutability) { match mutability { #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] StaticMutability::Mut(_) => self.word("mut "), StaticMutability::None => {} _ => unimplemented!("unknown StaticMutability"), } } } #[cfg(feature = "verbatim")] mod verbatim { use crate::algorithm::Printer; use crate::fixup::FixupContext; use crate::iter::IterDelimited; use crate::INDENT; use syn::ext::IdentExt; use syn::parse::{Parse, ParseStream, Result}; use syn::{ braced, token, Attribute, Block, Expr, Generics, Ident, Signature, StaticMutability, Stmt, Token, Type, TypeParamBound, Visibility, WhereClause, }; pub mod kw { syn::custom_keyword!(safe); } pub struct FlexibleItemConst { pub attrs: Vec, pub vis: Visibility, pub defaultness: bool, pub ident: Ident, pub generics: Generics, pub ty: Type, pub value: Option, } pub struct FlexibleItemFn { pub attrs: Vec, pub vis: Visibility, pub defaultness: bool, pub safety: Safety, pub sig: Signature, pub body: Option>, } pub struct FlexibleItemStatic { pub attrs: Vec, pub vis: Visibility, pub safety: Safety, pub mutability: StaticMutability, pub ident: Ident, pub ty: Option, pub expr: Option, } pub struct FlexibleItemType { pub attrs: Vec, pub vis: Visibility, pub defaultness: bool, pub ident: Ident, pub generics: Generics, pub bounds: Vec, pub definition: Option, pub where_clause_after_eq: Option, } pub enum Safety { Unsafe, Safe, Default, Disallowed, } pub enum WhereClauseLocation { // type Ty where T: 'static = T; BeforeEq, // type Ty = T where T: 'static; AfterEq, // TODO: goes away once the migration period on rust-lang/rust#89122 is over Both, } impl FlexibleItemConst { pub fn parse( attrs: Vec, vis: Visibility, defaultness: bool, input: ParseStream, ) -> Result { input.parse::()?; let ident = input.call(Ident::parse_any)?; let mut generics: Generics = input.parse()?; input.parse::()?; let ty: Type = input.parse()?; let value = if input.parse::>()?.is_some() { let expr: Expr = input.parse()?; Some(expr) } else { None }; generics.where_clause = input.parse()?; input.parse::()?; Ok(FlexibleItemConst { attrs, vis, defaultness, ident, generics, ty, value, }) } } impl FlexibleItemFn { pub fn parse( mut attrs: Vec, vis: Visibility, defaultness: bool, input: ParseStream, ) -> Result { let constness: Option = input.parse()?; let asyncness: Option = input.parse()?; let safety: Safety = input.parse()?; let lookahead = input.lookahead1(); let sig: Signature = if lookahead.peek(Token![extern]) || lookahead.peek(Token![fn]) { input.parse()? } else { return Err(lookahead.error()); }; let lookahead = input.lookahead1(); let body = if lookahead.peek(Token![;]) { input.parse::()?; None } else if lookahead.peek(token::Brace) { let content; braced!(content in input); attrs.extend(content.call(Attribute::parse_inner)?); Some(content.call(Block::parse_within)?) } else { return Err(lookahead.error()); }; Ok(FlexibleItemFn { attrs, vis, defaultness, safety, sig: Signature { constness, asyncness, unsafety: None, ..sig }, body, }) } } impl FlexibleItemStatic { pub fn parse(attrs: Vec, vis: Visibility, input: ParseStream) -> Result { let safety: Safety = input.parse()?; input.parse::()?; let mutability: StaticMutability = input.parse()?; let ident = input.parse()?; let lookahead = input.lookahead1(); let has_type = lookahead.peek(Token![:]); let has_expr = lookahead.peek(Token![=]); if !has_type && !has_expr { return Err(lookahead.error()); } let ty: Option = if has_type { input.parse::()?; input.parse().map(Some)? } else { None }; let expr: Option = if input.parse::>()?.is_some() { input.parse().map(Some)? } else { None }; input.parse::()?; Ok(FlexibleItemStatic { attrs, vis, safety, mutability, ident, ty, expr, }) } } impl FlexibleItemType { pub fn parse( attrs: Vec, vis: Visibility, defaultness: bool, input: ParseStream, where_clause_location: WhereClauseLocation, ) -> Result { input.parse::()?; let ident: Ident = input.parse()?; let mut generics: Generics = input.parse()?; let mut bounds = Vec::new(); if input.parse::>()?.is_some() { loop { if input.peek(Token![where]) || input.peek(Token![=]) || input.peek(Token![;]) { break; } bounds.push(input.parse::()?); if input.peek(Token![where]) || input.peek(Token![=]) || input.peek(Token![;]) { break; } input.parse::()?; } } match where_clause_location { WhereClauseLocation::BeforeEq | WhereClauseLocation::Both => { generics.where_clause = input.parse()?; } WhereClauseLocation::AfterEq => {} } let definition = if input.parse::>()?.is_some() { Some(input.parse()?) } else { None }; let where_clause_after_eq = match where_clause_location { WhereClauseLocation::AfterEq | WhereClauseLocation::Both if generics.where_clause.is_none() => { input.parse()? } _ => None, }; input.parse::()?; Ok(FlexibleItemType { attrs, vis, defaultness, ident, generics, bounds, definition, where_clause_after_eq, }) } } impl Parse for Safety { fn parse(input: ParseStream) -> Result { if input.peek(Token![unsafe]) { input.parse::()?; Ok(Safety::Unsafe) } else if input.peek(kw::safe) { input.parse::()?; Ok(Safety::Safe) } else { Ok(Safety::Default) } } } impl Printer { pub fn flexible_item_const(&mut self, item: &FlexibleItemConst) { self.outer_attrs(&item.attrs); self.cbox(INDENT); self.visibility(&item.vis); if item.defaultness { self.word("default "); } self.word("const "); self.ident(&item.ident); self.generics(&item.generics); self.word(": "); self.cbox(-INDENT); self.ty(&item.ty); self.end(); if let Some(value) = &item.value { self.word(" = "); self.neverbreak(); self.ibox(-INDENT); self.expr(value, FixupContext::NONE); self.end(); } self.where_clause_oneline_semi(&item.generics.where_clause); self.end(); self.hardbreak(); } pub fn flexible_item_fn(&mut self, item: &FlexibleItemFn) { self.outer_attrs(&item.attrs); self.cbox(INDENT); self.visibility(&item.vis); if item.defaultness { self.word("default "); } self.signature(&item.sig, &item.safety); if let Some(body) = &item.body { self.where_clause_for_body(&item.sig.generics.where_clause); self.word("{"); self.hardbreak_if_nonempty(); self.inner_attrs(&item.attrs); for stmt in body.iter().delimited() { self.stmt(&stmt, stmt.is_last); } self.offset(-INDENT); self.end(); self.word("}"); } else { self.where_clause_semi(&item.sig.generics.where_clause); self.end(); } self.hardbreak(); } pub fn flexible_item_static(&mut self, item: &FlexibleItemStatic) { self.outer_attrs(&item.attrs); self.cbox(0); self.visibility(&item.vis); self.safety(&item.safety); self.word("static "); self.static_mutability(&item.mutability); self.ident(&item.ident); if let Some(ty) = &item.ty { self.word(": "); self.ty(ty); } if let Some(expr) = &item.expr { self.word(" = "); self.neverbreak(); self.expr(expr, FixupContext::NONE); } self.word(";"); self.end(); self.hardbreak(); } pub fn flexible_item_type(&mut self, item: &FlexibleItemType) { self.outer_attrs(&item.attrs); self.cbox(INDENT); self.visibility(&item.vis); if item.defaultness { self.word("default "); } self.word("type "); self.ident(&item.ident); self.generics(&item.generics); for bound in item.bounds.iter().delimited() { if bound.is_first { self.word(": "); } else { self.space(); self.word("+ "); } self.type_param_bound(&bound); } if let Some(definition) = &item.definition { self.where_clause_oneline(&item.generics.where_clause); self.word("= "); self.neverbreak(); self.ibox(-INDENT); self.ty(definition); self.end(); self.where_clause_oneline_semi(&item.where_clause_after_eq); } else { self.where_clause_oneline_semi(&item.generics.where_clause); } self.end(); self.hardbreak(); } pub fn safety(&mut self, safety: &Safety) { match safety { Safety::Unsafe => self.word("unsafe "), Safety::Safe => self.word("safe "), Safety::Default => {} Safety::Disallowed => unreachable!(), } } } } prettyplease-0.2.37/src/iter.rs000064400000000000000000000016541046102023000145440ustar 00000000000000use std::iter::Peekable; use std::ops::Deref; pub struct Delimited { is_first: bool, iter: Peekable, } pub trait IterDelimited: Iterator + Sized { fn delimited(self) -> Delimited { Delimited { is_first: true, iter: self.peekable(), } } } impl IterDelimited for I {} pub struct IteratorItem { value: T, pub is_first: bool, pub is_last: bool, } impl Iterator for Delimited { type Item = IteratorItem; fn next(&mut self) -> Option { let item = IteratorItem { value: self.iter.next()?, is_first: self.is_first, is_last: self.iter.peek().is_none(), }; self.is_first = false; Some(item) } } impl Deref for IteratorItem { type Target = T; fn deref(&self) -> &Self::Target { &self.value } } prettyplease-0.2.37/src/lib.rs000064400000000000000000000412711046102023000143460ustar 00000000000000//! [![github]](https://github.com/dtolnay/prettyplease) [![crates-io]](https://crates.io/crates/prettyplease) [![docs-rs]](https://docs.rs/prettyplease) //! //! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github //! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust //! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs //! //!
//! //! **prettyplease::unparse** — a minimal `syn` syntax tree pretty-printer //! //!
//! //! # Overview //! //! This is a pretty-printer to turn a `syn` syntax tree into a `String` of //! well-formatted source code. In contrast to rustfmt, this library is intended //! to be suitable for arbitrary generated code. //! //! Rustfmt prioritizes high-quality output that is impeccable enough that you'd //! be comfortable spending your career staring at its output — but that //! means some heavyweight algorithms, and it has a tendency to bail out on code //! that is hard to format (for example [rustfmt#3697], and there are dozens //! more issues like it). That's not necessarily a big deal for human-generated //! code because when code gets highly nested, the human will naturally be //! inclined to refactor into more easily formattable code. But for generated //! code, having the formatter just give up leaves it totally unreadable. //! //! [rustfmt#3697]: https://github.com/rust-lang/rustfmt/issues/3697 //! //! This library is designed using the simplest possible algorithm and data //! structures that can deliver about 95% of the quality of rustfmt-formatted //! output. In my experience testing real-world code, approximately 97-98% of //! output lines come out identical between rustfmt's formatting and this //! crate's. The rest have slightly different linebreak decisions, but still //! clearly follow the dominant modern Rust style. //! //! The tradeoffs made by this crate are a good fit for generated code that you //! will *not* spend your career staring at. For example, the output of //! `bindgen`, or the output of `cargo-expand`. In those cases it's more //! important that the whole thing be formattable without the formatter giving //! up, than that it be flawless. //! //!
//! //! # Feature matrix //! //! Here are a few superficial comparisons of this crate against the AST //! pretty-printer built into rustc, and rustfmt. The sections below go into //! more detail comparing the output of each of these libraries. //! //! | | prettyplease | rustc | rustfmt | //! |:---|:---:|:---:|:---:| //! | non-pathological behavior on big or generated code | 💚 | ❌ | ❌ | //! | idiomatic modern formatting ("locally indistinguishable from rustfmt") | 💚 | ❌ | 💚 | //! | throughput | 60 MB/s | 39 MB/s | 2.8 MB/s | //! | number of dependencies | 3 | 72 | 66 | //! | compile time including dependencies | 2.4 sec | 23.1 sec | 29.8 sec | //! | buildable using a stable Rust compiler | 💚 | ❌ | ❌ | //! | published to crates.io | 💚 | ❌ | ❌ | //! | extensively configurable output | ❌ | ❌ | 💚 | //! | intended to accommodate hand-maintained source code | ❌ | ❌ | 💚 | //! //!
//! //! # Comparison to rustfmt //! //! - [input.rs](https://github.com/dtolnay/prettyplease/blob/0.1.0/examples/input.rs) //! - [output.prettyplease.rs](https://github.com/dtolnay/prettyplease/blob/0.1.0/examples/output.prettyplease.rs) //! - [output.rustfmt.rs](https://github.com/dtolnay/prettyplease/blob/0.1.0/examples/output.rustfmt.rs) //! //! If you weren't told which output file is which, it would be practically //! impossible to tell — **except** for line 435 in the rustfmt output, //! which is more than 1000 characters long because rustfmt just gave up //! formatting that part of the file: //! //! ``` //! # const _: &str = stringify! {{{ //! match segments[5] { //! 0 => write!(f, "::{}", ipv4), //! 0xffff => write!(f, "::ffff:{}", ipv4), //! _ => unreachable!(), //! } //! } else { # [derive (Copy , Clone , Default)] struct Span { start : usize , len : usize , } let zeroes = { let mut longest = Span :: default () ; let mut current = Span :: default () ; for (i , & segment) in segments . iter () . enumerate () { if segment == 0 { if current . len == 0 { current . start = i ; } current . len += 1 ; if current . len > longest . len { longest = current ; } } else { current = Span :: default () ; } } longest } ; # [doc = " Write a colon-separated part of the address"] # [inline] fn fmt_subslice (f : & mut fmt :: Formatter < '_ > , chunk : & [u16]) -> fmt :: Result { if let Some ((first , tail)) = chunk . split_first () { write ! (f , "{:x}" , first) ? ; for segment in tail { f . write_char (':') ? ; write ! (f , "{:x}" , segment) ? ; } } Ok (()) } if zeroes . len > 1 { fmt_subslice (f , & segments [.. zeroes . start]) ? ; f . write_str ("::") ? ; fmt_subslice (f , & segments [zeroes . start + zeroes . len ..]) } else { fmt_subslice (f , & segments) } } //! } else { //! const IPV6_BUF_LEN: usize = (4 * 8) + 7; //! let mut buf = [0u8; IPV6_BUF_LEN]; //! let mut buf_slice = &mut buf[..]; //! # }}; //! ``` //! //! This is a pretty typical manifestation of rustfmt bailing out in generated //! code — a chunk of the input ends up on one line. The other //! manifestation is that you're working on some code, running rustfmt on save //! like a conscientious developer, but after a while notice it isn't doing //! anything. You introduce an intentional formatting issue, like a stray indent //! or semicolon, and run rustfmt to check your suspicion. Nope, it doesn't get //! cleaned up — rustfmt is just not formatting the part of the file you //! are working on. //! //! The prettyplease library is designed to have no pathological cases that //! force a bail out; the entire input you give it will get formatted in some //! "good enough" form. //! //! Separately, rustfmt can be problematic to integrate into projects. It's //! written using rustc's internal syntax tree, so it can't be built by a stable //! compiler. Its releases are not regularly published to crates.io, so in Cargo //! builds you'd need to depend on it as a git dependency, which precludes //! publishing your crate to crates.io also. You can shell out to a `rustfmt` //! binary, but that'll be whatever rustfmt version is installed on each //! developer's system (if any), which can lead to spurious diffs in checked-in //! generated code formatted by different versions. In contrast prettyplease is //! designed to be easy to pull in as a library, and compiles fast. //! //!
//! //! # Comparison to rustc_ast_pretty //! //! - [input.rs](https://github.com/dtolnay/prettyplease/blob/0.1.0/examples/input.rs) //! - [output.prettyplease.rs](https://github.com/dtolnay/prettyplease/blob/0.1.0/examples/output.prettyplease.rs) //! - [output.rustc.rs](https://github.com/dtolnay/prettyplease/blob/0.1.0/examples/output.rustc.rs) //! //! This is the pretty-printer that gets used when rustc prints source code, //! such as `rustc -Zunpretty=expanded`. It's used also by the standard //! library's `stringify!` when stringifying an interpolated macro_rules AST //! fragment, like an $:expr, and transitively by `dbg!` and many macros in the //! ecosystem. //! //! Rustc's formatting is mostly okay, but does not hew closely to the dominant //! contemporary style of Rust formatting. Some things wouldn't ever be written //! on one line, like this `match` expression, and certainly not with a comma in //! front of the closing brace: //! //! ``` //! # const _: &str = stringify! { //! fn eq(&self, other: &IpAddr) -> bool { //! match other { IpAddr::V4(v4) => self == v4, IpAddr::V6(_) => false, } //! } //! # }; //! ``` //! //! Some places use non-multiple-of-4 indentation, which is definitely not the //! norm: //! //! ``` //! # const _: &str = stringify! { //! pub const fn to_ipv6_mapped(&self) -> Ipv6Addr { //! let [a, b, c, d] = self.octets(); //! Ipv6Addr{inner: //! c::in6_addr{s6_addr: //! [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, //! 0xFF, a, b, c, d],},} //! } //! # }; //! ``` //! //! And although there isn't an egregious example of it in the link because the //! input code is pretty tame, in general rustc_ast_pretty has pathological //! behavior on generated code. It has a tendency to use excessive horizontal //! indentation and rapidly run out of width: //! //! ``` //! # const _: &str = stringify! { //! ::std::io::_print(::core::fmt::Arguments::new_v1(&[""], //! &match (&msg,) { //! _args => //! [::core::fmt::ArgumentV1::new(_args.0, //! ::core::fmt::Display::fmt)], //! })); //! # }; //! ``` //! //! The snippets above are clearly different from modern rustfmt style. In //! contrast, prettyplease is designed to have output that is practically //! indistinguishable from rustfmt-formatted code. //! //!
//! //! # Example //! //! ``` //! // [dependencies] //! // prettyplease = "0.2" //! // syn = { version = "2", default-features = false, features = ["full", "parsing"] } //! //! const INPUT: &str = stringify! { //! use crate::{ //! lazy::{Lazy, SyncLazy, SyncOnceCell}, panic, //! sync::{ atomic::{AtomicUsize, Ordering::SeqCst}, //! mpsc::channel, Mutex, }, //! thread, //! }; //! impl Into for T where U: From { //! fn into(self) -> U { U::from(self) } //! } //! }; //! //! fn main() { //! let syntax_tree = syn::parse_file(INPUT).unwrap(); //! let formatted = prettyplease::unparse(&syntax_tree); //! print!("{}", formatted); //! } //! ``` //! //!
//! //! # Algorithm notes //! //! The approach and terminology used in the implementation are derived from //! [*Derek C. Oppen, "Pretty Printing" (1979)*][paper], on which //! rustc_ast_pretty is also based, and from rustc_ast_pretty's implementation //! written by Graydon Hoare in 2011 (and modernized over the years by dozens of //! volunteer maintainers). //! //! [paper]: http://i.stanford.edu/pub/cstr/reports/cs/tr/79/770/CS-TR-79-770.pdf //! //! The paper describes two language-agnostic interacting procedures `Scan()` //! and `Print()`. Language-specific code decomposes an input data structure //! into a stream of `string` and `break` tokens, and `begin` and `end` tokens //! for grouping. Each `begin`–`end` range may be identified as either //! "consistent breaking" or "inconsistent breaking". If a group is consistently //! breaking, then if the whole contents do not fit on the line, *every* `break` //! token in the group will receive a linebreak. This is appropriate, for //! example, for Rust struct literals, or arguments of a function call. If a //! group is inconsistently breaking, then the `string` tokens in the group are //! greedily placed on the line until out of space, and linebroken only at those //! `break` tokens for which the next string would not fit. For example, this is //! appropriate for the contents of a braced `use` statement in Rust. //! //! Scan's job is to efficiently accumulate sizing information about groups and //! breaks. For every `begin` token we compute the distance to the matched `end` //! token, and for every `break` we compute the distance to the next `break`. //! The algorithm uses a ringbuffer to hold tokens whose size is not yet //! ascertained. The maximum size of the ringbuffer is bounded by the target //! line length and does not grow indefinitely, regardless of deep nesting in //! the input stream. That's because once a group is sufficiently big, the //! precise size can no longer make a difference to linebreak decisions and we //! can effectively treat it as "infinity". //! //! Print's job is to use the sizing information to efficiently assign a //! "broken" or "not broken" status to every `begin` token. At that point the //! output is easily constructed by concatenating `string` tokens and breaking //! at `break` tokens contained within a broken group. //! //! Leveraging these primitives (i.e. cleverly placing the all-or-nothing //! consistent breaks and greedy inconsistent breaks) to yield //! rustfmt-compatible formatting for all of Rust's syntax tree nodes is a fun //! challenge. //! //! Here is a visualization of some Rust tokens fed into the pretty printing //! algorithm. Consistently breaking `begin`—`end` pairs are represented //! by `«`⁠`»`, inconsistently breaking by `‹`⁠`›`, `break` by `·`, //! and the rest of the non-whitespace are `string`. //! //! ```text //! use crate::«{· //! ‹ lazy::«{·‹Lazy,· SyncLazy,· SyncOnceCell›·}»,· //! panic,· //! sync::«{· //! ‹ atomic::«{·‹AtomicUsize,· Ordering::SeqCst›·}»,· //! mpsc::channel,· Mutex›,· //! }»,· //! thread›,· //! }»;· //! «‹«impl<«·T‹›,· U‹›·»>» Into<«·U·»>· for T›· //! where· //! U:‹ From<«·T·»>›,· //! {· //! « fn into(·«·self·») -> U {· //! ‹ U::from(«·self·»)›· //! » }· //! »}· //! ``` //! //! The algorithm described in the paper is not quite sufficient for producing //! well-formatted Rust code that is locally indistinguishable from rustfmt's //! style. The reason is that in the paper, the complete non-whitespace contents //! are assumed to be independent of linebreak decisions, with Scan and Print //! being only in control of the whitespace (spaces and line breaks). In Rust as //! idiomatically formatted by rustfmt, that is not the case. Trailing commas //! are one example; the punctuation is only known *after* the broken vs //! non-broken status of the surrounding group is known: //! //! ``` //! # struct Struct { x: u64, y: bool } //! # let xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx = 0; //! # let yyyyyyyyyyyyyyyyyyyyyyyyyyyyyy = true; //! # //! let _ = Struct { x: 0, y: true }; //! //! let _ = Struct { //! x: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, //! y: yyyyyyyyyyyyyyyyyyyyyyyyyyyyyy, //<- trailing comma if the expression wrapped //! }; //! ``` //! //! The formatting of `match` expressions is another case; we want small arms on //! the same line as the pattern, and big arms wrapped in a brace. The presence //! of the brace punctuation, comma, and semicolon are all dependent on whether //! the arm fits on the line: //! //! ``` //! # struct Entry { nanos: u32 } //! # let total_nanos = 0u64; //! # let mut total_secs = 0u64; //! # let tmp; //! # let entry = Entry { nanos: 0 }; //! # const NANOS_PER_SEC: u32 = 1_000_000_000; //! # //! match total_nanos.checked_add(entry.nanos as u64) { //! Some(n) => tmp = n, //<- small arm, inline with comma //! None => { //! total_secs = total_secs //! .checked_add(total_nanos / NANOS_PER_SEC as u64) //! .expect("overflow in iter::sum over durations"); //! } //<- big arm, needs brace added, and also semicolon^ //! } //! ``` //! //! The printing algorithm implementation in this crate accommodates all of //! these situations with conditional punctuation tokens whose selection can be //! deferred and populated after it's known that the group is or is not broken. #![doc(html_root_url = "https://docs.rs/prettyplease/0.2.37")] #![allow( clippy::bool_to_int_with_if, clippy::cast_possible_wrap, clippy::cast_sign_loss, clippy::derive_partial_eq_without_eq, clippy::doc_markdown, clippy::enum_glob_use, clippy::items_after_statements, clippy::let_underscore_untyped, clippy::match_like_matches_macro, clippy::match_same_arms, clippy::module_name_repetitions, clippy::must_use_candidate, clippy::needless_pass_by_value, clippy::ref_option, clippy::similar_names, clippy::struct_excessive_bools, clippy::too_many_lines, clippy::unused_self, clippy::vec_init_then_push )] #![cfg_attr(all(test, exhaustive), feature(non_exhaustive_omitted_patterns_lint))] mod algorithm; mod attr; mod classify; mod convenience; mod data; mod expr; mod file; mod fixup; mod generics; mod item; mod iter; mod lifetime; mod lit; mod mac; mod pat; mod path; mod precedence; mod ring; mod stmt; mod token; mod ty; use crate::algorithm::Printer; use syn::File; // Target line width. const MARGIN: isize = 89; // Number of spaces increment at each level of block indentation. const INDENT: isize = 4; // Every line is allowed at least this much space, even if highly indented. const MIN_SPACE: isize = 60; pub fn unparse(file: &File) -> String { let mut p = Printer::new(); p.file(file); p.eof() } prettyplease-0.2.37/src/lifetime.rs000064400000000000000000000002751046102023000153750ustar 00000000000000use crate::algorithm::Printer; use syn::Lifetime; impl Printer { pub fn lifetime(&mut self, lifetime: &Lifetime) { self.word("'"); self.ident(&lifetime.ident); } } prettyplease-0.2.37/src/lit.rs000064400000000000000000000032711046102023000143660ustar 00000000000000use crate::algorithm::Printer; use proc_macro2::Literal; use syn::{Lit, LitBool, LitByte, LitByteStr, LitCStr, LitChar, LitFloat, LitInt, LitStr}; impl Printer { pub fn lit(&mut self, lit: &Lit) { match lit { #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] Lit::Str(lit) => self.lit_str(lit), Lit::ByteStr(lit) => self.lit_byte_str(lit), Lit::CStr(lit) => self.lit_c_str(lit), Lit::Byte(lit) => self.lit_byte(lit), Lit::Char(lit) => self.lit_char(lit), Lit::Int(lit) => self.lit_int(lit), Lit::Float(lit) => self.lit_float(lit), Lit::Bool(lit) => self.lit_bool(lit), Lit::Verbatim(lit) => self.lit_verbatim(lit), _ => unimplemented!("unknown Lit"), } } pub fn lit_str(&mut self, lit: &LitStr) { self.word(lit.token().to_string()); } fn lit_byte_str(&mut self, lit: &LitByteStr) { self.word(lit.token().to_string()); } fn lit_c_str(&mut self, lit: &LitCStr) { self.word(lit.token().to_string()); } fn lit_byte(&mut self, lit: &LitByte) { self.word(lit.token().to_string()); } fn lit_char(&mut self, lit: &LitChar) { self.word(lit.token().to_string()); } fn lit_int(&mut self, lit: &LitInt) { self.word(lit.token().to_string()); } fn lit_float(&mut self, lit: &LitFloat) { self.word(lit.token().to_string()); } fn lit_bool(&mut self, lit: &LitBool) { self.word(if lit.value { "true" } else { "false" }); } fn lit_verbatim(&mut self, token: &Literal) { self.word(token.to_string()); } } prettyplease-0.2.37/src/mac.rs000064400000000000000000000635441046102023000143470ustar 00000000000000use crate::algorithm::Printer; use crate::path::PathKind; use crate::token::Token; use crate::INDENT; use proc_macro2::{Delimiter, Spacing, TokenStream}; use syn::{Ident, Macro, MacroDelimiter}; impl Printer { pub fn mac(&mut self, mac: &Macro, ident: Option<&Ident>, semicolon: bool) { if mac.path.is_ident("macro_rules") { if let Some(ident) = ident { self.macro_rules(ident, &mac.tokens); return; } } #[cfg(feature = "verbatim")] if ident.is_none() && self.standard_library_macro(mac, semicolon) { return; } self.path(&mac.path, PathKind::Simple); self.word("!"); if let Some(ident) = ident { self.nbsp(); self.ident(ident); } let (open, close, delimiter_break) = match mac.delimiter { MacroDelimiter::Paren(_) => ("(", ")", Self::zerobreak as fn(&mut Self)), MacroDelimiter::Brace(_) => (" {", "}", Self::hardbreak as fn(&mut Self)), MacroDelimiter::Bracket(_) => ("[", "]", Self::zerobreak as fn(&mut Self)), }; self.word(open); if !mac.tokens.is_empty() { self.cbox(INDENT); delimiter_break(self); self.ibox(0); self.macro_rules_tokens(mac.tokens.clone(), false); self.end(); delimiter_break(self); self.offset(-INDENT); self.end(); } self.word(close); if semicolon { self.word(";"); } } fn macro_rules(&mut self, name: &Ident, rules: &TokenStream) { enum State { Start, Matcher, Equal, Greater, Expander, } use State::*; self.word("macro_rules! "); self.ident(name); self.word(" {"); self.cbox(INDENT); self.hardbreak_if_nonempty(); let mut state = State::Start; for tt in rules.clone() { let token = Token::from(tt); match (state, token) { (Start, Token::Group(delimiter, stream)) => { self.delimiter_open(delimiter); if !stream.is_empty() { self.cbox(INDENT); self.zerobreak(); self.ibox(0); self.macro_rules_tokens(stream, true); self.end(); self.zerobreak(); self.offset(-INDENT); self.end(); } self.delimiter_close(delimiter); state = Matcher; } (Matcher, Token::Punct('=', Spacing::Joint)) => { self.word(" ="); state = Equal; } (Equal, Token::Punct('>', Spacing::Alone)) => { self.word(">"); state = Greater; } (Greater, Token::Group(_delimiter, stream)) => { self.word(" {"); self.neverbreak(); if !stream.is_empty() { self.cbox(INDENT); self.hardbreak(); self.ibox(0); self.macro_rules_tokens(stream, false); self.end(); self.hardbreak(); self.offset(-INDENT); self.end(); } self.word("}"); state = Expander; } (Expander, Token::Punct(';', Spacing::Alone)) => { self.word(";"); self.hardbreak(); state = Start; } _ => unimplemented!("bad macro_rules syntax"), } } match state { Start => {} Expander => { self.word(";"); self.hardbreak(); } _ => self.hardbreak(), } self.offset(-INDENT); self.end(); self.word("}"); } pub fn macro_rules_tokens(&mut self, stream: TokenStream, matcher: bool) { #[derive(PartialEq)] enum State { Start, Dollar, DollarCrate, DollarIdent, DollarIdentColon, DollarParen, DollarParenSep, Pound, PoundBang, Dot, Colon, Colon2, Ident, IdentBang, Delim, Other, } use State::*; let mut state = Start; let mut previous_is_joint = true; for tt in stream { let token = Token::from(tt); let (needs_space, next_state) = match (&state, &token) { (Dollar, Token::Ident(_)) if matcher => (false, DollarIdent), (Dollar, Token::Ident(ident)) if ident == "crate" => (false, DollarCrate), (Dollar, Token::Ident(_)) => (false, Other), (DollarIdent, Token::Punct(':', Spacing::Alone)) => (false, DollarIdentColon), (DollarIdentColon, Token::Ident(_)) => (false, Other), (DollarParen, Token::Punct('+' | '*' | '?', Spacing::Alone)) => (false, Other), (DollarParen, Token::Ident(_) | Token::Literal(_)) => (false, DollarParenSep), (DollarParen, Token::Punct(_, Spacing::Joint)) => (false, DollarParen), (DollarParen, Token::Punct(_, Spacing::Alone)) => (false, DollarParenSep), (DollarParenSep, Token::Punct('+' | '*', _)) => (false, Other), (Pound, Token::Punct('!', _)) => (false, PoundBang), (Dollar, Token::Group(Delimiter::Parenthesis, _)) => (false, DollarParen), (Pound | PoundBang, Token::Group(Delimiter::Bracket, _)) => (false, Other), (Ident, Token::Group(Delimiter::Parenthesis | Delimiter::Bracket, _)) => { (false, Delim) } (Ident, Token::Punct('!', Spacing::Alone)) => (false, IdentBang), (IdentBang, Token::Group(Delimiter::Parenthesis | Delimiter::Bracket, _)) => { (false, Other) } (Colon, Token::Punct(':', _)) => (false, Colon2), (_, Token::Group(Delimiter::Parenthesis | Delimiter::Bracket, _)) => (true, Delim), (_, Token::Group(Delimiter::Brace | Delimiter::None, _)) => (true, Other), (_, Token::Ident(ident)) if !is_keyword(ident) => { (state != Dot && state != Colon2, Ident) } (_, Token::Literal(lit)) if lit.to_string().ends_with('.') => (state != Dot, Other), (_, Token::Literal(_)) => (state != Dot, Ident), (_, Token::Punct(',' | ';', _)) => (false, Other), (_, Token::Punct('.', _)) if !matcher => (state != Ident && state != Delim, Dot), (_, Token::Punct(':', Spacing::Joint)) => { (state != Ident && state != DollarCrate, Colon) } (_, Token::Punct('$', _)) => (true, Dollar), (_, Token::Punct('#', _)) => (true, Pound), (_, _) => (true, Other), }; if !previous_is_joint { if needs_space { self.space(); } else if let Token::Punct('.', _) = token { self.zerobreak(); } } previous_is_joint = match token { Token::Punct(_, Spacing::Joint) | Token::Punct('$', _) => true, _ => false, }; self.single_token( token, if matcher { |printer, stream| printer.macro_rules_tokens(stream, true) } else { |printer, stream| printer.macro_rules_tokens(stream, false) }, ); state = next_state; } } } pub(crate) fn requires_semi(delimiter: &MacroDelimiter) -> bool { match delimiter { MacroDelimiter::Paren(_) | MacroDelimiter::Bracket(_) => true, MacroDelimiter::Brace(_) => false, } } fn is_keyword(ident: &Ident) -> bool { match ident.to_string().as_str() { "as" | "async" | "await" | "box" | "break" | "const" | "continue" | "crate" | "dyn" | "else" | "enum" | "extern" | "fn" | "for" | "if" | "impl" | "in" | "let" | "loop" | "macro" | "match" | "mod" | "move" | "mut" | "pub" | "ref" | "return" | "static" | "struct" | "trait" | "type" | "unsafe" | "use" | "where" | "while" | "yield" => true, _ => false, } } #[cfg(feature = "verbatim")] mod standard_library { use crate::algorithm::Printer; use crate::expr; use crate::fixup::FixupContext; use crate::iter::IterDelimited; use crate::path::PathKind; use crate::INDENT; use syn::ext::IdentExt; use syn::parse::{Parse, ParseStream, Parser, Result}; use syn::punctuated::Punctuated; use syn::{ parenthesized, token, Attribute, Expr, ExprAssign, ExprPath, Ident, Lit, Macro, Pat, Path, Token, Type, Visibility, }; enum KnownMacro { Expr(Expr), Exprs(Vec), Cfg(Cfg), Matches(Matches), ThreadLocal(Vec), VecArray(Punctuated), VecRepeat { elem: Expr, n: Expr }, } enum Cfg { Eq(Ident, Option), Call(Ident, Vec), } struct Matches { expression: Expr, pattern: Pat, guard: Option, } struct ThreadLocal { attrs: Vec, vis: Visibility, name: Ident, ty: Type, init: Expr, } struct FormatArgs { format_string: Expr, args: Vec, } impl Parse for FormatArgs { fn parse(input: ParseStream) -> Result { let format_string: Expr = input.parse()?; let mut args = Vec::new(); while !input.is_empty() { input.parse::()?; if input.is_empty() { break; } let arg = if input.peek(Ident::peek_any) && input.peek2(Token![=]) && !input.peek2(Token![==]) { let key = input.call(Ident::parse_any)?; let eq_token: Token![=] = input.parse()?; let value: Expr = input.parse()?; Expr::Assign(ExprAssign { attrs: Vec::new(), left: Box::new(Expr::Path(ExprPath { attrs: Vec::new(), qself: None, path: Path::from(key), })), eq_token, right: Box::new(value), }) } else { input.parse()? }; args.push(arg); } Ok(FormatArgs { format_string, args, }) } } impl KnownMacro { fn parse_expr(input: ParseStream) -> Result { let expr: Expr = input.parse()?; Ok(KnownMacro::Expr(expr)) } fn parse_expr_comma(input: ParseStream) -> Result { let expr: Expr = input.parse()?; input.parse::>()?; Ok(KnownMacro::Exprs(vec![expr])) } fn parse_exprs(input: ParseStream) -> Result { let exprs = input.parse_terminated(Expr::parse, Token![,])?; Ok(KnownMacro::Exprs(Vec::from_iter(exprs))) } fn parse_assert(input: ParseStream) -> Result { let mut exprs = Vec::new(); let cond: Expr = input.parse()?; exprs.push(cond); if input.parse::>()?.is_some() && !input.is_empty() { let format_args: FormatArgs = input.parse()?; exprs.push(format_args.format_string); exprs.extend(format_args.args); } Ok(KnownMacro::Exprs(exprs)) } fn parse_assert_cmp(input: ParseStream) -> Result { let mut exprs = Vec::new(); let left: Expr = input.parse()?; exprs.push(left); input.parse::()?; let right: Expr = input.parse()?; exprs.push(right); if input.parse::>()?.is_some() && !input.is_empty() { let format_args: FormatArgs = input.parse()?; exprs.push(format_args.format_string); exprs.extend(format_args.args); } Ok(KnownMacro::Exprs(exprs)) } fn parse_cfg(input: ParseStream) -> Result { fn parse_single(input: ParseStream) -> Result { let ident: Ident = input.parse()?; if input.peek(token::Paren) && (ident == "all" || ident == "any") { let content; parenthesized!(content in input); let list = content.call(parse_multiple)?; Ok(Cfg::Call(ident, list)) } else if input.peek(token::Paren) && ident == "not" { let content; parenthesized!(content in input); let cfg = content.call(parse_single)?; content.parse::>()?; Ok(Cfg::Call(ident, vec![cfg])) } else if input.peek(Token![=]) { input.parse::()?; let string: Lit = input.parse()?; Ok(Cfg::Eq(ident, Some(string))) } else { Ok(Cfg::Eq(ident, None)) } } fn parse_multiple(input: ParseStream) -> Result> { let mut vec = Vec::new(); while !input.is_empty() { let cfg = input.call(parse_single)?; vec.push(cfg); if input.is_empty() { break; } input.parse::()?; } Ok(vec) } let cfg = input.call(parse_single)?; input.parse::>()?; Ok(KnownMacro::Cfg(cfg)) } fn parse_env(input: ParseStream) -> Result { let mut exprs = Vec::new(); let name: Expr = input.parse()?; exprs.push(name); if input.parse::>()?.is_some() && !input.is_empty() { let error_msg: Expr = input.parse()?; exprs.push(error_msg); input.parse::>()?; } Ok(KnownMacro::Exprs(exprs)) } fn parse_format_args(input: ParseStream) -> Result { let format_args: FormatArgs = input.parse()?; let mut exprs = format_args.args; exprs.insert(0, format_args.format_string); Ok(KnownMacro::Exprs(exprs)) } fn parse_matches(input: ParseStream) -> Result { let expression: Expr = input.parse()?; input.parse::()?; let pattern = input.call(Pat::parse_multi_with_leading_vert)?; let guard = if input.parse::>()?.is_some() { Some(input.parse()?) } else { None }; input.parse::>()?; Ok(KnownMacro::Matches(Matches { expression, pattern, guard, })) } fn parse_thread_local(input: ParseStream) -> Result { let mut items = Vec::new(); while !input.is_empty() { let attrs = input.call(Attribute::parse_outer)?; let vis: Visibility = input.parse()?; input.parse::()?; let name: Ident = input.parse()?; input.parse::()?; let ty: Type = input.parse()?; input.parse::()?; let init: Expr = input.parse()?; if input.is_empty() { break; } input.parse::()?; items.push(ThreadLocal { attrs, vis, name, ty, init, }); } Ok(KnownMacro::ThreadLocal(items)) } fn parse_vec(input: ParseStream) -> Result { if input.is_empty() { return Ok(KnownMacro::VecArray(Punctuated::new())); } let first: Expr = input.parse()?; if input.parse::>()?.is_some() { let len: Expr = input.parse()?; Ok(KnownMacro::VecRepeat { elem: first, n: len, }) } else { let mut vec = Punctuated::new(); vec.push_value(first); while !input.is_empty() { let comma: Token![,] = input.parse()?; vec.push_punct(comma); if input.is_empty() { break; } let next: Expr = input.parse()?; vec.push_value(next); } Ok(KnownMacro::VecArray(vec)) } } fn parse_write(input: ParseStream) -> Result { let mut exprs = Vec::new(); let dst: Expr = input.parse()?; exprs.push(dst); input.parse::()?; let format_args: FormatArgs = input.parse()?; exprs.push(format_args.format_string); exprs.extend(format_args.args); Ok(KnownMacro::Exprs(exprs)) } fn parse_writeln(input: ParseStream) -> Result { let mut exprs = Vec::new(); let dst: Expr = input.parse()?; exprs.push(dst); if input.parse::>()?.is_some() && !input.is_empty() { let format_args: FormatArgs = input.parse()?; exprs.push(format_args.format_string); exprs.extend(format_args.args); } Ok(KnownMacro::Exprs(exprs)) } } impl Printer { pub fn standard_library_macro(&mut self, mac: &Macro, mut semicolon: bool) -> bool { let name = mac.path.segments.last().unwrap().ident.to_string(); let parser = match name.as_str() { "addr_of" | "addr_of_mut" => KnownMacro::parse_expr, "assert" | "debug_assert" => KnownMacro::parse_assert, "assert_eq" | "assert_ne" | "debug_assert_eq" | "debug_assert_ne" => { KnownMacro::parse_assert_cmp } "cfg" => KnownMacro::parse_cfg, "compile_error" | "include" | "include_bytes" | "include_str" | "option_env" => { KnownMacro::parse_expr_comma } "concat" | "concat_bytes" | "dbg" => KnownMacro::parse_exprs, "const_format_args" | "eprint" | "eprintln" | "format" | "format_args" | "format_args_nl" | "panic" | "print" | "println" | "todo" | "unimplemented" | "unreachable" => KnownMacro::parse_format_args, "env" => KnownMacro::parse_env, "matches" => KnownMacro::parse_matches, "thread_local" => KnownMacro::parse_thread_local, "vec" => KnownMacro::parse_vec, "write" => KnownMacro::parse_write, "writeln" => KnownMacro::parse_writeln, _ => return false, }; let known_macro = match parser.parse2(mac.tokens.clone()) { Ok(known_macro) => known_macro, Err(_) => return false, }; self.path(&mac.path, PathKind::Simple); self.word("!"); match &known_macro { KnownMacro::Expr(expr) => { self.word("("); self.cbox(INDENT); self.zerobreak(); self.expr(expr, FixupContext::NONE); self.zerobreak(); self.offset(-INDENT); self.end(); self.word(")"); } KnownMacro::Exprs(exprs) => { self.word("("); self.cbox(INDENT); self.zerobreak(); for elem in exprs.iter().delimited() { self.expr(&elem, FixupContext::NONE); self.trailing_comma(elem.is_last); } self.offset(-INDENT); self.end(); self.word(")"); } KnownMacro::Cfg(cfg) => { self.word("("); self.cfg(cfg); self.word(")"); } KnownMacro::Matches(matches) => { self.word("("); self.cbox(INDENT); self.zerobreak(); self.expr(&matches.expression, FixupContext::NONE); self.word(","); self.space(); self.pat(&matches.pattern); if let Some(guard) = &matches.guard { self.space(); self.word("if "); self.expr(guard, FixupContext::NONE); } self.zerobreak(); self.offset(-INDENT); self.end(); self.word(")"); } KnownMacro::ThreadLocal(items) => { self.word(" {"); self.cbox(INDENT); self.hardbreak_if_nonempty(); for item in items { self.outer_attrs(&item.attrs); self.cbox(0); self.visibility(&item.vis); self.word("static "); self.ident(&item.name); self.word(": "); self.ty(&item.ty); self.word(" = "); self.neverbreak(); self.expr(&item.init, FixupContext::NONE); self.word(";"); self.end(); self.hardbreak(); } self.offset(-INDENT); self.end(); self.word("}"); semicolon = false; } KnownMacro::VecArray(vec) => { if vec.is_empty() { self.word("[]"); } else if expr::simple_array(vec) { self.cbox(INDENT); self.word("["); self.zerobreak(); self.ibox(0); for elem in vec.iter().delimited() { self.expr(&elem, FixupContext::NONE); if !elem.is_last { self.word(","); self.space(); } } self.end(); self.trailing_comma(true); self.offset(-INDENT); self.word("]"); self.end(); } else { self.word("["); self.cbox(INDENT); self.zerobreak(); for elem in vec.iter().delimited() { self.expr(&elem, FixupContext::NONE); self.trailing_comma(elem.is_last); } self.offset(-INDENT); self.end(); self.word("]"); } } KnownMacro::VecRepeat { elem, n } => { self.word("["); self.cbox(INDENT); self.zerobreak(); self.expr(elem, FixupContext::NONE); self.word(";"); self.space(); self.expr(n, FixupContext::NONE); self.zerobreak(); self.offset(-INDENT); self.end(); self.word("]"); } } if semicolon { self.word(";"); } true } fn cfg(&mut self, cfg: &Cfg) { match cfg { Cfg::Eq(ident, value) => { self.ident(ident); if let Some(value) = value { self.word(" = "); self.lit(value); } } Cfg::Call(ident, args) => { self.ident(ident); self.word("("); self.cbox(INDENT); self.zerobreak(); for arg in args.iter().delimited() { self.cfg(&arg); self.trailing_comma(arg.is_last); } self.offset(-INDENT); self.end(); self.word(")"); } } } } } prettyplease-0.2.37/src/pat.rs000064400000000000000000000172471046102023000143720ustar 00000000000000use crate::algorithm::Printer; use crate::fixup::FixupContext; use crate::iter::IterDelimited; use crate::path::PathKind; use crate::INDENT; use proc_macro2::TokenStream; use syn::{ FieldPat, Pat, PatIdent, PatOr, PatParen, PatReference, PatRest, PatSlice, PatStruct, PatTuple, PatTupleStruct, PatType, PatWild, }; impl Printer { pub fn pat(&mut self, pat: &Pat) { match pat { #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] Pat::Const(pat) => self.expr_const(pat), Pat::Ident(pat) => self.pat_ident(pat), Pat::Lit(pat) => self.expr_lit(pat), Pat::Macro(pat) => self.expr_macro(pat), Pat::Or(pat) => self.pat_or(pat), Pat::Paren(pat) => self.pat_paren(pat), Pat::Path(pat) => self.expr_path(pat), Pat::Range(pat) => self.expr_range(pat, FixupContext::NONE), Pat::Reference(pat) => self.pat_reference(pat), Pat::Rest(pat) => self.pat_rest(pat), Pat::Slice(pat) => self.pat_slice(pat), Pat::Struct(pat) => self.pat_struct(pat), Pat::Tuple(pat) => self.pat_tuple(pat), Pat::TupleStruct(pat) => self.pat_tuple_struct(pat), Pat::Type(pat) => self.pat_type(pat), Pat::Verbatim(pat) => self.pat_verbatim(pat), Pat::Wild(pat) => self.pat_wild(pat), _ => unimplemented!("unknown Pat"), } } fn pat_ident(&mut self, pat: &PatIdent) { self.outer_attrs(&pat.attrs); if pat.by_ref.is_some() { self.word("ref "); } if pat.mutability.is_some() { self.word("mut "); } self.ident(&pat.ident); if let Some((_at_token, subpat)) = &pat.subpat { self.word(" @ "); self.pat(subpat); } } fn pat_or(&mut self, pat: &PatOr) { self.outer_attrs(&pat.attrs); let mut consistent_break = false; for case in &pat.cases { match case { Pat::Lit(_) | Pat::Wild(_) => {} _ => { consistent_break = true; break; } } } if consistent_break { self.cbox(0); } else { self.ibox(0); } for case in pat.cases.iter().delimited() { if !case.is_first { self.space(); self.word("| "); } self.pat(&case); } self.end(); } fn pat_paren(&mut self, pat: &PatParen) { self.outer_attrs(&pat.attrs); self.word("("); self.pat(&pat.pat); self.word(")"); } fn pat_reference(&mut self, pat: &PatReference) { self.outer_attrs(&pat.attrs); self.word("&"); if pat.mutability.is_some() { self.word("mut "); } self.pat(&pat.pat); } fn pat_rest(&mut self, pat: &PatRest) { self.outer_attrs(&pat.attrs); self.word(".."); } fn pat_slice(&mut self, pat: &PatSlice) { self.outer_attrs(&pat.attrs); self.word("["); for elem in pat.elems.iter().delimited() { self.pat(&elem); self.trailing_comma(elem.is_last); } self.word("]"); } fn pat_struct(&mut self, pat: &PatStruct) { self.outer_attrs(&pat.attrs); self.cbox(INDENT); self.path(&pat.path, PathKind::Expr); self.word(" {"); self.space_if_nonempty(); for field in pat.fields.iter().delimited() { self.field_pat(&field); self.trailing_comma_or_space(field.is_last && pat.rest.is_none()); } if let Some(rest) = &pat.rest { self.pat_rest(rest); self.space(); } self.offset(-INDENT); self.end(); self.word("}"); } fn pat_tuple(&mut self, pat: &PatTuple) { self.outer_attrs(&pat.attrs); self.word("("); self.cbox(INDENT); self.zerobreak(); for elem in pat.elems.iter().delimited() { self.pat(&elem); if pat.elems.len() == 1 { if pat.elems.trailing_punct() { self.word(","); } self.zerobreak(); } else { self.trailing_comma(elem.is_last); } } self.offset(-INDENT); self.end(); self.word(")"); } fn pat_tuple_struct(&mut self, pat: &PatTupleStruct) { self.outer_attrs(&pat.attrs); self.path(&pat.path, PathKind::Expr); self.word("("); self.cbox(INDENT); self.zerobreak(); for elem in pat.elems.iter().delimited() { self.pat(&elem); self.trailing_comma(elem.is_last); } self.offset(-INDENT); self.end(); self.word(")"); } pub fn pat_type(&mut self, pat: &PatType) { self.outer_attrs(&pat.attrs); self.pat(&pat.pat); self.word(": "); self.ty(&pat.ty); } #[cfg(not(feature = "verbatim"))] fn pat_verbatim(&mut self, pat: &TokenStream) { unimplemented!("Pat::Verbatim `{}`", pat); } #[cfg(feature = "verbatim")] fn pat_verbatim(&mut self, tokens: &TokenStream) { use syn::parse::{Parse, ParseStream, Result}; use syn::{braced, Attribute, Block, Token}; enum PatVerbatim { Ellipsis, Box(Pat), Const(PatConst), } struct PatConst { attrs: Vec, block: Block, } impl Parse for PatVerbatim { fn parse(input: ParseStream) -> Result { let lookahead = input.lookahead1(); if lookahead.peek(Token![box]) { input.parse::()?; let inner = Pat::parse_single(input)?; Ok(PatVerbatim::Box(inner)) } else if lookahead.peek(Token![const]) { input.parse::()?; let content; let brace_token = braced!(content in input); let attrs = content.call(Attribute::parse_inner)?; let stmts = content.call(Block::parse_within)?; Ok(PatVerbatim::Const(PatConst { attrs, block: Block { brace_token, stmts }, })) } else if lookahead.peek(Token![...]) { input.parse::()?; Ok(PatVerbatim::Ellipsis) } else { Err(lookahead.error()) } } } let pat: PatVerbatim = match syn::parse2(tokens.clone()) { Ok(pat) => pat, Err(_) => unimplemented!("Pat::Verbatim `{}`", tokens), }; match pat { PatVerbatim::Ellipsis => { self.word("..."); } PatVerbatim::Box(pat) => { self.word("box "); self.pat(&pat); } PatVerbatim::Const(pat) => { self.word("const "); self.cbox(INDENT); self.small_block(&pat.block, &pat.attrs); self.end(); } } } fn pat_wild(&mut self, pat: &PatWild) { self.outer_attrs(&pat.attrs); self.word("_"); } fn field_pat(&mut self, field_pat: &FieldPat) { self.outer_attrs(&field_pat.attrs); if field_pat.colon_token.is_some() { self.member(&field_pat.member); self.word(": "); } self.pat(&field_pat.pat); } } prettyplease-0.2.37/src/path.rs000064400000000000000000000142161046102023000145330ustar 00000000000000use crate::algorithm::Printer; use crate::iter::IterDelimited; use crate::INDENT; use std::ptr; use syn::{ AngleBracketedGenericArguments, AssocConst, AssocType, Constraint, GenericArgument, ParenthesizedGenericArguments, Path, PathArguments, PathSegment, QSelf, }; #[derive(Copy, Clone, PartialEq)] pub enum PathKind { // a::B Simple, // a::B Type, // a::B:: Expr, } impl Printer { pub fn path(&mut self, path: &Path, kind: PathKind) { assert!(!path.segments.is_empty()); for segment in path.segments.iter().delimited() { if !segment.is_first || path.leading_colon.is_some() { self.word("::"); } self.path_segment(&segment, kind); } } pub fn path_segment(&mut self, segment: &PathSegment, kind: PathKind) { self.ident(&segment.ident); self.path_arguments(&segment.arguments, kind); } fn path_arguments(&mut self, arguments: &PathArguments, kind: PathKind) { match arguments { PathArguments::None => {} PathArguments::AngleBracketed(arguments) => { self.angle_bracketed_generic_arguments(arguments, kind); } PathArguments::Parenthesized(arguments) => { self.parenthesized_generic_arguments(arguments); } } } fn generic_argument(&mut self, arg: &GenericArgument) { match arg { #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] GenericArgument::Lifetime(lifetime) => self.lifetime(lifetime), GenericArgument::Type(ty) => self.ty(ty), GenericArgument::Const(expr) => self.const_argument(expr), GenericArgument::AssocType(assoc) => self.assoc_type(assoc), GenericArgument::AssocConst(assoc) => self.assoc_const(assoc), GenericArgument::Constraint(constraint) => self.constraint(constraint), _ => unimplemented!("unknown GenericArgument"), } } pub fn angle_bracketed_generic_arguments( &mut self, generic: &AngleBracketedGenericArguments, path_kind: PathKind, ) { if generic.args.is_empty() || path_kind == PathKind::Simple { return; } if path_kind == PathKind::Expr { self.word("::"); } self.word("<"); self.cbox(INDENT); self.zerobreak(); // Print lifetimes before types/consts/bindings, regardless of their // order in self.args. #[derive(Ord, PartialOrd, Eq, PartialEq)] enum Group { First, Second, } fn group(arg: &GenericArgument) -> Group { match arg { #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] GenericArgument::Lifetime(_) => Group::First, GenericArgument::Type(_) | GenericArgument::Const(_) | GenericArgument::AssocType(_) | GenericArgument::AssocConst(_) | GenericArgument::Constraint(_) => Group::Second, _ => Group::Second, } } let last = generic.args.iter().max_by_key(|param| group(param)); for current_group in [Group::First, Group::Second] { for arg in &generic.args { if group(arg) == current_group { self.generic_argument(arg); self.trailing_comma(ptr::eq(arg, last.unwrap())); } } } self.offset(-INDENT); self.end(); self.word(">"); } fn assoc_type(&mut self, assoc: &AssocType) { self.ident(&assoc.ident); if let Some(generics) = &assoc.generics { self.angle_bracketed_generic_arguments(generics, PathKind::Type); } self.word(" = "); self.ty(&assoc.ty); } fn assoc_const(&mut self, assoc: &AssocConst) { self.ident(&assoc.ident); if let Some(generics) = &assoc.generics { self.angle_bracketed_generic_arguments(generics, PathKind::Type); } self.word(" = "); self.const_argument(&assoc.value); } fn constraint(&mut self, constraint: &Constraint) { self.ident(&constraint.ident); if let Some(generics) = &constraint.generics { self.angle_bracketed_generic_arguments(generics, PathKind::Type); } self.ibox(INDENT); for bound in constraint.bounds.iter().delimited() { if bound.is_first { self.word(": "); } else { self.space(); self.word("+ "); } self.type_param_bound(&bound); } self.end(); } fn parenthesized_generic_arguments(&mut self, arguments: &ParenthesizedGenericArguments) { self.cbox(INDENT); self.word("("); self.zerobreak(); for ty in arguments.inputs.iter().delimited() { self.ty(&ty); self.trailing_comma(ty.is_last); } self.offset(-INDENT); self.word(")"); self.return_type(&arguments.output); self.end(); } pub fn qpath(&mut self, qself: &Option, path: &Path, kind: PathKind) { let qself = if let Some(qself) = qself { qself } else { self.path(path, kind); return; }; assert!(qself.position < path.segments.len()); self.word("<"); self.ty(&qself.ty); let mut segments = path.segments.iter(); if qself.position > 0 { self.word(" as "); for segment in segments.by_ref().take(qself.position).delimited() { if !segment.is_first || path.leading_colon.is_some() { self.word("::"); } self.path_segment(&segment, PathKind::Type); if segment.is_last { self.word(">"); } } } else { self.word(">"); } for segment in segments { self.word("::"); self.path_segment(segment, kind); } } } prettyplease-0.2.37/src/precedence.rs000064400000000000000000000117751046102023000157030ustar 00000000000000use syn::{ AttrStyle, Attribute, BinOp, Expr, ExprArray, ExprAsync, ExprAwait, ExprBlock, ExprBreak, ExprCall, ExprConst, ExprContinue, ExprField, ExprForLoop, ExprIf, ExprIndex, ExprInfer, ExprLit, ExprLoop, ExprMacro, ExprMatch, ExprMethodCall, ExprParen, ExprPath, ExprRepeat, ExprReturn, ExprStruct, ExprTry, ExprTryBlock, ExprTuple, ExprUnsafe, ExprWhile, ExprYield, ReturnType, }; // Reference: https://doc.rust-lang.org/reference/expressions.html#expression-precedence #[derive(Copy, Clone, PartialEq, PartialOrd)] pub enum Precedence { // return, break, closures Jump, // = += -= *= /= %= &= |= ^= <<= >>= Assign, // .. ..= Range, // || Or, // && And, // let Let, // == != < > <= >= Compare, // | BitOr, // ^ BitXor, // & BitAnd, // << >> Shift, // + - Sum, // * / % Product, // as Cast, // unary - * ! & &mut Prefix, // paths, loops, function calls, array indexing, field expressions, method calls Unambiguous, } impl Precedence { pub(crate) const MIN: Self = Precedence::Jump; pub(crate) fn of_binop(op: &BinOp) -> Self { match op { #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] BinOp::Add(_) | BinOp::Sub(_) => Precedence::Sum, BinOp::Mul(_) | BinOp::Div(_) | BinOp::Rem(_) => Precedence::Product, BinOp::And(_) => Precedence::And, BinOp::Or(_) => Precedence::Or, BinOp::BitXor(_) => Precedence::BitXor, BinOp::BitAnd(_) => Precedence::BitAnd, BinOp::BitOr(_) => Precedence::BitOr, BinOp::Shl(_) | BinOp::Shr(_) => Precedence::Shift, BinOp::Eq(_) | BinOp::Lt(_) | BinOp::Le(_) | BinOp::Ne(_) | BinOp::Ge(_) | BinOp::Gt(_) => Precedence::Compare, BinOp::AddAssign(_) | BinOp::SubAssign(_) | BinOp::MulAssign(_) | BinOp::DivAssign(_) | BinOp::RemAssign(_) | BinOp::BitXorAssign(_) | BinOp::BitAndAssign(_) | BinOp::BitOrAssign(_) | BinOp::ShlAssign(_) | BinOp::ShrAssign(_) => Precedence::Assign, _ => Precedence::MIN, } } pub(crate) fn of(e: &Expr) -> Self { fn prefix_attrs(attrs: &[Attribute]) -> Precedence { for attr in attrs { if let AttrStyle::Outer = attr.style { return Precedence::Prefix; } } Precedence::Unambiguous } match e { #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] Expr::Closure(e) => match e.output { ReturnType::Default => Precedence::Jump, ReturnType::Type(..) => prefix_attrs(&e.attrs), }, Expr::Break(ExprBreak { expr, .. }) | Expr::Return(ExprReturn { expr, .. }) | Expr::Yield(ExprYield { expr, .. }) => match expr { Some(_) => Precedence::Jump, None => Precedence::Unambiguous, }, Expr::Assign(_) => Precedence::Assign, Expr::Range(_) => Precedence::Range, Expr::Binary(e) => Precedence::of_binop(&e.op), Expr::Let(_) => Precedence::Let, Expr::Cast(_) => Precedence::Cast, Expr::RawAddr(_) | Expr::Reference(_) | Expr::Unary(_) => Precedence::Prefix, Expr::Array(ExprArray { attrs, .. }) | Expr::Async(ExprAsync { attrs, .. }) | Expr::Await(ExprAwait { attrs, .. }) | Expr::Block(ExprBlock { attrs, .. }) | Expr::Call(ExprCall { attrs, .. }) | Expr::Const(ExprConst { attrs, .. }) | Expr::Continue(ExprContinue { attrs, .. }) | Expr::Field(ExprField { attrs, .. }) | Expr::ForLoop(ExprForLoop { attrs, .. }) | Expr::If(ExprIf { attrs, .. }) | Expr::Index(ExprIndex { attrs, .. }) | Expr::Infer(ExprInfer { attrs, .. }) | Expr::Lit(ExprLit { attrs, .. }) | Expr::Loop(ExprLoop { attrs, .. }) | Expr::Macro(ExprMacro { attrs, .. }) | Expr::Match(ExprMatch { attrs, .. }) | Expr::MethodCall(ExprMethodCall { attrs, .. }) | Expr::Paren(ExprParen { attrs, .. }) | Expr::Path(ExprPath { attrs, .. }) | Expr::Repeat(ExprRepeat { attrs, .. }) | Expr::Struct(ExprStruct { attrs, .. }) | Expr::Try(ExprTry { attrs, .. }) | Expr::TryBlock(ExprTryBlock { attrs, .. }) | Expr::Tuple(ExprTuple { attrs, .. }) | Expr::Unsafe(ExprUnsafe { attrs, .. }) | Expr::While(ExprWhile { attrs, .. }) => prefix_attrs(attrs), Expr::Group(e) => Precedence::of(&e.expr), Expr::Verbatim(_) => Precedence::Unambiguous, _ => Precedence::Unambiguous, } } } prettyplease-0.2.37/src/ring.rs000064400000000000000000000033321046102023000145330ustar 00000000000000use std::collections::VecDeque; use std::ops::{Index, IndexMut, Range}; pub struct RingBuffer { data: VecDeque, // Abstract index of data[0] in infinitely sized queue offset: usize, } impl RingBuffer { pub fn new() -> Self { RingBuffer { data: VecDeque::new(), offset: 0, } } pub fn is_empty(&self) -> bool { self.data.is_empty() } pub fn len(&self) -> usize { self.data.len() } pub fn push(&mut self, value: T) -> usize { let index = self.offset + self.data.len(); self.data.push_back(value); index } pub fn clear(&mut self) { self.data.clear(); } pub fn index_range(&self) -> Range { self.offset..self.offset + self.data.len() } pub fn first(&self) -> &T { &self.data[0] } pub fn first_mut(&mut self) -> &mut T { &mut self.data[0] } pub fn pop_first(&mut self) -> T { self.offset += 1; self.data.pop_front().unwrap() } pub fn last(&self) -> &T { self.data.back().unwrap() } pub fn last_mut(&mut self) -> &mut T { self.data.back_mut().unwrap() } pub fn second_last(&self) -> &T { &self.data[self.data.len() - 2] } pub fn pop_last(&mut self) { self.data.pop_back().unwrap(); } } impl Index for RingBuffer { type Output = T; fn index(&self, index: usize) -> &Self::Output { &self.data[index.checked_sub(self.offset).unwrap()] } } impl IndexMut for RingBuffer { fn index_mut(&mut self, index: usize) -> &mut Self::Output { &mut self.data[index.checked_sub(self.offset).unwrap()] } } prettyplease-0.2.37/src/stmt.rs000064400000000000000000000154631046102023000145730ustar 00000000000000use crate::algorithm::Printer; use crate::classify; use crate::expr; use crate::fixup::FixupContext; use crate::mac; use crate::INDENT; use syn::{BinOp, Expr, Stmt}; impl Printer { pub fn stmt(&mut self, stmt: &Stmt, is_last: bool) { match stmt { Stmt::Local(local) => { self.outer_attrs(&local.attrs); self.ibox(0); self.word("let "); self.pat(&local.pat); if let Some(local_init) = &local.init { self.word(" = "); self.neverbreak(); self.subexpr( &local_init.expr, local_init.diverge.is_some() && classify::expr_trailing_brace(&local_init.expr), FixupContext::NONE, ); if let Some((_else, diverge)) = &local_init.diverge { self.space(); self.word("else "); self.end(); self.neverbreak(); self.cbox(INDENT); if let Some(expr) = expr::simple_block(diverge) { self.small_block(&expr.block, &[]); } else { self.expr_as_small_block(diverge, INDENT); } } } self.end(); self.word(";"); self.hardbreak(); } Stmt::Item(item) => self.item(item), Stmt::Expr(expr, None) => { if break_after(expr) { self.ibox(0); self.expr_beginning_of_line(expr, false, true, FixupContext::new_stmt()); if add_semi(expr) { self.word(";"); } self.end(); self.hardbreak(); } else { self.expr_beginning_of_line(expr, false, true, FixupContext::new_stmt()); } } Stmt::Expr(expr, Some(_semi)) => { if let Expr::Verbatim(tokens) = expr { if tokens.is_empty() { return; } } self.ibox(0); self.expr_beginning_of_line(expr, false, true, FixupContext::new_stmt()); if !remove_semi(expr) { self.word(";"); } self.end(); self.hardbreak(); } Stmt::Macro(stmt) => { self.outer_attrs(&stmt.attrs); let semicolon = stmt.semi_token.is_some() || !is_last && mac::requires_semi(&stmt.mac.delimiter); self.mac(&stmt.mac, None, semicolon); self.hardbreak(); } } } } pub fn add_semi(expr: &Expr) -> bool { match expr { #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] Expr::Assign(_) | Expr::Break(_) | Expr::Continue(_) | Expr::Return(_) | Expr::Yield(_) => { true } Expr::Binary(expr) => { match expr.op { #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] BinOp::AddAssign(_) | BinOp::SubAssign(_) | BinOp::MulAssign(_) | BinOp::DivAssign(_) | BinOp::RemAssign(_) | BinOp::BitXorAssign(_) | BinOp::BitAndAssign(_) | BinOp::BitOrAssign(_) | BinOp::ShlAssign(_) | BinOp::ShrAssign(_) => true, BinOp::Add(_) | BinOp::Sub(_) | BinOp::Mul(_) | BinOp::Div(_) | BinOp::Rem(_) | BinOp::And(_) | BinOp::Or(_) | BinOp::BitXor(_) | BinOp::BitAnd(_) | BinOp::BitOr(_) | BinOp::Shl(_) | BinOp::Shr(_) | BinOp::Eq(_) | BinOp::Lt(_) | BinOp::Le(_) | BinOp::Ne(_) | BinOp::Ge(_) | BinOp::Gt(_) => false, _ => unimplemented!("unknown BinOp"), } } Expr::Group(group) => add_semi(&group.expr), Expr::Array(_) | Expr::Async(_) | Expr::Await(_) | Expr::Block(_) | Expr::Call(_) | Expr::Cast(_) | Expr::Closure(_) | Expr::Const(_) | Expr::Field(_) | Expr::ForLoop(_) | Expr::If(_) | Expr::Index(_) | Expr::Infer(_) | Expr::Let(_) | Expr::Lit(_) | Expr::Loop(_) | Expr::Macro(_) | Expr::Match(_) | Expr::MethodCall(_) | Expr::Paren(_) | Expr::Path(_) | Expr::Range(_) | Expr::RawAddr(_) | Expr::Reference(_) | Expr::Repeat(_) | Expr::Struct(_) | Expr::Try(_) | Expr::TryBlock(_) | Expr::Tuple(_) | Expr::Unary(_) | Expr::Unsafe(_) | Expr::Verbatim(_) | Expr::While(_) => false, _ => false, } } pub fn break_after(expr: &Expr) -> bool { if let Expr::Group(group) = expr { if let Expr::Verbatim(verbatim) = group.expr.as_ref() { return !verbatim.is_empty(); } } true } fn remove_semi(expr: &Expr) -> bool { match expr { #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] Expr::ForLoop(_) | Expr::While(_) => true, Expr::Group(group) => remove_semi(&group.expr), Expr::If(expr) => match &expr.else_branch { Some((_else_token, else_branch)) => remove_semi(else_branch), None => true, }, Expr::Array(_) | Expr::Assign(_) | Expr::Async(_) | Expr::Await(_) | Expr::Binary(_) | Expr::Block(_) | Expr::Break(_) | Expr::Call(_) | Expr::Cast(_) | Expr::Closure(_) | Expr::Continue(_) | Expr::Const(_) | Expr::Field(_) | Expr::Index(_) | Expr::Infer(_) | Expr::Let(_) | Expr::Lit(_) | Expr::Loop(_) | Expr::Macro(_) | Expr::Match(_) | Expr::MethodCall(_) | Expr::Paren(_) | Expr::Path(_) | Expr::Range(_) | Expr::RawAddr(_) | Expr::Reference(_) | Expr::Repeat(_) | Expr::Return(_) | Expr::Struct(_) | Expr::Try(_) | Expr::TryBlock(_) | Expr::Tuple(_) | Expr::Unary(_) | Expr::Unsafe(_) | Expr::Verbatim(_) | Expr::Yield(_) => false, _ => false, } } prettyplease-0.2.37/src/token.rs000064400000000000000000000045701046102023000147210ustar 00000000000000use crate::algorithm::Printer; use proc_macro2::{Delimiter, Ident, Literal, Spacing, TokenStream, TokenTree}; impl Printer { pub fn single_token(&mut self, token: Token, group_contents: fn(&mut Self, TokenStream)) { match token { Token::Group(delimiter, stream) => self.token_group(delimiter, stream, group_contents), Token::Ident(ident) => self.ident(&ident), Token::Punct(ch, _spacing) => self.token_punct(ch), Token::Literal(literal) => self.token_literal(&literal), } } fn token_group( &mut self, delimiter: Delimiter, stream: TokenStream, group_contents: fn(&mut Self, TokenStream), ) { self.delimiter_open(delimiter); if !stream.is_empty() { if delimiter == Delimiter::Brace { self.space(); } group_contents(self, stream); if delimiter == Delimiter::Brace { self.space(); } } self.delimiter_close(delimiter); } pub fn ident(&mut self, ident: &Ident) { self.word(ident.to_string()); } pub fn token_punct(&mut self, ch: char) { self.word(ch.to_string()); } pub fn token_literal(&mut self, literal: &Literal) { self.word(literal.to_string()); } pub fn delimiter_open(&mut self, delimiter: Delimiter) { self.word(match delimiter { Delimiter::Parenthesis => "(", Delimiter::Brace => "{", Delimiter::Bracket => "[", Delimiter::None => return, }); } pub fn delimiter_close(&mut self, delimiter: Delimiter) { self.word(match delimiter { Delimiter::Parenthesis => ")", Delimiter::Brace => "}", Delimiter::Bracket => "]", Delimiter::None => return, }); } } pub enum Token { Group(Delimiter, TokenStream), Ident(Ident), Punct(char, Spacing), Literal(Literal), } impl From for Token { fn from(tt: TokenTree) -> Self { match tt { TokenTree::Group(group) => Token::Group(group.delimiter(), group.stream()), TokenTree::Ident(ident) => Token::Ident(ident), TokenTree::Punct(punct) => Token::Punct(punct.as_char(), punct.spacing()), TokenTree::Literal(literal) => Token::Literal(literal), } } } prettyplease-0.2.37/src/ty.rs000064400000000000000000000241061046102023000142320ustar 00000000000000use crate::algorithm::Printer; use crate::fixup::FixupContext; use crate::iter::IterDelimited; use crate::path::PathKind; use crate::INDENT; use proc_macro2::TokenStream; use syn::{ Abi, BareFnArg, BareVariadic, ReturnType, Type, TypeArray, TypeBareFn, TypeGroup, TypeImplTrait, TypeInfer, TypeMacro, TypeNever, TypeParen, TypePath, TypePtr, TypeReference, TypeSlice, TypeTraitObject, TypeTuple, }; impl Printer { pub fn ty(&mut self, ty: &Type) { match ty { #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] Type::Array(ty) => self.type_array(ty), Type::BareFn(ty) => self.type_bare_fn(ty), Type::Group(ty) => self.type_group(ty), Type::ImplTrait(ty) => self.type_impl_trait(ty), Type::Infer(ty) => self.type_infer(ty), Type::Macro(ty) => self.type_macro(ty), Type::Never(ty) => self.type_never(ty), Type::Paren(ty) => self.type_paren(ty), Type::Path(ty) => self.type_path(ty), Type::Ptr(ty) => self.type_ptr(ty), Type::Reference(ty) => self.type_reference(ty), Type::Slice(ty) => self.type_slice(ty), Type::TraitObject(ty) => self.type_trait_object(ty), Type::Tuple(ty) => self.type_tuple(ty), Type::Verbatim(ty) => self.type_verbatim(ty), _ => unimplemented!("unknown Type"), } } fn type_array(&mut self, ty: &TypeArray) { self.word("["); self.ty(&ty.elem); self.word("; "); self.expr(&ty.len, FixupContext::NONE); self.word("]"); } fn type_bare_fn(&mut self, ty: &TypeBareFn) { if let Some(bound_lifetimes) = &ty.lifetimes { self.bound_lifetimes(bound_lifetimes); } if ty.unsafety.is_some() { self.word("unsafe "); } if let Some(abi) = &ty.abi { self.abi(abi); } self.word("fn("); self.cbox(INDENT); self.zerobreak(); for bare_fn_arg in ty.inputs.iter().delimited() { self.bare_fn_arg(&bare_fn_arg); self.trailing_comma(bare_fn_arg.is_last && ty.variadic.is_none()); } if let Some(variadic) = &ty.variadic { self.bare_variadic(variadic); self.zerobreak(); } self.offset(-INDENT); self.end(); self.word(")"); self.return_type(&ty.output); } fn type_group(&mut self, ty: &TypeGroup) { self.ty(&ty.elem); } fn type_impl_trait(&mut self, ty: &TypeImplTrait) { self.word("impl "); for type_param_bound in ty.bounds.iter().delimited() { if !type_param_bound.is_first { self.word(" + "); } self.type_param_bound(&type_param_bound); } } fn type_infer(&mut self, ty: &TypeInfer) { let _ = ty; self.word("_"); } fn type_macro(&mut self, ty: &TypeMacro) { let semicolon = false; self.mac(&ty.mac, None, semicolon); } fn type_never(&mut self, ty: &TypeNever) { let _ = ty; self.word("!"); } fn type_paren(&mut self, ty: &TypeParen) { self.word("("); self.ty(&ty.elem); self.word(")"); } fn type_path(&mut self, ty: &TypePath) { self.qpath(&ty.qself, &ty.path, PathKind::Type); } fn type_ptr(&mut self, ty: &TypePtr) { self.word("*"); if ty.mutability.is_some() { self.word("mut "); } else { self.word("const "); } self.ty(&ty.elem); } fn type_reference(&mut self, ty: &TypeReference) { self.word("&"); if let Some(lifetime) = &ty.lifetime { self.lifetime(lifetime); self.nbsp(); } if ty.mutability.is_some() { self.word("mut "); } self.ty(&ty.elem); } fn type_slice(&mut self, ty: &TypeSlice) { self.word("["); self.ty(&ty.elem); self.word("]"); } fn type_trait_object(&mut self, ty: &TypeTraitObject) { self.word("dyn "); for type_param_bound in ty.bounds.iter().delimited() { if !type_param_bound.is_first { self.word(" + "); } self.type_param_bound(&type_param_bound); } } fn type_tuple(&mut self, ty: &TypeTuple) { self.word("("); self.cbox(INDENT); self.zerobreak(); for elem in ty.elems.iter().delimited() { self.ty(&elem); if ty.elems.len() == 1 { self.word(","); self.zerobreak(); } else { self.trailing_comma(elem.is_last); } } self.offset(-INDENT); self.end(); self.word(")"); } #[cfg(not(feature = "verbatim"))] fn type_verbatim(&mut self, ty: &TokenStream) { unimplemented!("Type::Verbatim `{}`", ty); } #[cfg(feature = "verbatim")] fn type_verbatim(&mut self, tokens: &TokenStream) { use syn::parse::{Parse, ParseStream, Result}; use syn::punctuated::Punctuated; use syn::{token, FieldsNamed, Token, TypeParamBound}; enum TypeVerbatim { Ellipsis, AnonStruct(AnonStruct), AnonUnion(AnonUnion), DynStar(DynStar), MutSelf(MutSelf), } struct AnonStruct { fields: FieldsNamed, } struct AnonUnion { fields: FieldsNamed, } struct DynStar { bounds: Punctuated, } struct MutSelf { ty: Option, } impl Parse for TypeVerbatim { fn parse(input: ParseStream) -> Result { let lookahead = input.lookahead1(); if lookahead.peek(Token![struct]) { input.parse::()?; let fields: FieldsNamed = input.parse()?; Ok(TypeVerbatim::AnonStruct(AnonStruct { fields })) } else if lookahead.peek(Token![union]) && input.peek2(token::Brace) { input.parse::()?; let fields: FieldsNamed = input.parse()?; Ok(TypeVerbatim::AnonUnion(AnonUnion { fields })) } else if lookahead.peek(Token![dyn]) { input.parse::()?; input.parse::()?; let bounds = input.parse_terminated(TypeParamBound::parse, Token![+])?; Ok(TypeVerbatim::DynStar(DynStar { bounds })) } else if lookahead.peek(Token![mut]) { input.parse::()?; input.parse::()?; let ty = if input.is_empty() { None } else { input.parse::()?; let ty: Type = input.parse()?; Some(ty) }; Ok(TypeVerbatim::MutSelf(MutSelf { ty })) } else if lookahead.peek(Token![...]) { input.parse::()?; Ok(TypeVerbatim::Ellipsis) } else { Err(lookahead.error()) } } } let ty: TypeVerbatim = match syn::parse2(tokens.clone()) { Ok(ty) => ty, Err(_) => unimplemented!("Type::Verbatim `{}`", tokens), }; match ty { TypeVerbatim::Ellipsis => { self.word("..."); } TypeVerbatim::AnonStruct(ty) => { self.cbox(INDENT); self.word("struct {"); self.hardbreak_if_nonempty(); for field in &ty.fields.named { self.field(field); self.word(","); self.hardbreak(); } self.offset(-INDENT); self.end(); self.word("}"); } TypeVerbatim::AnonUnion(ty) => { self.cbox(INDENT); self.word("union {"); self.hardbreak_if_nonempty(); for field in &ty.fields.named { self.field(field); self.word(","); self.hardbreak(); } self.offset(-INDENT); self.end(); self.word("}"); } TypeVerbatim::DynStar(ty) => { self.word("dyn* "); for type_param_bound in ty.bounds.iter().delimited() { if !type_param_bound.is_first { self.word(" + "); } self.type_param_bound(&type_param_bound); } } TypeVerbatim::MutSelf(bare_fn_arg) => { self.word("mut self"); if let Some(ty) = &bare_fn_arg.ty { self.word(": "); self.ty(ty); } } } } pub fn return_type(&mut self, ty: &ReturnType) { match ty { ReturnType::Default => {} ReturnType::Type(_arrow, ty) => { self.word(" -> "); self.ty(ty); } } } fn bare_fn_arg(&mut self, bare_fn_arg: &BareFnArg) { self.outer_attrs(&bare_fn_arg.attrs); if let Some((name, _colon)) = &bare_fn_arg.name { self.ident(name); self.word(": "); } self.ty(&bare_fn_arg.ty); } fn bare_variadic(&mut self, variadic: &BareVariadic) { self.outer_attrs(&variadic.attrs); if let Some((name, _colon)) = &variadic.name { self.ident(name); self.word(": "); } self.word("..."); } pub fn abi(&mut self, abi: &Abi) { self.word("extern "); if let Some(name) = &abi.name { self.lit_str(name); self.nbsp(); } } } prettyplease-0.2.37/tests/test.rs000064400000000000000000000024001046102023000151210ustar 00000000000000use indoc::indoc; use proc_macro2::{Delimiter, Group, TokenStream}; use quote::quote; #[track_caller] fn test(tokens: TokenStream, expected: &str) { let syntax_tree: syn::File = syn::parse2(tokens).unwrap(); let pretty = prettyplease::unparse(&syntax_tree); assert_eq!(pretty, expected); } #[test] fn test_parenthesize_cond() { let s = Group::new(Delimiter::None, quote!(Struct {})); test( quote! { fn main() { if #s == #s {} } }, indoc! {" fn main() { if (Struct {}) == (Struct {}) {} } "}, ); } #[test] fn test_parenthesize_match_guard() { let expr_struct = Group::new(Delimiter::None, quote!(Struct {})); let expr_binary = Group::new(Delimiter::None, quote!(true && false)); test( quote! { fn main() { match () { () if let _ = #expr_struct => {} () if let _ = #expr_binary => {} } } }, indoc! {" fn main() { match () { () if let _ = Struct {} => {} () if let _ = (true && false) => {} } } "}, ); } prettyplease-0.2.37/tests/test_precedence.rs000064400000000000000000000762541046102023000173200ustar 00000000000000use proc_macro2::{Ident, Span, TokenStream}; use quote::ToTokens as _; use std::mem; use std::process::ExitCode; use syn::punctuated::Punctuated; use syn::visit_mut::{self, VisitMut}; use syn::{ token, AngleBracketedGenericArguments, Arm, BinOp, Block, Expr, ExprArray, ExprAssign, ExprAsync, ExprAwait, ExprBinary, ExprBlock, ExprBreak, ExprCall, ExprCast, ExprClosure, ExprConst, ExprContinue, ExprField, ExprForLoop, ExprIf, ExprIndex, ExprLet, ExprLit, ExprLoop, ExprMacro, ExprMatch, ExprMethodCall, ExprPath, ExprRange, ExprRawAddr, ExprReference, ExprReturn, ExprStruct, ExprTry, ExprTryBlock, ExprUnary, ExprUnsafe, ExprWhile, ExprYield, File, GenericArgument, Generics, Item, ItemConst, Label, Lifetime, LifetimeParam, Lit, LitInt, Macro, MacroDelimiter, Member, Pat, PatWild, Path, PathArguments, PathSegment, PointerMutability, QSelf, RangeLimits, ReturnType, Stmt, StmtMacro, Token, Type, TypeInfer, TypeParam, TypePath, UnOp, Visibility, }; struct FlattenParens; impl VisitMut for FlattenParens { fn visit_expr_mut(&mut self, e: &mut Expr) { while let Expr::Paren(paren) = e { *e = mem::replace(&mut *paren.expr, Expr::PLACEHOLDER); } visit_mut::visit_expr_mut(self, e); } } struct AsIfPrinted; impl VisitMut for AsIfPrinted { fn visit_generics_mut(&mut self, generics: &mut Generics) { if generics.params.is_empty() { generics.lt_token = None; generics.gt_token = None; } if let Some(where_clause) = &generics.where_clause { if where_clause.predicates.is_empty() { generics.where_clause = None; } } visit_mut::visit_generics_mut(self, generics); } fn visit_lifetime_param_mut(&mut self, param: &mut LifetimeParam) { if param.bounds.is_empty() { param.colon_token = None; } visit_mut::visit_lifetime_param_mut(self, param); } fn visit_stmt_mut(&mut self, stmt: &mut Stmt) { if let Stmt::Expr(expr, semi) = stmt { if let Expr::Macro(e) = expr { if match e.mac.delimiter { MacroDelimiter::Brace(_) => true, MacroDelimiter::Paren(_) | MacroDelimiter::Bracket(_) => semi.is_some(), } { let expr = match mem::replace(expr, Expr::PLACEHOLDER) { Expr::Macro(expr) => expr, _ => unreachable!(), }; *stmt = Stmt::Macro(StmtMacro { attrs: expr.attrs, mac: expr.mac, semi_token: *semi, }); } } } visit_mut::visit_stmt_mut(self, stmt); } fn visit_type_param_mut(&mut self, param: &mut TypeParam) { if param.bounds.is_empty() { param.colon_token = None; } visit_mut::visit_type_param_mut(self, param); } } #[test] fn test_permutations() -> ExitCode { fn iter(depth: usize, f: &mut dyn FnMut(Expr)) { let span = Span::call_site(); // Expr::Path f(Expr::Path(ExprPath { // `x` attrs: Vec::new(), qself: None, path: Path::from(Ident::new("x", span)), })); if false { f(Expr::Path(ExprPath { // `x::` attrs: Vec::new(), qself: None, path: Path { leading_colon: None, segments: Punctuated::from_iter([PathSegment { ident: Ident::new("x", span), arguments: PathArguments::AngleBracketed(AngleBracketedGenericArguments { colon2_token: Some(Token![::](span)), lt_token: Token![<](span), args: Punctuated::from_iter([GenericArgument::Type(Type::Path( TypePath { qself: None, path: Path::from(Ident::new("T", span)), }, ))]), gt_token: Token![>](span), }), }]), }, })); f(Expr::Path(ExprPath { // `::CONST` attrs: Vec::new(), qself: Some(QSelf { lt_token: Token![<](span), ty: Box::new(Type::Path(TypePath { qself: None, path: Path::from(Ident::new("T", span)), })), position: 1, as_token: Some(Token![as](span)), gt_token: Token![>](span), }), path: Path { leading_colon: None, segments: Punctuated::from_iter([ PathSegment::from(Ident::new("Trait", span)), PathSegment::from(Ident::new("CONST", span)), ]), }, })); } let depth = match depth.checked_sub(1) { Some(depth) => depth, None => return, }; // Expr::Assign iter(depth, &mut |expr| { iter(0, &mut |simple| { f(Expr::Assign(ExprAssign { // `x = $expr` attrs: Vec::new(), left: Box::new(simple.clone()), eq_token: Token![=](span), right: Box::new(expr.clone()), })); f(Expr::Assign(ExprAssign { // `$expr = x` attrs: Vec::new(), left: Box::new(expr.clone()), eq_token: Token![=](span), right: Box::new(simple), })); }); }); // Expr::Binary iter(depth, &mut |expr| { iter(0, &mut |simple| { for op in [ BinOp::Add(Token![+](span)), //BinOp::Sub(Token![-](span)), //BinOp::Mul(Token![*](span)), //BinOp::Div(Token![/](span)), //BinOp::Rem(Token![%](span)), //BinOp::And(Token![&&](span)), //BinOp::Or(Token![||](span)), //BinOp::BitXor(Token![^](span)), //BinOp::BitAnd(Token![&](span)), //BinOp::BitOr(Token![|](span)), //BinOp::Shl(Token![<<](span)), //BinOp::Shr(Token![>>](span)), //BinOp::Eq(Token![==](span)), BinOp::Lt(Token![<](span)), //BinOp::Le(Token![<=](span)), //BinOp::Ne(Token![!=](span)), //BinOp::Ge(Token![>=](span)), //BinOp::Gt(Token![>](span)), BinOp::ShlAssign(Token![<<=](span)), ] { f(Expr::Binary(ExprBinary { // `x + $expr` attrs: Vec::new(), left: Box::new(simple.clone()), op, right: Box::new(expr.clone()), })); f(Expr::Binary(ExprBinary { // `$expr + x` attrs: Vec::new(), left: Box::new(expr.clone()), op, right: Box::new(simple.clone()), })); } }); }); // Expr::Block f(Expr::Block(ExprBlock { // `{}` attrs: Vec::new(), label: None, block: Block { brace_token: token::Brace(span), stmts: Vec::new(), }, })); // Expr::Break f(Expr::Break(ExprBreak { // `break` attrs: Vec::new(), break_token: Token![break](span), label: None, expr: None, })); iter(depth, &mut |expr| { f(Expr::Break(ExprBreak { // `break $expr` attrs: Vec::new(), break_token: Token![break](span), label: None, expr: Some(Box::new(expr)), })); }); // Expr::Call iter(depth, &mut |expr| { f(Expr::Call(ExprCall { // `$expr()` attrs: Vec::new(), func: Box::new(expr), paren_token: token::Paren(span), args: Punctuated::new(), })); }); // Expr::Cast iter(depth, &mut |expr| { f(Expr::Cast(ExprCast { // `$expr as T` attrs: Vec::new(), expr: Box::new(expr), as_token: Token![as](span), ty: Box::new(Type::Path(TypePath { qself: None, path: Path::from(Ident::new("T", span)), })), })); }); // Expr::Closure iter(depth, &mut |expr| { f(Expr::Closure(ExprClosure { // `|| $expr` attrs: Vec::new(), lifetimes: None, constness: None, movability: None, asyncness: None, capture: None, or1_token: Token![|](span), inputs: Punctuated::new(), or2_token: Token![|](span), output: ReturnType::Default, body: Box::new(expr), })); }); // Expr::Field iter(depth, &mut |expr| { f(Expr::Field(ExprField { // `$expr.field` attrs: Vec::new(), base: Box::new(expr), dot_token: Token![.](span), member: Member::Named(Ident::new("field", span)), })); }); // Expr::If iter(depth, &mut |expr| { f(Expr::If(ExprIf { // `if $expr {}` attrs: Vec::new(), if_token: Token![if](span), cond: Box::new(expr), then_branch: Block { brace_token: token::Brace(span), stmts: Vec::new(), }, else_branch: None, })); }); // Expr::Let iter(depth, &mut |expr| { f(Expr::Let(ExprLet { attrs: Vec::new(), let_token: Token![let](span), pat: Box::new(Pat::Wild(PatWild { attrs: Vec::new(), underscore_token: Token![_](span), })), eq_token: Token![=](span), expr: Box::new(expr), })); }); // Expr::Range f(Expr::Range(ExprRange { // `..` attrs: Vec::new(), start: None, limits: RangeLimits::HalfOpen(Token![..](span)), end: None, })); iter(depth, &mut |expr| { f(Expr::Range(ExprRange { // `..$expr` attrs: Vec::new(), start: None, limits: RangeLimits::HalfOpen(Token![..](span)), end: Some(Box::new(expr.clone())), })); f(Expr::Range(ExprRange { // `$expr..` attrs: Vec::new(), start: Some(Box::new(expr)), limits: RangeLimits::HalfOpen(Token![..](span)), end: None, })); }); // Expr::Reference iter(depth, &mut |expr| { f(Expr::Reference(ExprReference { // `&$expr` attrs: Vec::new(), and_token: Token![&](span), mutability: None, expr: Box::new(expr), })); }); // Expr::Return f(Expr::Return(ExprReturn { // `return` attrs: Vec::new(), return_token: Token![return](span), expr: None, })); iter(depth, &mut |expr| { f(Expr::Return(ExprReturn { // `return $expr` attrs: Vec::new(), return_token: Token![return](span), expr: Some(Box::new(expr)), })); }); // Expr::Try iter(depth, &mut |expr| { f(Expr::Try(ExprTry { // `$expr?` attrs: Vec::new(), expr: Box::new(expr), question_token: Token![?](span), })); }); // Expr::Unary iter(depth, &mut |expr| { for op in [ UnOp::Deref(Token![*](span)), //UnOp::Not(Token![!](span)), //UnOp::Neg(Token![-](span)), ] { f(Expr::Unary(ExprUnary { // `*$expr` attrs: Vec::new(), op, expr: Box::new(expr.clone()), })); } }); if false { // Expr::Array f(Expr::Array(ExprArray { // `[]` attrs: Vec::new(), bracket_token: token::Bracket(span), elems: Punctuated::new(), })); // Expr::Async f(Expr::Async(ExprAsync { // `async {}` attrs: Vec::new(), async_token: Token![async](span), capture: None, block: Block { brace_token: token::Brace(span), stmts: Vec::new(), }, })); // Expr::Await iter(depth, &mut |expr| { f(Expr::Await(ExprAwait { // `$expr.await` attrs: Vec::new(), base: Box::new(expr), dot_token: Token![.](span), await_token: Token![await](span), })); }); // Expr::Block f(Expr::Block(ExprBlock { // `'a: {}` attrs: Vec::new(), label: Some(Label { name: Lifetime::new("'a", span), colon_token: Token![:](span), }), block: Block { brace_token: token::Brace(span), stmts: Vec::new(), }, })); iter(depth, &mut |expr| { f(Expr::Block(ExprBlock { // `{ $expr }` attrs: Vec::new(), label: None, block: Block { brace_token: token::Brace(span), stmts: Vec::from([Stmt::Expr(expr.clone(), None)]), }, })); f(Expr::Block(ExprBlock { // `{ $expr; }` attrs: Vec::new(), label: None, block: Block { brace_token: token::Brace(span), stmts: Vec::from([Stmt::Expr(expr, Some(Token![;](span)))]), }, })); }); // Expr::Break f(Expr::Break(ExprBreak { // `break 'a` attrs: Vec::new(), break_token: Token![break](span), label: Some(Lifetime::new("'a", span)), expr: None, })); iter(depth, &mut |expr| { f(Expr::Break(ExprBreak { // `break 'a $expr` attrs: Vec::new(), break_token: Token![break](span), label: Some(Lifetime::new("'a", span)), expr: Some(Box::new(expr)), })); }); // Expr::Closure f(Expr::Closure(ExprClosure { // `|| -> T {}` attrs: Vec::new(), lifetimes: None, constness: None, movability: None, asyncness: None, capture: None, or1_token: Token![|](span), inputs: Punctuated::new(), or2_token: Token![|](span), output: ReturnType::Type( Token![->](span), Box::new(Type::Path(TypePath { qself: None, path: Path::from(Ident::new("T", span)), })), ), body: Box::new(Expr::Block(ExprBlock { attrs: Vec::new(), label: None, block: Block { brace_token: token::Brace(span), stmts: Vec::new(), }, })), })); // Expr::Const f(Expr::Const(ExprConst { // `const {}` attrs: Vec::new(), const_token: Token![const](span), block: Block { brace_token: token::Brace(span), stmts: Vec::new(), }, })); // Expr::Continue f(Expr::Continue(ExprContinue { // `continue` attrs: Vec::new(), continue_token: Token![continue](span), label: None, })); f(Expr::Continue(ExprContinue { // `continue 'a` attrs: Vec::new(), continue_token: Token![continue](span), label: Some(Lifetime::new("'a", span)), })); // Expr::ForLoop iter(depth, &mut |expr| { f(Expr::ForLoop(ExprForLoop { // `for _ in $expr {}` attrs: Vec::new(), label: None, for_token: Token![for](span), pat: Box::new(Pat::Wild(PatWild { attrs: Vec::new(), underscore_token: Token![_](span), })), in_token: Token![in](span), expr: Box::new(expr.clone()), body: Block { brace_token: token::Brace(span), stmts: Vec::new(), }, })); f(Expr::ForLoop(ExprForLoop { // `'a: for _ in $expr {}` attrs: Vec::new(), label: Some(Label { name: Lifetime::new("'a", span), colon_token: Token![:](span), }), for_token: Token![for](span), pat: Box::new(Pat::Wild(PatWild { attrs: Vec::new(), underscore_token: Token![_](span), })), in_token: Token![in](span), expr: Box::new(expr), body: Block { brace_token: token::Brace(span), stmts: Vec::new(), }, })); }); // Expr::Index iter(depth, &mut |expr| { f(Expr::Index(ExprIndex { // `$expr[0]` attrs: Vec::new(), expr: Box::new(expr), bracket_token: token::Bracket(span), index: Box::new(Expr::Lit(ExprLit { attrs: Vec::new(), lit: Lit::Int(LitInt::new("0", span)), })), })); }); // Expr::Loop f(Expr::Loop(ExprLoop { // `loop {}` attrs: Vec::new(), label: None, loop_token: Token![loop](span), body: Block { brace_token: token::Brace(span), stmts: Vec::new(), }, })); f(Expr::Loop(ExprLoop { // `'a: loop {}` attrs: Vec::new(), label: Some(Label { name: Lifetime::new("'a", span), colon_token: Token![:](span), }), loop_token: Token![loop](span), body: Block { brace_token: token::Brace(span), stmts: Vec::new(), }, })); // Expr::Macro f(Expr::Macro(ExprMacro { // `m!()` attrs: Vec::new(), mac: Macro { path: Path::from(Ident::new("m", span)), bang_token: Token![!](span), delimiter: MacroDelimiter::Paren(token::Paren(span)), tokens: TokenStream::new(), }, })); f(Expr::Macro(ExprMacro { // `m! {}` attrs: Vec::new(), mac: Macro { path: Path::from(Ident::new("m", span)), bang_token: Token![!](span), delimiter: MacroDelimiter::Brace(token::Brace(span)), tokens: TokenStream::new(), }, })); // Expr::Match iter(depth, &mut |expr| { f(Expr::Match(ExprMatch { // `match $expr {}` attrs: Vec::new(), match_token: Token![match](span), expr: Box::new(expr.clone()), brace_token: token::Brace(span), arms: Vec::new(), })); f(Expr::Match(ExprMatch { // `match x { _ => $expr }` attrs: Vec::new(), match_token: Token![match](span), expr: Box::new(Expr::Path(ExprPath { attrs: Vec::new(), qself: None, path: Path::from(Ident::new("x", span)), })), brace_token: token::Brace(span), arms: Vec::from([Arm { attrs: Vec::new(), pat: Pat::Wild(PatWild { attrs: Vec::new(), underscore_token: Token![_](span), }), guard: None, fat_arrow_token: Token![=>](span), body: Box::new(expr.clone()), comma: None, }]), })); f(Expr::Match(ExprMatch { // `match x { _ if $expr => {} }` attrs: Vec::new(), match_token: Token![match](span), expr: Box::new(Expr::Path(ExprPath { attrs: Vec::new(), qself: None, path: Path::from(Ident::new("x", span)), })), brace_token: token::Brace(span), arms: Vec::from([Arm { attrs: Vec::new(), pat: Pat::Wild(PatWild { attrs: Vec::new(), underscore_token: Token![_](span), }), guard: Some((Token![if](span), Box::new(expr))), fat_arrow_token: Token![=>](span), body: Box::new(Expr::Block(ExprBlock { attrs: Vec::new(), label: None, block: Block { brace_token: token::Brace(span), stmts: Vec::new(), }, })), comma: None, }]), })); }); // Expr::MethodCall iter(depth, &mut |expr| { f(Expr::MethodCall(ExprMethodCall { // `$expr.method()` attrs: Vec::new(), receiver: Box::new(expr.clone()), dot_token: Token![.](span), method: Ident::new("method", span), turbofish: None, paren_token: token::Paren(span), args: Punctuated::new(), })); f(Expr::MethodCall(ExprMethodCall { // `$expr.method::()` attrs: Vec::new(), receiver: Box::new(expr), dot_token: Token![.](span), method: Ident::new("method", span), turbofish: Some(AngleBracketedGenericArguments { colon2_token: Some(Token![::](span)), lt_token: Token![<](span), args: Punctuated::from_iter([GenericArgument::Type(Type::Path( TypePath { qself: None, path: Path::from(Ident::new("T", span)), }, ))]), gt_token: Token![>](span), }), paren_token: token::Paren(span), args: Punctuated::new(), })); }); // Expr::RawAddr iter(depth, &mut |expr| { f(Expr::RawAddr(ExprRawAddr { // `&raw const $expr` attrs: Vec::new(), and_token: Token![&](span), raw: Token![raw](span), mutability: PointerMutability::Const(Token![const](span)), expr: Box::new(expr), })); }); // Expr::Struct f(Expr::Struct(ExprStruct { // `Struct {}` attrs: Vec::new(), qself: None, path: Path::from(Ident::new("Struct", span)), brace_token: token::Brace(span), fields: Punctuated::new(), dot2_token: None, rest: None, })); // Expr::TryBlock f(Expr::TryBlock(ExprTryBlock { // `try {}` attrs: Vec::new(), try_token: Token![try](span), block: Block { brace_token: token::Brace(span), stmts: Vec::new(), }, })); // Expr::Unsafe f(Expr::Unsafe(ExprUnsafe { // `unsafe {}` attrs: Vec::new(), unsafe_token: Token![unsafe](span), block: Block { brace_token: token::Brace(span), stmts: Vec::new(), }, })); // Expr::While iter(depth, &mut |expr| { f(Expr::While(ExprWhile { // `while $expr {}` attrs: Vec::new(), label: None, while_token: Token![while](span), cond: Box::new(expr.clone()), body: Block { brace_token: token::Brace(span), stmts: Vec::new(), }, })); f(Expr::While(ExprWhile { // `'a: while $expr {}` attrs: Vec::new(), label: Some(Label { name: Lifetime::new("'a", span), colon_token: Token![:](span), }), while_token: Token![while](span), cond: Box::new(expr), body: Block { brace_token: token::Brace(span), stmts: Vec::new(), }, })); }); // Expr::Yield f(Expr::Yield(ExprYield { // `yield` attrs: Vec::new(), yield_token: Token![yield](span), expr: None, })); iter(depth, &mut |expr| { f(Expr::Yield(ExprYield { // `yield $expr` attrs: Vec::new(), yield_token: Token![yield](span), expr: Some(Box::new(expr)), })); }); } } let mut failures = 0; macro_rules! fail { ($($message:tt)*) => {{ eprintln!($($message)*); failures += 1; return; }}; } let mut assert = |mut original: Expr| { let span = Span::call_site(); // `const _: () = $expr;` let pretty = prettyplease::unparse(&File { shebang: None, attrs: Vec::new(), items: Vec::from([Item::Const(ItemConst { attrs: Vec::new(), vis: Visibility::Inherited, const_token: Token![const](span), ident: Ident::from(Token![_](span)), generics: Generics::default(), colon_token: Token![:](span), ty: Box::new(Type::Infer(TypeInfer { underscore_token: Token![_](span), })), eq_token: Token![=](span), expr: Box::new(original.clone()), semi_token: Token![;](span), })]), }); let mut parsed = match syn::parse_file(&pretty) { Ok(parsed) => parsed, _ => fail!("failed to parse: {pretty}{original:#?}"), }; let item = match parsed.items.as_mut_slice() { [Item::Const(item)] => item, _ => unreachable!(), }; let mut parsed = mem::replace(&mut *item.expr, Expr::PLACEHOLDER); AsIfPrinted.visit_expr_mut(&mut original); FlattenParens.visit_expr_mut(&mut parsed); if original != parsed { fail!( "before: {}\n{:#?}\nafter: {}\n{:#?}", original.to_token_stream(), original, parsed.to_token_stream(), parsed, ); } if pretty.contains("(||") { // https://github.com/dtolnay/prettyplease/issues/99 return; } let no_paren = pretty.replace(['(', ')'], ""); if pretty != no_paren { if let Ok(mut parsed2) = syn::parse_file(&no_paren) { let item = match parsed2.items.as_mut_slice() { [Item::Const(item)] => item, _ => unreachable!(), }; if original == *item.expr { fail!("redundant parens: {}", pretty); } } } }; iter(if cfg!(debug_assertions) { 3 } else { 4 }, &mut assert); if failures > 0 { eprintln!("FAILURES: {failures}"); ExitCode::FAILURE } else { ExitCode::SUCCESS } }